﻿/*	[MHz.Effect.TextSuggest.js]
 *  (c) 2006, 2007 MHZ. (http://www.mediamob.co.kr // http://www.openblog.com)
 *	reference >> prototype.js // ajaxinAction // rico.js
 *	Last Update : 2007.01.04
 *
 *	How to use ?
 *	---------------------------------------------------------------------------
 *	+ <script src="./lib/prototype/prototype.js" type="text/javascript"></script>
 *
 *	var suggestOptions = 
 *	{
 *		suggestDivClassName	:'suggestDiv',		//suggest div css className
 *		suggestionClassName	:'suggestion',		//suggestion css className
 *		matchClassName		:'match',			//match string css classname
 *		matchTextWidth		:true,				
 *		selectionColor		:'#b1c09c',			//selection font color
 *		matchAnywhere		:false,				//like '%' + keyword + '%' ?
 *		ignoreCase			:false,				//upCase // lowCase
 *		count				:10,				//list count
 *		ffSetInterval		:1					//ff interval
 *	};
 *	
 *	function injectSuggestBehavior() {suggest = new TextSuggest('ElementName', 'XmlAddress', suggestOptions);} 
 * 	window.load = injectSuggestBehavior; //pageload call function
 *	---------------------------------------------------------------------------
 *	+ XmlAddress ?? Xml Type.
 *		+	<ajax-response>
 *		+		<response type='object' id='txtInput_updater'>
 *		+			<matches>
 *		+				<entry>
 *		+					<text>media &amp; world</text>
 *		+					<value>4801</value>
 *		+				</entry>
 *		+			</matches>
 *		+		</response>
 *		+	</ajax-response>
 */

//<![CDATA[
TextSuggest = Class.create();
TextSuggest.prototype = 
{	
	//initialize TextSuggest
	initialize : function (element, url, options) 
	{
		var browser			= navigator.userAgent.toLowerCase();	//Browser Type.
		this.isIE			= browser.indexOf("msie")!=-1;			//is IE.
		this.isOpera		= browser.indexOf("opera")!=-1;			//is OPERA
		this.isMozilla		= browser.indexOf("mozilla")!=-1;		//is Mozilla
		this.id				= element;								//Element
		this.textInput		= $(this.id);							//Element Input
		this.suggestions	= [];									//Array Suggest
		this.setOptions(options);
		this.initAjax(url);
		this.injectSuggestBehavior();
	},
	
	//initAjax 실행시킬 함수와 연결을 한다.
	initAjax: function(url){
		//Ajax를 실행시킬 함수를 설정한다.
		ajaxEngine.registerRequest( this.id + '_request', url );
		ajaxEngine.registerAjaxObject( this.id + '_updater', this );
	},
	
	//options value
	setOptions: function(options) 
	{
		this.options = Object.extend({
		//option setting
			suggestDivClassName	:'suggestDiv',		//suggest div css className
			suggestionClassName	:'suggestion',		//suggestion css className
			matchClassName		:'match',			//match string css classname
			matchTextWidth		:true,				
			selectionColor		:'#b1c09c',			//selection font color
			matchAnywhere		:false,				//like '%' + keyword + '%' ?
			ignoreCase			:false,				//upCase // lowCase
			count				:10,				//view list count
			ffSetInterval		:1
		}, options || {});
	},
	
	//injectSuggestBehavior
	injectSuggestBehavior: function() 
	{
		if(this.isIE)//browser.indexOf("msie")!=-1;
		{
			this.textInput.autocomplete = "off";
		}
		
		var keyEventHandler = new TextSuggestKeyHandler(this);
		//this.textInput is $(this.id) # element
		//new input box and hidden box this.id+_preventtsubmit, this.id+_hidden
		new Insertion.After(this.textInput,'<input type="text" id="'+this.id+'_preventtsubmit'+'" style="display:none"/>' );
		new Insertion.After( this.textInput,'<input type="hidden" name="'+this.id+'_hidden'+'" id="'+this.id+'_hidden'+'"/>');
		this.createSuggestionsDiv();
	},
	
	//handleTextInput
	handleTextInput: function() 
	{
		var currentRequest		= this.textInput.value;
		var previousRequest		= this.lastRequestString;	//이전에 요청한 문자열
		this.lastRequestString	= currentRequest;		//현재 요청할 문자열
		
		if (this.lastRequestString=="")
		{//문자열이 비었다면 ??
			this.hideSuggestions();
		}
		else if (this.lastRequestString!=previousRequest) 
		{//문자열이 이전 문자열과 다르다면 새로운 suggest를 보인다.
			this.sendRequestForSuggestions();
		}
	},
	
	//move selection
	moveSelectionUp: function() 
	{//moveup
		if(this.selectedIndex>0){this.updateSelection(this.selectedIndex - 1);}
		this.setInputFromSelection();
		this.lastRequestString = this.textInput.value;
	},

	moveSelectionDown: function() 
	{//movedown
		if(this.selectedIndex<(this.suggestions.length-1)){this.updateSelection(this.selectedIndex + 1);}
		this.setInputFromSelection();
		this.lastRequestString = this.textInput.value;
	},

	updateSelection: function(n) 
	{//background color update
		var span = $( this.id + "_" + this.selectedIndex);
		if(span){span.style.backgroundColor = "";}
		
		this.selectedIndex = n;		
		var span = $(this.id + "_" + this.selectedIndex);
		if (span){span.style.backgroundColor = this.options.selectionColor;}
	},
	
	/* script migration replace Ajax.Pro Method //*/
	sendRequestForSuggestions: function() 
	{
		if(this.handlingRequest) 
		{
			this.pendingRequest = true;
			return;
		}
		
		this.handlingRequest = true;
		this.callRicoAjaxEngine();
	},
	
	
	callRicoAjaxEngine: function() 
	{
		var callParms = [];
		callParms.push( this.id + '_request');
		callParms.push( 'id='             + this.id);
		callParms.push( 'count='          + this.options.count);
		callParms.push( 'suggest='		  + encodeURIComponent(this.lastRequestString));
		callParms.push( 'match_anywhere=' + this.options.matchAnywhere);
		callParms.push( 'ignore_case='    + this.options.ignoreCase);
		
		//alert(this.lastRequestString);
		var additionalParms = this.options.requestParameters || [];
		for( var i=0 ; i < additionalParms.length ; i++ )
			callParms.push(additionalParms[i]);
		ajaxEngine.sendRequest.apply(ajaxEngine,callParms);
	},

	ajaxUpdate: function(ajaxResponse) 
	{
		this.createSuggestions(ajaxResponse);
		if(this.suggestions.length==0)
		{
			this.hideSuggestions();
			$(this.id+"_hidden").value = "";
		}
		else
		{
			this.updateSuggestionsDiv();
			this.showSuggestions();
			this.updateSelection(0);
		}
		
		this.handlingRequest = false;
		if (this.pendingRequest) 
		{
			this.pendingRequest    = false;
			this.lastRequestString = this.textInput.value;
			this.sendRequestForSuggestions();
		}
	},
	
	createSuggestions: function(ajaxResponse) 
	{
		this.suggestions = [];
		var entries = ajaxResponse.getElementsByTagName('entry');
		for ( var i = 0 ; i < entries.length ; i++ ) 
		{
			var strText  = this.getElementContent(entries[i].getElementsByTagName('text')[0]);
			var strValue = this.getElementContent(entries[i].getElementsByTagName('value')[0]);
			this.suggestions.push( { text: strText, value: strValue } );
		}
	},

	setInputFromSelection: function() 
	{
		var hiddenInput = $( this.id + "_hidden" );
		var suggestion  = this.suggestions[this.selectedIndex];		
		if(this.suggestionsDiv.style.display!="none")
		{
			if(suggestion)
			{
				this.textInput.value	= suggestion.text;
				hiddenInput.value    = suggestion.value;
				//this.hideSuggestions();//style.display = 'none';
			}
		}
	},
	
	showSuggestions: function() 
	{
		var divStyle = this.suggestionsDiv.style;
		if(divStyle.display==''){return;}
		this.positionSuggestionsDiv();
		divStyle.display = '';
	},

	positionSuggestionsDiv: function() 
	{
		var textPos = RicoUtil.toDocumentPosition(this.textInput);
		var divStyle = this.suggestionsDiv.style;
		divStyle.top  = (textPos.y + this.textInput.offsetHeight) + "px";
		divStyle.left = textPos.x + "px";

		if ( this.options.matchTextWidth )
		{
			divStyle.width = (this.textInput.offsetWidth- this.padding()) + "px";
		}
	},

	padding: function() 
	{
		try
		{
			var styleFunc = RicoUtil.getElementsComputedStyle;
			var lPad    = styleFunc( this.suggestionsDiv, "paddingLeft",      "padding-left" );
			var rPad    = styleFunc( this.suggestionsDiv, "paddingRight",     "padding-right" );
			var lBorder = styleFunc( this.suggestionsDiv, "borderLeftWidth",  "border-left-width" );
			var rBorder = styleFunc( this.suggestionsDiv, "borderRightWidth", "border-right-width" );

			lPad    = isNaN(lPad)    ? 0 : lPad;
			rPad    = isNaN(rPad)    ? 0 : rPad;
			lBorder = isNaN(lBorder) ? 0 : lBorder;
			rBorder = isNaN(rBorder) ? 0 : rBorder;
			
			return parseInt(lPad) + parseInt(rPad) + parseInt(lBorder) + parseInt(rBorder);
		}
		catch(e)
		{
			return 0;
		}
	},

	hideSuggestions: function() {this.suggestionsDiv.style.display = 'none';},

	createSuggestionsDiv: function() 
	{
		this.suggestionsDiv = document.createElement("div");
		this.suggestionsDiv.className = this.options.suggestDivClassName;
		
		var divStyle = this.suggestionsDiv.style;
		divStyle.position = 'absolute';
		divStyle.zIndex   = 101;
		divStyle.display  = "none";
		
		this.textInput.parentNode.appendChild(this.suggestionsDiv);
	},

	updateSuggestionsDiv: function() 
	{
		this.suggestionsDiv.innerHTML = "";
		var suggestLines = this.createSuggestionSpans();
		for ( var i = 0 ; i < suggestLines.length ; i++ )
		{
			this.suggestionsDiv.appendChild(suggestLines[i]);
		}
	},

	createSuggestionSpans: function() 
	{
		var regExpFlags = "";
		if(this.options.ignoreCase)
		{
			regExpFlags = 'i';
		}
		
		var startRegExp = "^";
		if(this.options.matchAnywhere)
		{
			startRegExp = '';
		}

		var regExp  = new RegExp( startRegExp + this.lastRequestString, regExpFlags );
		
		var suggestionSpans = [];
		for ( var i = 0 ; i < this.suggestions.length ; i++ )
		{
			suggestionSpans.push(this.createSuggestionSpan( i, regExp ))
		}

		return suggestionSpans;
	},

	createSuggestionSpan: function( n, regExp ) 
	{
		var suggestion = this.suggestions[n];
		
		var suggestionSpan = document.createElement("span");
		suggestionSpan.className = this.options.suggestionClassName;
		suggestionSpan.style.width   = '100%';
		suggestionSpan.style.display = 'block';
		suggestionSpan.id            = this.id + "_" + n;
		suggestionSpan.onmouseover   = this.mouseoverHandler.bindAsEventListener(this);
		suggestionSpan.onclick       = this.itemClickHandler.bindAsEventListener(this);
		
		var textValues = this.splitTextValues(suggestion.text,this.lastRequestString.length,regExp);
		
		var textMatchSpan = document.createElement("span");
		textMatchSpan.id            = this.id + "_match_" + n;
		textMatchSpan.className     = this.options.matchClassName;
		textMatchSpan.onmouseover   = this.mouseoverHandler.bindAsEventListener(this);
		textMatchSpan.onclick       = this.itemClickHandler.bindAsEventListener(this);
		textMatchSpan.appendChild(document.createTextNode(textValues.mid));
		suggestionSpan.appendChild(document.createTextNode(textValues.start));
		suggestionSpan.appendChild(textMatchSpan);
		suggestionSpan.appendChild(document.createTextNode(textValues.end));

		return suggestionSpan;
	},

	//handlers
	mouseoverHandler: function(e) 
	{
		var src = e.srcElement ? e.srcElement : e.target;
		var index = parseInt(src.id.substring(src.id.lastIndexOf('_')+1));
		this.updateSelection(index);
	},

	itemClickHandler: function(e) 
	{
		this.mouseoverHandler(e);
		this.hideSuggestions();
		this.textInput.focus();
	},

	splitTextValues: function(text,len,regExp)
	{
		var startPos  = text.search(regExp);
		var matchText = text.substring( startPos, startPos + len );
		var startText = startPos == 0 ? "" : text.substring(0, startPos);
		var endText   = text.substring( startPos + len );
		return { start: startText, mid: matchText, end: endText };
	},

	getElementContent: function(element) 
	{
		return element.firstChild.data;
	}
}


TextSuggestKeyHandler = Class.create();
TextSuggestKeyHandler.prototype = 
{
	initialize: function(textSuggest) 
	{
		this.textSuggest = textSuggest;
		this.input       = this.textSuggest.textInput;
		this.addKeyHandling();
		//addon by io2tree // is FF // PeriodicalExecuter
		if(this.textSuggest.isMozilla)
		{
			this.addPeriodicalExecuter();
		}
	},
	
	//addon PeriodicalExecuter function
	addPeriodicalExecuter: function()
	{
		var textSuggestPe = new PeriodicalExecuter(this.keyupHandler.bindAsEventListener(this),this.textSuggest.options.ffSetInterval);
	},

	addKeyHandling: function() 
	{
		this.input.onkeyup    = this.keyupHandler.bindAsEventListener(this);
		this.input.onkeydown  = this.keydownHandler.bindAsEventListener(this);
		this.input.onblur     = this.onblurHandler.bindAsEventListener(this);
		if(this.isOpera){this.input.onkeypress = this.keyupHandler.bindAsEventListener(this);}
	},

	keydownHandler: function(e) 
	{
		var upArrow   = 38;
		var downArrow = 40;
		
		if(e.keyCode == upArrow) 
		{
			this.textSuggest.moveSelectionUp();
			setTimeout(this.moveCaretToEnd.bind(this),1);
		}
		else if(e.keyCode==downArrow)
		{
			this.textSuggest.moveSelectionDown();
		}
	},

	keyupHandler: function(e) 
	{
		if(this.input.length==0 && !this.isOpera)
		{
			this.textSuggest.hideSuggestions();
		}
		
		if(!this.handledSpecialKeys(e))
		{
			this.textSuggest.handleTextInput();
		}
	},

	handledSpecialKeys: function(e) 
	{
		var enterKey  = 13;
		var upArrow   = 38;
		var downArrow = 40;
		
		if(e.keyCode==upArrow || e.keyCode==downArrow ) 
		{
			return true;
		}
		else if(e.keyCode == enterKey) 
		{	
			this.textSuggest.setInputFromSelection();
			return true;
		}
		
		return false;
	},

	moveCaretToEnd: function() 
	{
		var pos = this.input.value.length;
		if(this.input.setSelectionRange) 
		{
			this.input.setSelectionRange(pos,pos);
		}
		else if(this.input.createTextRange)
		{
			var m = this.input.createTextRange();
			m.moveStart('character',pos);
			m.collapse();
			m.select();
		}
	},

	onblurHandler: function(e) 
	{
		if(this.textSuggest.suggestionsDiv.style.display=='')
		{
			this.textSuggest.setInputFromSelection();
		}
		this.textSuggest.hideSuggestions();
	}
};
//]]>