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 : {};
	
	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].html.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.script.indexOf("?") >= 0 ? "&" : "?") + this.parameters.varname + "=" + escape(this.textBox.value) + "&ms=" + new Date().getTime();
  	
	  var onSuccessFunc = function (req) { pointer.setSuggestions(req) };
	  var onErrorFunc = function (status) { /* alert("Error: " + status); ######## */ };
	  var myAjax = new LibraryDM.AjaxSuggest();
	  myAjax.makeRequest( url, this.submitMethod, onSuccessFunc, onErrorFunc );
  },
  
  setSuggestions: function (req) {
	  this.aSuggestions = [];
  	
	  if (this.parameters.xml && 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'), 'html':results[i].childNodes[0].nodeValue, 'link':results[i].getAttribute('link') }  );
      }
	  }
	  else if (req.responseText != "") {
      var jsondata = eval('(' + req.responseText + ')');
      for (var i=0; i < jsondata.results.length; i++)
        this.aSuggestions.push({ 'id':jsondata.results[i].id, 'html':jsondata.results[i].html, 'link':jsondata.results[i].link });
	  }
  	
	  this.idAs = this.textBox.id + 'AutoSuggest';
	  this.createList(this.aSuggestions);
  },

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

	  // 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:this.textBox.id + "AutoSuggestUL"});

    if (arr.length > 0) {
      for (var i = 0; i < arr.length; i++) {
        var span 		= LibraryDM.DOM.createElement("span", {}, arr[i].html, true);
        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 );
      }
      showDiv = true;
    }
    else if (this.parameters.showNoResults) {
      var li = LibraryDM.DOM.createElement(  "li", {id:this.textBox.id + "NoResults", className:"warning"}, this.parameters.noResults  );
      ul.appendChild( li );
      showDiv = true;
    }

    if (showDiv) {
      div.appendChild( ul );
      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.parameters.width + "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(this.textBox.id + "AutoSuggestUL");
	  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(this.textBox.id + "AutoSuggestUL");
	  if (!list) return false;
  	
    if (list.childNodes.length > 0 && list.childNodes[0].id != this.textBox.id + "NoResults") {
      if (this.iHighlighted > 0) this.clearHighlight();
      this.iHighlighted = Number(n);
      list.childNodes[this.iHighlighted-1].className = "highLight";
    }

	  this.killTimeout();
  },

  clearHighlight: function() {
	  var list = LibraryDM.DOM.getElement(this.textBox.id + "AutoSuggestUL");
	  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 ].html;
  		
		  // 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.onResultSelected) == "function") this.parameters.onResultSelected( 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.AjaxSuggest) == "undefined") LibraryDM.AjaxSuggest = {}

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

LibraryDM.AjaxSuggest.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(method, 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.AjaxSuggest.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) );
}