﻿// --------------------------------------------------------------
// Use the following options to modify the plugin's functionality
// --------------------------------------------------------------
// inputElement: $('#SearchBox')
// searchDelay: 500
// url: '/randomhouse.com.au/search/finder.aspx'
// saveBandwidth: false
// --------------------------------------------------------------
// The saveBandwidth option will reduce the number of querys
// made to the database at the expense of speed
// --------------------------------------------------------------

(function ($) {
	$.fn.predictiveSearch = function (userOptions) {
		// Define variables
		var defaults, options, resultsElement, requestIsRunning, priorityRequestIsRunning, inputHasFocus, previousQuery, request, searchTimeout, saveBandwidthTimeout;

		// Set default parameters
		defaults = {
			inputElement: $('#SearchBox'),
			searchDelay: 500,
			url: '/search/finder.aspx',
			saveBandwidth: false
		};

		// Store element selector and merge options
		options = $.extend(defaults, userOptions);
		resultsElement = $(this);

		// Set miscellaneous variables
		requestIsRunning = priorityRequestIsRunning = inputHasFocus = false;
		previousQuery = request = searchTimeout = saveBandwidthTimeout = null;

		// Loop for each instance
		return this.each(function () {
			// Return the user input stripped of leading/trailing whitespace and special characters
			function inputValue() {
				return options.inputElement.val().replace(/-/g, ' ').replace(/é/ig, 'e').replace(/&/g, 'and').replace(/^(\s*)|([^a-z0-9 ]+)|(\s*$)/ig, '');
			}

			//Return whether the user input is valid or not
			function inputIsValid() {
				return inputValue().match(/[a-z0-9]{3,}/i);
			}

			// Check results exist
			function resultsExist() {
				return (resultsElement.find('li').length > 0 && inputValue().length > 2) ? true : false;
			}

			// Check arrow key
			function arrowEvent(event) {
				switch (event.keyCode) {
					case 38: return 'up';
					case 40: return 'down';
					default: return false;
				}
			}

			// Start a new query or hide the results
			function startRequest() {
				if (inputIsValid() && ((inputValue() !== previousQuery && !requestIsRunning) || priorityRequestIsRunning)) {
					getResults();
				} else if (!inputIsValid()) {
					resultsElement.hide().find('ul').empty();
					previousQuery = null;
				} else if (resultsElement.css('display') === 'none' && resultsExist()) {
					showResults();
				}
			}

			// Show results
			function showResults() {
				if (inputHasFocus) {
					resultsElement.show();
					resultsElement.find('li').each(function () {
						$(this).unbind();
						$(this).bind('mouseenter', function () {
							resultsElement.find('li').each(function () { $(this).removeClass('Active'); });
							$(this).addClass('Active');
							$(document).bind('keypress', function (event) {
								if (event.keyCode === 13) {
									event.preventDefault();
									$('.Active').trigger('mousedown');
								}
							});
						});
						$(this).bind('mouseleave', function () {
							$(this).removeClass('Active');
							$(document).unbind('keypress');
						});
						$(this).bind('mousedown', function () {
							options.inputElement.unbind('blur');
							window.location = $(this).find('a:first').attr('href');
						});
					});
				}
			}

			// Get results and process the data
			function getResults() {
				requestIsRunning = true;
				previousQuery = inputValue();
				request = $.get(options.url, {
					q: inputValue()
				}, function (data) {
					if (data.indexOf('<li>') !== -1) {
						resultsElement.find('ul').empty().html(data);
						showResults();
					}
					if (options.saveBandwidth) {
						requestIsRunning = priorityRequestIsRunning = false;
						clearTimeout(saveBandwidthTimeout);
						saveBandwidthTimeout = setTimeout(startRequest, options.searchDelay);
					} else {
						searchTimeout = setTimeout(function () {
							requestIsRunning = false;
							startRequest();
						}, options.searchDelay);
					}
				});
			}

			// Bind key events
			options.inputElement.bind('keyup', function (event) {
				if (arrowEvent(event) === 'up' && resultsExist()) {
					if (resultsElement.find('.Active').length > 0 && resultsElement.find('.Active').prev().length > 0) {
						resultsElement.find('.Active').trigger('mouseleave').prev().trigger('mouseenter');
					} else if (resultsElement.find('.Active').length > 0) {
						resultsElement.find('.Active').trigger('mouseleave');
						options.inputElement.trigger('mouseenter');
					}
				} else if (arrowEvent(event) === 'down' && resultsExist()) {
					if (resultsElement.find('.Active').length > 0 && resultsElement.find('.Active').next().length > 0) {
						resultsElement.find('.Active').trigger('mouseleave').next().trigger('mouseenter');
					} else if (resultsElement.find('.Active').length < 1) {
						resultsElement.find('li:first').trigger('mouseenter');
					}
				} else if (options.saveBandwidth && !priorityRequestIsRunning) {
					clearTimeout(saveBandwidthTimeout);
					requestIsRunning = priorityRequestIsRunning = false;
					if (request !== null) {
						request.abort();
					}
					if (event.keyCode === 32 && previousQuery !== inputValue() && inputIsValid()) {
						priorityRequestIsRunning = true;
						startRequest();
					} else {
						saveBandwidthTimeout = setTimeout(startRequest, options.searchDelay);
					}
				} else if (!options.saveBandwidth) {
					startRequest();
				}
			});

			// Bind focus and blur events
			options.inputElement.bind('focus', function () {
				inputHasFocus = true;
				options.inputElement.bind('blur', function (event) {
					inputHasFocus = false;
					resultsElement.hide();
					options.inputElement.unbind('blur');
				});
				startRequest();
			});

			// Stop form submit from blurring input
			$('.SearchButton').mousedown(function () {
				options.inputElement.unbind('blur');
			});

			// Stop finder click from blurring input
			$('.FinderView').click(function () {
				return false;
			});

			// Bind mouse click
			$('.FinderView').mousedown(function () {
				options.inputElement.unbind('blur');
				$('#PredictiveSearch').submit();
				return false;
			});

			// Remove bindings on unload
			$(window).unload(function () {
				resultsElement.remove();
				options.inputElement.unbind();
				defaults = options = resultsElement = request = null;
			});
		});
	};
} (jQuery));
