/**
 * Search Spotlight jQuery Plug-in - Compatible with jQuery-1.2.6.js
 *
 * @author Karl Stanton (Fi)
 * @version 1.0
 * @version 2.0 - jsLint Compatible
 * @version 3.0 - Changed from JSON to HTML returns
 * 
 * @version 3.1 - David Lindkvist (Fi) - Modified for Atari.com 
 * 	- Added highlighting of search term in result using <em> tags
 *  - Removed absolute positoning
 *  - Removed all timeout code since result pane always should be visible when there are results.
 *
 * Usage: 
 *   - Create an Input form element, and set an ID.
 *   - Make sure the ID of the input matches the CSS styling in spotlight.css or it wont function as expected
 *   - In a 'document ready' function, execute the plug in as follows:
 *   
	$(document).ready(function () {
		$("#spotlight").spotlight({
			minlength: [minimum number of characters required before a search is invoked],
			timeout: [time in milliseconds before spotlight revokes]
		});
	});
 * 
 *
 * Order of programming execution:
 *   - Bind keyup event to nominated search input box
 *	 - Build 1 shared instance of the results pane 
 *	 - Bind event listeners
 *     - When events are triggered, post search request via AJAX, insert the returned HTML
 *   - Put compiled DHTML into results pane
 *   - Check upper and lower bounds before displaying
**/
// Drop Down
(function ($) {
	$.fn.spotlight = function (options) {
		// Define our element
		if (!this[0]) {
			return;
		}
		var el = this[0];
		var id = el.id;
		
		// Set our default options
		options = $.extend({
			css: "spotlight",						// css namespace
			searchpath: "spotlight.php",			// file or location of search target
			resultspath: "results.php",				// file or location of the search results page
			minlength: 3,							// minimum number of characters required to invoke the search
			timeout: 1000, 							// timeout in milliseconds before revoked
			windowBuffer: 20,						// buffer for window boundry as each browser includes different parts of its window / viewspace into it's height equation. Can be adjusted to suit needs.
			zDepth: 9999, 							// depth of dropdown overlay
			style: null,							// header or footer? Footer drops up
			delay: 0,								// delay of keystroke before search to stop flooding
			use_cache: true,							// results of each search stored as an array, else use a cache buster
			localizedHelperText: null
		}, options || {});

		// Declare our objects
		var search_term, results, results_pane, results_open, secs, timerID, keyTimerId, loadingAnimation;
		// Dec Functions that rely on (jsLint apparent non-existing) functions
		var have_results = false;
		var keySecs = options.delay;
		var search_request, black_keyphrase, current_keyphrase;
		var cached_results = {};
		
		/**
		 * Initialize
		 */
		function build() {
			
			// Default the spotlight to show our helper text
			if(options.localizedHelperText) {
				$(el).val(options.localizedHelperText);
			}
			
			$(el).addClass("spotlight").attr("autocomplete", "off");

			// Now, we must build the empty results pane, but only if it doesn't exist
			if (!$("#spotlight-results").length) {
				// First the wrapper (which can be used for padding, etc)
				$(document.body).append('<div id="spotlight-results" class="' + options.css + '-results"></div>');

				// Now the content holder
				$('#spotlight-results').append('<div id="spotlight-result-pane" class="' + options.css + '-result-pane"></div><div class="' + options.css + '-view-all bottomCap"></div>');			
			}

			results = $('#spotlight-results');
			results_pane = $('#spotlight-result-pane');
			
			// Create our loading div
			if ($("#spotlightLoading").length <=  0) {
				loadingAnimation = $("<div id=\"spotlightLoading\">");
				$("body").append(loadingAnimation);
			}
		}
		
		/**
		 * We must check the bounds of the dropdown before we open.
		 * If we open below the window, we must open 'up'
		 */
		/*function checkDropDirection() {
			
			// The top of our search box element
			var el_top = $(el).offset().top;

			results.find(".videoOverlay").hide();

			// Height of the results pane + the height of the search box element
			var height = results.outerHeight(true);
			
			var scrollOffset = Number(el_top - AtariGlobal.getScrollXY()[1]);

			var elementTop = scrollOffset + $(el).outerHeight(true);

			// -2 for extra FiQ
			var DOMtop = el_top + $(el).outerHeight(true) - 2;

			// The bottom of the results pane if dropped down / -53px for the GUS
			var drop_down = ($(window).height() - 53) - (elementTop + height);
			
			var changedCss = false;
			
			// Find out if we're the footer spotlight (shorter and needs a new style for drop down and up)
			if (options.style === "footer") {
				// The top of the results pane if dropped up
				var drop_up = elementTop - height;
				
				// Do we exceed the bottom bound?
				if (drop_down < 0) {
					// Before we decide to pop it up top, we must find out if there is even enough space
					// to do so, if there isn't then we default back to dropping it down.
					// Now let's see if the drop_up value is greater than 0, ie: will it also go outside
					// our top bounds.... we only want drop up if we have more "room" aka a positive margin
					if (drop_up >=  0) {
						DOMtop = DOMtop - height - $(el).outerHeight(true) + 4;
						if (!results.hasClass("spotlight-results_footer")) {
							results.removeClass("spotlight-results, spotlight-results_footer_bot").addClass("spotlight-results_footer");
							changedCss = true;
						}
					} else {
						if (!results.hasClass("spotlight-results_footer_bot")) {
							results.addClass("spotlight-results_footer_bot").removeClass("spotlight-results, spotlight-results_footer");
							changedCss = true;
						}
					}
				}				
			} else {
				if (!results.hasClass("spotlight-results")) {
					results.addClass("spotlight-results").removeClass("spotlight-results_footer, spotlight-results_footer_bot");
					changedCss = true;
				}
			}
			

			
			// Now for the left hand side position
			var left = $(el).position().left - 1;
			// Fix Webkit bug and it's calculation problems (http://fixunix.com/1626217-post5.html)
			if (navigator.userAgent.indexOf('Firefox/3') !==  -1 || navigator.userAgent.indexOf('WebKit') !==  -1) {
				if (document.documentElement.clientWidth % 2 === 1) {
					left++;
				}
			}
			
			results.find(".videoOverlay").show();
			
			if (changedCss) {
				return checkDropDirection();
			} else {
				return [DOMtop, left];
			}
		}*/
		
		/**
		 *  Resets the timer
		 */
		/*function resetTimer() {
			secs = options.timeout / 1000;
		}*/

		/**
		 *  Resets and clears the timer
		 */
		/*function stopTimer() {
			resetTimer();
			clearTimeout(timerID);
		}*/

		/**
		 *  Close the Spotlight Results
		 */
		function closeResults() {

			// Close all results only if we don't have focus
			if (!$(el).hasClass("focus")) {
				$('#spotlight-results').parent().removeClass('hasResults');
				results.hide();
				results_open = false;
				stopTimer();
			
			}
			
		}

		/**
		 *  Runs the the search results pane timeout
		 */
		/*function beginTimer() {
			if (secs === 0) {
				closeResults(); // run the close function
				// callomniture
				//var intSearchTerm = $('.searchField').val().toLowerCase();
				var intSearchTerm = config.intSearchterm;
				//setOmniValues(this, 'o', 'intSearch', 'intSearchTerms=' + intSearchTerm + ',predSearchSel=', 'intSearchTerms=' + intSearchTerm, 'searches', 0, ';noProdSearch');
			} else {
				secs--;
				timerID = setTimeout(function () {
					beginTimer();
				}, 1000);
			}
		}*/

		/**
		 *  Opens the Spotlight
		 */
		function openResults() {

			//var position = checkDropDirection();
				
			/*	
			results.css({
				'top': position[0],
				'left': position[1],
				"z-index": options.zDepth
			}).show();
			*/
			results.show();
			
			results_open = true;
			//stopTimer();
			//beginTimer();
		}
		
		/**
		 *  Prevents flooding
		 */
		function resetKeyUpTimer() {
			keySecs = options.delay;
		}
		
		/**
		 * Stops the Timer
		 */
		function stopKeyUpTimer() {
			resetKeyUpTimer();
			clearTimeout(keyTimerId);
		}
		
		/**
		 * Triggers the href
		 * @param {string} href - location requested
		 */
		function fireLink(href) {
			closeResults();
			stopKeyUpTimer();
			if (href !== null && typeof href !== 'undefined') {
				var friendlyId = href.substr(href.lastIndexOf('/') + 1).toLowerCase();
				
				// callomniture
				//var intSearchTerm = $('.searchField').val().toLowerCase();
				var intSearchTerm = config.intSearchterm;
				//setOmniValues(this, 'o', 'intSearch', 'intSearchTerms=' + intSearchTerm + ',predSearchSel=' + friendlyId, 'intSearchTerms=' + intSearchTerm, 'searches', 0, ';noProdSearch');
				
				window.location = href;
			}
		}
		
		/**
		 * Replaces above bindResults method. We have changed from returning JSON to returning pure HTML
		 * @param {Object} data - HTML
		 */
		function bindHTMLResults(data) {
			
			// Append the DOM and read in result_count
			results_pane.hide().html(data);
			
			// bottomCap's visibility depends on whether we have results or not
			//var bottomCap = results.children(".bottomCap");
			//bottomCap.hide();
			
			// Did we get any results after all that?
			if (result_count > 0) {
			
				//add has results styleclass to parent
				$('#spotlight-results').parent().addClass('hasResults');
			
				black_keyphrase = "";
				
				// Bind rollovers
				
				$("li", "#spotlight-result-pane").hover(function () {
					$("li.selected", "#spotlight-result-pane").removeClass("selected");
					$(this).addClass("selected");
				}, function () {
					$(this).removeClass("selected");
				}).click(function () {
					var href = $(this).children("a").attr("href");
					if (typeof href === 'undefined') {
						href = $(this).children('h3').children("a").attr("href");
					} 
					fireLink(href);
					if (href !== null && typeof href !== 'undefined') {
						return false;
					}				
				});
					
					
				//highlight results
				var searchString = config.intSearchterm;
				var reg = new RegExp(searchString.toLowerCase());
				$("li a:not(li.viewall a)", "#spotlight-result-pane").each(function () {
					
					var text = $(this).text();
					var index = text.toLowerCase().search(reg);
			
					if(index > -1)
					{
						var emText = text.substring(0, index) + '<em>' + text.substr(index, searchString.length) + '</em>' + text.substring(index + searchString.length)
						$(this).html(emText);
					}
				});

				//add style class to last result
				$("li:first", "#spotlight-result-pane").addClass('first');
				$("li:last", "#spotlight-result-pane").addClass('last');
				
				results_pane.show();
			
				// Yes.... and the results pane has been compiled, but before we continue we must append the "view all results" div
				//bottomCap.html('<ul><li><a href="' + options.resultspath + search_term + '" class=\"more\">'+'View All Results'.localize()+'</a></li></ul>').show();
				
				// Now that we've built the result pane, display it!
				have_results = true;
				openResults();
				
			} else {
				// No results, do nothing. But, since we have no results, we must not allow any further searches that include the defunct keyword
				black_keyphrase = current_keyphrase;
				
				//remove results styleclass to parent
				$('#spotlight-results').parent().removeClass('hasResults');
			}
		
		}

		/**
		 * Try to abort the previous request
		 */
		function cancelAJAX() {
			try {
				search_request.abort();
			} catch (e) {}			
		}

		/**
		 * Starts the timer and also prevents flooding
		 */
		function beginKeyUpTimer() {
			if (keySecs <= 0) {
				
				search();
				
				stopKeyUpTimer();
			} else {
				keySecs--;
				keyTimerId = setTimeout(function () {
					beginKeyUpTimer();
				}, 1000);
			}
		}
		// End flood prevention
		
		/**
		 * Caches the entire HTML return in an associative array
		 * @param {String} data - HTML returned from AJAX
		 */
		function cacheResults(data) {
			
			cached_results[current_keyphrase] = data;
			
		}
			
		/**
		 * Executes the search. Binds results into an array to be used as cache for quicker results when editing
		 * already submitted keywords
		 */
		function search() {
			
			// Strip any non-AlphaNumeric characters
			search_term = search_term.replace(/[^A-Za-z0-9 ]+/g, '');

			// Only run if the input value has changed since the last search
			if (current_keyphrase !== search_term) {

				current_keyphrase = search_term;
				
				// Test to see if our current keyphrase matches the black_keyphrase via regular expression
				var re = new RegExp(black_keyphrase);
				if (current_keyphrase.match(re) && black_keyphrase !== "" && black_keyphrase !== undefined) {
					
					// We are still using the defunct keyword, do nothing.

				} else {

					// Cancel the previous ajax request
					cancelAJAX();
						
					// Search the cache to see if this keyword exists
					if (cached_results[current_keyphrase] !== undefined) {

						$("#spotlightLoading").hide();
						bindHTMLResults(cached_results[current_keyphrase]);

					// It doesn't exist, so fire off the AJAX request
					} else {
						
						// If we aren't using cache, install a cache buster
						var cache_buster = '';
						if (!options.use_cache) {
							cache_buster = cachebuster();
						}
	
						// Fire!
						search_request = $.ajax({
							type: "GET",
							url: options.searchpath,
							data: {
								keyphrase: current_keyphrase,
								c: cache_buster
							},
							beforeSend: function () {
								$("#spotlightLoading").show();
							},
							success: function (data) {
								$("#spotlightLoading").hide();
								
								// Cache
								if (options.use_cache) {
									cacheResults(data);
								}
								
								// Bind the HTML
								bindHTMLResults(data);
							}
							
						});
					
					}

				}	
			} else {
				if (have_results) {
					openResults();
				}
			}
		}

		/**
		 *  Tails a random number at the end of our JSON search string to stop cache
		 */
		function cachebuster() {
			return parseInt(Math.random() * 99999999, 10); // cache buster
		}
		
		/**
		 * This function searches the parents of the element 'e' to find out if it's the child of our main results pane,
		 * and returns a true or false value accordingly
		 * @param {Object} e - Event
		 */
		function find_parent(e) {
			
			// Search elements to see if this click was triggered by a child element of our results pane
			var par = false;
			
			// Turn the parent elements into an array
			var arr = $.makeArray($(e.target).parents());
			
			// Loop through them
			for (var i = 0; i <= arr.length;i++) {
				// Look for a match
				if ($(arr[i])[0].id === results[0].id) {
					par = true;
					break;
				}
			}
			
			return par;
		}
		
		/**
		 * The up/down keys can navigate the spotlight. This helps display and select the li's
		 * @param {Boolean} direction - up or down
		 */
		function highlightResult(direction) {
			//resetTimer();
			

			// See if anything is selected
			var current_el = $("#spotlight-results li.selected");
			
			var next_el, li;
			
			var initial_li = (direction) ? "first" : "last";

			// If not, select the first / last (depending on the direction) child element
			if (current_el.length === 0) {
				current_el = $("#spotlight-results li:" + initial_li);
				current_el.addClass("selected");
				return;
			}
			
			// Get index of this li against the spotlight container's children(li)
			var index = $("#spotlight-results li").index(current_el.get(0));
			var length = $("#spotlight-results li").length;

			
			// UP
			if (!direction) {

				li = (index - 1 >= 0) ? (index - 1) : length - 1;
				
			// Down
			} else {

				li = (index + 1 < length) ? (index + 1) : 0;

			}


			next_el = $("#spotlight-results li:eq(" + li + ")");

			$(current_el).removeClass("selected");
			$(next_el).addClass("selected");
			
		}
		
		/**
		 * Resets the serach input back to it's default state
		 */
		function resetSearchInput() {

			// If we're empty, reset it
			$(el).removeClass("focus, focus-data");
			$("#spotlightLoading").hide();

			had_focus = false;

			// Also remove the results
			have_results = false;
		}
		
		/*** Event Handlers ***/
		function setEvents() {

			// Let's clear that little helper text, and we only want to do this once...
			var had_focus = false;
			$(el).focus(function () {
				if (!had_focus) {
					$(this).val('');
					had_focus = true;
				}
				// Let's highlight the inbox, too.
				$(this).addClass("focus");
				
				// EA.com
				//$("#spotlightLoading").css("background-image", "url(/portal/css/assets/sprites/search_animation.gif)");
			});
			
			// If we lose focus and there's no data, then replace the helper text back to it's default state
			$(el).blur(function () {
				// Check for value
				if ($(this).val() === "") {

					resetSearchInput();
					
				} else {
					$(this).addClass("focus-data");		// <<---- IE 6 is having a problem with adding this...
				}
				$(this).removeClass("focus");			// <<---- ... and then removing this...
				
				// EA.com
				//$("#spotlightLoading").css("background-image", "url(/portal/css/assets/sprites/search_animation_white.gif)");
			});
			
			//On key up, check to see if we have a minimum 3 characters before posting a request
			$(el).keyup(function (e) {
				search_term = $(this).val();
				
				// Are we a valid input character (A-Z, a-z, 0-9, !-), del, ret)
				if (((e.which >= 32 && e.which <= 127) || e.which === 8)) {
					
					// Do we have a minimum of 3 characters to search with?
					if (search_term.length >= options.minlength) {
					
						//$("#spotlightLoading").css({
						//	"top": $(this).position().top + 5,
						//	"left": $(this).position().left + 6
						//});
						// Prevent flooding on the search
						stopKeyUpTimer();
						beginKeyUpTimer();
						
					// No, so cancel any pending AJAX requests
					} else {
						
						cancelAJAX();
						
						resetSearchInput();
						
						closeResults();
					}
					
				// If the search term is less than the minlength, or the user hit the ESC key, close it
				} else if (e.which === 27) {
					closeResults();
				}
				
				
			}).keydown(function (e) {
				
				// Up / Left Arrow
				if (e.which === 38 || e.which === 37) {
					highlightResult(0);
					return false;
				
				// Down /Right Arrow
				} else if (e.which === 40 || e.which === 39) {
					highlightResult(1);
					return false;
				
				// Enter Key
				} else if (e.which === 13) {
					var href = $("#spotlight-result-pane li.selected").children("a").attr("href");
					
					if (typeof href === 'undefined') {
						href = $("#spotlight-result-pane li.selected").children('h3').children("a").attr("href");
					}
					fireLink(href);
					
					if (href !== null && typeof href !== 'undefined') {
						return false;
					}
				}
			});

			// If we have already retrieved results, then lets show the results again to save the user from typing to reinvoke the results					
			$(el).click(function () {

				$("#spotlight-result-pane li.selected").removeClass("selected");

				closeResults();

				if (have_results) {
					openResults();
				}
			});
			
			// If we click on the page, we need to know if we've clicked outside of the results pane
			/*
			$(document).click(function (e) {
				// So, if the clicked target isn't the active dropdown, and if the dropdown is open...
				if ($(e.target).children().parents().length > 0 && results_open && !find_parent(e)) {
					closeResults();
				}
			});
			*/
			
			// Results Pane. If we roll over, stop the timer...
			/*
			results.mouseover(function (e) {
				if (find_parent(e)) {
					stopTimer();
				}
				
			// ... if we roll out, we have to check against our parents, then if we're good, start the timer again
			}).mouseout(function (e) {
				if (find_parent(e)) {
					resetTimer();
					beginTimer();
				}
			});
			*/
			
			// If the window resizes, close the spotlight
			/*
			$(window).resize(function () {
				closeResults();
			});
			*/
		}
		
		build();
		
		// Now go on to bind our events
		setEvents();
	};
}(jQuery));