LibraryDM = this;
if (typeof(LibraryDM.Autosuggest) == "undefined") LibraryDM.Autosuggest = {}

LibraryDM.AutoSuggest = function (textBoxID, params) {
	if (!document.getElementById) return false;

	this.textBox = LibraryDM.DOM.getElement(textBoxID);
	if (!this.textBox) return false;
	
	this.currentValue = "";
	this.nInputChars = 0;
	this.aSuggestions = [];
	this.iHighlighted = 0;

	this.parameters = (params) ? params : {};
	
	if (!this.parameters.minChars) this.parameters.minChars = 3;
	if (!this.parameters.method) this.parameters.method = "get";
	if (!this.parameters.varname) this.parameters.varname = "input";
	if (!this.parameters.className) this.parameters.className = "autosuggest";
	if (!this.parameters.timeOut) this.parameters.timeOut = 10000;
	if (!this.parameters.delay) this.parameters.delay = 100;
	if (!this.parameters.offsetY) this.parameters.offsetY = 0;
	if (!this.parameters.showNoResults) this.parameters.showNoResults = true;
	if (!this.parameters.noResults) this.parameters.noResults = "No results!";
	if (!this.parameters.maxHeight && this.parameters.maxHeight !== 0) this.parameters.maxHeight = 250;
	if (!this.parameters.cache && this.parameters.cache != false) this.parameters.cache = true;

	var pointer = this;
	
	this.textBox.onkeypress = function(ev) { return pointer.onKeyPress(ev); }
	this.textBox.onkeyup = function(ev) { return pointer.onKeyUp(ev); }
	this.textBox.setAttribute("autocomplete","off");
}

LibraryDM.AutoSuggest.prototype = {
  onKeyPress: function(ev) {
    var key = (window.event) ? window.event.keyCode : ev.keyCode;
	  var keyReturn = 13;
	  var keyTab = 9;
	  var keyEsc = 27;
  	
	  var bubble = true;
	  switch(key) {
		  case keyReturn:
			  this.setHighlightedValue();
			  bubble = false;
			  break;
		  case keyTab:
			  this.clearSuggestions();
		    break;
		  case keyEsc:
			  this.clearSuggestions();
			  break;
	  }
	  return bubble;
  },

  onKeyUp: function(ev) {
	  var key = (window.event) ? window.event.keyCode : ev.keyCode;
	  var keyUp = 38;
	  var keyDown = 40;
	  var keyEsc = 27;
  	
	  var bubble = true;
	  switch(key) {
		  case keyUp:
			  this.changeHighlight(key);
			  bubble = false;
			  break;
		  case keyDown:
			  this.changeHighlight(key);
			  bubble = false;
			  break;
		  case keyEsc:
		    bubble = false;
		    break;
		  default:
			  this.getSuggestions(this.textBox.value);
	  }
	  return bubble;
  }, 

  getSuggestions: function (val) {
	  // if input stays the same, do nothing
	  if (val == this.currentValue) return false;
  	
	  // input length is less than the min required to trigger a request
	  if (val.length < this.parameters.minChars) {
		  this.currentValue = "";
		  this.clearSuggestions();
		  return false;
	  }

	  // if caching enabled, and user is typing (ie. length of input is increasing)
	  // filter results out of aSuggestions from last request
	  if (val.length>this.nInputChars && this.aSuggestions.length && this.parameters.cache) {
		  var arr = [];
		  for (var i=0;i<this.aSuggestions.length;i++)
			  if (this.aSuggestions[i].value.substr(0,val.length).toLowerCase() == val.toLowerCase()) arr.push( this.aSuggestions[i] );
  		
		  this.currentValue = val;
		  this.nInputChars = val.length;
		  this.aSuggestions = arr;
  		
		  this.createList(this.aSuggestions);

		  return false;
	  }
	  else {
		  this.currentValue = val;
		  this.nInputChars = val.length;
		  var pointer = this;
		  clearTimeout(this.ajID);
		  this.ajID = setTimeout( function() { pointer.doAjaxRequest() }, this.parameters.delay );
	  }
	  return false;
  }, 

  doAjaxRequest: function () {
	  var pointer = this;
  	
	  // create ajax request
	  var url = this.parameters.script + this.parameters.varname + "=" + escape(this.textBox.value) + "&ms=" + new Date().getTime();
	  var submitMethod = this.parameters.method;
  	
	  var onSuccessFunc = function (req) { pointer.setSuggestions(req) };
	  var onErrorFunc = function (status) { alert("AJAX error: " + status); };
	  var myAjax = new LibraryDM.Ajax();
	  myAjax.makeRequest( url, submitMethod, onSuccessFunc, onErrorFunc );
  },
  
  setSuggestions: function (req) {
	  this.aSuggestions = [];
  	
	  if (this.parameters.json) {
	    if (req.responseText != "") {
		    //alert(req.responseText);
		    var jsondata = eval('(' + req.responseText + ')');
		    for (var i=0;i<jsondata.results.length;i++)
			    this.aSuggestions.push(  { 'id':jsondata.results[i].id, 'value':jsondata.results[i].value, 'link':jsondata.results[i].link }  );
			}
	  }
	  else {
	    if(req.responseXML != "") {
		    var xml = req.responseXML;
    	
		    var results = xml.getElementsByTagName('results')[0].childNodes;
		    for (var i=0;i<results.length;i++) {
			    if (results[i].hasChildNodes())
				    this.aSuggestions.push(  { 'id':results[i].getAttribute('id'), 'value':results[i].childNodes[0].nodeValue, 'link':results[i].getAttribute('link') }  );
		    }
		  }
	  }
  	
	  this.idAs = "as_"+this.textBox.id;
	  this.createList(this.aSuggestions);
  },

  createList: function(arr) {
	  var pointer = this;

	  // get rid of old list
	  // and clear the list removal timeout
	  LibraryDM.DOM.removeElement(this.idAs);
	  this.killTimeout();

	  // create holding div
	  var div = LibraryDM.DOM.createElement("div", {id:this.idAs, className:this.parameters.className});

	  // create and populate ul
	  var ul = LibraryDM.DOM.createElement("ul", {id:"as_ul"});
	  // loop throught arr of suggestions
	  // creating an LI element for each suggestion
	  for (var i=0;i<arr.length;i++) {
		  // format output with the input enclosed in a EM element
		  // (as HTML, not DOM)
		  //var st = val.toLowerCase().indexOf( this.currentValue.toLowerCase() );
		  //var output = val.substring(0,st) + "<em>" + val.substring(st, st+this.currentValue.length) + "</em>" + val.substring(st+this.currentValue.length);

		  var span 		= LibraryDM.DOM.createElement("span", {}, arr[i].value, true);
		  /*if (arr[i].link != "") {
			  var br			= LibraryDM.DOM.createElement("br", {});
			  span.appendChild(br);
			  var small		= LibraryDM.DOM.createElement("small", {}, arr[i].link);
			  span.appendChild(small);
		  }*/

		  var a 			= LibraryDM.DOM.createElement("a", { href:"#" });

		  a.appendChild(span);

		  a.name = i+1;
		  a.onclick = function () { pointer.setHighlightedValue(); return false; }
		  a.onmouseover = function () { pointer.setHighlight(this.name); }

		  var li 			= LibraryDM.DOM.createElement(  "li", {}, a  );

		  ul.appendChild( li );
	  }

	  // no results
	  if (arr.length == 0 && this.parameters.showNoResults) {
		  var li = LibraryDM.DOM.createElement(  "li", {className:"as_warning"}, this.parameters.noResults  );
		  ul.appendChild( li );
	  }

	  div.appendChild( ul );

	  // get position of target textfield
	  // position holding div below it
	  // set width of holding div to width of field
	  var pos = LibraryDM.DOM.getPos(this.textBox);
  	
	  div.style.left 		= pos.x + "px";
	  div.style.top 		= ( pos.y + this.textBox.offsetHeight + this.parameters.offsetY ) + "px";
	  div.style.width 	= this.textBox.offsetWidth + "px";

	  // set mouseover functions for div
	  // when mouse pointer leaves div, set a timeout to remove the list after an interval
	  // when mouse enters div, kill the timeout so the list won't be removed
	  div.onmouseover 	= function(){ pointer.killTimeout() }
	  div.onmouseout 		= function(){ pointer.resetTimeout() }

	  // add DIV to document
	  document.getElementsByTagName("body")[0].appendChild(div);
    
	  // currently no item is highlighted
	  this.iHighlighted = 0;

	  // remove list after an interval
	  var pointer = this;
	  this.toID = setTimeout(function () { pointer.clearSuggestions() }, this.parameters.timeOut);
  },

  changeHighlight: function(key) {
	  var list = LibraryDM.DOM.getElement("as_ul");
	  if (!list) return false;
  	
	  var n;
	  if (key == 40) n = this.iHighlighted + 1;
	  else if (key == 38) n = this.iHighlighted - 1;

	  if (n > list.childNodes.length) n = list.childNodes.length;
	  if (n < 1) n = 1;

	  this.setHighlight(n);
  },

  setHighlight: function(n) {
	  var list = LibraryDM.DOM.getElement("as_ul");
	  if (!list) return false;
  	
	  if (this.iHighlighted > 0) this.clearHighlight();
  	
	  this.iHighlighted = Number(n);
  	
	  list.childNodes[this.iHighlighted-1].className = "as_highlight";
	  this.killTimeout();
  },

  clearHighlight: function() {
	  var list = LibraryDM.DOM.getElement("as_ul");
	  if (!list) return false;
  	
	  if (this.iHighlighted > 0) {
		  list.childNodes[this.iHighlighted-1].className = "";
		  this.iHighlighted = 0;
	  }
  },

  setHighlightedValue: function () {
	  if (this.iHighlighted) {
		  // this.currentValue = this.textBox.value = this.aSuggestions[ this.iHighlighted-1 ].value; ########
		  this.currentValue = this.aSuggestions[ this.iHighlighted-1 ].value;
  		
		  // move cursor to end of input (safari)
		  this.textBox.focus();
		  if (this.textBox.selectionStart) this.textBox.setSelectionRange(this.currentValue.length, this.currentValue.length);
  		
		  this.clearSuggestions();
  		
		  // pass selected object to callBack function, if exists
		  if (typeof(this.parameters.callBack) == "function") this.parameters.callBack( this.aSuggestions[this.iHighlighted-1] );
	  }
  },

  killTimeout: function() {
	  clearTimeout(this.toID);
  },

  resetTimeout: function() {
	  clearTimeout(this.toID);
	  var pointer = this;
	  this.toID = setTimeout(function () { pointer.clearSuggestions() }, 2000);
  },

  clearSuggestions: function () {
	  this.killTimeout();
  	
	  var ele = LibraryDM.DOM.getElement(this.idAs);
	  var pointer = this;
	  if (ele) var fade = new LibraryDM.Fader(ele,1,0,250,function () { LibraryDM.DOM.removeElement(pointer.idAs) });
  }
}

// AJAX PROTOTYPE _____________________________________________
if (typeof(LibraryDM.Ajax) == "undefined") LibraryDM.Ajax = {}

LibraryDM.Ajax = function () {
	this.req = {};
	this.isIE = false;
}

LibraryDM.Ajax.prototype.makeRequest = function (url, method, onComp, onErr) {
	if (method != "POST") method = "GET";

	this.onComplete = onComp;
	this.onError = onErr;

	var pointer = this;

	// branch for native XMLHttpRequest object
	if (window.XMLHttpRequest) {
		this.req = new XMLHttpRequest();
		this.req.onreadystatechange = function () { pointer.processReqChange() };
		this.req.open("GET", url, true); //
		this.req.send(null);
	// branch for IE/Windows ActiveX version
	}
	else if (window.ActiveXObject) {
		this.req = new ActiveXObject("Microsoft.XMLHTTP");
		if (this.req) {
			this.req.onreadystatechange = function () { pointer.processReqChange() };
			this.req.open(method, url, true);
			this.req.send();
		}
	}
}

LibraryDM.Ajax.prototype.processReqChange = function() {
	// only if req shows "loaded"
	if (this.req.readyState == 4) {
		// only if "OK"
		if (this.req.status == 200) this.onComplete( this.req );
		else this.onError( this.req.status );
	}
}

// DOM PROTOTYPE _____________________________________________
if (typeof(LibraryDM.DOM) == "undefined") LibraryDM.DOM = {}

LibraryDM.DOM.createElement = function ( type, attr, cont, html ) {
	var ne = document.createElement( type );
	if (!ne) return false;
		
	for (var a in attr) ne[a] = attr[a];
		
 	if (typeof(cont) == "string" && !html) ne.appendChild( document.createTextNode(cont) );
	else if (typeof(cont) == "string" && html) ne.innerHTML = cont;
	else if (typeof(cont) == "object") ne.appendChild( cont );
	return ne;
}

LibraryDM.DOM.clearElement = function ( id ) {
	var ele = this.getElement( id );
	if (!ele) return false;
	
	while (ele.childNodes.length) ele.removeChild( ele.childNodes[0] );
	
	return true;
}

LibraryDM.DOM.removeElement = function ( ele ) {
	var e = this.getElement(ele);
	
	if (!e) return false;
	else if (e.parentNode.removeChild(e)) return true;
	else return false;
}

LibraryDM.DOM.replaceContent = function ( id, cont, html ) {
	var ele = this.getElement( id );
	if (!ele) return false;
	this.clearElement( ele );

	if (typeof(cont) == "string" && !html) ele.appendChild( document.createTextNode(cont) );
	else if (typeof(cont) == "string" && html) ele.innerHTML = cont;
	else if (typeof(cont) == "object") ele.appendChild( cont );
}

LibraryDM.DOM.getElement = function (ele) {
	if (typeof(ele) == "undefined") return false;
	else if (typeof(ele) == "string") {
		var re = document.getElementById( ele );
		if (!re) return false;
		else if (typeof(re.appendChild) != "undefined") return re;
		else return false;
	}
	else if (typeof(ele.appendChild) != "undefined") return ele;
	else return false;
}

LibraryDM.DOM.appendChildren = function ( id, arr ) {
	var ele = this.getElement( id );
	if (!ele) return false;

	if (typeof(arr) != "object") return false;

	for (var i=0;i<arr.length;i++) {
		var cont = arr[i];
		if (typeof(cont) == "string") ele.appendChild( document.createTextNode(cont) );
		else if (typeof(cont) == "object") ele.appendChild( cont );
	}
}

LibraryDM.DOM.getPos = function ( ele ) {
	var ele = this.getElement(ele);
	var obj = ele;
	var curleft = 0;
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if (obj.x) curleft += obj.x;
	var obj = ele;
	
	var curtop = 0;
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y) curtop += obj.y;
	return {x:curleft, y:curtop}
}

// FADER PROTOTYPE _____________________________________________
if (typeof(LibraryDM.Fader) == "undefined") LibraryDM.Fader = {}

LibraryDM.Fader = function (ele, from, to, fadetime, callBack) {
	if (!ele)
		return false;
	
	this.ele = ele;
	
	this.from = from;
	this.to = to;
	
	this.callBack = callBack;
	
	this.nDur = fadetime;
		
	this.nInt = 50;
	this.nTime = 0;
	
	var p = this;
	this.nID = setInterval(function() { p._fade() }, this.nInt);
}

LibraryDM.Fader.prototype._fade = function() {
	this.nTime += this.nInt;
	
	var ieop = Math.round( this._tween(this.nTime, this.from, this.to, this.nDur) * 100 );
	var op = ieop / 100;
	
	if (this.ele.filters) { // internet explorer
		try {
			this.ele.filters.item("DXImageTransform.Microsoft.Alpha").opacity = ieop;
		} catch (e) { 
			// If it is not set initially, the browser will throw an error.  This will set it if it is not set yet.
			this.ele.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity='+ieop+')';
		}
	}
	else this.ele.style.opacity = op;
	
	if (this.nTime == this.nDur) {
		clearInterval( this.nID );
		if (this.callBack != undefined) this.callBack();
	}
}

LibraryDM.Fader.prototype._tween = function(t,b,c,d) {
	return b + ( (c-b) * (t/d) );
}
