	/**
				* Removes an element from the page
			 */
			function removeNode(node) {
			  if (typeof node == 'string') {
					  node = $(node);
			  }
			  if (node && node.parentNode) {
				 return node.parentNode.removeChild(node);
			 }
			else {
			  return false;
			  }
			}

			/**
			 * Emulate PHP's ereg_replace function in javascript
			 */
			function eregReplace(search, replace, subject) {
				 return subject.replace(new RegExp(search,'g'), replace);
			}

			/**
			* Removes a class name from an element
			 */
			function removeClass(node, className) {
			  if (!hasClass(node, className)) {
			    return false;
			  }
			  node.className = eregReplace('(^| )'+ className +'($| )', '', node.className);
			  return true;
			}

			/**
			 * Creates an HTTP GET request and sends the response to the callback function.
			 *
			 * Note that dynamic arguments in the URI should be escaped with encodeURIComponent().
			 */
			function HTTPGet(uri, sendf, callbackFunction, callbackParameter) {
			  

			  var xmlHttp = null;
			  try{
				xmlHttp = new ActiveXObject("Microsoft.XMLHTTP")}
			  catch(sc){}

			  if(!xmlHttp && typeof XMLHttpRequest!="undefined"){
				xmlHttp=new XMLHttpRequest()
			   }

			  if (xmlHttp == null)
				return;
			  
			  var bAsync = true;
			  if (!callbackFunction) {
			    bAsync = false;
			  }
			  
			  xmlHttp.open('POST', uri, bAsync);
			  xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
			  xmlHttp.send(sendf);

			  if (bAsync) {
			    if (callbackFunction) {
			      xmlHttp.onreadystatechange = function() {
				if (xmlHttp.readyState == 4) {
				  callbackFunction(xmlHttp.responseText, xmlHttp, callbackParameter);
				}
			      }
			    }
			    return true;
			  }
			  else {
			    return xmlHttp.responseText;
			  }
			}

			/**
			 * Retrieves the absolute position of an element on the screen
			 */
			function absolutePosition(el) {
			  var sLeft = 0, sTop = 0;
			  var isDiv = /^div$/i.test(el.tagName);
			  if (isDiv && el.scrollLeft) {
			    sLeft = el.scrollLeft;
			  }
			  if (isDiv && el.scrollTop) {
			    sTop = el.scrollTop;
			  }
			  var r = { x: el.offsetLeft - sLeft, y: el.offsetTop - sTop };
			  if (el.offsetParent) {
			    var tmp = absolutePosition(el.offsetParent);
			    r.x += tmp.x;
			    r.y += tmp.y;
			  }
			  return r;
			};

			/**
			 * Adds a class name to an element
			 */
			function addClass(node, className) {
			  if (hasClass(node, className)) {
			    return false;
			  }
			  node.className += ' '+ className;
			  return true;
			}


			/**
			 * Adds a function to a given form's submit event
			 */
			function addSubmitEvent(form, func) {
			  var oldSubmit = form.onsubmit;
			  if (typeof oldSubmit != 'function') {
			    form.onsubmit = func;
			  }
			  else {
			    form.onsubmit = function() {
			      return oldSubmit() && func();
			    }
			  }
			}

			/**
			 * Returns true if an element has a specified class name
			 */
			function hasClass(node, className) {
			
			  if (node.className == className) {
			    return true;
			  }
			  var reg = new RegExp('(^| )'+ className +'($| )')
			  if (reg.test(node.className)) {
			    return true;
			  }
			  return false;
			}

			/**
			 * Attaches the autocomplete behaviour to all required fields
			 */
			function autocompleteAutoAttach() {
			
			  var acdb = [];
			  var inputs = document.getElementsByTagName('input');
			 
			  for (i = 0; input = inputs[i]; i++) {
			    
			    if (input && hasClass(input, 'query')) {
				  
			      uri = input.value;
			      if (!acdb[uri]) {
				acdb[uri] = new ACDB(uri);
			      }	  
			      //input = (input.id.substr(0, input.id.length - 13));
			      input.setAttribute("autocomplete","off");			      
			      addSubmitEvent(input.form, autocompleteSubmit);
			      new jsAC(input, acdb[uri]);
			    }
			  }
			}

			/**
			 * Prevents the form from submitting if the suggestions popup is open
			 */
			function autocompleteSubmit() {
			  var popup = document.getElementById('autocomplete');
			  if (popup) {
			    popup.owner.hidePopup();
			    return false;
			  }
			  return true;
			}


			/**
			 * An AutoComplete object
			 */
			function jsAC(input, db) {
			  var ac = this;
			  this.input = input;
			  this.db = db;
			  this.input.onkeydown = function (event) { return ac.onkeydown(this, event); };
			  this.input.onkeyup = function (event) { ac.onkeyup(this, event) };
			  this.input.onblur = function () { ac.hidePopup() };
			  this.popup = document.createElement('div');
			  this.popup.id = 'autocomplete';
			  this.popup.owner = this;
			};

			/**
			 * Hides the autocomplete suggestions
			 */
			jsAC.prototype.hidePopup = function (keycode) {
			  if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
			    this.input.value = this.selected.autocompleteValue;
			  }
			  if (this.popup.parentNode && this.popup.parentNode.tagName) {
			    removeNode(this.popup);
			  }
			  this.selected = false;
			}


			/**
			 * Handler for the "keydown" event
			 */
			jsAC.prototype.onkeydown = function (input, e) {
			  if (!e) {
			    e = window.event;
			  }
			  switch (e.keyCode) {
			    case 40: // down arrow
			      this.selectDown();
			      return false;
			    case 38: // up arrow
			      this.selectUp();
			      return false;
			    default: // all other keys
			      return true;
			  }
			}

			/**
			 * Handler for the "keyup" event
			 */
			jsAC.prototype.onkeyup = function (input, e) {
			  
			  if (!e) {
			    e = window.event;
			  }
			  switch (e.keyCode) {
			    case 16: // shift
			    case 17: // ctrl
			    case 18: // alt
			    case 20: // caps lock
			    case 33: // page up
			    case 34: // page down
			    case 35: // end
			    case 36: // home
			    case 37: // left arrow
			    case 38: // up arrow
			    case 39: // right arrow
			    case 40: // down arrow
			      return true;

			    case 9:  // tab
			    case 13: // enter
			    case 27: // esc
			      this.hidePopup(e.keyCode);
			      return true;

			    default: // all other keys
			      if (input.value.length > 0)
				this.populatePopup();
			      else
				this.hidePopup(e.keyCode);
			      return true;
			  }
			}

			/**
			 * Puts the currently highlighted suggestion into the autocomplete field
			 */
			jsAC.prototype.select = function (node) {
			  this.input.value = node.autocompleteValue;
			}

			/**
			 * Highlights the next suggestion
			 */
			jsAC.prototype.selectDown = function () {
			  if (this.selected && this.selected.nextSibling) {
			    this.highlight(this.selected.nextSibling);
			  }
			  else {
			    var lis = this.popup.getElementsByTagName('li');
			    if (lis.length > 0) {
			      this.highlight(lis[0]);
			    }
			  }
			}

			/**
			 * Highlights the previous suggestion
			 */
			jsAC.prototype.selectUp = function () {
			  if (this.selected && this.selected.previousSibling) {
			    this.highlight(this.selected.previousSibling);
			  }
			}

			/**
			 * Highlights a suggestion
			 */
			jsAC.prototype.highlight = function (node) {
			  removeClass(this.selected, 'selected');
			  addClass(node, 'selected');
			  this.selected = node;
			}

			/**
			 * Unhighlights a suggestion
			 */
			jsAC.prototype.unhighlight = function (node) {
			  removeClass(node, 'selected');
			  this.selected = false;
			}

			/**
			 * Positions the suggestions popup and starts a search
			 */
			jsAC.prototype.populatePopup = function () {
			  var ac = this;
			  var pos = absolutePosition(this.input);
			  this.selected = false;
			  this.popup.style.top   = (pos.y + this.input.offsetHeight - 2) +'px';
			  this.popup.style.left  = pos.x +'px';
			  this.popup.style.width = (this.input.offsetWidth - 2) +'px';
			  this.db.owner = this;
			  this.db.search(this.input.value);
			}

			/**
			 * Fills the suggestion popup with any matches received
			 */
			jsAC.prototype.found = function (matches) {
			  
			  while (this.popup.hasChildNodes()) {
			    this.popup.removeChild(this.popup.childNodes[0]);
			  }

			  if (!this.popup.parentNode || !this.popup.parentNode.tagName) {
			    document.getElementsByTagName('body')[0].appendChild(this.popup);
			  }

			  var ul = document.createElement('ul');
			  var ac = this;
			  if (matches.length > 0) {
			    for (i in matches) {
			      li = document.createElement('li');
			      div = document.createElement('div');  
			      div.innerHTML = '<table border="0" cellpadding="0" cellspacing="0" width="100%"><tr><td align="left">&nbsp;&nbsp;'+matches[i][1]+'</td><td align="right">'+matches[i][2]+'&nbsp;wyników&nbsp;</td></tr></table>';	  
			      li.appendChild(div);
			      li.autocompleteValue = matches[i][0];
			      li.onmousedown = function() { ac.select(this); };
			      li.onmouseover = function() { ac.highlight(this); };
			      li.onmouseout  = function() { ac.unhighlight(this); };
			      ul.appendChild(li);
			    }
			    this.popup.appendChild(ul);
			  }
			  else {
			    this.hidePopup();
			  }
			  removeClass(this.input, 'throbbing');
			}

			/**
			 * An AutoComplete DataBase object
			 */
			function ACDB(uri) {
			  this.uri = uri;
			  this.delay = 100;
			  this.cache = {};
			}

			/**
			 * Performs a cached and delayed search
			 */
			ACDB.prototype.search = function(searchString) {
			  this.searchString = searchString;
			  if (this.cache[searchString]) {
			    return this.owner.found(this.cache[searchString]);
			  }
			  if (this.timer) {
			    clearTimeout(this.timer);
			  }
			  var db = this;
			  this.timer = setTimeout(function() {
			    addClass(db.owner.input, 'throbbing');
			    
			    HTTPGet('/index.php?command=search/expander', 'word='+encodeURIComponent(searchString), db.receive, db);
			  }, this.delay);
			}

			/**
			 * HTTP callback function. Passes suggestions to the autocomplete object
			 */
			ACDB.prototype.receive = function(string, xmlhttp, acdb) {		
			  
			  // Note: Safari returns 'undefined' status if the request returns no data.
			  if (xmlhttp.status != 200 && typeof xmlhttp.status != 'undefined') {
			    removeClass(acdb.owner.input, 'throbbing');
			  }
			  // Split into array of key->value pairs
			  var matches = string.length > 0 ? string.split('||') : [];

			  for (i in matches) {
			    matches[i] = matches[i].length > 0 ? matches[i].split('|') : [];
			  }
			  acdb.cache[acdb.searchString] = matches;
			  acdb.owner.found(matches);
			}
