// $Id: activeselect.js,v 1.17 2006/04/14 15:07:50 jaza Exp $

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

/**
 * Attaches the activeselect behaviour to all required fields
 */
function activeselectAutoAttach() {
  var asdb = [];
  var inputs = document.getElementsByTagName('input');
  var input = null;
  for (var i = 0; input = inputs[i]; i++) {
    if (input && hasClass(input, 'activeselect-path')) {
  	  var index = input.id.substr(0, input.id.length - 18);
  	  var uri = input.value +'/'+ encodeURIComponent(index).substr(5);
  	  input = $(index + '-activeselect-extra');
  	  var extra = input.value;
  	  input = $(index + '-activeselect-targets');
  	  var targets = input.value;
  	  input = $(index);
  	  if (!asdb[uri]) {
        asdb[uri] = new ASDB(uri, targets);
      }

      new jsAS(input, asdb[uri], targets, extra);
    }
  }
}

/**
 * An ActiveSelect object
 */
function jsAS(input, db, targets, extra) {
  var as = this;
  this.input = input;
  this.db = db;
  this.input.onchange = function (event) { return as.onchange(this, event); };
  this.extra = extra;
  var targetsArray = targets.split(',');
  this.targets = [];
  for (var target = 0; target < targetsArray.length; target++) {
    var newTarget = $(targetsArray[target]);
    newTarget.owner = this;
    this.targets.push(newTarget);
  }
  // this only runs if the current element does not have a parent activeselect
  // linked to it - otherwise, IE has problems.
  if (!this.input.owner) {
    this.populateTargets();
  }
};

/**
 * Handler for the "onchange" event
 */
jsAS.prototype.onchange = function (input, e) {
  if (!e) {
    e = window.event;
  }

  this.populateTargets();
}

/**
 * Return the currently selected options as a pipe-delimited string
 */
jsAS.prototype.getSelectedOptions = function () {
  var selectedOptions = [];
  var maxWidth = 0;
  for (var i = 0; i < this.input.options.length; i++) {
    if (this.input.options[i].selected) {
      var optionString = this.input.options[i].value.replace(/\|/g, '&#124;') +'|'+ this.input.options[i].text.replace(/\|/g, '&#124;');
      selectedOptions.push(optionString);
    }
    if (this.input.options[i].text.length > maxWidth) {
      maxWidth = this.input.options[i].text.length;
    }
  }
  this.setSelectWidth(maxWidth);

  return selectedOptions.join('||');
}

/**
 * Sets the width of the activeselect element, and adjusts the position of the
 * throbber background image
 */
jsAS.prototype.setSelectWidth = function (width) {
  /*if (width != null) {
    this.selectWidth = ((width * 5) * 1.5) + 20;
  }
  this.input.style.width = this.selectWidth +'px';*/
  this.input.style.backgroundPosition = (95) +'px 2px';
}

/**
 * Sets the width of the specified target element
 */
jsAS.prototype.setTargetWidth = function (target, width) {
  /*if (width != null) {
    this.targets[target].targetWidth = (width * 5) * 1.2;
  }
  this.targets[target].style.width = this.targets[target].targetWidth +'px';*/
}

/**
 * Starts a search
 */
jsAS.prototype.populateTargets = function () {
  var as = this;
  this.db.owner = this;

  this.db.search(this.getSelectedOptions(), this.targets, this.extra);
}

/**
 * Fills the target select boxes with any matches received
 */
jsAS.prototype.populate = function (matches) {
  for (targetIndex in this.targets) {
    var target = this.targets[targetIndex];
    var matchesTarget = 0;
    for (targetElement in matches) {
      if ('edit-'+targetElement == target.id) {
        matchesTarget = targetElement;
        continue;
      }
    }
    if (matchesTarget) {
      this.targets[targetIndex].multiple = matches[matchesTarget]['multiple'];
      if (matches[matchesTarget]['multiple']) {
        if (target.name.indexOf('[]') == -1) {
          this.targets[targetIndex].name += '[]';
        }
      }
      else {
        var bracketIndex = target.name.indexOf('[]');
        if (bracketIndex != -1) {
          this.targets[targetIndex].name = target.name.substr(0, target.name.length-2);
        }
      }

      while (target.hasChildNodes()) {
        target.removeChild(target.childNodes[0]);
      }
      var targetMatches = matches[matchesTarget]['options'];
      var maxWidth = 0;
      for (currMatch in targetMatches) {
        var value = currMatch;
        var text = targetMatches[currMatch]['value'];
        var selected = targetMatches[currMatch]['selected'];

        if (text.length > maxWidth) {
          maxWidth = text.length;
        }
        // 'new Option()' used instead of appendChild(), because IE6 refuses to
        // display option text if the latter method is used (otherwise they seem
        // to behave the same).
        this.targets[targetIndex].options[this.targets[targetIndex].options.length] = new Option(text, value, false, selected);
        if (selected && !this.targets[targetIndex].multiple) {
          this.targets[targetIndex].selectedIndex = this.targets[targetIndex].options.length-1;
        }
      }
      if (this.targets[targetIndex].selectedIndex == -1) {
        this.targets[targetIndex].selectedIndex = 0;
      }

      if (hasClass(this.targets[targetIndex], 'form-activeselect')) {
        // Since IE does not support the DOM 2 methods for manually firing an
        // event, we must cater especially to its needs.
        // Reference: http://www.howtocreate.co.uk/tutorials/javascript/domevents
        if (document.createEvent) {
          // DOM 2 compliant method (Firefox / Opera / Safari / etc)
          var e = document.createEvent('HTMLEvents');
          e.initEvent('change', true, false);
          this.targets[targetIndex].dispatchEvent(e);
        }
        else if (document.createEventObject) {
          // IE special weird method
          var e = document.createEventObject();
          e.bubbles = true;
          e.cancelable = false;
          this.targets[targetIndex].fireEvent('onchange', e);
        }
      }
      else {
        this.setTargetWidth(targetIndex, maxWidth);
      }
    }
  }
  this.setSelectWidth(null);
}

/**
 * An ActiveSelect DataBase object
 */
function ASDB(uri, targets) {
  this.uri = uri;
  this.targets = targets;
  this.delay = 300;
  this.cache = {};
}

/**
 * Performs a cached and delayed search
 */
ASDB.prototype.search = function(searchString, targets, extra) {
  this.searchString = searchString;
  if (this.cache[searchString]) {
    return this.owner.populate(this.cache[searchString]);
  }
  if (this.timer) {
    clearTimeout(this.timer);
  }
  var db = this;
  this.timer = setTimeout(function() {
    //db.owner.input.style.width = db.owner.selectWidth +'px';
    db.owner.input.style.backgroundPosition = (95) +'px -18px'; //db.owner.selectWidth -
    var targetIds = [];
    for (var target = 0; target < targets.length; target++) {
      if (targets[target].id) {
        targetIds.push(targets[target].id.substr(5));
      }
    }
    var targetsString = targetIds.join(',');
    var uri = db.uri +'/'+ encodeURIComponent(targetsString) +'/'+ encodeURIComponent(searchString);
    uri += '/'+ encodeURIComponent(extra);
    HTTPGet(uri, db.receive, db);
  }, this.delay);
}

/**
 * HTTP callback function. Passes select options to the activeselect object
 */
ASDB.prototype.receive = function(string, xmlhttp, asdb) {
  // Note: Safari returns 'undefined' status if the request returns no data.
  if (xmlhttp.status != 200 && typeof xmlhttp.status != 'undefined') {
    asdb.owner.setSelectWidth(null);
    return alert('An HTTP error '+ xmlhttp.status +' occured.\n'+ asdb.uri);
  }
  // Split into array of key->value pairs
  if (string.length > 0) {
    var targets = parseJson(string);
    if (typeof targets['status'] == 'undefined' || targets['status'] != 0) {
      asdb.cache[asdb.searchString] = targets;
      asdb.owner.populate(targets);
    }
  }
}

