function scrollable(divId, firstCellId, secondCellId, speed, enableRandom, itemNum, itemWidth) {
	this.div = document.getElementById(divId);
	this.firstCell = document.getElementById(firstCellId);
	this.secondCell = document.getElementById(secondCellId);
	this.speed = speed;
	this.secondCell.innerHTML = this.firstCell.innerHTML;
	//this.div.scrollLeft = this.div.scrollWidth;
	if ( enableRandom == true )
		this.div.scrollLeft = (Math.floor(Math.random()*itemNum)) * itemWidth;
	else
		this.div.scrollLeft = 0;
	this.marquee = null;
	
	this.prev = function() {
		if ( this.marquee != null )
		{
			clearInterval(this.marquee);
			this.marquee = null;
		}
		
		var instance = this;
		this.marquee = setInterval(function(){instance.doLeftMarquee();}, instance.speed);
		this.div.onmouseover = function(){clearInterval(instance.marquee);instance.marquee=null;}
		this.div.onmouseout = function(){var i = instance;instance.marquee = setInterval(function(){i.doLeftMarquee();}, i.speed);}
	}
	
	this.next = function() {
		if ( this.marquee != null )
		{
			clearInterval(this.marquee);
			this.marquee = null;
		}
		
		var instance = this;
		this.marquee = setInterval(function(){instance.doRightMarquee();}, instance.speed);
		this.div.onmouseover = function(){clearInterval(instance.marquee)}
		this.div.onmouseout = function(){var i = instance;instance.marquee = setInterval(function(){i.doRightMarquee();}, i.speed);}
	}
	
	this.doLeftMarquee = function() {
		if ( this.secondCell.offsetWidth-this.div.scrollLeft <= 0 )
			this.div.scrollLeft -= this.firstCell.offsetWidth;
		else
			this.div.scrollLeft++;
	}
	
	this.doRightMarquee = function() {
		if ( this.div.scrollLeft <= 0 )
			this.div.scrollLeft += this.secondCell.offsetWidth;
		else
			this.div.scrollLeft--;
	}
	
	this.prev();
}

function rollOverImg(name, path) {
	document[name].src = path;
}

/*
 * Utility class
 */
var deffect = {

	popUpLayerId: "popUpLayer",
	divPopUpContainer: null,
	popUpLayerBackgroundId: "popUpLayerBg",
	divPopUpBackground: null,
	image_interval: null,
	checking_images_interval: 50,
	image_timeOut: null,
	checking_images_timeOut: 3000,
	autocompleter:{defaults:{
		inputClass: "ac_input",
		resultsClass: "ac_results",
		loadingClass: "ac_loading",
		headerClass: "ac_header",
		minChars: 1,
		delay: 400,
		matchCase: false,
		matchSubset: true,
		matchContains: false,
		cacheLength: 10,
		max: 100,
		mustMatch: false,
		extraParams: {},
		selectFirst: false,
		formatItem: function(row) { return row[0]; },
		formatMatch: null,
		autoFill: false,
		width: 0,
		multiple: false,
		multipleSeparator: ", ",
		highlight: function(value, term) {
			return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
		},
		scroll: true,
		scrollHeight: 180,
		includeHeader: true,
		isHeader: function(row) {
			if ( row[0].substring(0,1) == "[" && row[0].substring(row[0].length-1) == "]" )
				return true;
			return false;
		},
		formatHeader: function(row) { return row[0].substring(1, row[0].length-1); }
	}},
	userAgent: navigator.userAgent.toLowerCase(),
	onload_event_timeout: 0,
	array: {
		inArray:function(object, array) {
			for ( var i=0; i<array.length; i++ ) {
				if ( array[i] === object ) { return i; }
			}
			return -1;
		},
		
		remove:function(array, from, to) {
			var rest = array.slice((to || from) + 1 || array.length);
			array.length = from < 0 ? array.length + from : from;
			return array.push.apply(array, rest);
		}
	},

	$:function() {
		if ( arguments.length > 0 && typeof arguments[0] == 'function' ) {
			deffect.addLoadEvent(arguments[0]);
		} else if ( arguments.length == 1 && this.isElement(arguments[0]) ) {
			arguments[0].height = function() {
				return deffect.css(this, "height");
			}
			return arguments[0];
		} else {
			var oElm = document;
			var strTagName = "*";
			var separator = ".";
			var strClassName = arguments[0];
			if ( arguments.length > 1 ) {
				if ( arguments[0] != null )
					oElm = arguments[0];
				strClassName = arguments[1];
			}
			var arrayOfStr = strClassName.split(separator);
			if ( arrayOfStr.length > 1 ) {
				strTagName = arrayOfStr[0];
				strClassName = arrayOfStr[1];
			}
			var arrReturnElements = deffect.getElements(oElm, strTagName, strClassName);
			if ( arrReturnElements.length == 0 ) { arrReturnElements = deffect.getElements(oElm, strClassName, ""); }
			var lastshowntabindex, lastshownpaneindex;
			
			arrReturnElements.tabs = function() {
				if ( arguments.length > 0 ) {
					var R = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g;
					var Z = [];
					var nodes = document;
					var panes;
					while ( (W=R.exec(arguments[0]))!==null ) {
						Z.push(W[1]);	
					}
					for ( var i=0; i<Z.length; i++ ) {
						panes = deffect.$(nodes, Z[i]);
						if ( panes != null && panes.length > 0 ) {
							nodes = panes[0];
						}
					}
					if ( panes != null && panes.length > 0 ) {
						for ( var i=0; i<arrReturnElements.length; i++ ) {
							if ( arrReturnElements[i].hasChildNodes() ) {
								var temp = 0;
								for ( var j=0; j<arrReturnElements[i].childNodes.length; j++ ) {
									if ( arrReturnElements[i].childNodes[j].nodeType != 1 ) { continue; }
									if ( panes.length-1 >= temp ) {
										deffect.attr(arrReturnElements[i].childNodes[j], "tabsindex", j);
										deffect.attr(arrReturnElements[i].childNodes[j], "paneindex", temp);
										if ( temp == 0 ) {
											showTab(arrReturnElements[i].childNodes[j]);
										}
										var onclick = arguments[1];
										arrReturnElements[i].childNodes[j].onclick = function() {
											if ( lastshowntabindex != null && lastshowntabindex != parseInt(this.getAttribute("tabsindex")) ) {
												this.parentNode.childNodes[lastshowntabindex].className = "";
												if ( this.parentNode.childNodes[lastshowntabindex].hasChildNodes() ) {
													for ( var k=0; k<this.parentNode.childNodes[lastshowntabindex].childNodes.length; k++ ) {
														if ( this.parentNode.childNodes[lastshowntabindex].childNodes[k].nodeType != 1 ) { continue; }
														this.parentNode.childNodes[lastshowntabindex].childNodes[k].className = "";
													}
												}
												panes[lastshownpaneindex].style.display = "none";
											}
										
											if ( panes[parseInt(this.getAttribute("paneindex"))].style == null
												|| panes[parseInt(this.getAttribute("paneindex"))].style.display == ""
												|| panes[parseInt(this.getAttribute("paneindex"))].style.display == "none" )
												showTab(this);
												
											if ( typeof onclick == 'function' )
												onclick(this);
										}
										temp++;
									}
								}
							}
						}
					}
				}
				
				function showTab(object) {
					object.className = "current";
					if ( object.hasChildNodes() ) {
						for ( var i=0; i<object.childNodes.length; i++ ) {
							if ( object.childNodes[i].nodeType != 1 ) { continue; }
							object.childNodes[i].className = "current";
						}
					}
					panes[parseInt(object.getAttribute("paneindex"))].style.display = "block";
					lastshowntabindex = parseInt(object.getAttribute("tabsindex"));
					lastshownpaneindex = parseInt(object.getAttribute("paneindex"));
				}
			}
			
			arrReturnElements.autocomplete = function(urlOrData, options) {
				var isUrl = typeof urlOrData == "string";
				var autocompleters = [];
				/*
				options = {
					url: isUrl ? urlOrData : null,
					data: isUrl ? null : urlOrData,
					delay: isUrl ? deffect.autocompleter.defaults.delay : 10,
					max: options && !options.scroll ? 10 : 150,
					minChars: options && options.minChars ? options.minChars : deffect.autocompleter.defaults.minChars,
					matchCase: options && options.matchCase ? options.matchCase : deffect.autocompleter.defaults.matchCase
				};
				*/
				options = deffect.extend({}, deffect.autocompleter.defaults, {
					url: isUrl ? urlOrData : null,
					data: isUrl || typeof urlOrData == 'function' ? null : urlOrData,
					trigger: typeof urlOrData == 'function' ? urlOrData : null,
					delay: isUrl ? deffect.autocompleter.defaults.delay : 10,
					max: options && !options.scroll ? 10 : 150
				}, options);
				
				// if highlight is set to false, replace it with a do-nothing function
				options.highlight = options.highlight || function(value) { return value; };
				
				// if the formatMatch option is not specified, then use formatItem for backwards compatibility
				options.formatMatch = options.formatMatch || options.formatItem;
				
				for ( var i=0; i<arrReturnElements.length; i++ ) {
					autocompleters.push(new autocompleter(arrReturnElements[i], options));
				}
				
				return autocompleters;
			}
			
			return (arrReturnElements);
		}
	},
	
	getElements:function(oElm, strTagName, strClassName) {
		var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName);
		var arrReturnElements = [];
		if ( strClassName.charAt(0) == "#" ) {
			arrElements = new Array();
			var element = oElm.getElementById(strClassName.substring(1));
			if ( element != null )
				arrReturnElements.push(element);
		}
		for(var i=0; i<arrElements.length; i++){
			var temp = arrElements[i];      
			if(deffect.check(temp, strClassName)){
				arrReturnElements.push(temp);
			}
		}
		return arrReturnElements;
	},
	
	check:function(oElm, strClassName) {
		strClassName = strClassName.replace(/\-/g, "\\-");
		var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
		return oRegExp.test(oElm.className);
	},
	
	isElement:function(o) {
		return (
			typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
			typeof o === "object" && o.nodeType === 1 && typeof o.nodeName==="string"
		);
	},
	
	addLoadEvent:function(func) {
		var oldonload = window.onload;
		if (typeof window.onload != 'function') {
			window.onload = func;
		} else {
			window.onload = function() {
				if (oldonload)
					oldonload();
				func();
				/*
				setTimeout(func, this.onload_event_timeout);
				this.onload_event_timeout = this.onload_event_timeout + 500;
				*/
			}
		}
	},
	
	attr:function(object, name, index) {
		object.setAttribute(name, index+name.charAt(0));
	},
	
	openPopUpDiv:function(url, config) {
		if ( deffect.divPopUpContainer == null ) {
			deffect.divPopUpContainer = deffect.createEl('div',
				{'class':"popUpLayer", id:deffect.popUpLayerId, name:deffect.popUpLayerId},
				{},
				'');
			document.body.appendChild(deffect.divPopUpContainer);
			/* *** IMPORTANT: This is a BUG FIX for Internet Explorer *** */
			//if ( self.frames[deffect.popUpLayerId].name != deffect.popUpLayerId )
				//self.frames[deffect.popUpLayerId].name = deffect.popUpLayerId;
		}
		if ( deffect.divPopUpBackground == null ) {
			deffect.divPopUpBackground = deffect.createEl('div',
				{'class':"popUpLayerBg", id:deffect.popUpLayerBackgroundId, name:deffect.popUpLayerBackgroundId},
				{},
				'');
			document.body.appendChild(deffect.divPopUpBackground);
		}
		
		var request = connect.asyncRequest('GET', url, {success:callback, argument:config});

		return false;

		function callback(o) {
			if ( o.responseText != undefined ) {
				
				deffect.divPopUpContainer.innerHTML = o.responseText;
				
				// Register javascript function embedded in popup
				var script = deffect.divPopUpContainer.getElementsByTagName("script");
				for ( var i=0; i<script.length; i++ ) {
					eval(script[i].text);
				}
				
				//deffect.divPopUpContainer.style.width = "600px";
				//deffect.divPopUpContainer.style.height = "600px";
				deffect.divPopUpContainer.style.overflow = "auto";
				deffect.divPopUpContainer.style.position = "absolute";
				deffect.divPopUpContainer.style.visibility = "hidden";
				deffect.divPopUpContainer.style.zIndex = 2;
				deffect.divPopUpContainer.style.backgroundColor = "#FFFFFF";
				deffect.divPopUpContainer.className = "overlayborder";
			
			}
			
			if ( o.argument != undefined )
			{
				var configuration = o.argument;
							
				if ( configuration.width )
					deffect.divPopUpContainer.style.width = configuration.width + "px";
					
				if ( configuration.height )
					deffect.divPopUpContainer.style.height = configuration.height + "px";
				
				if ( configuration.layercentered ) {
					var images = deffect.divPopUpContainer.getElementsByTagName("img");
					deffect.handleImages(images,
						function() {
							center(deffect.divPopUpContainer);
						}
					);
				} else if ( configuration.x != null || configuration.y != null ) {
					deffect.divPopUpContainer.style.left = parseInt(configuration.x + getDocumentScrollLeft()) + "px";
					deffect.divPopUpContainer.style.top = parseInt(configuration.y + getDocumentScrollTop()) + "px";
					
					deffect.divPopUpContainer.style.visibility = "visible";
				} else {
					deffect.divPopUpContainer.style.left = "0px";
					deffect.divPopUpContainer.style.top = "0px";
					
					deffect.divPopUpContainer.style.visibility = "visible";
				}
				
				if ( parseInt(deffect.divPopUpContainer.style.left) < 0 )
					deffect.divPopUpContainer.style.left = "0px";
				if ( parseInt(deffect.divPopUpContainer.style.top) < 0 )
					deffect.divPopUpContainer.style.top = "0px";
					
				if ( configuration.modal ) {
					deffect.divPopUpBackground.style.width = getDocumentWidth() + 'px';
					deffect.divPopUpBackground.style.height = getDocumentHeight() + 'px';
					deffect.divPopUpBackground.style.visibility = "visible";
					deffect.divPopUpBackground.style.zIndex = 1;
					deffect.divPopUpBackground.style.opacity = 0.5;
					deffect.divPopUpBackground.style.filter = "alpha(opacity=50)";
					//deffect.divPopUpBackground.style.backgroundColor = "#000000"; // Black color
					deffect.divPopUpBackground.style.backgroundColor = "#FFFFFF";
					deffect.divPopUpBackground.style.left = "0px";
					deffect.divPopUpBackground.style.top = "0px";
					deffect.divPopUpBackground.style.overflow = "auto";
					deffect.divPopUpBackground.style.position = "absolute";
				}
			}
				
			//deffect.divPopUpContainer.style.visibility = "visible";
			
			function getDimensions(element) {
				var display = element.style.display;
				if (display != 'none' && display != null) // Safari bug
					return {width: element.offsetWidth, height: element.offsetHeight};

				var originalWidth = element.clientWidth;
				var originalHeight = element.clientHeight;
				return {width: originalWidth, height: originalHeight};
			}
			
			function getViewportWidth() {
				var O = self.innerWidth;
				var P = document.compatMode;
				if ( P ) { O=(P=="CSS1Compat")?document.documentElement.clientWidth:document.body.clientWidth; }
				return O;
			}
				
			function getViewportHeight() {
				var O = self.innerHeight;
				var P = document.compatMode;
				if ( P ) { O=(P=="CSS1Compat")?document.documentElement.clientHeight:document.body.clientHeight; }
				return O;
			}
			
			function getDocumentScrollLeft(O) {
				O = O||document;
				return Math.max(O.documentElement.scrollLeft, O.body.scrollLeft);
			}
			
			function getDocumentScrollTop(O) {
				O = O||document;
				return Math.max(O.documentElement.scrollTop, O.body.scrollTop);
			}
			
			function getDocumentHeight(){
				var P=(document.compatMode!="CSS1Compat")?document.body.scrollHeight:document.documentElement.scrollHeight;
				var O=Math.max(P, getViewportHeight());
				return O;
			}

			function getDocumentWidth(){
				var P=(document.compatMode!="CSS1Compat")?document.body.scrollWidth:document.documentElement.scrollWidth;
				var O=Math.max(P, getViewportWidth());
				return O;
			}
			
			function center(element) {
				// retrieve required dimensions
				var eltDims = getDimensions(element);
					
				// calculate the center of the page using the browser and element dimensions
				var x = parseInt((getViewportWidth() - eltDims.width) / 2 + getDocumentScrollLeft());
				var y = parseInt((getViewportHeight() - eltDims.height) / 2 + getDocumentScrollTop());
					
				element.style.left = x + "px";
				element.style.top = y + "px";

				element.style.visibility = "visible";
			}
		}
	},
	
	createEl:function(tagName, arr_attributes, arr_style, innerText) {
		var element = document.createElement(tagName);
		if (arr_attributes) {
			for (var k in arr_attributes) {
				if (k == 'class')
					element.className = arr_attributes[k];
				else if (k == 'id')
					element.id = arr_attributes[k];
				else
					element.setAttribute(k, arr_attributes[k]);
			}
		}
		if (arr_style) {
			for (var k in arr_style)
				element.style[k] = arr_style[k];
		}
		if (innerText)
			element.appendChild(document.createTextNode(innerText));
		return element;
	},
	
	closePopUpDiv:function() {
		if ( deffect.divPopUpContainer != null ) {
			deffect.divPopUpContainer.innerHTML = "";
			deffect.divPopUpContainer.style.visibility = "hidden";
		}
		if ( deffect.divPopUpBackground != null )
			deffect.divPopUpBackground.style.visibility = "hidden";
		return false;
	},
	
	isImageReady:function(img) {
		// During the onload event, IE correctly identifies any images that
		// werenˇ¦t downloaded as not complete. Others should too. Gecko-based
		// browsers act like NS4 in that they report this incorrectly.
		if ( !img.complete )
			return false;
	
		// However, they do have two very useful properties:naturalWidth and
		// naturalHeight. These give the true size of the image. If it failed
		// to load, either of these should be zero.
		if ( typeof img.naturalWidth != "undefined" && img.naturalWidth == 0 )
			return false;
	
		return true;
	},
	
	areImagesReady:function(images) {
		for ( var i=0; i<images.length; i++ ) {
			if ( !this.isImageReady(images[i]) )
				return false;
		}
		return true;
	},
	
	handleImages:function(images, handler) {
		var S = this;
		if ( this.image_interval == null ) {
			if ( this.checking_images_timeOut ) {
				this.image_timeOut = window.setTimeout(
					function(){
						window.clearInterval(S.image_interval);
						S.image_interval = null;
						handler();
					}, this.checking_images_timeOut
				);
			}
			this.image_interval = window.setInterval(
				function() {
					if ( S.areImagesReady(images) ) {
						window.clearInterval(S.image_interval);
						S.image_interval = null;
						if ( S.checking_images_timeOut ) { window.clearTimeout(S.image_timeOut); }
						handler();
					}
				}, this.checking_images_interval
			);
		}
	},
	
	extend:function() {
		// copy reference to target object
		var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options;
	
		// Handle a deep copy situation
		if ( target.constructor == Boolean ) {
			deep = target;
			target = arguments[1] || {};
			// skip the boolean and the target
			i = 2;
		}
	
		// Handle case when target is a string or something (possible in deep copy)
		if ( typeof target != "object" && typeof target != "function" )
			target = {};
	
		// extend jQuery itself if only one argument is passed
		if ( length == i ) {
			target = this;
			--i;
		}
	
		for ( ; i < length; i++ )
			// Only deal with non-null/undefined values
			if ( (options = arguments[ i ]) != null )
				// Extend the base object
				for ( var name in options ) {
					var src = target[ name ], copy = options[ name ];
	
					// Prevent never-ending loop
					if ( target === copy )
						continue;
	
					// Recurse if we're merging object values
					if ( deep && copy && typeof copy == "object" && !copy.nodeType )
						target[ name ] = jQuery.extend( deep, 
							// Never move original objects, clone them
							src || ( copy.length != null ? [ ] : { } )
						, copy );
	
					// Don't bring in undefined values
					else if ( copy !== undefined )
						target[ name ] = copy;
	
				}
	
		// Return the modified object
		return target;
	},
	
	browser:function() {
		return {
			version: (this.userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
			safari: /webkit/.test( this.userAgent ),
			opera: /opera/.test( this.userAgent ),
			msie: /msie/.test( this.userAgent ) && !/opera/.test( this.userAgent ),
			mozilla: /mozilla/.test( this.userAgent ) && !/(compatible|webkit)/.test( this.userAgent )
		};
	},
	
	each:function(object, callback) {
		var name, i = 0, length = object.length;
		
		if ( length == undefined ) {
			for ( name in object )
				if ( callback.call( object[ name ], name, object[ name ] ) === false )
					break;
		} else {
			for ( var value = object[0];
				i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
		}
		
		return object;
	},
	
	bind:function(elem, types, handler) {
		deffect.each(types.split(/\s+/), function(index, type) {
			if (elem.addEventListener)
				elem.addEventListener(type, handler, false);
			else if (elem.attachEvent)
				elem.attachEvent("on" + type, handler);
		});
	},
	
	css:function(elem, property, otherStyle, style){
		if ( property == "width" || property == "height" ) {
			var L,G={position:"absolute",visibility:"hidden",display:"block"},K=property=="width"?["Left","Right"]:["Top","Bottom"];
			function I() {
				L=property=="width"?elem.offsetWidth:elem.offsetHeight;
				if(style==="border"){return;}
				deffect.each(K, function(){
					if(!style){L-=parseFloat(curCSS(elem,"padding"+this,true))||0}
					if(style==="margin"){L+=parseFloat(curCSS(elem,"margin"+this,true))||0}else{L-=parseFloat(curCSS(elem,"border"+this+"Width",true))||0}
				})
			}
			if(elem.offsetWidth!==0){I()}else{swap(elem,G,I)}
			return Math.max(0,Math.round(L));
		}
		return curCSS(elem, property, otherStyle);
		
		function swap(H, G, I){
			var E={};
			for(var F in G){
				E[F]=H.style[F];
				H.style[F]=G[F];
			}
			I.call(H);
			for(var F in G){H.style[F]=E[F]}
		}
		
		function curCSS(I, F, G){
			var L,E=I.style;
			if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}
			if(F.match(/float/i)){F=w}
			if(!G&&E&&E[F]){
				L=E[F]
			}else{
				var q=document.defaultView||{};
				if(q.getComputedStyle){
					if(F.match(/float/i)){F="float"}
					F=F.replace(/([A-Z])/g,"-$1").toLowerCase();
					var M=q.getComputedStyle(I,null);
					if(M){L=M.getPropertyValue(F)}
					if(F=="opacity"&&L==""){L="1"}
				}else{
					if(I.currentStyle){
						var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});
						L=I.currentStyle[F]||I.currentStyle[J];
						if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){
							var H=E.left,K=I.runtimeStyle.left;
							I.runtimeStyle.left=I.currentStyle.left;
							E.left=L||0;
							L=E.pixelLeft+"px";
							E.left=H;
							I.runtimeStyle.left=K
						}
					}
				}
			}
			return L
		}
	}
	
}
/*
 *
 */
 
var connect = {

	transaction_id:0,
	poll:{},
	polling_interval:50,
	timeOut:{},
	polling_timeOut:5000,

	asyncRequest:function(method, url, handler) {
		xmlhttp = null;
		if ( window.XMLHttpRequest ) {
			// code for Firefox, Opera, IE7, etc.
			xmlhttp = new XMLHttpRequest();
		} else if ( window.ActiveXObject ) {
			// code for IE6, IE5
			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		}
		if ( xmlhttp != null ) {
			//xmlhttp.onreadystatechange = state_Change;
			xmlhttp.open(method.toUpperCase(), url, true);
			xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
			xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			this.handleReadyState(xmlhttp, handler);
			xmlhttp.send(null);
		} else {
			alert("Your browser does not support XMLHTTP.");
		}
		
		function state_Change() {
			if ( xmlhttp.readyState == 4 ) {
				// 4 = "loaded"
				if ( xmlhttp.status == 200 ) {
					// 200 = "OK"
					if ( handler.success )
						if ( !handler.scope )
							handler.success(xmlhttp);
				} else {
					alert("Problem retrieving data:" + xmlhttp.statusText);
				}
			}
		}
	},
	
	getTransactionId:function() {
		var tId = this.transaction_id;
		this.transaction_id++;
		return tId;
	},
	
	handleReadyState:function(q, R) {
		var S = this;
		var tId = this.getTransactionId();
		if ( this.polling_timeOut ) {
			this.timeOut[tId] = window.setTimeout(
				function() {
					if ( S.poll[tId] != null ) {
						window.clearInterval(S.poll[tId]);
						delete S.poll[tId];
					}
				},
				this.polling_timeOut
			);
		}
		this.poll[tId] = window.setInterval(
			function(){
				if ( q && q.readyState === 4 ){
					window.clearInterval(S.poll[tId]);
					delete S.poll[tId];
					S.handleTransactionResponse(q, R);
				}
			},
			this.polling_interval
		);
	},
	
	handleTransactionResponse:function(w, V) {
		var R, q;
		try {
			if ( w.status !== undefined && w.status !== 0 )
				R = w.status;
			else
				R = 13030;
		} catch(m) {
			R = 13030;
		}
		if ( R >= 200 && R < 300 || R === 1223 ) {
			q = this.createResponseObject(w, (V&&V.argument)?V.argument:undefined);
			if ( V ) {
				if ( V.success ) {
					if ( !V.scope )
						V.success(q);
					else
						V.success.apply(V.scope);
				}
			}
		} else {
			alert("Problem retrieving data:" + w.statusText);
		}
		this.releaseObject(w);
		q = null;
	},
	
	createResponseObject:function(S, d) {
		var m={};
		m.responseText = S.responseText;
		m.responseXML = S.responseXML;
		if ( typeof d !== undefined )
			m.argument = d;
		return m;
	},
	
	releaseObject:function(o) { o = null; }

}

function autocompleter(input, options) {
	
	//alert("auto"+input.id);
	var KEY = {
		UP: 38,
		DOWN: 40,
		DEL: 46,
		TAB: 9,
		RETURN: 13,
		ESC: 27,
		COMMA: 188,
		PAGEUP: 33,
		PAGEDOWN: 34,
		BACKSPACE: 8
	};
	var timeout;
	var previousValue = "";
	var dataCache = getCache(options);
	var hasFocus = 0;
	var lastKeyPressCode;
	var config = {
		mouseDownOnSelect: false
	};
	var select = getSelect(options, input, selectCurrent, config);
	var cache = {};
	var expando = "deffect" + new Date, uuid = 0;
	var handle = null;
	var searchValue = "";
	
	function selectCurrent() {
		var selected = select.selected();
		if( !selected )
			return false;
		
		var v = selected.result;
		previousValue = v;
		
		if ( options.multiple ) {
			var words = trimWords($input.val());
			if ( words.length > 1 ) {
				var seperator = options.multipleSeparator.length;
				var cursorAt = $(input).selection().start;
				var wordAt, progress = 0;
				$.each(words, function(i, word) {
					progress += word.length;
					if (cursorAt <= progress) {
						wordAt = i;
						return false;
					}
					progress += seperator;
				});
				words[wordAt] = v;
				// TODO this should set the cursor to the right position, but it gets overriden somewhere
				//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
				v = words.join( options.multipleSeparator );
			}
			v += options.multipleSeparator;
		}
		
		input.value = v;
		hideResultsNow();
		//$input.trigger("result", [selected.data, selected.value]);
		return true;
	}
	
	deffect.bind(input, "keydown", function(event) {
		// a keypress means the input has focus
		// avoids issue where input had focus before the autocomplete was applied
		hasFocus = 1;
		// track last key pressed
		lastKeyPressCode = event.keyCode;
		switch(event.keyCode) {
		
			case KEY.UP:
				if ( event.preventDefault )
					event.preventDefault();
				if ( select.visible() ) {
					select.prev();
				} else {
					onChange(0, true);
				}
				break;
				
			case KEY.DOWN:
				if ( event.preventDefault )
					event.preventDefault();
				if ( select.visible() ) {
					select.next();
				} else {
					onChange(0, true);
				}
				break;
				
			case KEY.PAGEUP:
				if ( event.preventDefault )
					event.preventDefault();
				if ( select.visible() ) {
					select.pageUp();
				} else {
					onChange(0, true);
				}
				break;
				
			case KEY.PAGEDOWN:
				if ( event.preventDefault )
					event.preventDefault();
				if ( select.visible() ) {
					select.pageDown();
				} else {
					onChange(0, true);
				}
				break;
			
			// matches also semicolon
			case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
			case KEY.TAB:
			case KEY.RETURN:
				if( selectCurrent() ) {
					// stop default to prevent a form submit, Opera needs special handling
					if ( event.preventDefault )
						event.preventDefault();
					blockSubmit = true;
					return false;
				}
				break;
				
			case KEY.ESC:
				select.hide();
				break;
				
			default:
				clearTimeout(timeout);
				timeout = setTimeout(onChange, options.delay);
				break;
		}
	});
	deffect.bind(input, "focus", function(event) {
		// track whether the field has focus, we shouldn't process any
		// results if the field no longer has focus
		hasFocus++;
	});
	deffect.bind(input, "blur", function(event) {
		hasFocus = 0;
		if ( !config.mouseDownOnSelect )
			hideResults();
	});
	deffect.bind(input, "click", function(event) {
		// show select when clicking in a focused field
		if ( hasFocus++ > 1 && !select.visible() )
			onChange(0, true);
	});
	/*
	if ( input.addEventListener )
		input.addEventListener("keydown", handle, false);
	else if ( input.attachEvent )
		input.attachEvent("on" + "keydown", handle);
	*/
	
	function onChange(crap, skipPrevCheck) {
		if( lastKeyPressCode == KEY.DEL ) {
			select.hide();
			return;
		}
		
		var currentValue = input.value;
		
		if ( !skipPrevCheck && currentValue == previousValue )
			return;
		
		previousValue = currentValue;
		
		currentValue = lastWord(currentValue);
		if ( currentValue.length >= options.minChars ) {
			addClass(input, options.loadingClass);
			if (!options.matchCase)
				currentValue = currentValue.toLowerCase();
			request(currentValue, receiveData, hideResultsNow);
		} else {
			stopLoading();
			select.hide();
		}
	}
	
	function trimWords(value) {
		if (!value)
			return [""];
		if (!options.multiple)
			return [$.trim(value)];
		return $.map(value.split(options.multipleSeparator), function(word) {
			return $.trim(value).length ? $.trim(word) : null;
		});
	}
	
	function lastWord(value) {
		if ( !options.multiple )
			return value;
		var words = trimWords(value);
		if (words.length == 1) 
			return words[0];
		var cursorAt = $(input).selection().start;
		if (cursorAt == value.length) {
			words = trimWords(value)
		} else {
			words = trimWords(value.replace(value.substring(cursorAt), ""));
		}
		return words[words.length - 1];
	}
	
	function getSelect(options, input, select, config) {
		var CLASSES = {
			ACTIVE: "ac_over"
		};
		
		var listItems,
			active = -1,
			data,
			term = "",
			needsInit = true,
			element,
			list,
			headers = [];
		
		input.offset = function() {
			var left = 0, top = 0, elem = this, results;
		
			if ( elem ) with ( deffect.browser() ) {
				var parent       = elem.parentNode,
					offsetChild  = elem,
					offsetParent = elem.offsetParent,
					doc          = elem.ownerDocument,
					safari2      = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
					css          = curCSS;
					fixed        = css(elem, "position") == "fixed";
		
				// Use getBoundingClientRect if available
				if ( !(mozilla && elem == document.body) && elem.getBoundingClientRect ) {
					var box = elem.getBoundingClientRect();
		
					// Add the document scroll offsets
					add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
						box.top  + Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
		
					// IE adds the HTML element's border, by default it is medium which is 2px
					// IE 6 and 7 quirks mode the border width is overwritable by the following css html { border: 0; }
					// IE 7 standards mode, the border is always 2px
					// This border/offset is typically represented by the clientLeft and clientTop properties
					// However, in IE6 and 7 quirks mode the clientLeft and clientTop properties are not updated when overwriting it via CSS
					// Therefore this method will be off by 2px in IE while in quirksmode
					add( -doc.documentElement.clientLeft, -doc.documentElement.clientTop );
		
				// Otherwise loop through the offsetParents and parentNodes
				} else {
		
					// Initial element offsets
					add( elem.offsetLeft, elem.offsetTop );
		
					// Get parent offsets
					while ( offsetParent ) {
						// Add offsetParent offsets
						add( offsetParent.offsetLeft, offsetParent.offsetTop );
		
						// Mozilla and Safari > 2 does not include the border on offset parents
						// However Mozilla adds the border for table or table cells
						if ( mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2 )
							border( offsetParent );
		
						// Add the document scroll offsets if position is fixed on any offsetParent
						if ( !fixed && css(offsetParent, "position") == "fixed" )
							fixed = true;
		
						// Set offsetChild to previous offsetParent unless it is the body element
						offsetChild  = /^body$/i.test(offsetParent.tagName) ? offsetChild : offsetParent;
						// Get next offsetParent
						offsetParent = offsetParent.offsetParent;
					}
		
					// Get parent scroll offsets
					while ( parent && parent.tagName && !/^body|html$/i.test(parent.tagName) ) {
						// Remove parent scroll UNLESS that parent is inline or a table to work around Opera inline/table scrollLeft/Top bug
						if ( !/^inline|table.*$/i.test(css(parent, "display")) )
							// Subtract parent scroll offsets
							add( -parent.scrollLeft, -parent.scrollTop );
		
						// Mozilla does not add the border for a parent that has overflow != visible
						if ( mozilla && css(parent, "overflow") != "visible" )
							border( parent );
		
						// Get next parent
						parent = parent.parentNode;
					}
		
					// Safari <= 2 doubles body offsets with a fixed position element/offsetParent or absolutely positioned offsetChild
					// Mozilla doubles body offsets with a non-absolutely positioned offsetChild
					if ( (safari2 && (fixed || css(offsetChild, "position") == "absolute")) ||
						(mozilla && css(offsetChild, "position") != "absolute") )
							add( -doc.body.offsetLeft, -doc.body.offsetTop );
		
					// Add the document scroll offsets if position is fixed
					if ( fixed )
						add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft),
							Math.max(doc.documentElement.scrollTop,  doc.body.scrollTop));
				}
		
				// Return an object with top and left properties
				results = { top: top, left: left };
			}
			
			function curCSS(elem, name, force) {
				var ret, style = elem.style;
				var defaultView = document.defaultView || {};
		
				// A helper method for determining if an element's values are broken
				function color( elem ) {
					if ( !deffect.browser().safari )
						return false;
		
					// defaultView is cached
					var ret = defaultView.getComputedStyle(elem, null);
					return !ret || ret.getPropertyValue("color") == "";
				}
		
				// We need to handle opacity special in IE
				if ( name == "opacity" && deffect.browser().msie ) {
					return "1";
				}
				// Opera sometimes will give the wrong display answer, this fixes it, see #2037
				if ( deffect.browser().opera && name == "display" ) {
					var save = style.outline;
					style.outline = "0 solid black";
					style.outline = save;
				}
		
				// Make sure we're using the right name for getting the float value
				if ( name.match( /float/i ) )
					name = styleFloat;
		
				if ( !force && style && style[ name ] )
					ret = style[ name ];
		
				else if ( defaultView.getComputedStyle ) {
		
					// Only "float" is needed here
					if ( name.match( /float/i ) )
						name = "float";
		
					name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase();
		
					var computedStyle = defaultView.getComputedStyle( elem, null );
		
					if ( computedStyle && !color( elem ) )
						ret = computedStyle.getPropertyValue( name );
		
					// If the element isn't reporting its values properly in Safari
					// then some display: none elements are involved
					else {
						var swap = [], stack = [], a = elem, i = 0;
		
						// Locate all of the parent display: none elements
						for ( ; a && color(a); a = a.parentNode )
							stack.unshift(a);
		
						// Go through and make them visible, but in reverse
						// (It would be better if we knew the exact display type that they had)
						for ( ; i < stack.length; i++ )
							if ( color( stack[ i ] ) ) {
								swap[ i ] = stack[ i ].style.display;
								stack[ i ].style.display = "block";
							}
		
						// Since we flip the display style, we have to handle that
						// one special, otherwise get the value
						ret = name == "display" && swap[ stack.length - 1 ] != null ?
							"none" :
							( computedStyle && computedStyle.getPropertyValue( name ) ) || "";
		
						// Finally, revert the display styles back
						for ( i = 0; i < swap.length; i++ )
							if ( swap[ i ] != null )
								stack[ i ].style.display = swap[ i ];
					}
		
					// We should always get a number back from opacity
					if ( name == "opacity" && ret == "" )
						ret = "1";
		
				} else if ( elem.currentStyle ) {
					var camelCase = name.replace(/\-(\w)/g, function(all, letter){
						return letter.toUpperCase();
					});
		
					ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
		
					// From the awesome hack by Dean Edwards
					// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
		
					// If we're not dealing with a regular pixel number
					// but a number that has a weird ending, we need to convert it to pixels
					if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) {
						// Remember the original values
						var left = style.left, rsLeft = elem.runtimeStyle.left;
		
						// Put in the new values to get a computed value out
						elem.runtimeStyle.left = elem.currentStyle.left;
						style.left = ret || 0;
						ret = style.pixelLeft + "px";
		
						// Revert the changed values
						style.left = left;
						elem.runtimeStyle.left = rsLeft;
					}
				}
		
				return ret;
			}
		
			function border(elem) {
				add(curCSS(elem, "borderLeftWidth", true), curCSS(elem, "borderTopWidth", true));
			}
		
			function add(l, t) {
				left += parseInt(l, 10) || 0;
				top += parseInt(t, 10) || 0;
			}
		
			return results;
		}
		
		// Create results
		function init() {
			if (!needsInit)
				return;
			/*
			element = $("<div/>")
			.hide()
			.addClass(options.resultsClass)
			.css("position", "absolute")
			.appendTo(document.body);
			*/
			element = document.createElement("div");
			addClass(element, options.resultsClass);
			css(element, "position", "absolute");
			element.is = function(selector) {
				return (this.style.visibility.indexOf(selector.substring(1)) > -1);
			}
			element.hide = function() {
				this.style.visibility = "hidden";
			}
			element.show = function() {
				this.style.visibility = "visible";
			}
			element.hide();
			document.body.appendChild(element);
		
			list = document.createElement("ul");
			list.empty = function() {
				// Remove any remaining nodes
				while ( this.firstChild )
					this.removeChild(this.firstChild);
			}
			list.find = function(selector) {
				var result = [];
				for (var i=0; i<this.childNodes.length; i++) {
					if ( this.childNodes[i].tagName == "LI" )
						result.push(this.childNodes[i]);
				}
				result.slice = function() {
					var temp = Array.prototype.slice.apply(this, arguments);
					temp.addClass = function(classNames) {
						deffect.each(temp, function(index, elem) {
							addClass(elem, classNames);
						});
						return temp;
					}
					temp.removeClass = function(classNames) {
						deffect.each(temp, function(index, elem) {
							removeClass(elem, classNames);
						});
					}
					return temp;
				}
				result.filter = function() {
					var temp = [];
					if ( arguments.length > 0 ) {
						for (var i=0; i<list.childNodes.length; i++) {
							if ( list.childNodes[i].className.indexOf(arguments[0].substring(1)) > -1 )
								temp.push(list.childNodes[i]);
						}
					}
					return temp;
				}
				return result;
			}
			deffect.bind(list, "mouseover", function(event) {
				if ( target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI' ) {
					for (var i=0; i<list.childNodes.length; i++) {
						if ( list.childNodes[i] == target(event) )
							active = i;
						removeClass(list.childNodes[i], CLASSES.ACTIVE);
					}
					if ( options.includeHeader ) {
						if ( !isActiveOnHeader() )
							addClass(target(event), CLASSES.ACTIVE);
					} else {
						addClass(target(event), CLASSES.ACTIVE);
					}
				}
			});
			/*
			list.onmouseover = function(event) {
				if ( target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI' ) {
					for (var i=0; i<this.childNodes.length; i++) {
						removeClass(this.childNodes[i], CLASSES.ACTIVE);
					}
					active = target(event);
					addClass(active, CLASSES.ACTIVE);
				}
			}
			*/
			deffect.bind(list, "click", function(event) {
				if ( options.includeHeader && isActiveOnHeader() )
					return false;
				
				addClass(target(event), CLASSES.ACTIVE);
				select();
				// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
				input.focus();
				return false;
			});
			/*
			list.onclick = function(event) {
				addClass(target(event), CLASSES.ACTIVE);
				select();
				// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
				input.focus();
				return false;
			}
			*/
			deffect.bind(list, "mousedown", function(event) {
				config.mouseDownOnSelect = true;
			});
			deffect.bind(list, "mouseup", function(event) {
				config.mouseDownOnSelect = false;
			});
			element.appendChild(list);
			/*
			list = $("<ul/>").appendTo(element).mouseover( function(event) {
				if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
					active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
					$(target(event)).addClass(CLASSES.ACTIVE);            
				}
			}).click(function(event) {
				$(target(event)).addClass(CLASSES.ACTIVE);
				select();
				// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
				input.focus();
				return false;
			}).mousedown(function() {
				config.mouseDownOnSelect = true;
			}).mouseup(function() {
				config.mouseDownOnSelect = false;
			});
			*/
			
			if( options.width > 0 )
				element.style.width = options.width + "px";
				
			needsInit = false;
		} 
		
		function target(event) {
			var element = event.target;
			if ( !element )
				element = event.srcElement;
			while ( element && element.tagName != "LI" )
				element = element.parentNode;
			// more fun with IE, sometimes event.target is empty, just ignore it then
			if ( !element )
				return [];
			return element;
		}
	
		function moveSelect(step) {
			listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
			movePosition(step);
			var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
			if ( options.scroll ) {
				var offset = 0;
				deffect.each(listItems.slice(0, active), function() {
					offset += this.offsetHeight;
				});
				if ( (offset + activeItem[0].offsetHeight - list.scrollTop) > list.clientHeight ) {
					list.scrollTop = offset + activeItem[0].offsetHeight - list.clientHeight;
				} else if ( offset < list.scrollTop ) {
					list.scrollTop = offset;
				}
			}
		}
		
		function movePosition(step) {
			active += step;
			if ( options.includeHeader ) {
				while ( isActiveOnHeader() ) {
					active += step;
				}
			}
			if (active < 0) {
				active = listItems.length;
				movePosition(-1);
			} else if (active >= listItems.length) {
				active = -1;
				movePosition(1);
			}
		}
		
		function isActiveOnHeader() {
			var result = false;
			for ( var i=0; i<headers.length; i++ ) {
				if ( headers[i] == active ) {
					result = true;
					break;
				}
			}
			return result;
		}
		
		function limitNumberOfItems(available) {
			return options.max && options.max < available
				? options.max
				: available;
		}
		
		function fillList() {
			list.empty();
			var max = limitNumberOfItems(data.length);
			var firstIndex = -1;
			for (var i=0; i < max; i++) {
				if (!data[i])
					continue;
				var isHeader = false;
				if ( options.includeHeader && options.isHeader(data[i].data, i+1, max, data[i].value, term) ) {
					headers.push(i);
					isHeader = true;
				}
				else if ( firstIndex == -1 )
					firstIndex = i;
				var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
				if ( isHeader ) { formatted = options.formatHeader(data[i].data, i+1, max, data[i].value, term); }
				if ( formatted === false )
					continue;
				//var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
				//$.data(li, "ac_data", data[i]);
				var li = document.createElement("li");
				if ( isHeader ) {
					// No need to highlight header
					li.innerHTML = formatted;
					addClass(li, options.headerClass);
				} else {
					li.innerHTML = options.highlight(formatted, term);
					addClass(li, (i%2 == 0 ? "ac_even" : "ac_odd"));
				}
				cacheData(li, "ac_data", data[i]);
				list.appendChild(li);
			}
			listItems = list.find("li");
			if ( options.selectFirst ) {
				//listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
				listItems.slice(firstIndex, firstIndex+1).addClass(CLASSES.ACTIVE);
				active = firstIndex;
			}
			// apply bgiframe if available
			/*
			if ( $.fn.bgiframe )
				list.bgiframe();
			*/
		}
		
		return {
			display: function(d, q) {
				init();
				data = d;
				term = q;
				fillList();
			},
			next: function() {
				moveSelect(1);
			},
			prev: function() {
				moveSelect(-1);
			},
			pageUp: function() {
				if (active != 0 && active - 8 < 0) {
					moveSelect( -active );
				} else {
					moveSelect(-8);
				}
			},
			pageDown: function() {
				if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
					moveSelect( listItems.size() - 1 - active );
				} else {
					moveSelect(8);
				}
			},
			hide: function() {
				element && element.hide();
				listItems && removeClass(listItems, CLASSES.ACTIVE);
				active = -1;
			},
			visible : function() {
				return element && element.is(":visible");
			},
			current: function() {
				return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
			},
			show: function() {
				var offset = input.offset();
				css(element, {
					width: (typeof options.width == "string" || options.width > 0 ? options.width : input.clientWidth) + "px",
					top: (offset.top + input.offsetHeight) + "px",
					left: offset.left + "px"
				});
				element.show();
				if ( options.scroll ) {
					list.scrollTop = 0;
					css(list, {
						maxHeight: options.scrollHeight + "px",
						overflow: 'auto'
					});
					
					if ( deffect.browser().msie && typeof document.body.style.maxHeight === "undefined" ) {
						var listHeight = 0;
						listItems.each(function() {
							listHeight += this.offsetHeight;
						});
						var scrollbarsVisible = listHeight > options.scrollHeight;
						list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
						if (!scrollbarsVisible) {
							// IE doesn't recalculate width when scrollbar disappears
							listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
						}
					}
					
				}
			},
			selected: function() {
				var selected = listItems && listItems.filter("." + CLASSES.ACTIVE);
				removeClass(selected[0], CLASSES.ACTIVE);
				return selected && selected.length && cacheData(selected[0], "ac_data");
			},
			emptyList: function (){
				list && list.empty();
			},
			unbind: function() {
				element && element.remove();
			}
		};
	}
	
	function getCache(options) {

		var data = {};
		var length = 0;
		
		function matchSubset(s, sub) {
			if (!options.matchCase) 
				s = s.toLowerCase();
			var i = s.indexOf(sub);
			if (options.matchContains == "word"){
				i = s.toLowerCase().search("\\b" + sub.toLowerCase());
			}
			if (i == -1) return false;
			return i == 0 || options.matchContains;
		};
		
		function add(q, value) {
			if (length > options.cacheLength){
				flush();
			}
			if (!data[q]){ 
				length++;
			}
			data[q] = value;
		}
		
		function populate(){
			if( !options.data ) return false;
			alert("populate");
			// track the matches
			var stMatchSets = {},
				nullData = 0;
	
			// no url was specified, we need to adjust the cache length to make sure it fits the local data store
			if( !options.url ) options.cacheLength = 1;
			
			// track all options for minChars = 0
			stMatchSets[""] = [];
			
			// loop through the array and create a lookup structure
			for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
				var rawValue = options.data[i];
				// if rawValue is a string, make an array otherwise just reference the array
				rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
				
				var value = options.formatMatch(rawValue, i+1, options.data.length);
				if ( value === false )
					continue;
					
				var firstChar = value.charAt(0).toLowerCase();
				// if no lookup array for this character exists, look it up now
				if( !stMatchSets[firstChar] ) 
					stMatchSets[firstChar] = [];
	
				// if the match is a string
				var row = {
					value: value,
					data: rawValue,
					result: options.formatResult && options.formatResult(rawValue) || value
				};
				
				// push the current match into the set list
				stMatchSets[firstChar].push(row);
	
				// keep track of minChars zero items
				if ( nullData++ < options.max ) {
					stMatchSets[""].push(row);
				}
			};
			
			// add the data items to the cache
			deffect.each(stMatchSets, function(i, value) {
				// increase the cache size
				options.cacheLength++;
				// add to the cache
				add(i, value);
			});
		}
		
		// populate any existing data
		setTimeout(populate, 25);
		
		function flush(){
			data = {};
			length = 0;
		}
		
		return {
			flush: flush,
			add: add,
			populate: populate,
			load: function(q) {
				if (!options.cacheLength || !length)
					return null;
				/* 
				 * if dealing w/local data and matchContains than we must make sure
				 * to loop through all the data collections looking for matches
				 */
				if( !options.url && options.matchContains ){
					// track all matches
					var csub = [];
					// loop through all the data grids for matches
					for( var k in data ){
						// don't search through the stMatchSets[""] (minChars: 0) cache
						// this prevents duplicates
						if( k.length > 0 ){
							var c = data[k];
							$.each(c, function(i, x) {
								// if we've got a match, add it to the array
								if (matchSubset(x.value, q)) {
									csub.push(x);
								}
							});
						}
					}				
					return csub;
				} else 
				// if the exact item exists, use it
				if (data[q]){
					return data[q];
				} else
				if (options.matchSubset) {
					for (var i = q.length - 1; i >= options.minChars; i--) {
						var c = data[q.substr(0, i)];
						if (c) {
							var csub = [];
							deffect.each(c, function(i, x) {
								if (matchSubset(x.value, q)) {
									csub[csub.length] = x;
								}
							});
							return csub;
						}
					}
				}
				return null;
			}
		};
	}
	
	function hideResults() {
		clearTimeout(timeout);
		timeout = setTimeout(hideResultsNow, 200);
	}
	
	function hideResultsNow() {
		var wasVisible = select.visible();
		select.hide();
		clearTimeout(timeout);
		stopLoading();
		if (options.mustMatch) {
			// call search and run callback
			$input.search(
				function (result){
					// if no value found, clear the input box
					if( !result ) {
						if (options.multiple) {
							var words = trimWords($input.val()).slice(0, -1);
							$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
						}
						else {
							$input.val( "" );
							$input.trigger("result", null);
						}
					}
				}
			);
		}
	}

	function receiveData(q, data) {
		if ( data && data.length && hasFocus ) {
			stopLoading();
			select.display(data, q);
			autoFill(q, data[0].value);
			select.show();
		} else {
			hideResultsNow();
		}
	}
	
	// fills in the input box w/the first match (assumed to be the best match)
	// q: the term entered
	// sValue: the first matching result
	function autoFill(q, sValue){
		// autofill in the complete box w/the first match as long as the user hasn't entered in more data
		// if the last user key pressed was backspace, don't autofill
		if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
			// fill in the value (keep the case the user has typed)
			$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
			// select the portion of the value not typed by the user (so the next character will erase)
			$(input).selection(previousValue.length, previousValue.length + sValue.length);
		}
	}
	
	function request(term, success, failure) {
		if ( !options.matchCase )
			term = term.toLowerCase();
		var data = dataCache.load(term);
		// recieve the cached data
		if ( data && data.length ) {
			success(term, data);
		// if an AJAX url has been supplied, try loading the data now
		} else if ( (typeof options.url == "string") && (options.url.length > 0) ) {
			var extraParams = {
				timestamp: +new Date()
			};
			$.each(options.extraParams, function(key, param) {
				extraParams[key] = typeof param == "function" ? param() : param;
			});
			
			$.ajax({
				// try to leverage ajaxQueue plugin to abort previous requests
				mode: "abort",
				// limit abortion to this input
				port: "autocomplete" + input.name,
				dataType: options.dataType,
				url: options.url,
				data: $.extend({
					q: lastWord(term),
					limit: options.max
				}, extraParams),
				success: function(data) {
					var parsed = options.parse && options.parse(data) || parse(data);
					dataCache.add(term, parsed);
					success(term, parsed);
				}
			});
		} else if ( typeof options.trigger == 'function' ) {
			searchValue = term;
			options.trigger.call(searchValue);
		} else {
			// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
			select.emptyList();
			failure(term);
		}
	}
	
	this.callback = function(data) {
		var parsed = options.parse && options.parse(data) || parse(data);
		receiveData(searchValue, parsed);
	}
	
	function parse(data) {
		var parsed = [];
		var rows = data;
		for (var i=0; i < rows.length; i++) {
			var row = trim(rows[i]);
			if ( row ) {
				row = row.split("|");
				parsed[parsed.length] = {
					data: row,
					value: row[0],
					result: options.formatResult && options.formatResult(row, row[0]) || row[0]
				};
			}
		}
		return parsed;
	}
	
	function trim(text) {
		return (text || "").replace( /^\s+|\s+$/g, "" );
	}
	
	function stopLoading() {
		removeClass(input, options.loadingClass);
	}
	
	function removeClass(elem, classNames) {		
		if (elem && elem.nodeType == 1)
			elem.className = classNames != undefined ?
				grep(elem.className.split(/\s+/), function(className){
					return !has(classNames, className);
				}).join(" ") :
				"";
		
		function grep(elems, callback, inv) {
			var ret = [];

			// Go through the array, only saving the items
			// that pass the validator function
			for ( var i = 0, length = elems.length; i < length; i++ )
				if ( !inv != !callback( elems[ i ], i ) )
					ret.push( elems[ i ] );

			return ret;
		}
	}
	
	function addClass(elem, classNames) {
		deffect.each((classNames || "").split(/\s+/), function(i, className){
			if ( elem.nodeType == 1 && !has(elem.className, className) )
				elem.className += (elem.className ? " " : "") + className;
		});
		return elem;
	}
	
	function has(elem, className) {
		return inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1;
	}
	
	function inArray(elem, array) {
		for ( var i = 0, length = array.length; i < length; i++ )
			// Use === because on IE, window == document
			if ( array[ i ] === elem )
				return i;

		return -1;
	}
	
	function css(elem, key, value) {
		if ( typeof key == 'string' ) {
			elem.style[key] = value;
		} else {
			var length = key.length;
			if ( length == undefined ) {
				for ( name in key ) {
					elem.style[name] = key[name];
				}
			}
		}
	}
	
	function cacheData(elem, name, data) {
		elem = elem == window ?
			windowData :
			elem;

		var id = elem[expando];

		// Compute a unique ID for the element
		if ( !id )
			id = elem[expando] = ++uuid;

		// Only generate the data cache if we're
		// trying to access or manipulate it
		if ( name && !cache[id] )
			cache[id] = {};

		// Prevent overriding the named cache with undefined values
		if ( data !== undefined )
			cache[id][name] = data;

		// Return the named cache data, or the ID for the element
		return name ?
			cache[id][name] :
			id;
	}
	
}

var accordion = {

	tempConfig: {},
	tempDefaultExpanded: [],

	expandit:function($targetHeader, $targetContent, config) {
		this.transformHeader($targetHeader, config, "expand");
		this.slideDown($targetHeader, $targetContent, this.getSpeed(config.animatespeed));
	},
	
	collapseit:function($targetHeader, $targetContent, config){
		this.transformHeader($targetHeader, config, "collapse");
		$targetContent.style.display = "none";
	},
	
	slideDown:function($targetHeader, $targetContent, speed) {
		/*
		var height = $targetContent.getAttribute("height");
		if ( height == null || height == "" ) {
			$targetContent.style.height = "100%";
			$targetContent.style.display = "block";
			height = deffect.$($targetContent).height();
			$targetContent.style.display = "none";
			$targetContent.setAttribute("height", height);
		}
		*/
		
		$targetContent.style.height = "100%";
		$targetContent.style.display = "block";
		var height = deffect.$($targetContent).height();
		$targetContent.style.display = "none";

		$targetContent.style.height = 0;
		$targetContent.style.overflow = "hidden";
		if ( $targetContent.style.display != "block" )
			$targetContent.style.display = "block";
		var startTime = new Date();
		var int = setInterval(function(){slideDown(height);}, 1);
		
		function slideDown(targetPos) {
			var currentPos = parseInt($targetContent.style.height);
			if ( currentPos < targetPos ) {
				currentPos += 1;
				$targetContent.style.height = correctPosition(currentPos, targetPos, speed) + "px";
				//alert($targetContent.style.height);
			} else {
				clearInterval(int);
				$targetContent.style.height = "100%";
			}
			/*
			$targetContent.style.height = (parseInt($targetContent.style.height) + speed) + "px";
			if ( parseInt($targetContent.style.height) + speed >= targetPos )
			{
				$targetContent.style.height = targetPos + "px";
				clearInterval(int);
			}
			*/
		}
		
		function correctPosition(currentPos, targetPos, duration) {
			var expected = (currentPos * duration * 1000 / targetPos);
			var elapsed = (new Date() - startTime);
			var tweak = 0;
			
			if ( elapsed < duration * 1000 ) { // check if falling behind
				tweak = Math.round((elapsed / expected - 1) * currentPos);
			} else { // went over duration, so jump to end
				tweak = targetPos - (currentPos + 1);
			}
			if ( tweak > 0 && isFinite(tweak) ) { // adjust if needed
				if ( currentPos + tweak >= targetPos ) {// dont go past last frame
					tweak = targetPos - (currentPos + 1);
				}
				
				currentPos += tweak;      
			}
			
			return currentPos;
		}
	},
	
	getSpeed:function(animatespeed) {
		switch (animatespeed)
		{
			/*
			case "fast":
				return 70;
			case "normal":
				return 35;
			case "slow":
				return 10;
			*/
			case "fast":
				return 0.1;
			case "normal":
				return 0.4;
			case "slow":
				return 1;
		}
	},
	
	transformHeader:function($targetHeader, config, state){
		if (config.htmlsetting.location=="prefix") //if change "prefix" HTML, locate dynamically added ".accordprefix" span tag and change it
			deffect.$($targetHeader, '*accordprefix')[0].innerHTML = ((state=="expand")? config.htmlsetting.expand: config.htmlsetting.collapse);
		else if (config.htmlsetting.location=="suffix")
			deffect.$($targetHeader, '*accordsuffix')[0].innerHTML = ((state=="expand")? config.htmlsetting.expand : config.htmlsetting.collapse);
	},

	init:function(config) {

		document.write('<style type="text/css">\n')
		if ( config.contentclass.indexOf('.') > -1 )
			document.write(config.contentclass+'{display: none}\n') //generate CSS to hide contents
		else
			document.write('.'+config.contentclass+'{display: none}\n') //generate CSS to hide contents
		document.write('<\/style>')
		
		deffect.addLoadEvent(function(){
			// Reload configuration if needed
			if ( accordion.tempConfig[accordion.getConfigId(config)] != undefined )
				config = deffect.extend({}, config, accordion.tempConfig[accordion.getConfigId(config)]);
		
			var $headers = null;
			var $subcontents = null;
			var lastexpanded={} //object to hold reference to last expanded header and content (jquery objects)
			var index = 0;
			var ID = -1;
			
			if ( config["rootclass"] != null && config["rootclass"] != "" ) {
				var temp = deffect.$(config["rootclass"]);
				if ( temp.length > 0 ) {
					$headers = deffect.$(temp[0], config["headerclass"]);
					$subcontents = deffect.$(temp[0], config["contentclass"]);
				}
			} else {
				$headers = deffect.$(config["headerclass"]);
			}
			if ( $headers != null && $headers.length > 0 && $subcontents == null )
				$subcontents = locateSubContents($headers[0], '*'+config["contentclass"]);
			
			if (typeof config.togglehtml=="undefined")
				config.htmlsetting={location: "none"}
			else
				config.htmlsetting={location: config.togglehtml[0], collapse: config.togglehtml[1], expand: config.togglehtml[2]} //store HTML settings as object properties
				
			config.oninit = (typeof config.oninit=="undefined")? function(){} : config.oninit //attach custom "oninit" event handler
			
			if ( config.defaultexpanded != null && config.defaultexpanded.length > 1 )
				config["collapseprev"] = false;
			
			var hidecontents = [];
			if ( config.hidecontent != null && config.hidecontent.length > 0 ) {
				for ( var i=0; i<config.hidecontent.length; i++ ) {
					hidecontents.push(config.hidecontent[i][0]);
				}
			}
			
			for ( var i=0; i<$headers.length; i++ ) {
				var $header = $headers[i];
				var $subcontent = $subcontents[index];
				
				deffect.attr($header, "headerindex", i);
				if ( $subcontent != null && ($headers.length == $subcontents.length || isSameLevel($header, $subcontent)) ) {
					//$subcontent.style.display = "block";
					//deffect.attr($header, "headerindex", index);
					//deffect.attr($subcontent, "contentindex", index);
					deffect.attr($header, "contentindex", index);
					
					// Hide items in sub content if needed
					while ( deffect.array.inArray(i, hidecontents) > -1 ) {
						for ( var j=0; j<hidecontents.length; j++ ) {
							if ( hidecontents[j] == i ) {
								var temp = deffect.$($subcontent, config.hidecontent[j][1]);
								if ( temp.length > 0 ) {
									if ( temp[config.hidecontent[j][2]] != null ) {
										temp[config.hidecontent[j][2]].style.display = "none";
									}
								}
								deffect.array.remove(hidecontents, j);
								deffect.array.remove(config.hidecontent, j);
								break;
							}
						}
					}
						
					$header.onclick=function(e){
						if ( $subcontents[parseInt(this.getAttribute("contentindex"))].style.display == "none" ) {
							if ( config["collapseprev"]
								&& lastexpanded.$header
								&& lastexpanded.$content != $subcontents[parseInt(this.getAttribute("contentindex"))] )
								accordion.collapseit(lastexpanded.$header, lastexpanded.$content, config)
								
							accordion.expandit($headers[parseInt(this.getAttribute("headerindex"))],
								$subcontents[parseInt(this.getAttribute("contentindex"))],
								config);
								
							lastexpanded = {$header:$headers[parseInt(this.getAttribute("headerindex"))], $content:$subcontents[parseInt(this.getAttribute("contentindex"))]}
						} else {
							accordion.collapseit($headers[parseInt(this.getAttribute("headerindex"))],
								$subcontents[parseInt(this.getAttribute("contentindex"))],
								config);
						}
						
						// Cancel default click behavior
						return false;
					};
					
					index++;
				}
				
				if ( config.htmlsetting.location == "prefix" || config.htmlsetting.location == "suffix" ) //add a SPAN element to header depending on user setting and if header is a container tag
					$header.innerHTML = "<span class='accordprefix'></span>" + $header.innerHTML + "<span class='accordsuffix'></span>";
				
				if ( (config.defaultexpanded != null && config.defaultexpanded.length > 0 && deffect.array.inArray(i, config.defaultexpanded) > -1) ||
					((ID = getTempExpandProfileID(config["contentclass"])) > -1 && isExpandedParent($subcontent, ID))
				) {
					accordion.transformHeader($header, config, "expand");
					if ( $subcontent != null ) {
						$subcontent.style.display = "block";
						if ( config["parentclass"] != null && config["parentclass"] != "" )
							accordion.tempDefaultExpanded.push({parentClass: config["parentclass"], childClass: config["headerclass"], child: $header});
					}
					lastexpanded = {$header:$headers[i], $content:$subcontents[i]};
				} else {
					accordion.transformHeader($header, config, "collapse");
					if ( $subcontent != null )
						$subcontent.style.display = "none";
				}
			}
			
			config.oninit();
		})
		
		function locateSubContents(node, cssClassName) {
			if ( node == null )
				return null;
				
			var stopFlag = false;
			var subcontents = null;
			var result = 0;
			while ( stopFlag == false ) {
				var parentNode = node.parentNode;
				if ( parentNode == null )
					stopFlag = true;
				else
					subcontents = deffect.$(parentNode, cssClassName);
				if ( subcontents != null && result == subcontents.length )
					stopFlag = true;
				else
					node = parentNode;
				result = subcontents.length;
			}
			return subcontents;
		}
		
		function isSameLevel(header, subContent) {			
			return ( getNextElement(header) == subContent );
		}
		
		function getNextElement(obj) {
			var temp = nextNode(obj);
			while ( temp && temp.nodeType != 1 ) {
				temp = nextNode(temp);
			}
			return temp;
		}
		
		// return next node in document order
		function nextNode(node) {
			if (!node) return null;
			if (node.firstChild)
				return node.firstChild;
			else
				return nextWide(node);
		}

		// helper function for nextNode()
		function nextWide(node) {
			if (!node) return null;
			if (node.nextSibling)
				return node.nextSibling;
			else
				return nextWide(node.parentNode);
		}
		
		function getStyle(x, styleProp) {
			//var x = document.getElementById(el);
			if (x.currentStyle)
				var y = x.currentStyle[styleProp];
			else if (window.getComputedStyle)
				var y = document.defaultView.getComputedStyle(x,null).getPropertyValue(styleProp);
			return y;
		}
		
		function getTempExpandProfileID(cssClassName) {
			if ( accordion.tempDefaultExpanded.length == 0 )
				return -1;
				
			for ( var j=0; j<accordion.tempDefaultExpanded.length; j++ ) {
				if ( accordion.tempDefaultExpanded[j].parentClass == cssClassName )
					return j;
			}
			
			return -1;
		}
		
		function isExpandedParent(node, expandedProfileID) {
			var temp = deffect.$(node, accordion.tempDefaultExpanded[expandedProfileID].childClass);
			if ( temp.length > 0 ) {
				for ( var j=0; j<temp.length; j++ ) {
					if ( temp[j] == accordion.tempDefaultExpanded[expandedProfileID].child ) {
						// Remove profile
						deffect.array.remove(accordion.tempDefaultExpanded, j);

						return true;
					}
				}
			}
			return false;
		}
		
	},
	
	reloadConfig:function(config) {
		var id = this.getConfigId(config);
		this.tempConfig[id] = config;
	},
	
	getConfigId:function(config) {
		return config["headerclass"] + "-" + config["contentclass"];
	},
	
	showItem:function(config) {
		if ( config.showcontent != null && config.showcontent.length > 0 ) {
			var $subcontents = null;
			if ( config["rootclass"] != null && config["rootclass"] != "" ) {
				var temp = deffect.$(config["rootclass"]);
				if ( temp.length > 0 )
					$subcontents = deffect.$(temp[0], config["contentclass"]);
			} else {
				$subcontents = deffect.$(config["contentclass"]);
			}
			if ( $subcontents != null && $subcontents.length > 0 ) {
				for ( var i=0; i<config.showcontent.length; i++ ) {
					var $subcontent = $subcontents[config.showcontent[i][0]];
					if ( $subcontent != null ) {
						var temp = deffect.$($subcontent, config.showcontent[i][1]);
						if ( temp.length > 0 ) {
							if ( temp[config.showcontent[i][2]] != null ) {
								temp[config.showcontent[i][2]].style.display = "block";
								if ( $subcontent.style.height != "" ) {
									//$subcontent.style.position="absolute";
									$subcontent.style.height = "100%";
									var temp = $subcontent.style.display;
									$subcontent.style.display = "block";
									//alert(deffect.$($subcontent).height());
									$subcontent.style.height = deffect.$($subcontent).height() + "px";
									$subcontent.style.display = temp;
								}
							}
						}
					}
				}
			}
		}
	}
	
}

var categoryBrowse = {
	
	expandCategory:function(btnObj, hideItemId, collapseMenuId, collapseImgPath, expandImgPath) {
		if (btnObj.src.indexOf("icon_expand.gif") != -1) {
			btnObj.src = collapseImgPath;
			var hideItemObj = document.getElementById(hideItemId);
			hideItemObj.style.display = 'none';
			var collapseMenuObj = document.getElementById(collapseMenuId);
			collapseMenuObj.style.position = 'relative';
			collapseMenuObj.style.visibility = 'visible';
			collapseMenuObj.style.height = collapseMenuObj.offsetHeight;
		} else {
			btnObj.src = expandImgPath;
			var hideItemObj = document.getElementById(hideItemId);
			hideItemObj.style.display = 'block';
			var collapseMenuObj = document.getElementById(collapseMenuId);
			collapseMenuObj.style.position = 'absolute';
			collapseMenuObj.style.visibility = 'hidden';
		}
	}
	
}