﻿/*
    mimass v1.0

    jQuery plugin that adds a suggest feature to textboxes.

    Copyright (C) 2009  Jason Kostempski

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/


//SETTINGS: (See DEFAULTS section for default values)
//	url:
//		URL the GET request will be sent to, must return JSON string array e.g. ['Val 1','Val 2','Val n'].
//	requestDataCallback:
//		Function to call that returns the data to be sent with the GET request.
//		When called the jQuery object of the active textbox is passed.

(function($) {
	$.fn.mimass = function(settings) {
		var options = $.extend({}, $.fn.mimass.defaults, settings);
		var $div = getResultsDiv();
		var $activeInput = $(this);
		var currentRequest = null;
		var resultsData = [];
		var selectedIndex = -1;
		var IE6 = $.browser.msie && $.browser.version.substr(0,1) == 6 && !window.XMLHttpRequest;

		return this.each(function() {
			$this = $(this);
			$this.attr("autocomplete", "off");
			$this.focus(focusWhisperer);
			$this.keydown(keyWhispererDown);
			$this.keyup(keyWhispererUp);
			$this.blur(blurWhisperer);
		});

		//////////EVENT HELPERS//////////

		function focusWhisperer(e) {
			$activeInput = $(this);
		};

		function keyWhispererDown(e) {
			switch (e.keyCode) {
				case 13: // Enter
				    if(selectedIndex < 0){
				        options.submitButton.click();
				    }else{
				        acceptSelection();
				    }
					e.preventDefault();
					break;
				case 37: // Left
				case 39: // Right
				case 9: // Tab
					acceptSelection();
					break;
				case 27: // Esc
				    hideDiv();
				    break;
				default:
					break;
			}
		};

		function keyWhispererUp(e) {
			switch (e.keyCode) {
				//No need to search search when the following keys are released.
				case 16: // Shift
				case 17: // Ctrl
				case 18: // Alt
				case 13: // Enter
				case 37: // Left
				case 39: // Right
				case 9: // Tab
				case 27: //Esc
					break;
				case 38: // Up
					setSelectedIndex(selectedIndex - 1);
					break;
				case 40: // Down
					setSelectedIndex(selectedIndex + 1);
					break;
				default:
					sendRequest();
					break;
			}
		};

		function blurWhisperer(e) {
			if (selectedIndex == -1) {
				hideDiv();
				abortCurrentRequest();
			}
		};

		function mouseWhispererOver(e) {
			var index = $div.children().index(this);
			setSelectedIndex(index);
		};


		//////////AJAX REQUEST HELPERS//////////

		function sendRequest() {
			abortCurrentRequest();

			if ($activeInput.val().length > 0) {
				currentRequest = $.getJSON(options.url, options.requestDataCallback($activeInput), requestComplete);
			}
			else {
				hideDiv();
			}
		};

		function abortCurrentRequest() {
			if (currentRequest !== null && currentRequest.readyState != 4) {
				currentRequest.abort();
			}
		};

		function requestComplete(json) {
			selectedIndex = -1;
			resultsData = json[1];
			$div.empty();

			$.each(resultsData, function(index) {
				$div.append('<div style="cursor: default;">' + resultsData[index] + '</div>');
				$itemDiv = $div.children(":last");
				$itemDiv.mouseover(mouseWhispererOver);
				$itemDiv.click(function(e) { acceptSelection(); e.stopPropagation(); $activeInput.focus();});
				$div.mouseleave(clearSelectedItem);
			});
			
			$itemDiv = $div.children(":last");
			if($itemDiv){
			    $itemDiv.addClass("ui-last");
			}

			if (resultsData.length > 0) {
				showDiv();
			}
			else {
				hideDiv();
			}
		};

		//////////ITEM SELECT HELPERS//////////

		function setSelectedIndex(index) {
			if ($div.css("display") == 'none') {
				selectedIndex = -1;
				return;
			}

			var itemDivs = $div.children();
			itemDivs.removeClass("ui-state-hover");

			selectedIndex = index;

			if (selectedIndex >= resultsData.length) {
				selectedIndex = 0;
			}
			else if (selectedIndex < 0) {
				selectedIndex = resultsData.length - 1;
			}

			$(itemDivs[selectedIndex]).addClass("ui-state-hover");
		};

		function clearSelectedItem() {
			selectedIndex = -1;
			$div.children().removeClass("ui-state-hover");
		};

		function acceptSelection() {
			if (selectedIndex > -1) {
				$activeInput.val(resultsData[selectedIndex]);
				hideDiv();
			}

			selectedIndex = -1;
		};

		//////////RESULTS DIV HELPERS//////////

		function getResultsDiv() {
			if ($("div.mimass").length == 0) {
				$("body").append(
					"<div style='display:none; position: absolute; width: auto; padding: 0.3em;' class='mimass ui-widget ui-widget-content'></div>"
				);
			}

			return $("div.mimass");
		};

		function showDiv() {
			if ($div.css("display") == 'none') {
				var pos = $activeInput.position();
				var hei = $activeInput.outerHeight();
				$div.css("top", pos.top + hei + 2);
				$div.css("left", pos.left);
				$div.show('blind', {}, 100);
				$div.css("width", "300px");
				if (IE6) {
                    $('embed, object, select').css('visibility', 'hidden');
	            }
			}
		};

		function hideDiv() {
			$div.hide();
			if (IE6) {
                $('embed, object, select').css('visibility', 'visible');
	        }
		};
	};

	//////////DEFAULTS//////////
	$.fn.mimass.defaults = {
		url: '.',
		requestDataCallback: function(input) {
			return { text: input.val() };
		},
		submitButton: false		
	};
})(jQuery);

