// TypeAhead - a javascript auto-complete functionality for web forms.
// v1.3 - July 28th 2005

// Copyright (c) 2005 Cédric Savarese <pro@4213miles.com>
// This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
// You may use it as long as you keep this copyright notice intact. 
// A link back to the formassembly.com is also appreciated.

// Usage Notes:
// ============
// var typeAhead = new TypeAhead(listName [, groupName]);   /* Instanciate the TypeAhead object */
// typeAhead.initInput(inputFieldID); 		    /* Binds the TypeAhead Object to an input field */

// Parameters: 
// 	listName:     Identifier for the suggestion list.
//  groupName:    (optional) Identifier to keep distinct lists per users, group, date.. or whatever.
//  inputFieldID: Id of the targeted input field (ex: <input type="text" id="inputFieldID" ... />)

// To bind the same TypeAhead instance to several text inputs, call typeAhead.initInput when the 
// input field gets the focus:
// document.getElementById('inputFieldID').onfocus = function() { typeAhead.initInput(this.id) };

// Change Log
// ==========
// v1.1  Added Suggestion drop-down ('google suggests' style)
// v1.2  Added Private suggestion lists
// v1.3  Refactored
function initTypeAhead(inputControl){

		var typeAhead = new TypeAhead(inputControl);
				
		if(arguments.length>2){
			var arg1 = arguments[1];
			var arg2 = arguments[2];
			XBrowserAddHandler(window,'load', function() {typeAhead.initInput(inputControl, arg1, arg2);});
		}
		else{

			XBrowserAddHandler(window,'load', function() {typeAhead.initInput(inputControl);} );
		}
		
		
}


function TypeAhead(listName /*[,userName]*/ ) {
	//for homepage, the id of the link that will direct connect to db 
	var connectBtnId = 'dbConnectBtn';
	var homepageFormId = 'frmdatabases';
	var connectPageLink = '../db_db/connect.php?dbname=';
	//this conditional is for development only, can be removed and replaced with the latter assignment of the url
	if(window.location.host == "localhost")
		this.typeAheadServiceURL = "http://" + window.location.host + "/baruch/library/db_db/include/typeahead/type-ahead.php";
	else
		this.typeAheadServiceURL = "http://" + window.location.host + "/db_db/include/typeahead/type-ahead.php";
	this.listName = listName			// Identifier for the suggestion list. Doesn't have to match the input field id or name attributes.	
	this.userName = "";					// group/user identifier (for private suggestions lists)
	if(arguments.length>1) 
		this.userName = arguments[1];

	this.inputId = null;				// id of the currently targeted text input
	this.inputElement = null; 			// reference to the currently targeted text input 

	this.suggestions = new Array();		// list of suggestions retrieved for the given input id
	this.matchingSuggestions = new Array();	// list of suggestions matching the user input
	this.suggestedText = "";			// latest suggested text
	this.userText = "";					// latest user typed text
	
	this.suggestionDropDown = null; 	// reference to the Drop Down DIV w/ the list of suggestions
	this.maxItemInDropDown = 200;		// max number of suggestions in the drop-down list
	this.connectBtn = null;				// element that will contain the link to the currently selected item
	this.homePageForm = null;
	var suggestedIndex = 0;				// index of the selected suggestion in the Drop Down list (changed w/ up & down arrows)
	var pressedKeyCount=0;
	
	var self = this;					// TypeAhead object reference
	var HTTPReq; 						// HTTP Request object reference
	var HTTPReqPost;					// HTTP Request object reference (used for POST Requests)

	var isWaitingForSuggestions = false; // Flag to show updated suggestion list
	var isThereMoreOnServer = false;	 // Flag to prevent useless server access

	//global iframe, so injected iframe can be removed
	var pIframe = null;
	var pCount = 0;
	//offsets are in ems
	var	pLeftOffset = 3.8;
	var pTopOffset = -0.1;
	var tryIframe = false;
	

	// Debug Output
	var debugOutput = document.getElementById('debugOutput'); 
	function debug(text) { 
		if(typeof debugOutput != "undefined" && debugOutput) 
			debugOutput.innerHTML = debugOutput.innerHTML+"<br /><hr />"+text; 
	}
	
	// INITIALIZATION ROUTINES ------------------------------------------------------------------------------------------------

	// init: runs when the object is instanciated.
	this.init = function() {
		debug("TypeAhead Object Initialization");
		if(window.ActiveXObject)
			HTTPReq = new ActiveXObject("Microsoft.XMLHTTP");
		else
			HTTPReq = new XMLHttpRequest();

		//init suggestions array with something
		self.getSuggestions(); 
	}
	
	// initInput: to be run when the 'inputId' field is displayed or get the focus.
	this.initInput = function() { /*[inputId]*/

		// Make sure we have a valid id and a valid reference to the text input
		if(arguments.length>0){
			self.inputId = arguments[0];
			
			if(arguments.length>2){
				pLeftOffset = arguments[1];
				pTopOffset = arguments[2];
				var isHomepage = true;
			}
		}
		if(self.inputId) self.inputElement = document.getElementById(self.inputId);
		if(self.inputElement && !self.inputId) {	
			if(!self.inputElement.id) self.inputElement.id = randomId();
			self.inputId = self.inputElement.id;
		}
		if(!self.inputId) return false;		
		self.userText = self.inputElement.value;

		debug("TypeAhead Field Initialization: "+ self.inputId);

		// Create markup for drop-down list of suggestions
		self.suggestionDropDown = document.getElementById("THDropDown-" + self.inputId);
		if(!self.suggestionDropDown) {
			//alert("creating dd div");
			self.suggestionDropDown = document.createElement('DIV');
			//iframe = document.createElement('IFRAME');
			self.suggestionDropDown.id = "THDropDown-" + self.inputId;
			self.suggestionDropDown.className = "THHideDropDown";
			self.suggestionDropDown = self.inputElement.parentNode.insertBefore(self.suggestionDropDown, self.inputElement.nextSibling);

			var newTop = getTop(self.inputElement);
			var newTop2 = newTop.toString();
			//self.suggestionDropDown.style.top = newTop2 + "px";
			//self.suggestionDropDown.style.left =  getLeft(self.inputElement).toString() + "px";
			self.suggestionDropDown.style.top = 21+pTopOffset+"em";
			self.suggestionDropDown.style.left =  21+pLeftOffset+"em";
			if(isHomepage && true){
				self.suggestionDropDown.style.height = "8em";
				self.suggestionDropDown.style.width	=	"20.3em";
				self.connectBtn = document.getElementById(connectBtnId);
				self.homePageForm = document.getElementById(homepageFormId);
				//take over the form
				self.homePageForm.method = "post";
				self.homePageForm.onkeypress = function(evt){
					if(!evt) evt = window.event;

					if(evt.keyCode == 13){
						self.inputElement.blur();

						var frmAction = connectPageLink+encodeURIComponent(self.inputElement.value);
						
						var dbForm = document.getElementById('frmdatabases');
						dbForm.action=frmAction;
						dbForm.submit();
						return false;
					}//if keyCode == 13
				}//self.homepageForm.onkeypress()
				self.connectBtn.onclick = function(evt){
					if(!evt) evt = window.event;
					
					var frmAction = connectPageLink+encodeURIComponent(self.inputElement.value);

					self.connectBtn.href=frmAction;					
				}//self.connectBtn.onclick
			}
			//self.suggestionDropDown.style.width = self.inputElement.offsetWidth.toString() + "px";
		}							
		
		// Handle user input
		self.inputElement.onkeyup = function (evt) {

			if(!evt) evt = window.event;
			if(tryIframe)
			if(document.getElementById('pIframeID')){
				if(document.getElementById('pIframeID').style.height != self.suggestionDropDown.offsetHeight)
					hideIframe();
			}

			switch(evt.keyCode) {
				case 8: // backspace
					self.userText = self.inputElement.value;					
					self.updateSuggestionList();
					
					self.showSuggestionList();				
					break;
				case 46: // delete
					self.userText = self.inputElement.value;
					self.updateSuggestionList();
					self.showSuggestionList();				
					break;				
				case 40:	// arrow down					
					if(suggestedIndex==0) {
						self.updateSuggestionList();
						self.showSuggestionList();		
					}
					if(suggestedIndex < self.matchingSuggestions.length) suggestedIndex++;					
					self.suggest();
					break;
				case 38:	// arrow up
					if(suggestedIndex > 1) suggestedIndex --;
					self.suggest();
					break;
				default:
					pressedKeyCount--;
					if (self.userText != self.inputElement.value) {
						suggestedIndex = 0;
						self.suggest(self.inputElement.value.toLowerCase());								
					}							
			}

		}
		self.inputElement.onkeydown = function (evt) {
			if(!evt) evt = window.event;
			if(evt.keyCode != 38 && evt.keyCode != 40 && evt.keyCode != 46 && evt.keyCode != 8)
				pressedKeyCount++;
		}
		self.inputElement.onfocus = function(evt) {
			//self.updateSuggestionList();
			//self.showSuggestionList();
		}
		// hides the suggestion drop-down when input field not in focus
		self.inputElement.onblur = function(evt) {
			if(tryIframe)
				hideIframe();
			//alert(event.x+"\n"+event.y);
			//get coordinates of mouse click and make sure its not on scroll bar
			//firefox doesn't have scroll bar problem, but doesn't also have event object
			if(!evt)evt = window.event;
			
				var xPos = evt.clientX;
				var yPos = evt.clientY;
				if(xPos > 390 && xPos < 413 && yPos > 427 && yPos < 580){
					//must reassign focus to the inputElement
					//otherwise it stays as "blurred" and will not be able to be blurred when it needs to be(such as clicking an item in the list)
					//however, readding focus upon every blur(or every click) prevents holding down the scroll bar and scrolling
					//this is a total hack
					window.setTimeout(function() {	self.inputElement.focus();}, 1000);
					//alert("not hiding, cuz you clicked on the scroll bar");
				}
				else{
					//alert("hiding, cuz you didn't click on the scroll bar");
					window.setTimeout(function() {				
						self.suggestionDropDown.className = self.suggestionDropDown.className.replace("THShowDropDown","THHideDropDown");
					},500);
				}
		}//end onblur
		
		self.suggestionDropDown.onclick = function(evt){
				//alert("clicked, about to blur");
			if(!isHomepage){
				//must select the db in the scroll box before calling changeBlurb
				if(!evt)evt = window.event;
				selectDB(evt);
				changeBlurb('blurbBody');				

				//alert("clicked, about to blur");
				self.inputElement.blur();
			}
			else{
				//if its the homepage, reassign the connect button link
				self.homePageForm.action = connectPageLink+escape(self.inputElement.value);
				self.connectBtn.href=connectPageLink+escape(self.inputElement.value);
			}		
		}//end onclick handler

	}

	// SUGGESTION LIST IN/OUT ------------------------------------------------------------------------------------------------
	this.getSuggestions = function(text) {
		
			if(text) self.userText = text;
			HTTPReq.abort();
			HTTPReq.onreadystatechange = self.populateSuggestionsFromService;	
			var pURL = self.typeAheadServiceURL + "?inputid="+encodeURIComponent(self.listName)+"&inputtxt="+encodeURIComponent(self.userText)+"&user="+encodeURIComponent(self.userName);

			HTTPReq.open("GET", self.typeAheadServiceURL + "?inputid="+encodeURIComponent(self.listName)+"&inputtxt="+encodeURIComponent(self.userText)+"&user="+encodeURIComponent(self.userName), true);
			HTTPReq.send(null);
			debug("request sent: "+"inputid="+encodeURIComponent(self.listName)+"&inputtxt="+encodeURIComponent(self.userText));

	}//end getSuggestions()
	this.populateSuggestionsFromService = function(evt) {
	    if (HTTPReq.readyState == 4) {			
		   	self.suggestions = HTTPReq.responseText.split("|");
			debug("response: "+ HTTPReq.responseText);			
	
			for(var z = 0;z<self.suggestions.length;z++){
			
				self.suggestions[z] = trim(self.suggestions[z]);

			}

			if(self.suggestions.length == 20) isThereMoreOnServer = true;

			if(isWaitingForSuggestions) {
				isWaitingForSuggestions = false;
				self.showSuggestionList();
			}
	    }
	}//end populateSuggestionsFromService
	
	// addSuggestion:
	// text: text to be added to the suggestion array
	// doPost [true/false]: post new text to the suggestion service (default true)
	this.addSuggestion = function(text /*[,doPost ]*/) {
		if(text) self.userText = text;
		else return;
		if(arguments.length>1) var doPost = arguments[1];
		else var doPost = true;
		
		// Add suggestion to the list
		var found = false;
		var l=self.suggestions.length;
		for(var i=0; i<l;i++) {			
			if(self.suggestions[i].toLowerCase() == text.toLowerCase()) { 
				found=true;
				break;
			}
		}
		if(!found) 
			self.suggestions.push(text);
		
		// Post suggestion
		if(doPost) {
			if(window.ActiveXObject)
				HTTPReqPost = new ActiveXObject("Microsoft.XMLHTTP");
			else
				HTTPReqPost = new XMLHttpRequest();
			if(!HTTPReqPost || typeof HTTPReqPost.setRequestHeader=='undefined') return; /* not implemented in Opera < 8.01 */
			   
			HTTPReqPost.open("POST", self.typeAheadServiceURL, true);
			HTTPReqPost.setRequestHeader('Content-Type','application/x-www-form-urlencoded');   
			HTTPReqPost.send("inputid="+encodeURIComponent(self.listName)+"&inputtxt="+encodeURIComponent(self.userText)+"&user="+encodeURIComponent(self.userName));
			debug("request posted: "+"inputid="+encodeURIComponent(self.listName)+"&inputtxt="+encodeURIComponent(self.userText));
		}
	}
	

	// USER INPUT OPERATIONS  ------------------------------------------------------------------------------------------------

	// suggest: replace input value with suggested text.
	this.suggest = function(text) {

		if(text) self.userText = text;
		
		self.suggestedText = "";
		if(suggestedIndex==0 && pressedKeyCount==0) { // can't suggest if more than one key is pressed at the same time
			self.updateSuggestionList();
			if(self.matchingSuggestions.length>0) {
				self.suggestedText = self.matchingSuggestions[0];
				var startIndex = self.inputElement.value.length; 
				self.inputElement.value = self.suggestedText; 
				self.selectText(startIndex,  self.suggestedText.length);
			}
		}
		if(suggestedIndex>0) {
			// used up/down arrow key to select in the drop-down list			
			if(self.matchingSuggestions.length>0) {
				self.suggestedText = self.matchingSuggestions[suggestedIndex-1];			
				var startIndex = self.inputElement.value.length; 
				self.inputElement.value = self.suggestedText; 
				self.selectText(startIndex,  self.suggestedText.length);
			}
		}
		if(self.suggestedText=="" && isThereMoreOnServer) {
			// no matching suggestions in store, try to get more from the server.
			debug("loading more suggestions...");
			isWaitingForSuggestions = true;
			self.getSuggestions();			
		} else  
			isWaitingForSuggestions = false;

		self.showSuggestionList();
	}
	
	this.updateSuggestionList = function() {
		suggestedIndex = 0;
		var newSuggestionArray = new Array();
		//maxIdx = self.suggestions.length>self.maxItemInDropDown?self.maxItemInDropDown:self.suggestions.length;
		//don't use self.maxItemInDropDown - populate as many databases as necessary
		maxIdx = self.suggestions.length;
		
		for (var i=0; i < maxIdx; i++) {
			var pInputText = trim(self.userText.toLowerCase());
			var pTempSuggestion = trim(self.suggestions[i].toLowerCase());
//			var pInputText = self.userText.toLowerCase();
//			var pTempSuggestion = self.suggestions[i].toLowerCase();
			var pResult = pTempSuggestion.indexOf(pInputText);

			if(pResult == 0){
				newSuggestionArray[newSuggestionArray.length] = self.suggestions[i];
			} 
		}		
		self.matchingSuggestions = newSuggestionArray;
		
	}
//********************************************************************
//********************************************************************
	this.showSuggestionList = function() {
		//only display after user has hit a key
		if(self.userText.length > 0){
			var htmlList="";
			for (var i=0; i < self.matchingSuggestions.length; i++) { 
				if (self.matchingSuggestions[i].toLowerCase().indexOf(self.userText.toLowerCase()) == 0) {
					if(suggestedIndex-1 == i) 
						htmlList += "<li class='THLIHover' onclick='document.getElementById(\""+ self.inputId + "\").value=unescape(\""+ escape(self.matchingSuggestions[i]) +"\")' onmouseover='this.className+=\" THLIHover\"' onmouseout='this.className=this.className.replace(\"THLIHover\",\"\")' >"+self.matchingSuggestions[i]+"</li>";
					else
						htmlList += "<li onclick='document.getElementById(\""+ self.inputId + "\").value=unescape(\""+ escape(self.matchingSuggestions[i]) +"\")' onmouseover='this.className+=\" THLIHover\"' onmouseout='this.className=this.className.replace(\"THLIHover\",\"\")' >"+self.matchingSuggestions[i]+"</li>";
				} 			
			}
			if(false)
			//if(htmlList=="") 
				if(isWaitingForSuggestions)
					self.suggestionDropDown.innerHTML = "loading more suggestions...";				
				else
					self.suggestionDropDown.innerHTML = "Start typing to receive suggestions";
			else{
				self.suggestionDropDown.innerHTML = "<ul>"+htmlList+"</ul>";
			}

			//*********	
			if(tryIframe)
			showIframe();
			//*********	

			self.suggestionDropDown.className = self.suggestionDropDown.className.replace("THHideDropDown","THShowDropDown");
		


			
			
		}//end userText.length > 0
	}//end showSuggestionList()
//********************************************************************
//********************************************************************

	// UTILITY FUNCTIONS  ------------------------------------------------------------------------------------------------
	this.selectText = function(startIndex, nbChars) {
		if (self.inputElement.createTextRange) { // for Internet Explorer
			var txtRange = self.inputElement.createTextRange(); 
			txtRange.moveStart("character", startIndex); 
			txtRange.moveEnd("character", nbChars - self.inputElement.value.length);      
			txtRange.select();           
    	}
		else if (self.inputElement.setSelectionRange) { // for Mozilla
        	self.inputElement.setSelectionRange(startIndex, nbChars);
	    }     
		//set focus back to the textbox
		self.inputElement.focus();    
	}

	function getTop(obj) {
		var cur = 0;
		if(obj.offsetParent) {		
			while(obj.offsetParent) {
				cur+=obj.offsetTop;
				obj = obj.offsetParent;
			}
		}
		//var cur = obj.style.pixelTop;

		return cur+pTopOffset;
	}
	function getLeft(obj) {
		var cur = 0;
		if(obj.offsetParent) {		
			while(obj.offsetParent) {
				cur+=obj.offsetLeft;
				obj = obj.offsetParent;
			}
		}
		return cur+pLeftOffset;
	}
	function randomId() {
		var rId = "";
		for (var i=0; i<6;i++)
			rId += String.fromCharCode(97 + Math.floor((Math.random()*24)))
		return rId;
	}
	// Iframe utility functions--------------------------------------------
	/*function showIframe(){
            var divPopup=self.suggestionDropDown;
            
			//window.status = "count: "+pCount;
            if (!IsIE()) 
            {
                //Just display the div
                //divPopup.style.visibility ="visible";
                return;
		    }
		    		    
            //Increase default zIndex of div by 1, so that DIV appears before IFrame
            divPopup.style.zIndex=divPopup.style.zIndex+1;
			
			var iFrame = document.createElement("IFRAME");
			iFrame.setAttribute("src", "");
			
	        //Match IFrame position with divPopup
	        iFrame.style.position="absolute";
	        iFrame.style.left   =divPopup.offsetLeft + 'px';
			iFrame.style.top    =divPopup.offsetTop + 'px';
		    iFrame.style.width  =divPopup.offsetWidth + 'px';
			iFrame.style.height =divPopup.offsetHeight +100+ 'px';
			iFrame.id = "pIframeID";
			//iFrame.style.border ="green groove 2px";
		    document.body.appendChild(iFrame);
		    
		    //Store iFrame in global variable, so it can get removed when divPopup is hidden
		    pIframe=iFrame;
		    window.status = "dd height: " +divPopup.offsetHeight+" iFrame height: "+iFrame.style.height;
	        //divPopup.style.visibility ="visible";

	}
	function hideIframe(){
			
			var divPopup = self.suggestionDropDown;

			//divPopup.style.visibility = "hidden";
			
			if (IsIE()) 
		    {
		    	document.body.removeChild(pIframe);
	            pIframe=null;
	        }	
	}
	function IsIE()
	{
		return ( navigator.appName=="Microsoft Internet Explorer" ); 
	}*/	
	// INITIALIZE INSTANCE ------------------------------------------------------------------------------------------------
	this.init();
}

// Utility functions
var XBrowserAddHandler = function (target,eventName,handlerName) {
	if(!target) return;
	if (target.addEventListener) { 
		target.addEventListener(eventName, function(e){eval(handlerName)(e);}, false);
	} else if (target.attachEvent) { 
		target.attachEvent("on" + eventName, function(e){eval(handlerName)(e);});
		} else { 
		// THIS CODE NOT TESTED 
		var originalHandler = target["on" + eventName]; 
		if (originalHandler) { 
		  target["on" + eventName] = function(e){originalHandler(e);eval(handlerName)(e);}; 
		} else { 
		  target["on" + eventName] = eval(handlerName); 
		} 
	} 
}

function trim(s) {
  while (s.substring(0,1) == ' ') {
    s = s.substring(1,s.length);
  }
  while (s.substring(s.length-1,s.length) == ' ') {
    s = s.substring(0,s.length-1);
  }
  return s;
}

