/*
Infomation
==========================================================================================
jQuery Plugin
	Name       : jquery.ajaxSuggest
	Version    : 1.1
	Update     : 2010-07-20
	Author     : sutara_lumpur
	Author-URI : http://d.hatena.ne.jp/sutara_lumpur/20100718/1279420832
	License    : MIT License (http://www.opensource.org/licenses/mit-license.php)
	Based-on   : Uses code and techniques from following libraries...
		* jquery.suggest 1.1
			Author     : Peter Vulgaris
			Author-URI : http://www.vulgarisoip.com/
==========================================================================================

Contents
==========================================================================================
	01. ??·?????
	02. ????????
	03. Suggest????? - ???
	04. Suggest????? - Ajax??
	05. Suggest????? - ???????
	06. ??????
==========================================================================================
*/
(function($) {
	$.ajaxSuggest = function(input_obj, source, options) {
		//Ajax???????????????
		$.ajaxSetup({cache: false});

		//================================================================================
		// 01. ??·?????
		//--------------------------------------------------------------------------------
		//**********************************************
		//??????
		//**********************************************
		var show_hide        = false; //??????????????????????
		var timer_show_hide  = false; //??????????????????????????
		var timer_val_change = false; //??????(????????????????)
		var reserve_click    = false; //?????????????????????mousedown???
		var $xhr             = false; //XMLHttp?????????
		var prev_value       = '';    //Suggest??????
		var user_value       = '';    //??????????????
		var is_select        = false; //??????????????$(input_obj)?????????
		var key_select       = false; //????????????

		//**********************************************
		//?????
		//**********************************************
		$(input_obj).attr('autocomplete','off');
			
		var $results = $('<ul></ul>')
			.addClass(options.results_class)
			.appendTo('body');
		
		//================================================================================
		// 02. ????????
		//--------------------------------------------------------------------------------
		//**********************************************
		//?????????
		//**********************************************
		//???(????????)
		if ($.browser.mozilla || $.browser.opera) {
			$(input_obj).keypress(processKey);
		}else{
			$(input_obj).keydown(processKey);
		}
		$(input_obj).focus(function() {
			show_hide = true;
			checkValChange();
		});
		$(input_obj).blur(function(ev) {
			//?????????
			clearTimeout(timer_val_change);

			//???????
			show_hide = false;

			//????????????
			checkShowHide();
		});
		$(input_obj).mousedown(function(ev) {
			reserve_click = true;

			//???????????
			clearTimeout(timer_show_hide);

			ev.stopPropagation();
		});
		$(input_obj).mouseup(function(ev) {
			$(input_obj).focus();
			reserve_click = false;
			ev.stopPropagation();
		});

		//**********************************************
		//body??
		//**********************************************
		$('body').mouseup(function() {
			//???????????
			clearTimeout(timer_show_hide);

			//???????
			show_hide = false;
			hideResult();
		});

		//================================================================================
		// 03. Suggest????? - ???
		//--------------------------------------------------------------------------------
		//**********************************************
		//??????????????
		//**********************************************
		function checkValChange() {
			timer_val_change = setTimeout(isChange,500);

			function isChange() {
				var now_value = $(input_obj).val();

				if(now_value != prev_value && !is_select) {					
					suggest();
				}
				is_select = false;
				prev_value = now_value;
				
				//??????????????????
				if(!getCurrentResult()) user_value = now_value;

				//????????????
				checkValChange();
			}
		}

		//**********************************************
		//????????????????
		//**********************************************
		function checkShowHide() {
			timer_show_hide = setTimeout(function() {
				if (show_hide == false && reserve_click == false){
					hideResult();
				}
			},500);
		}

		//**********************************************
		//????????
		//**********************************************
		function processKey(e) {
			if (
				(/27$|38$|40$|^9$/.test(e.keyCode) && $results.is(':visible')) ||
				(/^13$|^9$/.test(e.keyCode) && getCurrentResult())
			) {
				if (e.preventDefault)  e.preventDefault();
				if (e.stopPropagation) e.stopPropagation();

				e.cancelBubble = true;
				e.returnValue  = false;
				
				switch(e.keyCode) {
					case 38: // up
						key_select = true;
						prevResult();
						break;

					case 40: // down
						key_select = true;
						nextResult();
						break;

					case 9:  // tab
						hideResult();
						break;

					case 13: // return
						selectCurrentResult();
						break;

					case 27: //	escape
						hideResult();
						break;
				}
			} else {
				checkValChange();
			}
		}

		//================================================================================
		// 04. Suggest????? - Ajax??
		//--------------------------------------------------------------------------------
		//**********************************************
		//Ajax???
		//**********************************************
		function abortAjax() {
			if ($xhr){
				//jQuery1.4.3??????IE7????????????
				if($.browser.msie && $.browser.version == '7.0') return;
				$xhr.abort();
				$xhr = false;
			}
		}

		//**********************************************
		//Ajax??
		//**********************************************
		function suggest(){
			//Ajax????????
			abortAjax();
			
			//???Ajax????????
			$xhr = $.getJSON(
				options.source,
				{
					'q_word'   : $(input_obj).val(),
					'database' : options.database,
					'limit'    : options.limit,
					'order_by' : options.order_by
				},
				function(json_data, status){
					if(!json_data){
						hideResult();
					} else {
						displayItems(json_data);
					}
				}
			);
		}

		//================================================================================
		// 05. Suggest????? - ???????
		//--------------------------------------------------------------------------------
		//**********************************************
		//?????<ul>?????
		//**********************************************
		// @params array arr_candidate   DB????·????????
		//
		//arr_candidate???????<li>???????
		//????????????????
		function displayItems(arr_candidate) {
		
			//?????????????
			$results.empty();

			for (var i = 0; i < arr_candidate.length; i++) {
				//?????
				var $li = $('<li>' + arr_candidate[i] + '</li>');
				$results.append($li);
			}
			//----------------------------------------------
			//???????????
			//----------------------------------------------
			var offset = $(input_obj).offset();

			offset.top +=
				parseInt($(input_obj).height(), 10) +
				parseInt($(input_obj).css('border-top-width'), 10) +
				parseInt($(input_obj).css('padding-top'), 10) +
				parseInt($(input_obj).css('padding-bottom'), 10);
			if($.browser.msie){
				//IE??????offset??????
				//??????????????????????????
				offset.top += parseInt($results.scrollTop(), 10);
			}
			
			offset.left += parseInt($(input_obj).css('padding-left'), 10);
			if($.browser.msie || $.browser.opera){
				offset.left -= parseInt($(input_obj).css('border-left-width'), 10);
			}
			
			//?????
			$results
				.show()
				.css({'top':offset.top, 'left':offset.left})
				.width(
					parseInt($(input_obj).width()) +
					parseInt($(input_obj).css('padding-left'), 10) +
					parseInt($(input_obj).css('padding-right'), 10)
				);

			$results
				.children('li')
				.mouseover(function() {

					//Firefox????????????????????????
					//???????????????????????????
					if (key_select) {
						key_select = false;
						return;
					}

					$results.children('li').removeClass(options.select_class);
					$(this).addClass(options.select_class);
				})
				.mousedown(function(e) {
					reserve_click = true;

					//???????????
					clearTimeout(timer_show_hide);
					//ev.stopPropagation();
				})
				.mouseup(function(e) {
					reserve_click = false;

					//Firefox????????????????????????
					//???????????????????????????
					if (key_select) {
						key_select = false;
						return;
					}
					e.preventDefault();
					e.stopPropagation();
					selectCurrentResult();
				});
		}

		//**********************************************
		//???????????
		//**********************************************
		// @return object current_result ???????????????(<li>??)
		function getCurrentResult() {

			if (!$results.is(':visible')) return false;

			var $current_result = $results.children('li.' + options.select_class);

			if (!$current_result.length) $current_result = false;

			return $current_result;
		}
		//**********************************************
		//?????????????
		//**********************************************
		function selectCurrentResult() {

			var $current_result = getCurrentResult();
			if ($current_result) {
				$(input_obj).val($current_result.text());
				hideResult();

				//added
				prev_value = $(input_obj).val();
			}
			$(input_obj).focus();  //?????????????????
			$(input_obj).change(); //????????????????????
			if(options.direct){
				$(input_obj)
					.parents()
					.map(function () {
						if(this.tagName == 'FORM'){
							$(this).submit();
						}
					});
			}
		}
		//**********************************************
		//?????????
		//**********************************************
		function nextResult() {
			var $current_result = getCurrentResult();

			if ($current_result) {
				$current_result
					.removeClass(options.select_class)
					.next()
						.addClass(options.select_class);
			}else{
				$results.children('li:first-child').addClass(options.select_class);
			}
			//?????????????
			//????????????????????????
			is_select = true;
			if($current_result && $current_result.is('li:last-child')){
				$(input_obj).val(user_value);
			}else{
				$(input_obj).val($('.'+options.select_class).text());
			}
		}
		//**********************************************
		//?????????
		//**********************************************
		function prevResult() {
			var $current_result = getCurrentResult();

			if ($current_result) {
				$current_result
					.removeClass(options.select_class)
					.prev()
						.addClass(options.select_class);
			}else{
				$results.children('li:last-child').addClass(options.select_class);
			}
			//?????????????
			//????????????????????????
			is_select = true;
			if($current_result && $current_result.is('li:first-child')){
				$(input_obj).val(user_value);
			}else{
				$(input_obj).val($('.'+options.select_class).text());
			}
		}
		//**********************************************
		//????????
		//**********************************************
		function hideResult() {
			$results.hide();

			//Ajax????????
			abortAjax();
		}
	};
	//================================================================================
	// 06. ??????
	//--------------------------------------------------------------------------------
	$.fn.ajaxSuggest = function(source, options) {
		if (!source) return;

		//************************************************************
		//?????
		//************************************************************
		options = $.extend({
			//????
			source   : source,
			database : [{'table':'users', 'field':'name', 'order':'id'}], //????DB???
			limit    : 10,     //????????????????
			direct   : false,  //???????submit???????
			order_by : 'ASC',  //ORDER BY(SQL) ???????????????
			results_class : 'as_results', //???????<ul>
			select_class  : 'as_over'     //????<li>
		}, options);
		
		//order????????????field?????
		for(var i=0; i<options.database.length; i++){ 
			if(options.database[i].order === undefined){
				options.database[i].order = options.database[i].field;
			}
		}

		this.each(function() {
			new $.ajaxSuggest(this, source, options);
		});
		return this;
	};
})(jQuery);

