/**
 * Derived mostly from autocomplete code and other JS code obtained from Drupal.org
 * under the Gnu GPL.  Original autocomplete code written by David Carrington.
 * Original autocomplete here:
 * http://brandedthoughts.co.uk/story/drupal-autocomplete-demo
 *
 * - Trevor Hayes
 * July, 2005
 */

var req;
var isIE;
if(document.all) {
  isIE=true;
} else {
  isIE=false;
}

function ieHack(menu) {
  myIFrame = document.getElementById('iFrameIEHack');
  if(myIFrame) {
    myIFrame.style.width = menu.offsetWidth;
    myIFrame.style.height = menu.offsetHeight;
    myIFrame.style.top = menu.offsetTop;
    myIFrame.style.left = menu.offsetLeft;
    myIFrame.style.display = "inline";
  }
}

/**
 * Only enable Javascript functionality if all required features are supported.
 */
function isJsEnabled() {
  if (typeof document.jsEnabled == 'undefined') {
    // Note: ! casts to boolean implicitly.
    document.jsEnabled = !(
     !document.getElementsByTagName ||
     !document.createElement        ||
     !document.createTextNode       ||
     !document.documentElement      ||
     !document.getElementById);
  }
  return document.jsEnabled;
}

/**
 * Adds a function to the window onload event
 */
function addLoadEvent(func) {
  var oldOnload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldOnload();
      func();
    }
  }
}

// Check if we want to use Autocomplete
function isAutocompleteEnabled() {
  var ac = getCookie("ac");
  if(ac == "on") {
    return true;
  }
  return false;
}

// Global Killswitch
if (isJsEnabled()) {
  addLoadEvent(autocompleteAutoAttach);
}

/**
 * Attaches the autocomplete behaviour to all required fields
 */
function autocompleteAutoAttach() {
  if(!isAutocompleteEnabled()) {
    return;
  }
  var acdb = [];
  var inputs = document.getElementsByTagName('input');
  for (i = 0; input = inputs[i]; i++) {
    if (input && hasClass(input, 'autocomplete')) {
      uri = input.value;
      if (!acdb[uri]) {
        acdb[uri] = new ACDB(uri);
      }
      var id = input.id.substr(0, input.id.length - 13);
      input = document.getElementById(id);
      input.setAttribute('autocomplete', 'OFF'); // Note that this won't work if the field starts with focus when the page loads.
      input.form.onsubmit = autocompleteSubmit;
      new jsAC(input, acdb[uri]);
      input.blur(); // Need to blur input to turn off browser native autocomplete
      if(hasClass(input, 'focus')) {
        input.focus();
      }
    }
  }
}

/**
 * Prevents the form from submitting if the suggestions popup is open
 */
function autocompleteSubmit() {
  var popup = document.getElementById('autocomplete');
  if (popup) {
    if(popup.owner.selected) {
      popup.owner.hidePopup();
      return false;
    } else {
      popup.owner.hidePopup();
      return true;
    }
  }
  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;
  if(isIE) {
    myIFrame = document.getElementById('iFrameIEHack');
    if(myIFrame) {
      myIFrame.style.display = "none";
    }
  }
}


/**
 * 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) +'px';
  this.popup.style.left  = pos.x +'px';
  this.popup.style.width = (this.input.offsetWidth - 4) +'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 = matches[i][1];
      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);
    if(isIE) {
      ieHack(this.popup);
    }
  } else {
    this.hidePopup();
  }
}

/**
 * An AutoComplete DataBase object
 */
function ACDB(uri) {
  this.uri = uri;
  this.max = 15;
  this.cache = {};
}

/**
 * Performs a cached search
 */
ACDB.prototype.search = function(searchString) {
  this.searchString = searchString;
  var db = this;
  if (this.cache[searchString]) {
    return this.owner.found(this.cache[searchString]);
  } else {
    // Abort old connection if it has not finished yet
    if (req != null && req.readyState != 0 && req.readyState != 4) {
      req.abort();
    }

    if (window.XMLHttpRequest) {
      req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
      req = new ActiveXObject("Microsoft.XMLHTTP");
    }

    var url = this.owner.db.uri + escape(searchString);
    req.onreadystatechange = function() {if(req.readyState==4){db.parseXML(req, db)}};

    req.open("GET", url, true);
    req.send(null);
  }
}

ACDB.prototype.parseXML = function(req, acdb) {
  var matches = [];
  try {
    if (req.status == 200) {
      var autocomplete = req.responseXML.getElementsByTagName("autocomplete")[0];
      for (loop = 0; loop < autocomplete.childNodes.length; loop++) {
        var suggestion = autocomplete.getElementsByTagName("suggestion")[loop];
        var entry = suggestion.getElementsByTagName("entry")[0].childNodes[0].nodeValue;
        var display = suggestion.getElementsByTagName("display")[0].childNodes[0].nodeValue;
        display = display.replace(/%mss/g, "<font color=\"red\">");
        display = display.replace(/%mse/g, "</font>");
        matches[loop] = [entry, display];
      }
      acdb.cache[acdb.searchString] = matches;
      acdb.owner.found(matches);
// 204 = no content http code
// 1223 = no content IE code (?)
// 0 = intterupted request (if req.abort goes through and ends up status 4 anyway)
    } else if (req.status == 204 || req.status == 1223 || req.status == 0) {
      // no matches found; still need to call found to clear suggest table
      acdb.cache[acdb.searchString] = matches;
      acdb.owner.found(matches);
    } else {
      alert("Autocomplete request failed with status " + req.status);
      acdb.owner.found(matches);
    }
  } catch(error) {
    // Ignore error - we get an error if this function is called and req has already been closed by the next search call
  }
}

/**
 * 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;
};

/**
 * 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;
}

/**
 * Adds a class name to an element
 */
function addClass(node, className) {
  if (hasClass(node, className)) {
    return false;
  }
  node.className += ' '+ className;
  return true;
}

/**
 * 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;
}

/**
 * Toggles a class name on or off for an element
 */
function toggleClass(node, className) {
  if (!removeClass(node, className) && !addClass(node, className)) {
    return false;
  }
  return true;
}

/**
 * Emulate PHP's ereg_replace function in javascript
 */
function eregReplace(search, replace, subject) {
  return subject.replace(new RegExp(search,'g'), replace);
}

/**
 * Removes an element from the page
 */
function removeNode(node) {
  if (typeof node == 'string') {
    node = document.getElementById(node);
  }
  if (node && node.parentNode) {
    return node.parentNode.removeChild(node);
  }
  else {
    return false;
  }
}
