﻿/*  Copyright Mikhail Antropov, 2007, 2008  |  http://www.convincingmail.com/
 * ---------------------------------------------------------------------------
 *
 * The AutoComplete extender
 *
 * Details and latest version at:
 * http://www.convincingmail.com/
 *
 * This script is distributed under the GNU Lesser General Public License.
 * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
 * 
 * Please don't remove this header! 
 * If you like this control you can contribute to me by a link back  from your site
 *
 * v2.2
 */


Type.registerNamespace('ConvincingMail.AdvancedAutoSuggest');

ConvincingMail.AdvancedAutoSuggest.AdvancedAutoSuggestBehavior = function(element) {

  ConvincingMail.AdvancedAutoSuggest.AdvancedAutoSuggestBehavior.initializeBase(this, [element]);  
	this.inputFieldId; //An id of a textfield where user inputs
	this.updateFieldId; //An id of a textfield to update from click often a hidden field for ID to validate
	this.delay=100; //a delay between server calls in ms, We don't want to kill the server if a user types fast
	this.serviceUrl; //URL where to make the server call	
	this.lastCall = new Date().getTime(); //last call to the server
	this.additionalFields=new Array(0);	
	this.suggestionsDivId='suggestionsDiv';//an id of container for suggestions
	this.suggestionsDiv = $(this.suggestionsDivId); //a container of suggestions table		
	this.suggestEmptyField = false;
	this.template;
	
	if(!this.suggestionsDiv){		
		this.suggestionsDiv=Element.extend(document.createElement('div'));
		Element.extend(this.suggestionsDiv);
		this.suggestionsDiv.style.display='none';
		this.suggestionsDiv.style.position='absolute';		 
		$$('body')[0].appendChild(this.suggestionsDiv);
	}
	this.doHide = true; //indicates if we need to hide the suggestions box
	
	this.selectedIndex=0; 
	this.suggestionsList = new Array();
	this.suggestionsTrList = new Array();
	
	this._saveDesableSelect = new Array(0); //an array of select boxes to disable when IE6
	this._iframeElement;//an iframe to hide DDL IE6 fix


	//CSS		
	this.tableCss = "suggestionsTable";
	this.tableTrCss = "suggestionsTr";
	this.tableTdCss = "suggestionsTd";
	this.titleTrCss = "titleTr";
	this.titleTdCss = "titleTd";
	this.commentsTrCss = "commentsTr";
	this.commentsTdCss = "commentsTd";
	this.hilightedTrCss = "suggestionsHilightedTr";
	

	this.UpdateAdditionalFields = function UpdateAdditionalFields(textboxId){
		if(textboxId && textboxId!=""){
			this.additionalFields.push(textboxId);
		}
	}
	/*
	main function which makes the server call, parses results and shows them on screen
	*/
	this.ShowSuggestions = function ShowSuggestions(ev){		        	        
		Debug("ShowSuggestions Call")
		var k = ev.keyCode ? ev.keyCode : ev.rawEvent?ev.rawEvent.keyCode:null;
		if (k != Sys.UI.Key.esc && k != Sys.UI.Key.up && k != Sys.UI.Key.down && k != Sys.UI.Key.enter) {
			var textbox=$(this.inputFieldId);
			Debug("textbox:"+textbox);
			var text;		
			var additionalParams='';
			if(textbox!=null){				
				text=textbox.value;			
				Debug('textbox.value:'+text);	
				if((text!=null && ""!=text)||this.suggestEmptyField){			
					this.additionalFields.each(function(textbox){
						if(textbox.indexOf("=")>0){
							additionalParams+='&additionalParams=';
							additionalParams+=textbox.split('=')[0];
							additionalParams+='|';
							additionalParams+=$F(textbox.split('=')[1]);
						}else{
							additionalParams+='&additionalParams=';
							additionalParams+=$(textbox);
							additionalParams+='|';
							additionalParams+=$F(textbox);
						}
					}			
					);
					if(additionalParams==''){
						additionalParams="&additionalParams="
					}
					Debug('doing service request:'+'tryValue='+ encodeURIComponent(text)+additionalParams);							
					new Ajax.Request(this.serviceUrl, 
						{   
							method: 'post',   
							parameters:'tryValue='+ encodeURIComponent(text)+additionalParams,					
							onSuccess: this.OnSuccess.bindAsEventListener(this)
						}
					);
				}else {	
					this.doHide=true;
					this.HideSuggestions();
				}
			}
		}		
	}
	
	//creates a table from the results
	this.OnSuccess = function OnSuccess(transport){		
		Debug('Success!!! Raw Data:'+transport.responseText);
		this.suggestionsDiv.style.display='none';		
		//we remove all childs of an old container
		while (this.suggestionsDiv.firstChild) {
			this.suggestionsDiv.removeChild(this.suggestionsDiv.firstChild);
		}		
		var results = transport.responseText.stripTags().evalJSON(true);   								
		Debug('Real Data:'+results);
		var title, titleValue, description;	
		var resultedTable = document.createElement('table');
		var resultedTableTBody = document.createElement('tbody');
		var rowDiv;
		this.suggestionsDiv.appendChild(resultedTable);				
		resultedTable.appendChild(resultedTableTBody);
		resultedTable.cellSpacing="0";
		resultedTable.cellPadding="0";
		Element.extend(resultedTable);
		resultedTable.addClassName(this.tableCss);
		var i;	
		Array.clear(this.suggestionsList);
		Array.clear(this.suggestionsTrList);
		this.selectedIndex=-1; 							
		for(i=0;i<results.length;i++){			
			title=results[i].Title.replace('\r\n','');
			titleValue=results[i].TitleValue.replace('\r\n','');
			description=results[i].Description;
			idValue=results[i].Id;
			tr=document.createElement('tr');
			td=document.createElement('td');
			Element.extend(tr);Element.extend(td);
			tr.addClassName(this.tableTrCss);
			td.addClassName(this.tableTdCss);			
			resultedTableTBody.appendChild(tr);tr.appendChild(td);
			Array.add(this.suggestionsList,results[i]);
			
					
			Event.observe(td,'click', this.ReplaceValue.bindAsEventListener(this,i));		
			Event.observe(td,'mouseover', this.DisableHide.bindAsEventListener(this,i));
			Event.observe(td,'mouseout', this.EnableHide.bindAsEventListener(this,i));
			
			//we create an inner row table where we store visible title and description
			if(this.template!=null && this.template!="undefined"){
				rowDiv = Element.extend(document.createElement('div'));
				td.appendChild(rowDiv);
				Array.add(this.suggestionsTrList,rowDiv);
				rowDiv.update(this.template.evaluate(results[i]));
			}else{
			rowTable = document.createElement('table');
			rowTableBody = document.createElement('tbody'); 
			Array.add(this.suggestionsTrList,rowTable);
			//title row
			rowTitleTr=document.createElement('tr'); 		
			rowTitleTd=document.createElement('td');
			Element.extend(rowTable);Element.extend(rowTableBody);Element.extend(rowTitleTr);Element.extend(rowTitleTd);
			rowTitleTr.addClassName(this.titleTrCss);
			rowTitleTd.addClassName(this.titleTdCss);			
			td.appendChild(rowTable);rowTable.appendChild(rowTableBody);
			rowTableBody.appendChild(rowTitleTr);rowTitleTr.appendChild(rowTitleTd);
			rowTitleTd.update(this.FixText(title));	//we set title		
			//description row
			if(description!=null && description!=''){
				rowCommentsTr=document.createElement('tr');
				rowCommentsTd=document.createElement('td');	
				Element.extend(rowCommentsTr);Element.extend(rowCommentsTd);			
				rowCommentsTr.addClassName(this.commentsTrCss);
				rowCommentsTd.addClassName(this.commentsTdCss);			
				rowTableBody.appendChild(rowCommentsTr);		
				rowCommentsTr.appendChild(rowCommentsTd);
				rowCommentsTd.update(this.FixText(description)); //we set description		
			}
			}			
		}			
		this.PositionBox(this.inputFieldId);
		this.doHide = true;
	}
	/*
	replces special chars
	*/
	this.FixText = function FixText(text){
		text=text.replace(/\+\+\+\+/g, ",");
		text=text.replace(/\-\-\-\-/g, "|");
		return text;
	}
	/*
	Positions the box under the control
	*/
	this.PositionBox = function PositionBox(parentId){		
		var parentbox=$(parentId);	
		var zIndex = 0;		
		if(this.suggestionsDiv.style.display=='none'){						
				var yPos=parentbox.offsetTop+parentbox.offsetHeight;
				var xPos=parentbox.offsetLeft;//+parentbox.offsetWidth;				
				while(parentbox.offsetParent!=null){				
					parentbox=parentbox.offsetParent;
					yPos+=parentbox.offsetTop;
					xPos+=parentbox.offsetLeft;
					if(parentbox.style.zIndex && parentbox.style.zIndex>zIndex){
						zIndex=parentbox.style.zIndex;
					}
				}			
				this.suggestionsDiv.style.display='';	
				Debug("zIndex:"+zIndex);			
				this.suggestionsDiv.style.zIndex=zIndex+1;							
				this.suggestionsDiv.style.top=yPos+'px';
				this.suggestionsDiv.style.left=xPos+'px';							
				this.suggestionsDiv.style.width=$(parentId).offsetWidth+'px';
				this.suggestionsDiv.style.overflow='auto';
				Debug(Sys.Browser.name);
				Debug(Sys.Browser.version);
				//IE 6 fix to hide dropdowns
				if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
					Debug("Doing IE6 FIX");
					var i;
					var tagElements;
					i = 0;
					for (var k = 0 ; k < this._saveDesableSelect.length; k++) {					
						this._saveDesableSelect[k].tag.disabled = this._saveDesableSelect[k].visib;					
					}
					this._saveDesableSelect.clear();
					tagElements = document.getElementsByTagName('SELECT');
					Debug("Found "+tagElements.length+" SELECT BOXES");
					for (var k = 0; k < tagElements.length; k++) {						
						this._saveDesableSelect[i] = {tag: tagElements[k], visib: tagElements[k].disabled};
						tagElements[k].disabled = true;						
						i++;						
					}					
					var childFrame = this._iframeElement;
					var elt = this.suggestionsDiv;
					// copied from  PreviewScript.js function Sys$Preview$UI$PopupBehavior$show
					childFrame.style.top = elt.style.top;
					childFrame.style.left = elt.style.left;
					childFrame.style.width = elt.offsetWidth + "px";
					childFrame.style.height = elt.offsetHeight + "px";
					childFrame.style.display = elt.style.display;
					Debug("_iframeElement.style.top:"+childFrame.style.top);
					Debug("_iframeElement.style.left:"+childFrame.style.left);
					Debug("_iframeElement.style.width:"+childFrame.style.width);
					Debug("_iframeElement.style.height:"+childFrame.style.height);
					Debug("_iframeElement.style.display:"+childFrame.style.display);					
					if (elt.currentStyle && elt.currentStyle.zIndex) {
						childFrame.style.zIndex = elt.currentStyle.zIndex;
					}else if (elt.style.zIndex) {
						childFrame.style.zIndex = elt.style.zIndex;
					}					
					Debug("_iframeElement.style.zIndex:"+childFrame.style.zIndex);
				}
				
		}		
	}
	this.OnKeyDown = function OnKeyDown(ev) {
        var k = ev.keyCode ? ev.keyCode : ev.rawEvent.keyCode;
        if (k === Sys.UI.Key.esc) {
            this.doHide=true;
			this.HideSuggestions();
			Event.stop(ev);
        }
        else if (k === Sys.UI.Key.up) {
            if (this.selectedIndex > 0) {
				this.RemoveHighLight(ev,-1);
                this.selectedIndex--;
                this.HighLight(ev,-1);
                this.doHide=true;
                Event.stop(ev);       
            }
        }
        else if (k === Sys.UI.Key.down) {
            if (this.selectedIndex < (this.suggestionsList.length - 1)) {
				this.RemoveHighLight(ev,-1);
                this.selectedIndex++;
                this.HighLight(ev,-1); 
                this.doHide=true;
                Event.stop(ev);
            }
        }
        else if (k === Sys.UI.Key.enter) {
            if (this.selectedIndex !== -1) {
                this.ReplaceValue(ev,this.selectedIndex);
                Event.stop(ev);               
            }
        }        
        if (k !== Sys.UI.Key.tab) {
           // this.doHide=false;
        }
    }
	//replaces a value in the textbox on click
	this.ReplaceValue = function ReplaceValue(ev, index){
		this.selectedIndex=index; 		
		$(this.inputFieldId).value=this.suggestionsList[this.selectedIndex].TitleValue.replace('\r\n','').unescapeHTML();
		if(this.updateFieldId && $(this.updateFieldId)){
			$(this.updateFieldId).value=this.suggestionsList[this.selectedIndex].Id;
		}
		this.doHide=true;
		this.raiseItemSelected(this.suggestionsList[this.selectedIndex]);
		this.HideSuggestions();
	}
	
	//cleans updateFieldId text box
	this.CleanUpdateField = function CleanUpdateField(ev){
		var k = ev.keyCode ? ev.keyCode : ev.rawEvent?ev.rawEvent.keyCode:null;
        if (k != Sys.UI.Key.esc && k != Sys.UI.Key.up && k != Sys.UI.Key.down && k != Sys.UI.Key.enter) {
            if(this.updateFieldId && $(this.updateFieldId)){
				$(this.updateFieldId).value="";
			}
        }	
	}
	
	//hides suggestions div
	this.HideSuggestions = function HideSuggestions(){					
		if(this.doHide){				
			 //IE6 Bug with SELECT element always showing up on top
			if ((Sys.Browser.agent == Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
				for (var k = 0 ; k < this._saveDesableSelect.length; k++) {					
					this._saveDesableSelect[k].tag.disabled = this._saveDesableSelect[k].visib;					
				}
			}
        	this.suggestionsDiv.style.display='none';
        	if(this._iframeElement){
        		this._iframeElement.style.display='none';			
        	}
		}		
	}
	//function is to enable hide on onmouseout event of suggestions row
	this.EnableHide = function EnableHide(ev, index){
		this.RemoveHighLight(ev, index);
		this.doHide=true;
	}
	//function is to disable hide and highlight current row on mouseover event of suggestions row
	this.DisableHide = function DisableHide(ev,index){
		this.HighLight(ev,index);
		this.doHide=false;
	}
	this.RemoveHighLight =  function RemoveHighLight(ev,index){
		if (index==-1){index=this.selectedIndex}//else{this.selectedIndex=index}
		if(index!=-1){
			var element = this.suggestionsTrList[index];		
			Element.extend(element);
			element=element.ancestors().find(this.FindRow.bindAsEventListener(this))
			if(element!=null){
				element.removeClassName(this.hilightedTrCss);
			}			
		}
	}
	//highlights the row
	this.HighLight = function HighLight(ev,index){
		if(this.selectedIndex!=-1){
			this.RemoveHighLight(ev,-1);
		}
		if (index==-1){index=this.selectedIndex}else{this.selectedIndex=index}
			if(index!=-1){			
				var element = this.suggestionsTrList[index];
				Element.extend(element);
				element=element.ancestors().find(this.FindRow.bindAsEventListener(this))
				if(element!=null){
					element.addClassName(this.hilightedTrCss);			
				}			
		}
	}
	//a function to find a row from mouseover event to highlight it.	
	this.FindRow = function FindRow(el){
		Element.extend(el);		
		if(el && el.tagName && el.tagName.toLowerCase()=="td" && el.hasClassName(this.tableTdCss)){
			return el;
		}
	}
}

ConvincingMail.AdvancedAutoSuggest.AdvancedAutoSuggestBehavior.prototype = {

    initialize : function() {
        ConvincingMail.AdvancedAutoSuggest.AdvancedAutoSuggestBehavior.callBaseMethod(this, 'initialize');
        //#################################################################################################################
		//######### Constructor actions
		//#################################################################################################################	
		this.inputFieldId=this.get_element().id;
		Debug("Starting SuggestEngine inputFieldId:"+this.inputFieldId+" serviceUrl:"+this.serviceUrl+" additionalFields:"+this.additionalFields+" updateFieldId:"+this.updateFieldId);		
		//bind events to text box
		if(this.inputFieldId){	    
			new Form.Element.DelayedObserver(this.inputFieldId, this.delay, this.ShowSuggestions.bindAsEventListener(this));
			Event.observe(this.inputFieldId, 'keydown', this.OnKeyDown.bindAsEventListener(this));
			Event.observe(this.inputFieldId, 'keyup', this.CleanUpdateField.bindAsEventListener(this));
			Event.observe(this.inputFieldId, 'blur', this.HideSuggestions.bindAsEventListener(this));		
			Event.observe(this.inputFieldId, 'focus', this.ShowSuggestions.bindAsEventListener(this));		
		}
		if ((Sys.Browser.agent === Sys.Browser.InternetExplorer) && (Sys.Browser.version < 7)) {
			//IE6 fix		
			var childFrame;
			var elt = this.suggestionsDiv;
			// copied from  PreviewScript.js function Sys$Preview$UI$PopupBehavior$show
			childFrame = document.createElement("iframe");
			childFrame.src = "about:blank";
			childFrame.style.position = "absolute";
			childFrame.style.display = "none";
			childFrame.scrolling = "no";
			childFrame.frameBorder = "0";
			childFrame.style.filter = "progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)";
			elt.parentNode.insertBefore(childFrame, elt);
			// end copied
			this._iframeElement = childFrame;
		}
			
    },

    dispose : function() {
        // TODO: add your cleanup code here
        ConvincingMail.AdvancedAutoSuggest.AdvancedAutoSuggestBehavior.callBaseMethod(this, 'dispose');
    },    
    //-----------------
    get_UpdateField : function() {
        return this.updateFieldId;
    },

    set_UpdateField : function(value) {
        this.updateFieldId = value;
    },
    //-----------------
    get_Delay : function() {
        return this.delay;
    },

    set_Delay : function(value) {		
        this.delay = value==0?100:value;
    },
    //-----------------
    get_SuggestEmptyField : function() {
        return this.suggestEmptyField;
    },

    set_SuggestEmptyField : function(value) {		
        this.suggestEmptyField = value==0?100:value;
    },
    //-------------------
    get_ServiceUrl : function() {
        return this.serviceUrl;
    },

    set_ServiceUrl : function(value) {
        this.serviceUrl = value;
    },
    
    get_AdditionalFields : function() {
        return this.additionalFields;
    },

    set_AdditionalFields : function(value) {
		//split additional params and fills an array of additional fields
		if(value){value.split(',').each(this.UpdateAdditionalFields.bindAsEventListener(this))}       
    },
    
    get_SuggestionsDivId : function() {
        return this.suggestionsDivId;
    },

    set_SuggestionsDivId : function(value) {
		if(value && value!="") this.suggestionsDivId = value;       
    },
    
    add_itemSelected : function(handler) {       
        this.get_events().addHandler('itemSelected', handler);
    },
    remove_itemSelected : function(handler) {        
        this.get_events().removeHandler('itemSelected', handler);
    },
    raiseItemSelected : function(eventArgs) {        
        var handler = this.get_events().getHandler('itemSelected');
        if (handler) {
            handler(this, eventArgs);
        }
    },
    //Template
    get_Template : function() {
        return this.template;
    },

    set_Template : function(value) {    
        this.template = new Template(value);
    },
    
    //CSS
    get_TableCss : function() {
        return this.tableCss;
    },

    set_TableCss : function(value) {
		if(value && value!="")this.tableCss = value;
    },
    get_TableTrCss : function() {    
        return this.tableTrCss;
    },

    set_TableTrCss : function(value) {
        if(value && value!="")this.tableTrCss = value;
    },
    get_TableTdCss : function() {
        return this.tableTdCss;
    },

    set_TableTdCss : function(value) {
       if(value && value!="") this.tableTdCss = value;
    },
    
    get_TitleTrCss : function() {
        return this.titleTrCss;
    },

    set_TitleTrCss : function(value) {
       if(value && value!="") this.titleTrCss = value;
    },
    
    get_TitleTdCss : function() {
        return this.titleTdCss;
    },

    set_TitleTdCss : function(value) {
       if(value && value!="") this.titleTdCss = value;
    },
    //---------
    get_CommentsTrCss : function() {
        return this.commentsTrCss;
    },

    set_CommentsTrCss : function(value) {
       if(value && value!="") this.commentsTrCss = value;
    },
    //---------
    get_CommentsTdCss : function() {
        return this.commentsTdCss;
    },

    set_CommentsTdCss : function(value) {
       if(value && value!="") this.commentsTdCss = value;
    },
    //---------
    get_HilightedTrCss : function() {
        return this.hilightedTrCss;
    },

    set_HilightedTrCss : function(value) {
       if(value && value!="") this.hilightedTrCss = value;
    }
}

ConvincingMail.AdvancedAutoSuggest.AdvancedAutoSuggestBehavior.registerClass('ConvincingMail.AdvancedAutoSuggest.AdvancedAutoSuggestBehavior', AjaxControlToolkit.BehaviorBase);

// Delayed observer, like Form.Element.Observer, 
// but waits for delay after last key input

Form.Element.DelayedObserver = Class.create();
Form.Element.DelayedObserver.prototype = {
  initialize: function(element, delay, callback) {
    this.delay     = delay || 500;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element); 
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) return;
    if(this.timer) clearTimeout(this.timer);
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  } 
}
function Debug(info) {
	if($('debugConsole')){
		$('debugConsole').value+=info+"\n";
	}
 }

if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();