var useBSNns;

if (useBSNns) {
	if (typeof(bsn) == "undefined") {
		bsn = {}
	}
	_bsn = bsn;
} else {    
	_bsn = this;
}

if (typeof(_bsn.Autosuggest) == "undefined") {
	_bsn.Autosuggest = {}
}

_bsn.AutoSuggest = function (fldID, param, form) {
//	no DOM - give up!
	if (!document.getElementById) {
		return false;
	}

//	get field via DOM
	this.fld = _bsn.DOM.getElement(fldID);

	this.searchForm = form;

	if (!this.fld) {
		return false;
	}

//	init variables
	this.sInput 		= "";
	this.nInputChars 	= 0;
	this.aSuggestions 	= [];
	this.iHighlighted 	= 0;
//	parameters object
	this.oP = (param) ? param : {};

//	defaults
	this.oP.minchars 		= (this.oP.minchars) 	  ? this.oP.minchars 	  : (locale == 'en_US') ? 3 : 1 ;
	this.oP.method 			= (this.oP.method) 		  ? this.oP.method 		  : "get" ;
	this.oP.varname 		= (this.oP.varname) 	  ? this.oP.varname 	  : "input" ;
	this.oP.className 		= (this.oP.className) 	  ? this.oP.className 	  : "autosuggest" ;
	this.oP.timeout 		= (this.oP.timeout) 	  ? this.oP.timeout 	  : 2500 ;
	this.oP.delay 			= (this.oP.delay) 		  ? this.oP.delay 		  : 500 ;
	this.oP.offsety 		= (this.oP.offsety) 	  ? this.oP.offsety 	  : -5 ;
	this.oP.shownoresults 	= (this.oP.shownoresults) ? this.oP.shownoresults : true ;
	this.oP.noresults 		= (this.oP.noresults) 	  ? this.oP.noresults 	  : "No results!" ;
	this.oP.maxheight 		= (this.oP.maxheight) 	  ? this.oP.maxheight 	  : 250 ;
//TODO create smart cache
	this.oP.cache 			= (this.oP.cache) 		  ? this.oP.cache 		  : false ;

//	set keyup handler for field
//	and prevent autocomplete from client
	var pointer = this;

//	NOTE: not using addEventListener because UpArrow fired twice in Safari
//	_bsn.DOM.addEvent( this.fld, 'keyup', function(ev){ return pointer.onKeyPress(ev); } );

	this.fld.onkeypress 	= function(ev){ return pointer.onKeyPress(ev); }
	this.fld.onkeyup 		= function(ev){ return pointer.onKeyUp(ev); }

	this.fld.setAttribute("autocomplete", "off");
}

_bsn.AutoSuggest.prototype.onKeyPress = function(ev) {
	var key = (window.event) ? window.event.keyCode : ev.keyCode;

//	set responses to keydown events in the field
//	this allows the user to use the arrow keys to scroll through the results
//	ESCAPE clears the list
//	TAB sets the current highlighted value
	var RETURN = 13;
	var TAB = 9;
	var ESC = 27;

	var bubble = true;
	switch(key)	{
		case RETURN:
			this.setHighlightedValue();
			this.submitText();
			bubble = false;
			break;
		case ESC:
			this.clearHighlight();
			this.clearSuggestions();
		break;
	}
	return bubble;
}

_bsn.AutoSuggest.prototype.onKeyUp = function(ev) {
	var key = (window.event) ? window.event.keyCode : ev.keyCode;

//	set responses to keydown events in the field
//	this allows the user to use the arrow keys to scroll through the results
//	ESCAPE clears the list
//	TAB sets the current highlighted value

	var ARRUP = 38;
	var ARRDN = 40;
	var ENTER = 13;

	var bubble = true;

	switch(key) {
		case ENTER:
			this.setHighlightedValue();
			this.submitText();
//		if doesnt work just comment it
			bubble = false;
			break;

		case ARRUP:
			this.changeHighlight(key);
			bubble = false;
		break;

		case ARRDN:
			this.changeHighlight(key);
			bubble = false;
		break;

		default:
			this.getSuggestions(this.fld.value);
	}

	return bubble;
}

_bsn.AutoSuggest.prototype.getSuggestions = function (val) {
//	if input stays the same, do nothing
	if (val == this.sInput) {
		return false;
	}

//	input length is less than the min required to trigger a request
//	reset input string
//	do nothing
	if (val.length < this.oP.minchars) {
		this.sInput = "";
		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.oP.cache) {
		var arr = [];
		
		for (var i=0;i<this.aSuggestions.length;i++) {
//			TODO rewrite		
			if (this.aSuggestions[i].value.substr(0,val.length).toLowerCase() == val.toLowerCase()) {
				arr.push( this.aSuggestions[i] );
			}
		}
		
		this.sInput = val;
		this.nInputChars = val.length;
		this.aSuggestions = arr;
		
		this.createList(this.aSuggestions);
		
		return false;
	} else {
//		do new request
		this.sInput = val;
		this.nInputChars = val.length;

		var pointer = this;
		clearTimeout(this.ajID);
		this.ajID = setTimeout( function() { pointer.doAjaxRequest() }, this.oP.delay );
	}

	return false;
}

_bsn.AutoSuggest.prototype.getSuggestList = function (value, callback) {
	var url = this.oP.script+this.oP.varname+"="+escape(value);
	var meth = this.oP.meth;
	var pointer = this;
	var onSuccessFunc = function (req) { callback(pointer.getSuggestedPersons(req)); };
	var onErrorFunc = function (status) {  };

	var myAjax = new _ajx.Ajax();
	myAjax.makeRequest( url, meth, onSuccessFunc, onErrorFunc );
}

_bsn.AutoSuggest.prototype.doAjaxRequest = function () {

	var pointer = this;

	// create ajax request
//	var url = this.oP.script+this.oP.varname+"="+escape(this.fld.value);
	var url = this.oP.script+this.oP.varname+"="+this.fld.value;
	var meth = this.oP.meth;

	var onSuccessFunc = function (req) { pointer.setSuggestions(req) };
	var onErrorFunc = function (status) {  };

	var myAjax = new _ajx.Ajax();
	myAjax.makeRequest( url, meth, onSuccessFunc, onErrorFunc );
}

/**
 * Usage:
 * 
 * var as_xml = new AutoSuggest('search', options_xml, document.searchForm);
 *
 * as_xml.listSuggestions("George", function(xml) {
 *     alert("xml = " + xml.responseText);
 * });
 */

_bsn.AutoSuggest.prototype.listSuggestions = function (value, callback) {
    // create ajax request
	var url = this.oP.script + this.oP.varname+"="+escape(value);
	var meth = this.oP.meth;

	var onSuccessFunc = function (req) { callback(req); };
	var onErrorFunc = function (status) {  };

	var myAjax = new _ajx.Ajax();
	myAjax.makeRequest( url, meth, onSuccessFunc, onErrorFunc );
}

_bsn.AutoSuggest.prototype.getSuggestedPersons = function(req) {
	var suggests = [];

	var xml = req.responseXML;

	var results = xml.getElementsByTagName('results')[0].childNodes;

	for (var i=0; i < results.length; i++) {
		if (results[i].hasChildNodes()) {
			var id = results[i].getAttribute('id');
			var attributes = new Object();
			var attrTags = results[i].getElementsByTagName('attr');
			for(var j = 0; j< attrTags.length; j++) {
				var attributeName = attrTags[j].getAttribute('name');
				var attributeValue = attrTags[j].getAttribute('value');
				attributes[attributeName] = attributeValue;
			}

			suggests.push( { 	'id' : id,
						        'attributes' : attributes });
		}
	}
	return suggests;
		
}

_bsn.AutoSuggest.prototype.setSuggestions = function (req) {
	this.aSuggestions = this.getSuggestedPersons(req);
	
	this.idAs = "as_"+this.fld.id;
	this.createList(this.aSuggestions);
}

_bsn.AutoSuggest.prototype.createList = function(arr) {
	var pointer = this;

//	no result
	if (arr.length == 0){
		return;
	}

//	get rid of old list
//	and clear the list removal timeout

	
	_bsn.DOM.removeElement(this.idAs);
	this.killTimeout();

//	create holding div

	var div = _bsn.DOM.createElement("div", {id:this.idAs, className:this.oP.className});	

//	create and populate ul

	var innerHtml = "<ul name=\"as_ul\" id=\"as_ul\">";

//	loop throught arr of suggestions
//	creating an LI element for each suggestion

	var buttonHtml = "";

	for (var i = 0; i < arr.length; i++) {
		var val = arr[i].attributes.fullname;
		var img = arr[i].attributes.img;
		var st = val.toLowerCase().indexOf( this.sInput.toLowerCase() );
		var name = i + 1;
		
		innerHtml += "<li wrap='nowrap' nowrap>"+
					 "<table cellspacing=\"0\" rowspacing=\"0\" width=\"100%\">"
					 + "<tr>"
					 + "<td style='background-color:#111111;width:36px;'>"
					 + "<img src=\"" + img + "\" style=\"width: 36px; border: 0px; vertical-align: top\" />"
					 + "</td>"
					 + "<td style=\"padding-left:7px;\" nowrap>" +
					 	"<a style=\"cursor:pointer;\" name=\""+name+"\" id=\""+name+"\">" 
					 		+ hightLightUserInput(val, this.sInput) + ""
					 + "</a>"
					 + "</td>"
					 + "</tr></table>";

	}

//	get position of target textfield
//	position holding div below it
//	set width of holding div to width of field
	var pos = _bsn.DOM.getPos(this.fld);

	div.style.left 		= pos.x + "px";
	div.style.top 		= ( pos.y + this.fld.offsetHeight + this.oP.offsety + 1) + "px";
	div.style.width 	= (this.fld.offsetWidth - 2) + "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() }

	div.innerHTML = innerHtml + "</ul>";
	
//	add DIV to document
	document.getElementsByTagName("body")[0].appendChild(div);

//	currently no item is highlighted
	this.iHighlighted = 0;
	
	var links =	div.getElementsByTagName("a");
	for(var i=0; i<links.length; i++) {
		var el = links[i];
		if (el.addEventListener) {
			el.addEventListener ("click",function () { pointer.setHighlightedValue(this.name); return false; },false);
			el.addEventListener ("mouseover",function () { pointer.setHighlight(this.name); },false);
			el.addEventListener ("mouseout",function () { pointer.clearHighlight(); },false);	
		} else {
			el.onclick = function () { pointer.setHighlightedValue(this.name); return false; };
			el.onmouseover = function () { pointer.setHighlight(this.name); };
			el.onmouseout = function () { pointer.clearHighlight(); };
		}
	}
	
//	remove list after an interval
	var pointer = this;
	this.toID = setTimeout(function () { pointer.clearSuggestions() }, this.oP.timeout);
}

function hightLightUserInput(name, input) {
	var result = name;
	var userInput = input.split(" ");
	for (var i=0; i < userInput.length; i++) {
		var partIndex = result.toLowerCase().search(userInput[i].toLowerCase());
		var toReplace = result.substring(partIndex, partIndex + userInput[i].length);
		result = result.replace(toReplace, "<em>" + toReplace + "</em>");
	}
	return result;
}


_bsn.AutoSuggest.prototype.submitText = function() {
}

_bsn.AutoSuggest.prototype.changeHighlight = function(key) {	
	var list = _bsn.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);
}

_bsn.AutoSuggest.prototype.setHighlight = function(n) {
	
	var list = _bsn.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";
//XXX
	this.killTimeout();
}

_bsn.AutoSuggest.prototype.clearHighlight = function() {
	var list = _bsn.DOM.getElement("as_ul");
	if (!list) {
		return false;
	}

	if (this.iHighlighted > 0) {
		list.childNodes[this.iHighlighted-1].className = "";
		this.iHighlighted = 0;
	}
}

_bsn.AutoSuggest.prototype.setHighlightedValue = function (iHighlighted) {
		if(iHighlighted == null)
			iHighlighted = this.iHighlighted;
		if(this.aSuggestions[ iHighlighted-1 ] == null || this.aSuggestions[ iHighlighted-1 ].attributes == null)
			return;
		this.sInput = this.fld.value = this.aSuggestions[ iHighlighted-1 ].attributes.fullname;

//		move cursor to end of input (safari)

		this.fld.focus();
		if (this.fld.selectionStart){
			this.fld.setSelectionRange(this.sInput.length, this.sInput.length);
		}
		this.clearSuggestions();

//		pass selected object to callback function, if exists
		if (typeof(this.oP.callback) == "function") {
			this.oP.callback( this.aSuggestions[iHighlighted-1] );
		}
		
		submitForm(this.aSuggestions[ iHighlighted-1 ].id, this.aSuggestions[ iHighlighted-1 ].attributes.fullname);
}

_bsn.AutoSuggest.prototype.killTimeout = function() {
	clearTimeout(this.toID);
}

_bsn.AutoSuggest.prototype.resetTimeout = function() {
	clearTimeout(this.toID);
	var pointer = this;
	this.toID = setTimeout(function () { pointer.clearSuggestions() }, 1000);
}

_bsn.AutoSuggest.prototype.clearSuggestions = function () {
	this.killTimeout();

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

// DOM PROTOTYPE

if (typeof(_bsn.DOM) == "undefined") {
	_bsn.DOM = {}
}

_bsn.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;
}

_bsn.DOM.clearElement = function ( id ) {
	var ele = this.getElement( id );

	if (!ele) {
		return false;
	}

	while (ele.childNodes.length) {
		ele.removeChild( ele.childNodes[0] );
	}
	return true;
}

_bsn.DOM.removeElement = function ( ele ) {
	var e = this.getElement(ele);

	if (!e) {
		return false;
	} else if (e.parentNode.removeChild(e)) {
		return true;
	} else {
		return false;
	}
}

_bsn.DOM.getElement = function ( ele ) {
	if (typeof(ele) == "undefined") {
		return false;
	} else if (typeof(ele) == "string") {
		var re = document.getElementById( ele );
//		be carefull
		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;
	}
}

_bsn.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(_bsn.Fader) == "undefined"){
	_bsn.Fader = {}
}

_bsn.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);
}

_bsn.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 {
//		other browser
		this.ele.style.opacity = op;
	}

	if (this.nTime == this.nDur){
		clearInterval( this.nID );
		if (this.callback != undefined){
			this.callback();
		}
	}
}

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