/*  Prototype JavaScript framework, version 1.5.0_rc1
 *  (c) 2005 Sam Stephenson <sam@conio.net>
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.5.0_rc1',
  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

  emptyFunction: function() {},
  K: function(x) {return x}
}

var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

var Abstract = new Object();

Object.extend = function(destination, source) {
  for (var property in source) {
    destination[property] = source[property];
  }
  return destination;
}

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (object == undefined) return 'undefined';
      if (object == null) return 'null';
      return object.inspect ? object.inspect() : object.toString();
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({}, object);
  }
});

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this, args = $A(arguments), object = args.shift();
  return function(event) {
    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
  }
}

Object.extend(Number.prototype, {
  toColorPart: function() {
    var digits = this.toString(16);
    if (this < 16) return '0' + digits;
    return digits;
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  }
});

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0; i < arguments.length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) {}
    }

    return returnValue;
  }
}

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.callback(this);
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
}
Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += (replacement(match) || '').toString();
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = count === undefined ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return this;
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = truncation === undefined ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : this;
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  escapeHTML: function() {
    var div = document.createElement('div');
    var text = document.createTextNode(this);
    div.appendChild(text);
    return div.innerHTML;
  },

  unescapeHTML: function() {
    var div = document.createElement('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
  },

  toQueryParams: function() {
    var pairs = this.match(/^\??(.*)$/)[1].split('&');
    return pairs.inject({}, function(params, pairString) {
      var pair  = pairString.split('=');
      var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
      params[decodeURIComponent(pair[0])] = value;
      return params;
    });
  },

  toArray: function() {
    return this.split('');
  },

  camelize: function() {
    var oStringList = this.split('-');
    if (oStringList.length == 1) return oStringList[0];

    var camelizedString = this.indexOf('-') == 0
      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
      : oStringList[0];

    for (var i = 1, len = oStringList.length; i < len; i++) {
      var s = oStringList[i];
      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
    }

    return camelizedString;
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.replace(/\\/g, '\\\\');
    if (useDoubleQuotes)
      return '"' + escapedString.replace(/"/g, '\\"') + '"';
    else
      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (typeof replacement == 'function') return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
}

String.prototype.parseQuery = String.prototype.toQueryParams;

var Template = Class.create();
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Template.prototype = {
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern  = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    return this.template.gsub(this.pattern, function(match) {
      var before = match[1];
      if (before == '\\') return match[2];
      return before + (object[match[3]] || '').toString();
    });
  }
}

var $break    = new Object();
var $continue = new Object();

var Enumerable = {
  each: function(iterator) {
    var index = 0;
    try {
      this._each(function(value) {
        try {
          iterator(value, index++);
        } catch (e) {
          if (e != $continue) throw e;
        }
      });
    } catch (e) {
      if (e != $break) throw e;
    }
  },

  all: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      result = result && !!(iterator || Prototype.K)(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator) {
    var result = false;
    this.each(function(value, index) {
      if (result = !!(iterator || Prototype.K)(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },

  detect: function (iterator) {
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(pattern, iterator) {
    var results = [];
    this.each(function(value, index) {
      var stringValue = value.toString();
      if (stringValue.match(pattern))
        results.push((iterator || Prototype.K)(value, index));
    })
    return results;
  },

  include: function(object) {
    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inject: function(memo, iterator) {
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.collect(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (result == undefined || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (result == undefined || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator) {
    var trues = [], falses = [];
    this.each(function(value, index) {
      ((iterator || Prototype.K)(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value, index) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator) {
    return this.collect(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.collect(Prototype.K);
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (typeof args.last() == 'function')
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
}

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0; i < iterable.length; i++)
      results.push(iterable[i]);
    return results;
  }
}

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse)
  Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0; i < this.length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != undefined || value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(value && value.constructor == Array ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  indexOf: function(object) {
    for (var i = 0; i < this.length; i++)
      if (this[i] == object) return i;
    return -1;
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function() {
    return this.inject([], function(array, value) {
      return array.include(value) ? array : array.concat([value]);
    });
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});
var Hash = {
  _each: function(iterator) {
    for (var key in this) {
      var value = this[key];
      if (typeof value == 'function') continue;

      var pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  },

  keys: function() {
    return this.pluck('key');
  },

  values: function() {
    return this.pluck('value');
  },

  merge: function(hash) {
    return $H(hash).inject($H(this), function(mergedHash, pair) {
      mergedHash[pair.key] = pair.value;
      return mergedHash;
    });
  },

  toQueryString: function() {
    return this.map(function(pair) {
      return pair.map(encodeURIComponent).join('=');
    }).join('&');
  },

  inspect: function() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }
}

function $H(object) {
  var hash = Object.extend({}, object || {});
  Object.extend(hash, Enumerable);
  Object.extend(hash, Hash);
  return hash;
}
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
}

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
}

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responderToAdd) {
    if (!this.include(responderToAdd))
      this.responders.push(responderToAdd);
  },

  unregister: function(responderToRemove) {
    this.responders = this.responders.without(responderToRemove);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (responder[callback] && typeof responder[callback] == 'function') {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) {}
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate: function() {
    Ajax.activeRequestCount++;
  },

  onComplete: function() {
    Ajax.activeRequestCount--;
  }
});

Ajax.Base = function() {};
Ajax.Base.prototype = {
  setOptions: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      parameters:   ''
    }
    Object.extend(this.options, options || {});
  },

  responseIsSuccess: function() {
    return this.transport.status == undefined
        || this.transport.status == 0
        || (this.transport.status >= 200 && this.transport.status < 300);
  },

  responseIsFailure: function() {
    return !this.responseIsSuccess();
  }
}

Ajax.Request = Class.create();
Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(url, options) {
    this.transport = Ajax.getTransport();
    this.setOptions(options);
    this.request(url);
  },

  request: function(url) {
    var parameters = this.options.parameters || '';
    if (parameters.length > 0) parameters += '&_=';

    /* Simulate other verbs over post */
    if (this.options.method != 'get' && this.options.method != 'post') {
      parameters += (parameters.length > 0 ? '&' : '') + '_method=' + this.options.method;
      this.options.method = 'post';
    }

    try {
      this.url = url;
      if (this.options.method == 'get' && parameters.length > 0)
        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;

      Ajax.Responders.dispatch('onCreate', this, this.transport);

      this.transport.open(this.options.method, this.url,
        this.options.asynchronous);

      if (this.options.asynchronous)
        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      var body = this.options.postBody ? this.options.postBody : parameters;
      this.transport.send(this.options.method == 'post' ? body : null);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    } catch (e) {
      this.dispatchException(e);
    }
  },

  setRequestHeaders: function() {
    var requestHeaders =
      ['X-Requested-With', 'XMLHttpRequest',
       'X-Prototype-Version', Prototype.Version,
       'Accept', 'text/javascript, text/html, application/xml, text/xml, */*'];

    if (this.options.method == 'post') {
      requestHeaders.push('Content-type', this.options.contentType);

      /* Force "Connection: close" for Mozilla browsers to work around
       * a bug where XMLHttpReqeuest sends an incorrect Content-length
       * header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType)
        requestHeaders.push('Connection', 'close');
    }

    if (this.options.requestHeaders)
      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);

    for (var i = 0; i < requestHeaders.length; i += 2)
      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState != 1)
      this.respondToReadyState(this.transport.readyState);
  },

  header: function(name) {
    try {
      return this.transport.getResponseHeader(name);
    } catch (e) {}
  },

  evalJSON: function() {
    try {
      return eval('(' + this.header('X-JSON') + ')');
    } catch (e) {}
  },

  evalResponse: function() {
    try {
      return eval(this.transport.responseText);
    } catch (e) {
      this.dispatchException(e);
    }
  },

  respondToReadyState: function(readyState) {
    var event = Ajax.Request.Events[readyState];
    var transport = this.transport, json = this.evalJSON();

    if (event == 'Complete') {
      try {
        (this.options['on' + this.transport.status]
         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(transport, json);
      } catch (e) {
        this.dispatchException(e);
      }

      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
        this.evalResponse();
    }

    try {
      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
      Ajax.Responders.dispatch('on' + event, this, transport, json);
    } catch (e) {
      this.dispatchException(e);
    }

    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
    if (event == 'Complete')
      this.transport.onreadystatechange = Prototype.emptyFunction;
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Updater = Class.create();

Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  initialize: function(container, url, options) {
    this.containers = {
      success: container.success ? $(container.success) : $(container),
      failure: container.failure ? $(container.failure) :
        (container.success ? null : $(container))
    }

    this.transport = Ajax.getTransport();
    this.setOptions(options);

    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    this.options.onComplete = (function(transport, object) {
      this.updateContent();
      onComplete(transport, object);
    }).bind(this);

    this.request(url);
  },

  updateContent: function() {
    var receiver = this.responseIsSuccess() ?
      this.containers.success : this.containers.failure;
    var response = this.transport.responseText;

    if (!this.options.evalScripts)
      response = response.stripScripts();

    if (receiver) {
      if (this.options.insertion) {
        new this.options.insertion(receiver, response);
      } else {
        Element.update(receiver, response);
      }
    }

    if (this.responseIsSuccess()) {
      if (this.onComplete)
        setTimeout(this.onComplete.bind(this), 10);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create();
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  initialize: function(container, url, options) {
    this.setOptions(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = {};
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(request) {
    if (this.options.decay) {
      this.decay = (request.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = request.responseText;
    }
    this.timer = setTimeout(this.onTimerEvent.bind(this),
      this.decay * this.frequency * 1000);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $() {
  var results = [], element;
  for (var i = 0; i < arguments.length; i++) {
    element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);
    results.push(Element.extend(element));
  }
  return results.reduce();
}

document.getElementsByClassName = function(className, parentElement) {
  var children = ($(parentElement) || document.body).getElementsByTagName('*');
  return $A(children).inject([], function(elements, child) {
    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
      elements.push(Element.extend(child));
    return elements;
  });
}

/*--------------------------------------------------------------------------*/

if (!window.Element)
  var Element = new Object();

Element.extend = function(element) {
  if (!element) return;
  if (_nativeExtensions || element.nodeType == 3) return element;

  if (!element._extended && element.tagName && element != window) {
    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;

    if (element.tagName == 'FORM')
      Object.extend(methods, Form.Methods);
    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
      Object.extend(methods, Form.Element.Methods);

    for (var property in methods) {
      var value = methods[property];
      if (typeof value == 'function')
        element[property] = cache.findOrStore(value);
    }
  }

  element._extended = true;
  return element;
}

Element.extend.cache = {
  findOrStore: function(value) {
    return this[value] = this[value] || function() {
      return value.apply(null, [this].concat($A(arguments)));
    }
  }
}

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    $(element).style.display = 'none';
    return element;
  },

  show: function(element) {
    $(element).style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, html) {
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  replace: function(element, html) {
    element = $(element);
    if (element.outerHTML) {
      element.outerHTML = html.stripScripts();
    } else {
      var range = element.ownerDocument.createRange();
      range.selectNodeContents(element);
      element.parentNode.replaceChild(
        range.createContextualFragment(html.stripScripts()), element);
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    element = $(element);
    return $A(element.getElementsByTagName('*'));
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    element = $(element);
    if (typeof selector == 'string')
      selector = new Selector(selector);
    return selector.match(element);
  },

  up: function(element, expression, index) {
    return Selector.findElement($(element).ancestors(), expression, index);
  },

  down: function(element, expression, index) {
    return Selector.findElement($(element).descendants(), expression, index);
  },

  previous: function(element, expression, index) {
    return Selector.findElement($(element).previousSiblings(), expression, index);
  },

  next: function(element, expression, index) {
    return Selector.findElement($(element).nextSiblings(), expression, index);
  },

  getElementsBySelector: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  getElementsByClassName: function(element, className) {
    element = $(element);
    return document.getElementsByClassName(className, element);
  },

  getHeight: function(element) {
    element = $(element);
    return element.offsetHeight;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).include(className);
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element).add(className);
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    Element.classNames(element).remove(className);
    return element;
  },

  observe: function() {
    Event.observe.apply(Event, arguments);
    return $A(arguments).first();
  },

  stopObserving: function() {
    Event.stopObserving.apply(Event, arguments);
    return $A(arguments).first();
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.match(/^\s*$/);
  },

  childOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);
    while (element = element.parentNode)
      if (element == ancestor) return true;
    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var x = element.x ? element.x : element.offsetLeft,
        y = element.y ? element.y : element.offsetTop;
    window.scrollTo(x, y);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    var value = element.style[style.camelize()];
    if (!value) {
      if (document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = css ? css.getPropertyValue(style) : null;
      } else if (element.currentStyle) {
        value = element.currentStyle[style.camelize()];
      }
    }

    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
      if (Element.getStyle(element, 'position') == 'static') value = 'auto';

    return value == 'auto' ? null : value;
  },

  setStyle: function(element, style) {
    element = $(element);
    for (var name in style)
      element.style[name.camelize()] = style[name];
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    if (Element.getStyle(element, 'display') != 'none')
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = '';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = 'none';
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element._overflow = element.style.overflow || 'auto';
    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  }
}

// IE is missing .innerHTML support for TABLE-related elements
if(document.all){
  Element.Methods.update = function(element, html) {
    element = $(element);
    var tagName = element.tagName.toUpperCase();
    if (['THEAD','TBODY','TR','TD'].indexOf(tagName) > -1) {
      var div = document.createElement('div');
      switch (tagName) {
        case 'THEAD':
        case 'TBODY':
          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
          depth = 2;
          break;
        case 'TR':
          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
          depth = 3;
          break;
        case 'TD':
          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
          depth = 4;
      }
      $A(element.childNodes).each(function(node){
        element.removeChild(node)
      });
      depth.times(function(){ div = div.firstChild });

      $A(div.childNodes).each(
        function(node){ element.appendChild(node) });
    } else {
      element.innerHTML = html.stripScripts();
    }
    setTimeout(function() {html.evalScripts()}, 10);
    return element;
  }
}

Object.extend(Element, Element.Methods);

var _nativeExtensions = false;

if (!window.HTMLElement && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  /* Emulate HTMLElement, HTMLFormElement, HTMLInputElement, HTMLTextAreaElement,
     and HTMLSelectElement in Safari */
  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
    var klass = window['HTML' + tag + 'Element'] = {};
    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
  });
}

Element.addMethods = function(methods) {
  Object.extend(Element.Methods, methods || {});

  function copy(methods, destination) {
    var cache = Element.extend.cache;
    for (var property in methods) {
      var value = methods[property];
      destination[property] = cache.findOrStore(value);
    }
  }

  if (typeof HTMLElement != 'undefined') {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Form.Methods, HTMLFormElement.prototype);
    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
      copy(Form.Element.Methods, klass.prototype);
    });
    _nativeExtensions = true;
  }
}

var Toggle = new Object();
Toggle.display = Element.toggle;

/*--------------------------------------------------------------------------*/

Abstract.Insertion = function(adjacency) {
  this.adjacency = adjacency;
}

Abstract.Insertion.prototype = {
  initialize: function(element, content) {
    this.element = $(element);
    this.content = content.stripScripts();

    if (this.adjacency && this.element.insertAdjacentHTML) {
      try {
        this.element.insertAdjacentHTML(this.adjacency, this.content);
      } catch (e) {
        var tagName = this.element.tagName.toLowerCase();
        if (tagName == 'tbody' || tagName == 'tr') {
          this.insertContent(this.contentFromAnonymousTable());
        } else {
          throw e;
        }
      }
    } else {
      this.range = this.element.ownerDocument.createRange();
      if (this.initializeRange) this.initializeRange();
      this.insertContent([this.range.createContextualFragment(this.content)]);
    }

    setTimeout(function() {content.evalScripts()}, 10);
  },

  contentFromAnonymousTable: function() {
    var div = document.createElement('div');
    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
    return $A(div.childNodes[0].childNodes[0].childNodes);
  }
}

var Insertion = new Object();

Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  initializeRange: function() {
    this.range.setStartBefore(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment, this.element);
    }).bind(this));
  }
});

Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(true);
  },

  insertContent: function(fragments) {
    fragments.reverse(false).each((function(fragment) {
      this.element.insertBefore(fragment, this.element.firstChild);
    }).bind(this));
  }
});

Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.appendChild(fragment);
    }).bind(this));
  }
});

Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  initializeRange: function() {
    this.range.setStartAfter(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment,
        this.element.nextSibling);
    }).bind(this));
  }
});

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set(this.toArray().concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set(this.select(function(className) {
      return className != classNameToRemove;
    }).join(' '));
  },

  toString: function() {
    return this.toArray().join(' ');
  }
}

Object.extend(Element.ClassNames.prototype, Enumerable);
var Selector = Class.create();
Selector.prototype = {
  initialize: function(expression) {
    this.params = {classNames: []};
    this.expression = expression.toString().strip();
    this.parseExpression();
    this.compileMatcher();
  },

  parseExpression: function() {
    function abort(message) { throw 'Parse error in selector: ' + message; }

    if (this.expression == '')  abort('empty expression');

    var params = this.params, expr = this.expression, match, modifier, clause, rest;
    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
      params.attributes = params.attributes || [];
      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
      expr = match[1];
    }

    if (expr == '*') return this.params.wildcard = true;

    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
      modifier = match[1], clause = match[2], rest = match[3];
      switch (modifier) {
        case '#':       params.id = clause; break;
        case '.':       params.classNames.push(clause); break;
        case '':
        case undefined: params.tagName = clause.toUpperCase(); break;
        default:        abort(expr.inspect());
      }
      expr = rest;
    }

    if (expr.length > 0) abort(expr.inspect());
  },

  buildMatchExpression: function() {
    var params = this.params, conditions = [], clause;

    if (params.wildcard)
      conditions.push('true');
    if (clause = params.id)
      conditions.push('element.id == ' + clause.inspect());
    if (clause = params.tagName)
      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
    if ((clause = params.classNames).length > 0)
      for (var i = 0; i < clause.length; i++)
        conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
    if (clause = params.attributes) {
      clause.each(function(attribute) {
        var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
        var splitValueBy = function(delimiter) {
          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
        }

        switch (attribute.operator) {
          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
          case '|=':      conditions.push(
                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
                          ); break;
          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
          case '':
          case undefined: conditions.push(value + ' != null'); break;
          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
        }
      });
    }

    return conditions.join(' && ');
  },

  compileMatcher: function() {
    this.match = new Function('element', 'if (!element.tagName) return false; \
      return ' + this.buildMatchExpression());
  },

  findElements: function(scope) {
    var element;

    if (element = $(this.params.id))
      if (this.match(element))
        if (!scope || Element.childOf(element, scope))
          return [element];

    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');

    var results = [];
    for (var i = 0; i < scope.length; i++)
      if (this.match(element = scope[i]))
        results.push(Element.extend(element));

    return results;
  },

  toString: function() {
    return this.expression;
  }
}

Object.extend(Selector, {
  matchElements: function(elements, expression) {
    var selector = new Selector(expression);
    return elements.select(selector.match.bind(selector));
  },

  findElement: function(elements, expression, index) {
    if (typeof expression == 'number') index = expression, expression = false;
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    return expressions.map(function(expression) {
      return expression.strip().split(/\s+/).inject([null], function(results, expr) {
        var selector = new Selector(expr);
        return results.inject([], function(elements, result) {
          return elements.concat(selector.findElements(result || element));
        });
      });
    }).flatten();
  }
});

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  }
};

Form.Methods = {
  serialize: function(form) {
    var elements = Form.getElements($(form));
    var queryComponents = new Array();

    for (var i = 0; i < elements.length; i++) {
      var queryComponent = Form.Element.serialize(elements[i]);
      if (queryComponent)
        queryComponents.push(queryComponent);
    }

    return queryComponents.join('&');
  },

  getElements: function(form) {
    form = $(form);
    var elements = new Array();

    for (var tagName in Form.Element.Serializers) {
      var tagElements = form.getElementsByTagName(tagName);
      for (var j = 0; j < tagElements.length; j++)
        elements.push(tagElements[j]);
    }
    return elements;
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name)
      return inputs;

    var matchingInputs = new Array();
    for (var i = 0; i < inputs.length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) ||
          (name && input.name != name))
        continue;
      matchingInputs.push(input);
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    var elements = Form.getElements(form);
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      element.blur();
      element.disabled = 'true';
    }
    return form;
  },

  enable: function(form) {
    form = $(form);
    var elements = Form.getElements(form);
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      element.disabled = '';
    }
    return form;
  },

  findFirstElement: function(form) {
    return Form.getElements(form).find(function(element) {
      return element.type != 'hidden' && !element.disabled &&
        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    Field.activate(Form.findFirstElement(form));
    return form;
  }
}

Object.extend(Form, Form.Methods);

/*--------------------------------------------------------------------------*/

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
}

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter) {
      var key = encodeURIComponent(parameter[0]);
      if (key.length == 0) return;

      if (parameter[1].constructor != Array)
        parameter[1] = [parameter[1]];

      return parameter[1].map(function(value) {
        return key + '=' + encodeURIComponent(value);
      }).join('&');
    }
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter)
      return parameter[1];
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    element.focus();
    if (element.select)
      element.select();
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = '';
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.blur();
    element.disabled = 'true';
    return element;
  }
}

Object.extend(Form.Element, Form.Element.Methods);
var Field = Form.Element;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element);
      default:
        return Form.Element.Serializers.textarea(element);
    }
    return false;
  },

  inputSelector: function(element) {
    if (element.checked)
      return [element.name, element.value];
  },

  textarea: function(element) {
    return [element.name, element.value];
  },

  select: function(element) {
    return Form.Element.Serializers[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var value = '', opt, index = element.selectedIndex;
    if (index >= 0) {
      opt = element.options[index];
      value = opt.value || opt.text;
    }
    return [element.name, value];
  },

  selectMany: function(element) {
    var value = [];
    for (var i = 0; i < element.length; i++) {
      var opt = element.options[i];
      if (opt.selected)
        value.push(opt.value || opt.text);
    }
    return [element.name, value];
  }
}

/*--------------------------------------------------------------------------*/

var $F = Form.Element.getValue;

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
  initialize: function(element, frequency, callback) {
    this.frequency = frequency;
    this.element   = $(element);
    this.callback  = callback;

    this.lastValue = this.getValue();
    this.registerCallback();
  },

  registerCallback: function() {
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  onTimerEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
}

Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    var elements = Form.getElements(this.element);
    for (var i = 0; i < elements.length; i++)
      this.registerCallback(elements[i]);
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
}

Form.Element.EventObserver = Class.create();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) {
  var Event = new Object();
}

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,

  element: function(event) {
    return event.target || event.srcElement;
  },

  isLeftClick: function(event) {
    return (((event.which) && (event.which == 1)) ||
            ((event.button) && (event.button == 1)));
  },

  pointerX: function(event) {
    return event.pageX || (event.clientX +
      (document.documentElement.scrollLeft || document.body.scrollLeft));
  },

  pointerY: function(event) {
    return event.pageY || (event.clientY +
      (document.documentElement.scrollTop || document.body.scrollTop));
  },

  stop: function(event) {
    if (event.preventDefault) {
      event.preventDefault();
      event.stopPropagation();
    } else {
      event.returnValue = false;
      event.cancelBubble = true;
    }
  },

  // find the first node with the given tagName, starting from the
  // node the event was triggered on; traverses the DOM upwards
  findElement: function(event, tagName) {
    var element = Event.element(event);
    while (element.parentNode && (!element.tagName ||
        (element.tagName.toUpperCase() != tagName.toUpperCase())))
      element = element.parentNode;
    return element;
  },

  observers: false,

  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
  },

  unloadCache: function() {
    if (!Event.observers) return;
    for (var i = 0; i < Event.observers.length; i++) {
      Event.stopObserving.apply(this, Event.observers[i]);
      Event.observers[i][0] = null;
    }
    Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.attachEvent))
      name = 'keydown';

    Event._observeAndCache(element, name, observer, useCapture);
  },

  stopObserving: function(element, name, observer, useCapture) {
    element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.detachEvent))
      name = 'keydown';

    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      try {
        element.detachEvent('on' + name, observer);
      } catch (e) {}
    }
  }
});

/* prevent memory leaks in IE */
if (navigator.appVersion.match(/\bMSIE\b/))
  Event.observe(window, 'unload', Event.unloadCache, false);
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  realOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return [valueL, valueT];
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return [valueL, valueT];
  },

  offsetParent: function(element) {
    if (element.offsetParent) return element.offsetParent;
    if (element == document.body) return element;

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return element;

    return document.body;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = this.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = this.realOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = this.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  page: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent==document.body)
        if (Element.getStyle(element,'position')=='absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!window.opera || element.tagName=='BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return [valueL, valueT];
  },

  clone: function(source, target) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || {})

    // find page position of source
    source = $(source);
    var p = Position.page(source);

    // find coordinate system to use
    target = $(target);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(target,'position') == 'absolute') {
      parent = Position.offsetParent(target);
      delta = Position.page(parent);
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  },

  absolutize: function(element) {
    element = $(element);
    if (element.style.position == 'absolute') return;
    Position.prepare();

    var offsets = Position.positionedOffset(element);
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';;
    element.style.left   = left + 'px';;
    element.style.width  = width + 'px';;
    element.style.height = height + 'px';;
  },

  relativize: function(element) {
    element = $(element);
    if (element.style.position == 'relative') return;
    Position.prepare();

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
  }
}

// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  Position.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return [valueL, valueT];
  }
}

Element.addMethods();




// Effects.js

// script.aculo.us effects.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006

// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// See scriptaculous.js for full license.  

// converts rgb() and #xxx to #xxxxxx format,  
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function() {  
  var color = '#';  
  if(this.slice(0,4) == 'rgb(') {  
    var cols = this.slice(4,this.length-1).split(',');  
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
  } else {  
    if(this.slice(0,1) == '#') {  
      if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
      if(this.length==7) color = this.toLowerCase();  
    }  
  }  
  return(color.length==7 ? color : (arguments[0] || this));  
}

/*--------------------------------------------------------------------------*/

Element.collectTextNodes = function(element) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
}

Element.collectTextNodesIgnoreClass = function(element, className) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
}

Element.setContentZoom = function(element, percent) {
  element = $(element);  
  Element.setStyle(element, {fontSize: (percent/100) + 'em'});   
  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
}

Element.getOpacity = function(element){  
  var opacity;
  if (opacity = Element.getStyle(element, 'opacity'))  
    return parseFloat(opacity);  
  if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))  
    if(opacity[1]) return parseFloat(opacity[1]) / 100;  
  return 1.0;  
}

Element.setOpacity = function(element, value){  
  element= $(element);  
  if (value == 1){
    Element.setStyle(element, { opacity: 
      (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 
      0.999999 : 1.0 });
    if(/MSIE/.test(navigator.userAgent) && !window.opera)  
      Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});  
  } else {  
    if(value < 0.00001) value = 0;  
    Element.setStyle(element, {opacity: value});
    if(/MSIE/.test(navigator.userAgent) && !window.opera)  
     Element.setStyle(element, 
       { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
                 'alpha(opacity='+value*100+')' });  
  }
}  
 
Element.getInlineOpacity = function(element){  
  return $(element).style.opacity || '';
}  

Element.childrenWithClassName = function(element, className, findFirst) {
  var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
  var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) { 
    return (c.className && c.className.match(classNameRegExp));
  });
  if(!results) results = [];
  return results;
}

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

/*--------------------------------------------------------------------------*/

Array.prototype.call = function() {
  var args = arguments;
  this.each(function(f){ f.apply(this, args) });
}

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  tagifyText: function(element) {
    if(typeof Builder == 'undefined')
      throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
      
    var tagifyStyle = 'position:relative';
    if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if(child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            Builder.node('span',{style: tagifyStyle},
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if(((typeof element == 'object') || 
        (typeof element == 'function')) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || {});
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, arguments[2] || {});
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

var Effect2 = Effect; // deprecated

/* ------------- transitions ------------- */

Effect.Transitions = {}

Effect.Transitions.linear = Prototype.K;

Effect.Transitions.sinoidal = function(pos) {
  return (-Math.cos(pos*Math.PI)/2) + 0.5;
}
Effect.Transitions.reverse  = function(pos) {
  return 1-pos;
}
Effect.Transitions.flicker = function(pos) {
  return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
}
Effect.Transitions.wobble = function(pos) {
  return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
}
Effect.Transitions.pulse = function(pos) {
  return (Math.floor(pos*10) % 2 == 0 ? 
    (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
}
Effect.Transitions.none = function(pos) {
  return 0;
}
Effect.Transitions.full = function(pos) {
  return 1;
}

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create();
Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
  initialize: function() {
    this.effects  = [];
    this.interval = null;
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();
    
    var position = (typeof effect.options.queue == 'string') ? 
      effect.options.queue : effect.options.queue.position;
    
    switch(position) {
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);
    
    if(!this.interval) 
      this.interval = setInterval(this.loop.bind(this), 40);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if(this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    this.effects.invoke('loop', timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if(typeof queueName != 'string') return queueName;
    
    if(!this.instances[queueName])
      this.instances[queueName] = new Effect.ScopedQueue();
      
    return this.instances[queueName];
  }
}
Effect.Queue = Effect.Queues.get('global');

Effect.DefaultOptions = {
  transition: Effect.Transitions.sinoidal,
  duration:   1.0,   // seconds
  fps:        25.0,  // max. 25fps due to Effect.Queue implementation
  sync:       false, // true for combining
  from:       0.0,
  to:         1.0,
  delay:      0.0,
  queue:      'parallel'
}

Effect.Base = function() {};
Effect.Base.prototype = {
  position: null,
  start: function(options) {
    this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn + (this.options.duration*1000);
    this.event('beforeStart');
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if(timePos >= this.startOn) {
      if(timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if(this.finish) this.finish(); 
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / (this.finishOn - this.startOn);
      var frame = Math.round(pos * this.options.fps * this.options.duration);
      if(frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  render: function(pos) {
    if(this.state == 'idle') {
      this.state = 'running';
      this.event('beforeSetup');
      if(this.setup) this.setup();
      this.event('afterSetup');
    }
    if(this.state == 'running') {
      if(this.options.transition) pos = this.options.transition(pos);
      pos *= (this.options.to-this.options.from);
      pos += this.options.from;
      this.position = pos;
      this.event('beforeUpdate');
      if(this.update) this.update(pos);
      this.event('afterUpdate');
    }
  },
  cancel: function() {
    if(!this.options.sync)
      Effect.Queues.get(typeof this.options.queue == 'string' ? 
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if(this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    return '#<Effect:' + $H(this).inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
}

Effect.Parallel = Class.create();
Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if(effect.finish) effect.finish(position);
      effect.event('afterFinish');
    });
  }
});

Effect.Opacity = Class.create();
Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
      this.element.setStyle({zoom: 1});
    var options = Object.extend({
      from: this.element.getOpacity() || 0.0,
      to:   1.0
    }, arguments[1] || {});
    this.start(options);
  },
  update: function(position) {
    this.element.setOpacity(position);
  }
});

Effect.Move = Class.create();
Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Bug in Opera: Opera returns the "real" position of a static element or
    // relative element that does not have top/left explicitly set.
    // ==> Always set top and left for position relative elements in your stylesheets 
    // (to 0 if you do not need them) 
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if(this.options.mode == 'absolute') {
      // absolute movement, so we need to calc deltaX and deltaY
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
      top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element, 
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
};

Effect.Scale = Class.create();
Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
  initialize: function(element, percent) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || {});
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');
    
    this.originalStyle = {};
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if(fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if(this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if(/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if(!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if(this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = {};
    if(this.options.scaleX) d.width = Math.round(width) + 'px';
    if(this.options.scaleY) d.height = Math.round(height) + 'px';
    if(this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if(this.elementPositioning == 'absolute') {
        if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if(this.options.scaleY) d.top = -topd + 'px';
        if(this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    this.element.setStyle(d);
  }
});

Effect.Highlight = Class.create();
Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = {
      backgroundImage: this.element.getStyle('background-image') };
    this.element.setStyle({backgroundImage: 'none'});
    if(!this.options.endcolor)
      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    if(!this.options.restorecolor)
      this.options.restorecolor = this.element.getStyle('background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
  },
  update: function(position) {
    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = Class.create();
Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    this.start(arguments[1] || {});
  },
  setup: function() {
    Position.prepare();
    var offsets = Position.cumulativeOffset(this.element);
    if(this.options.offset) offsets[1] += this.options.offset;
    var max = window.innerHeight ? 
      window.height - window.innerHeight :
      document.body.scrollHeight - 
        (document.documentElement.clientHeight ? 
          document.documentElement.clientHeight : document.body.clientHeight);
    this.scrollStart = Position.deltaY;
    this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
  },
  update: function(position) {
    Position.prepare();
    window.scrollTo(Position.deltaX, 
      this.scrollStart + (position*this.delta));
  }
});

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
  from: element.getOpacity() || 1.0,
  to:   0.0,
  afterFinishInternal: function(effect) { 
    if(effect.options.to!=0) return;
    effect.element.hide();
    effect.element.setStyle({opacity: oldOpacity}); 
  }}, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from);
    effect.element.show(); 
  }}, arguments[1] || {});
  return new Effect.Opacity(element,options);
}

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = { 
    opacity: element.getInlineOpacity(), 
    position: element.getStyle('position'),
    top:  element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height
  };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200, 
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
     Object.extend({ duration: 1.0, 
      beforeSetupInternal: function(effect) {
        Position.absolutize(effect.effects[0].element)
      },
      afterFinishInternal: function(effect) {
         effect.effects[0].element.hide();
         effect.effects[0].element.setStyle(oldStyle); }
     }, arguments[1] || {})
   );
}

Effect.BlindUp = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide();
        effect.element.undoClipping();
      } 
    }, arguments[1] || {})
  );
}

Effect.BlindDown = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping();
      effect.element.setStyle({height: '0px'});
      effect.element.show(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || {}));
}

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element, Object.extend({
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      new Effect.Scale(effect.element, 1, { 
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) { 
          effect.element.makePositioned();
          effect.element.makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide();
          effect.element.undoClipping();
          effect.element.undoPositioned();
          effect.element.setStyle({opacity: oldOpacity});
        }
      })
    }
  }, arguments[1] || {}));
}

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left'),
    opacity: element.getInlineOpacity() };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) {
          effect.effects[0].element.makePositioned(); 
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide();
          effect.effects[0].element.undoPositioned();
          effect.effects[0].element.setStyle(oldStyle);
        } 
      }, arguments[1] || {}));
}

Effect.Shake = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element, 
      { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
        effect.element.undoPositioned();
        effect.element.setStyle(oldStyle);
  }}) }}) }}) }}) }}) }});
}

Effect.SlideDown = function(element) {
  element = $(element);
  element.cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.firstChild.makePositioned();
      if(window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping();
      effect.element.setStyle({height: '0px'});
      effect.element.show(); },
    afterUpdateInternal: function(effect) {
      effect.element.firstChild.setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping(); 
      // IE will crash if child is undoPositioned first
      if(/MSIE/.test(navigator.userAgent) && !window.opera){
        effect.element.undoPositioned();
        effect.element.firstChild.undoPositioned();
      }else{
        effect.element.firstChild.undoPositioned();
        effect.element.undoPositioned();
      }
      effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || {})
  );
}

Effect.SlideUp = function(element) {
  element = $(element);
  element.cleanWhitespace();
  var oldInnerBottom = $(element.firstChild).getStyle('bottom');
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    restoreAfterFinish: true,
    beforeStartInternal: function(effect) {
      effect.element.makePositioned();
      effect.element.firstChild.makePositioned();
      if(window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping();
      effect.element.show(); },  
    afterUpdateInternal: function(effect) {
      effect.element.firstChild.setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
    afterFinishInternal: function(effect) {
      effect.element.hide();
      effect.element.undoClipping();
      effect.element.firstChild.undoPositioned();
      effect.element.undoPositioned();
      effect.element.setStyle({bottom: oldInnerBottom}); }
   }, arguments[1] || {})
  );
}

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, 
    { restoreAfterFinish: true,
      beforeSetup: function(effect) {
        effect.element.makeClipping(effect.element); },  
      afterFinishInternal: function(effect) {
        effect.element.hide(effect.element); 
        effect.element.undoClipping(effect.element); }
  });
}

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();    
  var initialMoveX, initialMoveY;
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0; 
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }
  
  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01, 
    beforeSetup: function(effect) {
      effect.element.hide();
      effect.element.makeClipping();
      effect.element.makePositioned();
    },
    afterFinishInternal: function(effect) {
      new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) {
               effect.effects[0].element.setStyle({height: '0px'});
               effect.effects[0].element.show(); 
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping();
               effect.effects[0].element.undoPositioned();
               effect.effects[0].element.setStyle(oldStyle); 
             }
           }, options)
      )
    }
  });
}

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || {});
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':  
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned();
           effect.effects[0].element.makeClipping(); },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide();
           effect.effects[0].element.undoClipping();
           effect.effects[0].element.undoPositioned();
           effect.effects[0].element.setStyle(oldStyle); }
       }, options)
  );
}

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || {};
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 3.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
}

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  Element.makeClipping(element);
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide();
        effect.element.undoClipping(); 
        effect.element.setStyle(oldStyle);
      } });
  }}, arguments[1] || {}));
};

['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each( 
  function(f) { Element.Methods[f] = Element[f]; }
);

Element.Methods.visualEffect = function(element, effect, options) {
  s = effect.gsub(/_/, '-').camelize();
  effect_class = s.charAt(0).toUpperCase() + s.substring(1);
  new Effect[effect_class](element, options);
  return $(element);
};

Element.addMethods();

// script.aculo.us dragdrop.js v1.6.4, Wed Sep 06 11:30:58 CEST 2006

// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
// 
// See scriptaculous.js for full license.

/*--------------------------------------------------------------------------*/

if(typeof Effect == 'undefined')
  throw("dragdrop.js requires including script.aculo.us' effects.js library");

var Droppables = {
  drops: [],

  remove: function(element) {
    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
  },

  add: function(element) {
    element = $(element);
    var options = Object.extend({
      greedy:     true,
      hoverclass: null,
      tree:       false
    }, arguments[1] || {});

    // cache containers
    if(options.containment) {
      options._containers = [];
      var containment = options.containment;
      if((typeof containment == 'object') && 
        (containment.constructor == Array)) {
        containment.each( function(c) { options._containers.push($(c)) });
      } else {
        options._containers.push($(containment));
      }
    }
    
    if(options.accept) options.accept = [options.accept].flatten();

    Element.makePositioned(element); // fix IE
    options.element = element;

    this.drops.push(options);
  },
  
  findDeepestChild: function(drops) {
    deepest = drops[0];
      
    for (i = 1; i < drops.length; ++i)
      if (Element.isParent(drops[i].element, deepest.element))
        deepest = drops[i];
    
    return deepest;
  },

  isContained: function(element, drop) {
    var containmentNode;
    if(drop.tree) {
      containmentNode = element.treeNode; 
    } else {
      containmentNode = element.parentNode;
    }
    return drop._containers.detect(function(c) { return containmentNode == c });
  },
  
  isAffected: function(point, element, drop) {
    return (
      (drop.element!=element) &&
      ((!drop._containers) ||
        this.isContained(element, drop)) &&
      ((!drop.accept) ||
        (Element.classNames(element).detect( 
          function(v) { return drop.accept.include(v) } ) )) &&
      Position.within(drop.element, point[0], point[1]) );
  },

  deactivate: function(drop) {
    if(drop.hoverclass)
      Element.removeClassName(drop.element, drop.hoverclass);
    this.last_active = null;
  },

  activate: function(drop) {
    if(drop.hoverclass)
      Element.addClassName(drop.element, drop.hoverclass);
    this.last_active = drop;
  },

  show: function(point, element) {
    if(!this.drops.length) return;
    var affected = [];
    
    if(this.last_active) this.deactivate(this.last_active);
    this.drops.each( function(drop) {
      if(Droppables.isAffected(point, element, drop))
        affected.push(drop);
    });
        
    if(affected.length>0) {
      drop = Droppables.findDeepestChild(affected);
      Position.within(drop.element, point[0], point[1]);
      if(drop.onHover)
        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
      
      Droppables.activate(drop);
    }
  },

  fire: function(event, element) {
    if(!this.last_active) return;
    Position.prepare();

    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
      if (this.last_active.onDrop) 
        this.last_active.onDrop(element, this.last_active.element, event);
  },

  reset: function() {
    if(this.last_active)
      this.deactivate(this.last_active);
  }
}

var Draggables = {
  drags: [],
  observers: [],
  
  register: function(draggable) {
    if(this.drags.length == 0) {
      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
      
      Event.observe(document, "mouseup", this.eventMouseUp);
      Event.observe(document, "mousemove", this.eventMouseMove);
      Event.observe(document, "keypress", this.eventKeypress);
    }
    this.drags.push(draggable);
  },
  
  unregister: function(draggable) {
    this.drags = this.drags.reject(function(d) { return d==draggable });
    if(this.drags.length == 0) {
      Event.stopObserving(document, "mouseup", this.eventMouseUp);
      Event.stopObserving(document, "mousemove", this.eventMouseMove);
      Event.stopObserving(document, "keypress", this.eventKeypress);
    }
  },
  
  activate: function(draggable) {
    if(draggable.options.delay) { 
      this._timeout = setTimeout(function() { 
        Draggables._timeout = null; 
        window.focus(); 
        Draggables.activeDraggable = draggable; 
      }.bind(this), draggable.options.delay); 
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
      this.activeDraggable = draggable;
    }
  },
  
  deactivate: function() {
    this.activeDraggable = null;
  },
  
  updateDrag: function(event) {
    if(!this.activeDraggable) return;
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    // Mozilla-based browsers fire successive mousemove events with
    // the same coordinates, prevent needless redrawing (moz bug?)
    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
    this._lastPointer = pointer;
    
    this.activeDraggable.updateDrag(event, pointer);
  },
  
  endDrag: function(event) {
    if(this._timeout) { 
      clearTimeout(this._timeout); 
      this._timeout = null; 
    }
    if(!this.activeDraggable) return;
    this._lastPointer = null;
    this.activeDraggable.endDrag(event);
    this.activeDraggable = null;
  },
  
  keyPress: function(event) {
    if(this.activeDraggable)
      this.activeDraggable.keyPress(event);
  },
  
  addObserver: function(observer) {
    this.observers.push(observer);
    this._cacheObserverCallbacks();
  },
  
  removeObserver: function(element) {  // element instead of observer fixes mem leaks
    this.observers = this.observers.reject( function(o) { return o.element==element });
    this._cacheObserverCallbacks();
  },
  
  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
    if(this[eventName+'Count'] > 0)
      this.observers.each( function(o) {
        if(o[eventName]) o[eventName](eventName, draggable, event);
      });
    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
  },
  
  _cacheObserverCallbacks: function() {
    ['onStart','onEnd','onDrag'].each( function(eventName) {
      Draggables[eventName+'Count'] = Draggables.observers.select(
        function(o) { return o[eventName]; }
      ).length;
    });
  }
}

/*--------------------------------------------------------------------------*/

var Draggable = Class.create();
Draggable._dragging    = {};

Draggable.prototype = {
  initialize: function(element) {
    var defaults = {
      handle: false,
      reverteffect: function(element, top_offset, left_offset) {
        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
          queue: {scope:'_draggable', position:'end'}
        });
      },
      endeffect: function(element) {
        var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity, 
          queue: {scope:'_draggable', position:'end'},
          afterFinish: function(){ 
            Draggable._dragging[element] = false 
          }
        }); 
      },
      zindex: 1000,
      revert: false,
      scroll: false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
      delay: 0
    };
    
    if(arguments[1] && typeof arguments[1].endeffect == 'undefined')
      Object.extend(defaults, {
        starteffect: function(element) {
          element._opacity = Element.getOpacity(element);
          Draggable._dragging[element] = true;
          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7}); 
        }
      });
    
    var options = Object.extend(defaults, arguments[1] || {});

    this.element = $(element);
    
    if(options.handle && (typeof options.handle == 'string')) {
      var h = Element.childrenWithClassName(this.element, options.handle, true);
      if(h.length>0) this.handle = h[0];
    }
    if(!this.handle) this.handle = $(options.handle);
    if(!this.handle) this.handle = this.element;
    
    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
      options.scroll = $(options.scroll);
      this._isScrollChild = Element.childOf(this.element, options.scroll);
    }

    Element.makePositioned(this.element); // fix IE    

    this.delta    = this.currentDelta();
    this.options  = options;
    this.dragging = false;   

    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
    Event.observe(this.handle, "mousedown", this.eventMouseDown);
    
    Draggables.register(this);
  },
  
  destroy: function() {
    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
    Draggables.unregister(this);
  },
  
  currentDelta: function() {
    return([
      parseInt(Element.getStyle(this.element,'left') || '0'),
      parseInt(Element.getStyle(this.element,'top') || '0')]);
  },
  
  initDrag: function(event) {
    if(typeof Draggable._dragging[this.element] != 'undefined' &&
      Draggable._dragging[this.element]) return;
    if(Event.isLeftClick(event)) {    
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if(src.tagName && (
        src.tagName=='INPUT' ||
        src.tagName=='SELECT' ||
        src.tagName=='OPTION' ||
        src.tagName=='BUTTON' ||
        src.tagName=='TEXTAREA')) return;
        
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      var pos     = Position.cumulativeOffset(this.element);
      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
      
      Draggables.activate(this);
      Event.stop(event);
    }
  },
  
  startDrag: function(event) {
    this.dragging = true;
    
    if(this.options.zindex) {
      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
      this.element.style.zIndex = this.options.zindex;
    }
    
    if(this.options.ghosting) {
      this._clone = this.element.cloneNode(true);
      Position.absolutize(this.element);
      this.element.parentNode.insertBefore(this._clone, this.element);
    }
    
    if(this.options.scroll) {
      if (this.options.scroll == window) {
        var where = this._getWindowScroll(this.options.scroll);
        this.originalScrollLeft = where.left;
        this.originalScrollTop = where.top;
      } else {
        this.originalScrollLeft = this.options.scroll.scrollLeft;
        this.originalScrollTop = this.options.scroll.scrollTop;
      }
    }
    
    Draggables.notify('onStart', this, event);
        
    if(this.options.starteffect) this.options.starteffect(this.element);
  },
  
  updateDrag: function(event, pointer) {
    if(!this.dragging) this.startDrag(event);
    Position.prepare();
    Droppables.show(pointer, this.element);
    Draggables.notify('onDrag', this, event);
    
    this.draw(pointer);
    if(this.options.change) this.options.change(this);
    
    if(this.options.scroll) {
      this.stopScrolling();
      
      var p;
      if (this.options.scroll == window) {
        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
      } else {
        p = Position.page(this.options.scroll);
        p[0] += this.options.scroll.scrollLeft;
        p[1] += this.options.scroll.scrollTop;
        
        p[0] += (window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0);
        p[1] += (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0);
        
        p.push(p[0]+this.options.scroll.offsetWidth);
        p.push(p[1]+this.options.scroll.offsetHeight);
      }
      var speed = [0,0];
      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
      this.startScrolling(speed);
    }
    
    // fix AppleWebKit rendering
    if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
    
    Event.stop(event);
  },
  
  finishDrag: function(event, success) {
    this.dragging = false;

    if(this.options.ghosting) {
      Position.relativize(this.element);
      Element.remove(this._clone);
      this._clone = null;
    }

    if(success) Droppables.fire(event, this.element);
    Draggables.notify('onEnd', this, event);

    var revert = this.options.revert;
    if(revert && typeof revert == 'function') revert = revert(this.element);
    
    var d = this.currentDelta();
    if(revert && this.options.reverteffect) {
      this.options.reverteffect(this.element, 
        d[1]-this.delta[1], d[0]-this.delta[0]);
    } else {
      this.delta = d;
    }

    if(this.options.zindex)
      this.element.style.zIndex = this.originalZ;

    if(this.options.endeffect) 
      this.options.endeffect(this.element);
      
    Draggables.deactivate(this);
    Droppables.reset();
  },
  
  keyPress: function(event) {
    if(event.keyCode!=Event.KEY_ESC) return;
    this.finishDrag(event, false);
    Event.stop(event);
  },
  
  endDrag: function(event) {
    if(!this.dragging) return;
    this.stopScrolling();
    this.finishDrag(event, true);
    Event.stop(event);
  },
  
  draw: function(point) {
    var pos = Position.cumulativeOffset(this.element);
    if(this.options.ghosting) {
      var r   = Position.realOffset(this.element);
      window.status = r.inspect();
      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
    }
    
    var d = this.currentDelta();
    pos[0] -= d[0]; pos[1] -= d[1];
    
    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
    }
    
    var p = [0,1].map(function(i){ 
      return (point[i]-pos[i]-this.offset[i]) 
    }.bind(this));
    
    if(this.options.snap) {
      if(typeof this.options.snap == 'function') {
        p = this.options.snap(p[0],p[1],this);
      } else {
      if(this.options.snap instanceof Array) {
        p = p.map( function(v, i) {
          return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
      } else {
        p = p.map( function(v) {
          return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
      }
    }}
    
    var style = this.element.style;
    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
      style.left = p[0] + "px";
    if((!this.options.constraint) || (this.options.constraint=='vertical'))
      style.top  = p[1] + "px";
    
    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  },
  
  stopScrolling: function() {
    if(this.scrollInterval) {
      clearInterval(this.scrollInterval);
      this.scrollInterval = null;
      Draggables._lastScrollPointer = null;
    }
  },
  
  startScrolling: function(speed) {
    if(!(speed[0] || speed[1])) return;
    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
    this.lastScrolled = new Date();
    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
  },
  
  scroll: function() {
    var current = new Date();
    var delta = current - this.lastScrolled;
    this.lastScrolled = current;
    if(this.options.scroll == window) {
      with (this._getWindowScroll(this.options.scroll)) {
        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
          var d = delta / 1000;
          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
        }
      }
    } else {
      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
    }
    
    Position.prepare();
    Droppables.show(Draggables._lastPointer, this.element);
    Draggables.notify('onDrag', this);
    if (this._isScrollChild) {
      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
      if (Draggables._lastScrollPointer[0] < 0)
        Draggables._lastScrollPointer[0] = 0;
      if (Draggables._lastScrollPointer[1] < 0)
        Draggables._lastScrollPointer[1] = 0;
      this.draw(Draggables._lastScrollPointer);
    }
    
    if(this.options.change) this.options.change(this);
  },
  
  _getWindowScroll: function(w) {
    var T, L, W, H;
    with (w.document) {
      if (w.document.documentElement && documentElement.scrollTop) {
        T = documentElement.scrollTop;
        L = documentElement.scrollLeft;
      } else if (w.document.body) {
        T = body.scrollTop;
        L = body.scrollLeft;
      }
      if (w.innerWidth) {
        W = w.innerWidth;
        H = w.innerHeight;
      } else if (w.document.documentElement && documentElement.clientWidth) {
        W = documentElement.clientWidth;
        H = documentElement.clientHeight;
      } else {
        W = body.offsetWidth;
        H = body.offsetHeight
      }
    }
    return { top: T, left: L, width: W, height: H };
  }
}

/*--------------------------------------------------------------------------*/

var SortableObserver = Class.create();
SortableObserver.prototype = {
  initialize: function(element, observer) {
    this.element   = $(element);
    this.observer  = observer;
    this.lastValue = Sortable.serialize(this.element);
  },
  
  onStart: function() {
    this.lastValue = Sortable.serialize(this.element);
  },
  
  onEnd: function() {
    Sortable.unmark();
    if(this.lastValue != Sortable.serialize(this.element))
      this.observer(this.element)
  }
}

var Sortable = {
  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
  
  sortables: {},
  
  _findRootElement: function(element) {
    while (element.tagName != "BODY") {  
      if(element.id && Sortable.sortables[element.id]) return element;
      element = element.parentNode;
    }
  },

  options: function(element) {
    element = Sortable._findRootElement($(element));
    if(!element) return;
    return Sortable.sortables[element.id];
  },
  
  destroy: function(element){
    var s = Sortable.options(element);
    
    if(s) {
      Draggables.removeObserver(s.element);
      s.droppables.each(function(d){ Droppables.remove(d) });
      s.draggables.invoke('destroy');
      
      delete Sortable.sortables[s.element.id];
    }
  },

  create: function(element) {
    element = $(element);
    var options = Object.extend({ 
      element:     element,
      tag:         'li',       // assumes li children, override with tag: 'tagname'
      dropOnEmpty: false,
      tree:        false,
      treeTag:     'ul',
      overlap:     'vertical', // one of 'vertical', 'horizontal'
      constraint:  'vertical', // one of 'vertical', 'horizontal', false
      containment: element,    // also takes array of elements (or id's); or false
      handle:      false,      // or a CSS class
      only:        false,
      delay:       0,
      hoverclass:  null,
      ghosting:    false,
      scroll:      false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      format:      this.SERIALIZE_RULE,
      onChange:    Prototype.emptyFunction,
      onUpdate:    Prototype.emptyFunction
    }, arguments[1] || {});

    // clear any old sortable with same element
    this.destroy(element);

    // build options for the draggables
    var options_for_draggable = {
      revert:      true,
      scroll:      options.scroll,
      scrollSpeed: options.scrollSpeed,
      scrollSensitivity: options.scrollSensitivity,
      delay:       options.delay,
      ghosting:    options.ghosting,
      constraint:  options.constraint,
      handle:      options.handle };

    if(options.starteffect)
      options_for_draggable.starteffect = options.starteffect;

    if(options.reverteffect)
      options_for_draggable.reverteffect = options.reverteffect;
    else
      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
        element.style.top  = 0;
        element.style.left = 0;
      };

    if(options.endeffect)
      options_for_draggable.endeffect = options.endeffect;

    if(options.zindex)
      options_for_draggable.zindex = options.zindex;

    // build options for the droppables  
    var options_for_droppable = {
      overlap:     options.overlap,
      containment: options.containment,
      tree:        options.tree,
      hoverclass:  options.hoverclass,
      onHover:     Sortable.onHover
      //greedy:      !options.dropOnEmpty
    }
    
    var options_for_tree = {
      onHover:      Sortable.onEmptyHover,
      overlap:      options.overlap,
      containment:  options.containment,
      hoverclass:   options.hoverclass
    }

    // fix for gecko engine
    Element.cleanWhitespace(element); 

    options.draggables = [];
    options.droppables = [];

    // drop on empty handling
    if(options.dropOnEmpty || options.tree) {
      Droppables.add(element, options_for_tree);
      options.droppables.push(element);
    }

    (this.findElements(element, options) || []).each( function(e) {
      // handles are per-draggable
      var handle = options.handle ? 
        Element.childrenWithClassName(e, options.handle)[0] : e;    
      options.draggables.push(
        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
      Droppables.add(e, options_for_droppable);
      if(options.tree) e.treeNode = element;
      options.droppables.push(e);      
    });
    
    if(options.tree) {
      (Sortable.findTreeElements(element, options) || []).each( function(e) {
        Droppables.add(e, options_for_tree);
        e.treeNode = element;
        options.droppables.push(e);
      });
    }

    // keep reference
    this.sortables[element.id] = options;

    // for onupdate
    Draggables.addObserver(new SortableObserver(element, options.onUpdate));

  },

  // return all suitable-for-sortable elements in a guaranteed order
  findElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.tag);
  },
  
  findTreeElements: function(element, options) {
    return Element.findChildren(
      element, options.only, options.tree ? true : false, options.treeTag);
  },

  onHover: function(element, dropon, overlap) {
    if(Element.isParent(dropon, element)) return;

    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
      return;
    } else if(overlap>0.5) {
      Sortable.mark(dropon, 'before');
      if(dropon.previousSibling != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, dropon);
        if(dropon.parentNode!=oldParentNode) 
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    } else {
      Sortable.mark(dropon, 'after');
      var nextElement = dropon.nextSibling || null;
      if(nextElement != element) {
        var oldParentNode = element.parentNode;
        element.style.visibility = "hidden"; // fix gecko rendering
        dropon.parentNode.insertBefore(element, nextElement);
        if(dropon.parentNode!=oldParentNode) 
          Sortable.options(oldParentNode).onChange(element);
        Sortable.options(dropon.parentNode).onChange(element);
      }
    }
  },
  
  onEmptyHover: function(element, dropon, overlap) {
    var oldParentNode = element.parentNode;
    var droponOptions = Sortable.options(dropon);
        
    if(!Element.isParent(dropon, element)) {
      var index;
      
      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
      var child = null;
            
      if(children) {
        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
        
        for (index = 0; index < children.length; index += 1) {
          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
            offset -= Element.offsetSize (children[index], droponOptions.overlap);
          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
            child = index + 1 < children.length ? children[index + 1] : null;
            break;
          } else {
            child = children[index];
            break;
          }
        }
      }
      
      dropon.insertBefore(element, child);
      
      Sortable.options(oldParentNode).onChange(element);
      droponOptions.onChange(element);
    }
  },

  unmark: function() {
    if(Sortable._marker) Element.hide(Sortable._marker);
  },

  mark: function(dropon, position) {
    // mark on ghosting only
    var sortable = Sortable.options(dropon.parentNode);
    if(sortable && !sortable.ghosting) return; 

    if(!Sortable._marker) {
      Sortable._marker = $('dropmarker') || document.createElement('DIV');
      Element.hide(Sortable._marker);
      Element.addClassName(Sortable._marker, 'dropmarker');
      Sortable._marker.style.position = 'absolute';
      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
    }    
    var offsets = Position.cumulativeOffset(dropon);
    Sortable._marker.style.left = offsets[0] + 'px';
    Sortable._marker.style.top = offsets[1] + 'px';
    
    if(position=='after')
      if(sortable.overlap == 'horizontal') 
        Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
      else
        Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
    
    Element.show(Sortable._marker);
  },
  
  _tree: function(element, options, parent) {
    var children = Sortable.findElements(element, options) || [];
  
    for (var i = 0; i < children.length; ++i) {
      var match = children[i].id.match(options.format);

      if (!match) continue;
      
      var child = {
        id: encodeURIComponent(match ? match[1] : null),
        element: element,
        parent: parent,
        children: new Array,
        position: parent.children.length,
        container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
      }
      
      /* Get the element containing the children and recurse over it */
      if (child.container)
        this._tree(child.container, options, child)
      
      parent.children.push (child);
    }

    return parent; 
  },

  /* Finds the first element of the given tag type within a parent element.
    Used for finding the first LI[ST] within a L[IST]I[TEM].*/
  _findChildrenElement: function (element, containerTag) {
    if (element && element.hasChildNodes)
      for (var i = 0; i < element.childNodes.length; ++i)
        if (element.childNodes[i].tagName == containerTag)
          return element.childNodes[i];
  
    return null;
  },

  tree: function(element) {
    element = $(element);
    var sortableOptions = this.options(element);
    var options = Object.extend({
      tag: sortableOptions.tag,
      treeTag: sortableOptions.treeTag,
      only: sortableOptions.only,
      name: element.id,
      format: sortableOptions.format
    }, arguments[1] || {});
    
    var root = {
      id: null,
      parent: null,
      children: new Array,
      container: element,
      position: 0
    }
    
    return Sortable._tree (element, options, root);
  },

  /* Construct a [i] index for a particular node */
  _constructIndex: function(node) {
    var index = '';
    do {
      if (node.id) index = '[' + node.position + ']' + index;
    } while ((node = node.parent) != null);
    return index;
  },

  sequence: function(element) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[1] || {});
    
    return $(this.findElements(element, options) || []).map( function(item) {
      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
    });
  },

  setSequence: function(element, new_sequence) {
    element = $(element);
    var options = Object.extend(this.options(element), arguments[2] || {});
    
    var nodeMap = {};
    this.findElements(element, options).each( function(n) {
        if (n.id.match(options.format))
            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
        n.parentNode.removeChild(n);
    });
   
    new_sequence.each(function(ident) {
      var n = nodeMap[ident];
      if (n) {
        n[1].appendChild(n[0]);
        delete nodeMap[ident];
      }
    });
  },
  
  serialize: function(element) {
    element = $(element);
    var options = Object.extend(Sortable.options(element), arguments[1] || {});
    var name = encodeURIComponent(
      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
    
    if (options.tree) {
      return Sortable.tree(element, arguments[1]).children.map( function (item) {
        return [name + Sortable._constructIndex(item) + "[id]=" + 
                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
      }).flatten().join('&');
    } else {
      return Sortable.sequence(element, arguments[1]).map( function(item) {
        return name + "[]=" + encodeURIComponent(item);
      }).join('&');
    }
  }
}

/* Returns true if child is contained within element */
Element.isParent = function(child, element) {
  if (!child.parentNode || child == element) return false;

  if (child.parentNode == element) return true;

  return Element.isParent(child.parentNode, element);
}

Element.findChildren = function(element, only, recursive, tagName) {    
  if(!element.hasChildNodes()) return null;
  tagName = tagName.toUpperCase();
  if(only) only = [only].flatten();
  var elements = [];
  $A(element.childNodes).each( function(e) {
    if(e.tagName && e.tagName.toUpperCase()==tagName &&
      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
        elements.push(e);
    if(recursive) {
      var grandchildren = Element.findChildren(e, only, recursive, tagName);
      if(grandchildren) elements.push(grandchildren);
    }
  });

  return (elements.length>0 ? elements.flatten() : []);
}

Element.offsetSize = function (element, type) {
  if (type == 'vertical' || type == 'height')
    return element.offsetHeight;
  else
    return element.offsetWidth;
}
/* Global Variables */
var applicationPath;
var blnRunOnReadyStateComplete = false;
var scrollWindowInterval = null;
var scrollWindowStepSize = 1;

String.prototype.startsWith = function(s) 
{
	return (this.match('^'+s)==s);
}

String.prototype.endsWith = function(s) 
{
	return (this.match(s+'$')==s);
}

String.prototype.htmlEncode = function()
{
	var encodedHtml = escape(this);
	
	encodedHtml = encodedHtml.replace(/\//g,"%2F");
	encodedHtml = encodedHtml.replace(/\?/g,"%3F");
	encodedHtml = encodedHtml.replace(/=/g,"%3D");
	encodedHtml = encodedHtml.replace(/&/g,"%26");
	encodedHtml = encodedHtml.replace(/@/g,"%40");
	
	return encodedHtml;
}

function confirmDelete(sName)
{
	if(confirm('Are you sure you want to delete ' + sName + '?'))
	{
		return true;
	}
	else 
	{
		return false;
	}
}
//Tooltip typing delay detection var
var bShowTooltip = true;




/*********************************************
*General functions
*********************************************/

function fireOnEnter(that, e, strFunction)
{
	if(!e)
	{
		e = window.event;
	} 
    var intKeyCode = (e.which?e.which:e.keyCode);

    if(intKeyCode == 13)
	{
		eval(strFunction);
		return false;
	}
	return true;
}

function fireOnEscape(that, e, strFunction)
{
	if(!e)
	{
		e = window.event;
	}
    var intKeyCode = (e.which?e.which:e.keyCode);

    if(intKeyCode == 27)
	{
		eval(strFunction);
		return false;
	}
	return true;
}


function maxLength(field, maxlimit) 
{
	if (field.value.length > maxlimit) 
	{ 
		field.value = field.value.substring(0, maxlimit); 
		//alert('You have reached the maximum ' + maxlimit + ' characters.'); 
		raiseMessage('stoperror', 'You have reached the maximum ' + maxlimit + ' characters.', field.id);
		return (false); // Prevent the keypress occurring 
	} 
}


function ResetTimeout()
{
	//debug_print('ResetTimeout | '+window.parent.document.all.hidCountdown.value+' | '+window.parent.document.all.hidTimeout.value);
	window.parent.document.all.hidCountdown.value = window.parent.document.all.hidTimeout.value;
}

function TimeFormat(sText) 
{
	var sT = sText.value;
	var lHours = 0;
	var lMins = 0;

	sT = '0000' + sT;
	sT = sT.substr(sT.length - 5, 5);
	
	if (sT.indexOf(':') == -1) {
		sT = sT.substr(sT.length - 4, 2) + ':' + sT.substr(sT.length - 2, 2);
	}
	
	lHours = sT.substr(0,2)/1;
	lMins = sT.substr(3,2)/1;
	
	if (isNaN(lHours)){
		lHours = 0;
	}else{
		if (lHours>23){
			lHours = 0;
		}			
	}

	if (isNaN(lMins)){
		lMins = 0;
	}else{
		if (lMins>59){
			lMins = 0;
		}
	}
 
	lHours = '00' + lHours;
	lHours = lHours.substr(lHours.length - 2, 2);
	
	lMins = '00' + lMins;
	lMins = lMins.substr(lMins.length - 2, 2);

	sText.value = lHours + ':' + lMins;
}

function pre_load_images() 
{
	
	if (document.images) {
		//Create an image array if there isn't one already
		if (!document.image_array) {
			document.image_array = new Array();
		}
		//Find out how big it is
		var image_count = document.image_array.length;
		//Get an array of the strings passed in to this procedure
		document.argument_array = pre_load_images.arguments;
		//If the string isn't already there, add it
		for (i=0; i < document.argument_array.length; i++) {
			if (document.argument_array[i].indexOf("#")!=0) 
			{
			    //debug_print('pre_load_image : '+document.argument_array[i]);
				document.image_array[image_count] = new Image;
				document.image_array[image_count++].src = document.argument_array[i];
			}
		}
	}
}

function image_restore(e) 
{ 
	if(!e)
	{
		e = window.event;
	}
	
	var objSrcElement = (e.target) ? e.target : e.srcElement;
	if (document.old_image != '') 
	{
		var image_path = objSrcElement.src;
		if (image_path.substr(image_path.length-6,3) == 'on.') 
		{
			objSrcElement.src = document.old_image;
		}
	}
}

function image_swap(e) 
{ 
	if(!e)
	{
		e = window.event;
	}
	var objSrcElement = (e.target) ? e.target : e.srcElement;
	var image_path = objSrcElement.src;
	var image_ext = image_path.substr(image_path.length-3,3);
	//If it is currently an off image, change to an on image, and store the old image
	if (image_path.substr(image_path.length-7,3) == 'off') {
		document.old_image = image_path;
		image_path = image_path.substr(0,image_path.length-7) + 'on.'+image_ext;
		objSrcElement.src = image_path;		
	}	
}

function Print() {
	self.print();
}

function Help(page) {
	window.open('help/' + page);
}

function changeButtonImage(That){
	
	if(That.innerHTML!=''&&That.children.length>1){
		var sImgIcon = That.children(0).src;
		var sImgText = That.children(1).src;
	
		if(sImgIcon.substr(sImgIcon.length-7,7)!='_on.gif'){
			sImgIcon = sImgIcon.substr(0,sImgIcon.length-4) + '_on.gif';
			sImgText = sImgText.substr(0,sImgText.length-4) + '_on.gif'}
		else{
			sImgIcon = sImgIcon.substr(0,sImgIcon.length-7) + '.gif';
			sImgText = sImgText.substr(0,sImgText.length-7) + '.gif'
		}
		That.children(0).src = sImgIcon;
		That.children(1).src = sImgText;
	}
	else{
		var sImg = '' ;
		
		if(That.src!=undefined){
			sImg = That.src;
		}
		
		
		if(sImg.substr(sImg.length-7,7)!='_on.gif'){
			sImg = sImg.substr(0,sImg.length-4) + '_on.gif';}
		else{
			sImg = sImg.substr(0,sImg.length-7) + '.gif';
		}
		
		That.src = sImg;
	}
}

function changePngButtonImage(That){

	if(That.innerHTML!=''&&That.children.length>1){
		var sImgIcon = That.children(0).src;
		var sImgText = That.children(1).src;
	
		if(sImgIcon.substr(sImgIcon.length-9,9)!='_over.png'){
			sImgIcon = sImgIcon.substr(0,sImgIcon.length-4) + '_over.png';
			sImgText = sImgText.substr(0,sImgText.length-4) + '_over.png'}
		else{
			sImgIcon = sImgIcon.substr(0,sImgIcon.length-9) + '.png';
			sImgText = sImgText.substr(0,sImgText.length-9) + '.png'
		}
		That.children(0).src = sImgIcon;
		That.children(1).src = sImgText;
	}
	else{
		var sImg = '' ;
		
		if(That.src!=undefined){
			sImg = That.src;
		}
		
		
		if(sImg.substr(sImg.length-9,9)!='_over.png'){
			sImg = sImg.substr(0,sImg.length-4) + '_over.png';}
		else{
			sImg = sImg.substr(0,sImg.length-9) + '.png';
		}
		
		That.src = sImg;
	}
}

// Over class name functions
function Over(That)
{
	That.className=That.className + 'Over';
	
}
function Out(That)
{
	if (That.className.substr(That.className.length-4,4)=='Over'){
		That.className=That.className.substr(0,That.className.length-4);
	}
	
}

function menuOver(That) {
	That.className=That.className + 'Over';
	changeButtonImage(That);
	
}
function menuOut(That) {
	if (That.className.substr(That.className.length-4,4)=='Over') {
		That.className=That.className.substr(0,That.className.length-4);
		changeButtonImage(That);
	}
	
}

function numeric_keypress(e, that, isFloat, minimum, maximum, onEnterFunction)
{
    var intKeyCode = (e.which?e.which:e.keyCode);
    if((intKeyCode > 47)&&(intKeyCode < 58))
    {
        return true;
    }
    if(intKeyCode == 13)
    {
		if(onEnterFunction)
		{
			eval(onEnterFunction);
		}
        return false;
    }
    else if(intKeyCode == 43)
    {
		that.value = parseFloat(that.value) + 1;
		if((typeof(maximum) != 'undefined')&&(maximum != null))
		{
			if(parseFloat(that.value) > maximum)
			{
				that.value = maximum;
			}
		}
        return false;
    }
    else if((intKeyCode == 95) || (intKeyCode == 45))
    {
		that.value = parseFloat(that.value) - 1;
		if((typeof(minimum) != 'undefined')&&(minimum != null))
		{
			if(parseFloat(that.value) < minimum)
			{
				that.value = minimum;
			}
		}
        return false;
    }
    else if((intKeyCode == 46) || (intKeyCode == 62))
    {
		if(isFloat)
		{
			if(that.value.indexOf('.') > -1)
			{
				return false;
			}
			return true;
		}
		else
		{
			return false;
		}
    }
    else if(intKeyCode == 27)
    {
		that.value = 0;
        return false;
    }
    
    window.status = 'Invalid keypress ' + intKeyCode;
        
    return false;
}

function currency_keypress(e, that, minimum, maximum, onEnterFunction)
{
    var intKeyCode = (e.which?e.which:e.keyCode);
    var value = toNumericValue(that.value);
    var originalValue = value;
    
    if((intKeyCode > 47)&&(intKeyCode < 58))
    {
        return true;
    }
    if(intKeyCode == 13)
    {
		if(onEnterFunction)
		{
			if(originalValue != value)
			{
				that.value = formatCurrency(value);
			}
			eval(onEnterFunction);
		}
        return false;
    }
    else if(intKeyCode == 43)
    {
		value = value + 1;
		if((typeof(maximum) != 'undefined')&&(maximum != null))
		{
			if(value > maximum)
			{
				value = maximum;
			}
		}
		
		if(originalValue != value)
		{
			that.value = formatCurrency(value);
		}
        return false;
    }
    else if((intKeyCode == 95) || (intKeyCode == 45))
    {
		value = value - 1;
		if((typeof(minimum) != 'undefined')&&(minimum != null))
		{
			if(value < minimum)
			{
				value = minimum;				
			}
		}
		
		if(originalValue != value)
		{
			that.value = formatCurrency(value);
		}
        return false;
    }
    else if((intKeyCode == 46) || (intKeyCode == 62))
    {
		if(that.value.indexOf('.') > -1)
		{
			return false;
		}
		return true;		
    }    
    else if(intKeyCode == 27)
    {
		that.value = '£0.00';
        return false;
    }
    
    window.status = 'Invalid keypress ' + intKeyCode;
        
    return false;
}



function showContextMenu(e, menuId, hidMenuContextId, strCommandArgument)
{
	var menu = document.getElementById(menuId);
	var isRightClick;
	
	if(menu != null)
	{
		if(!e)
		{
			e = window.event;
		}
	
		if (e.which) isRightClick = (e.which == 3);
		else if (e.button) isRightClick = (e.button == 2);

		if((e.type == 'contextmenu')||(isRightClick))
		{	
			var hidMenuContext = document.getElementById(hidMenuContextId);


			hidMenuContext.value = strCommandArgument;
			var menuOffset = 2
			menu.style.left = e.x - menuOffset;
			menu.style.top = e.y - menuOffset;
			menu.style.display = '';
			e.cancelBubble = true;
		    
			return false;
		}
    }
    
    return true;
}

function toNumericValue(value)
{
	var intLength = value.length;
	var newValue = '';
	var currentChar = '';
	var foundDecimalPoint = false;
	
	for(var intIndex = 0; intIndex < intLength; intIndex++)
	{
		currentChar = value.charAt(intIndex);
		
		switch(currentChar)
		{
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
			case '0':
			
				newValue += currentChar;
				break;
				
			case '.':
				
				if(foundDecimalPoint == false)
				{
					newValue += new String(currentChar);
					foundDecimalPoint = true;
				}
				else
				{
					intIndex = intLength;
				}
				break;
			
		}
	}		
	
	if (newValue == '')
	{
		return 0;
	}
	
	return parseFloat(newValue);
}

function formatCurrency(num) 
{
	num = num.toString().replace(/\£|\,/g,'');
	if(isNaN(num))
		num = '0';
	
	var sign = (num == (num = Math.abs(num)));
	num = Math.floor(num*100+0.50000000001);
	var pence = num%100;
	num = Math.floor(num/100).toString();
	
	if(pence<10)
		pence = '0' + pence;
		
/*		
	for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
		num = num.substring(0,num.length-(4*i+3))+ ',' +
				num.substring(num.length-(4*i+3));*/
				
	return (((sign)?'':'-') + '£' + num + '.' + pence);
}


function trapESC(menu)
{
	var key = window.event.keyCode;
	if (key == 27)
	{
		menu.style.display = 'none';
	}
}

// Calculates the object's absolute position, and width and height
function GetAbsPosition(object)
{
	var position = new Object;
	position.x = 0;
	position.y = 0;

	if(object) 
	{
		position.x = object.offsetLeft;
		position.y = object.offsetTop;
		
		if(object.offsetParent) 
		{
			var parentpos = GetAbsPosition(object.offsetParent);
			position.x += parentpos.x;
			position.y += parentpos.y;
		}
	}

	position.cx = object.offsetWidth;
	position.cy = object.offsetHeight;

	return position;
}


function getOffset1(that)
{

    //nicked from quirksmode.org
	var curleft = curtop = 0;
	if (that.offsetParent) 
	{
		curleft = that.offsetLeft
		curtop = that.offsetTop
		while (that == that.offsetParent) 
		{
			curleft += that.offsetLeft
			curtop += that.offsetTop
		}
	}
	return [curleft,curtop];
}

/*********************************************
*Parent control id functions
*********************************************/

function getParentId(id)
{
    //returns the id of the parent control from a child control id.
    //e.g. 
    var iIndexOf = 0;
    var sCtl = new String();
    sCtl = id;

    while(sCtl.indexOf('_',iIndexOf + 1) != -1)
    {
        iIndexOf = sCtl.indexOf('_',iIndexOf + 1);
    }	
    
    return sCtl.slice(0,iIndexOf);   
}


/*********************************************
*Tooltip functions
*********************************************/

function tooltip_show(that, html)
{
    if(that.readOnly){
        return;
    }
    
    var pos = new Array();
    pos = getOffset(that);
    
    try
    {
		//debug_print('Tooltip_show | '+html+':')
		tooltip.absShow(html, pos[1]-20, pos[0]);
    }
	catch (e)
	{
	}
	
}

function getOffset(that)
{
	//debug_print('getOffset | '+that.id);
    //nicked from quirksmode.org
	var curleft = curtop = 0;
	if (that.offsetParent) 
	{
		curleft = that.offsetLeft
		curtop = that.offsetTop
		while (that == that.offsetParent) 
		{
			curleft += that.offsetLeft
			curtop += that.offsetTop
		}
	}
	//debug_print('getOffset | curleft | '+curleft);
	//debug_print('getOffset | curtop | '+curtop);
	return [curleft,curtop];
}

/******
* Based on [ubertooltip.js] v1.0
* 2001-01-20
* Author: chrisken
* URL: http://www.cs.utexas.edu/users/chrisken/ubertooltip.html
* Supports: opacifier.js
***/
function doLoad() { 
	var sTooltip;
	sTooltip = '<div id="divTooltip" ';
	sTooltip += 'class="tooltip" ';
	sTooltip += '>';
 
    var oTooltip = document.createElement(sTooltip)
    document.body.insertBefore(oTooltip);
	tooltip = new overToolTip("divTooltip", 1); 
	tooltip.init();
}

function overToolTip(id, delay)
{
	this.id = id;
	this.mouseX = 0;
	this.mouseY = 0;
	this.updateToolTip = false;
	this.obj = null;
	this.fader = null;
	this.delay = delay || 500;
	this.getMouseX = function() { return this.mouseX; }
	this.getMouseY = function() { return this.mouseY; }
	this.show = function(html, top, left)
	{
		if (document.layers) return;
		showLayer(this.id);
		if (this.fader)	{ this.fader.setTrans(0); this.fader.fadeIn(); }
		this.obj.innerHTML = html;
		this.updateToolTip = true;
		this.update(top, left);
	}
	this.absShow = function(html, top, left)
	{
		if (document.layers) return;
		showLayer(this.id);
		if (this.fader)	{ this.fader.setTrans(0); this.fader.fadeIn(); }
		this.obj.innerHTML = html;
		this.updateToolTip = true;
		this.absUpdate(top, left);
	}
	this.update = function(top, left)
	{
		this.mouseX = window.event.clientX;
		this.mouseY = window.event.clientY;
		switch (top) {
		case '':
			this.obj.style.left = (this.mouseX + 5) + "px";
			this.obj.style.top = (this.mouseY - 20) + "px";
			break;
		default:
			var num_top = parseInt(top);
			var num_left = parseInt(left);
			this.obj.style.left = (this.mouseX + num_top) + "px";
			this.obj.style.top = (this.mouseY + num_left) + "px";
		}
	}
	this.absUpdate = function(abs_top, abs_left)
	{
		switch (abs_top) {
		case '':
			this.obj.style.left = (this.mouseX + 5) + "px";
			this.obj.style.top = (this.mouseY - 20) + "px";
			break;
		default:
			var num_top = parseInt(abs_top);
			var num_left = parseInt(abs_left);
			this.obj.style.top = num_top.toString() + "px";
			this.obj.style.left = num_left.toString() + "px";
		}
	}
	this.hide = function()
	{
		if (document.layers) return;
		this.updateToolTip = false;
		if (this.fader) this.fader.setTrans(0);
		this.obj.innerHTML = "";
		this.obj.style.left = this.obj.style.top = "0px";
		hideLayer(this.id);
	}
	this.init = function()
	{
		this.obj = document.getLayer(this.id);
		if (this.obj == null) alert('Error: tooltip layer initialized to "' + this.id + '",\nbut a <div> with that id does not exist.');
		if (window.Opacifier)
		{
			this.fader = new Opacifier(this.id, this.delay, 100, 0, null, 10);
			this.fader.setTrans(0);
			this.fader.stop();
			this.fader.norepeat();
		}	
	}
	this.mouseMove = function( e )
	{
		var mouseX, mouseY;
		if (typeof(event) != "undefined")
		{
			mouseX = event.clientX - orvset;
			mouseX +=  document.body.scrollLeft;	
			mouseY = event.clientY;
			mouseY += document.body.scrollTop;
		}
		else
		{
			mouseX = e.pageX;
			mouseY = e.pageY + 10;
		}
		this.mouseX = mouseX;
		this.mouseY = mouseY;
		if (this.obj != null && this.updateToolTip) this.update('');
	}
	this.edit = function(url,ref)
	{
		//window.open('admin/tooltip_edit.aspx?url=' + url + '&ref=' + ref)
		var str = window.showModalDialog('tooltip_edit.aspx?url=' + url + '&ref=' + ref,null,"center:yes;font-size:10px;dialogWidth:60;dialogHeight:20;help:off;status:off;scroll:off");
		//reload window if dialog not cancelled
		alert('return value = ' + str);
		//self.location.reload();
	}
	return this;
}
/*********************************************
*END Tooltip functions
*********************************************/

/*********************************************
*MINI DHTML Library functions
*********************************************/
// mini-dhtml library, intentionally doesn't support NS 4

if (!document.getElementById){
	if (document.all) document.getElementById = function(id){return (document.all[id]? document.all[id] : false );}
	if (document.layers) document.getElementById = function(id,parentObj){
		if(document.layers[id]){
			document.layers[id].style = document.layers[id];
			document.layers[id].d = document.layers[id].document;
			return document.layers[id];
		}
		return false;
	}
}
document.getLayer = document.getElementById;

function showLayer( id )
{
	if (!document.layers) document.getLayer(id).style.visibility = "visible";
}

function hideLayer( id )
{
	if (!document.layers) document.getLayer(id).style.visibility = "hidden";
}

/*********************************************
*END MINI DHTML Library functions
*********************************************/


function sDate(thedate) {
	var sMonth = thedate.getMonth();
	switch (sMonth) {
	case 0:
		sMonth = ' January ';
		break;
	case 1:
		sMonth = ' February ';
		break;
	case 2:
		sMonth = ' March ';
		break;
	case 3:
		sMonth = ' April ';
		break;
	case 4:
		sMonth = ' May ';
		break;
	case 5:
		sMonth = ' June ';
		break;
	case 6:
		sMonth = ' July ';
		break;
	case 7:
		sMonth = ' August ';
		break;
	case 8:
		sMonth = ' September ';
		break;
	case 9:
		sMonth = ' October ';
		break;
	case 10:
		sMonth = ' November ';
		break;
	case 11:
		sMonth = ' December ';
		break;
	}
	return thedate.getDate() + sMonth + thedate.getFullYear();
}

function sDateTime(thedate) {
	
	var sMonth = thedate.getMonth();
	switch (sMonth) {
	case 0:
		sMonth = ' January ';
		break;
	case 1:
		sMonth = ' February ';
		break;
	case 2:
		sMonth = ' March ';
		break;
	case 3:
		sMonth = ' April ';
		break;
	case 4:
		sMonth = ' May ';
		break;
	case 5:
		sMonth = ' June ';
		break;
	case 6:
		sMonth = ' July ';
		break;
	case 7:
		sMonth = ' August ';
		break;
	case 8:
		sMonth = ' September ';
		break;
	case 9:
		sMonth = ' October ';
		break;
	case 10:
		sMonth = ' November ';
		break;
	case 11:
		sMonth = ' December ';
		break;
	}
	return thedate.getDate() + sMonth + thedate.getFullYear() + ' ' + thedate.getHours() + ':' + thedate.getMinutes();
}

function getElementbyClass(rootobj, classname){
	var temparray=new Array();
	var inc=0;
	for (i=0; i<rootobj.length; i++){
	if (rootobj[i].className==classname)
	temparray[inc++]=rootobj[i];
	}
	return temparray;
}

var tmrReset = null;
var blnFormDisabled = false;

function formDisable(bDisable)
{
	//debug_print('formDisable | '+bDisable);

	//var main_window = window.top.frames['main'];
	var main_window = window;
	//var menu_window = window.top.frames['sideBar'];
	//var header_window = window.top.frames['header'];
	
	if (main_window.document.getElementsByTagName('body'))
	{
		var aBody = main_window.document.getElementsByTagName('body');
		try
		{
		    aBody[0].setActive();
		}
		catch(ex){}
	}
	
	formFrameDisable(main_window, bDisable);
	//formFrameDisable(menu_window, bDisable);
	//formFrameDisable(header_window, bDisable);
}

function formFrameDisable(main_window, bDisable)
{

	if (main_window)
	{
	    var divMainProtect = main_window.document.getElementById('divMainProtect');
	    
		if (divMainProtect == null)
		{
			divMainProtect = main_window.document.createElement('div');
			divMainProtect.id = 'divMainProtect';
			divMainProtect.className = 'divProtect';	
            divMainProtect.style.width='0';
            divMainProtect.style.height='0';	
            divMainProtect.style.display='none';
            divMainProtect.style.top='0';	
            divMainProtect.style.left='0';
            divMainProtect.style.position='absolute';
            /*
            if(main_window.document.getElementById('base'))
            {
				main_window.document.getElementById('base').appendChild(divMainProtect);
			}
			else
			{	//Page has probably not rendered correctly therefore fall back to using body.
				main_window.document.body.appendChild(divMainProtect);
			}
			*/
			main_window.document.body.appendChild(divMainProtect);
							
		}
		
		if((bDisable==false)||(bDisable=='false'))
		{
			blnFormDisabled = false;
			divMainProtect.style.display = 'none';
			//new Effect.Fade(divMainProtect, {duration: .2, queue: 'end'});
            divMainProtect.style.width='0';
            divMainProtect.style.height='0';		
            
            tmrReset = setTimeout('resetProtectSize()', 200)	
		}
		else if(blnFormDisabled == false)
		{			
		    //divMainProtect.style.width = '100%';
			//divMainProtect.style.height = '100%';
			blnFormDisabled = true;
			divMainProtect.style.width = getDocumentWidth();
			divMainProtect.style.height = getDocumentHeight();
			divMainProtect.style.display = 'block';
			//new Effect.Appear(divMainProtect, {duration: .4, queue: 'end'});
		}
	}
}

function resetProtectSize()
{
	var main_window = window;
	var divMainProtect = main_window.document.getElementById('divMainProtect');
	
	divMainProtect.style.width='0';
    divMainProtect.style.height='0';
    
    tmrReset = null;
}

function getDocumentHeight()
{
	//TODO: check browser compatablity
	if ((document.body)&&(document.body.scrollHeight))
		return (document.body.scrollHeight)+ 'px';
	else
		return '100%'		
		
}

function getDocumentWidth()
{
	//TODO: check browser compatablity
	if ((document.body)&&(document.body.scrollWidth))
		return (document.body.scrollWidth)+ 'px';
	else
		return '200%'	
}

function setText(textbox_id, text)
{
	//debug_print('setText |' + textbox_id + ' to ' + text + '');
	if(document.getElementById(textbox_id))
	{
		document.getElementById(textbox_id).value = text;
		return true;
	}
	else
	{
		return false;
	}
}


function toNormalCase(this_string, word_seperator, all_words)
{
/*
*toNormalCase sets the first letter of one or more words to capital
*
* this_string    - string ('')     - The string to be capitalised
* word_seperator - string (' ')    - Character between words 
* all_words      - boolean (false) - False capitalises first word only, true capitalises all words
*/
    //Init vars
    var first_letter = new String();
    var other_letters = new String();
    var temp_string = new String();
    //check parameters
    if (word_seperator==null)
    {
        word_seperator = ' ';
    }
    if (all_words!=true)
    {
        all_words=false
    }
    this_string = this_string.toLowerCase();
    //All words or just the first?
    if (all_words)
    {
        //Capitalise all words
        var temp_words = new Array();
        temp_words = this_string.split(word_seperator);
        var word_num = 0;
        //Iterate through words
        for (word_num = 0; word_num<temp_words.length; word_num++)
        {
            first_letter = temp_words[word_num].charAt(0);
            other_letters = temp_words[word_num].substring(1,temp_words[word_num].length);
            first_letter = first_letter.toUpperCase();
            if (temp_string=='')
            {
                temp_string += first_letter + other_letters
            }
            else
            {
                temp_string += word_seperator + first_letter + other_letters
            }
        }
    }
    else
    {
        //Capitalise first word only
        first_letter = this_string.charAt(0);
        other_letters = this_string.substring(1,this_string.length);
        first_letter = first_letter.toUpperCase();
        temp_string = first_letter + other_letters
    }
    return (temp_string);
}

function isMaxLength(that, maxLength)
{
    if (that.value.length > maxLength)
        that.value = that.value.substring(0, maxLength - 1)
}

function isValidDecimalPercent(that, min, max)
{
    /*
    Returns a boolean based on whether the value passed in is a number and is between the min and max values.
        true - if all criteria were met
        false - if any of the criteria were not met. 
    */
    var return_value = true;
    if (isNaN(that))
    {
            return_value = false;
    }
    else
    {
        if (that > max || that < min)
        {
            return_value = false;
        }
        if (that.indexOf('.')>-1)
        {
            var this_value = that.toString();
            var value_array = new Array();
            value_array = this_value.split('.');
            //alert(value_array.length);
            if (value_array.length > 0)
            {
                var decimal_part = value_array[1].toString();
                //alert(decimal_part);
            
                if (decimal_part.length > 2)
                {
                    return_value = false;
                }
            }
        }
    }
    return return_value;
}

function focusFirst() {
	//focus the first text input
	//var els = document.forms[0].elements;
	var els=document.getElementsByTagName('input');
	try {
		for(var i=0;els.length-1;i++) {
			if(els[i].type == 'text') {
				if(!els[i].disabled && !els[i].readOnly) {
					els[i].focus()
					document.execCommand("selectAll",false);
					break;
				}
			}
		}	
	}
	catch(e) {}
}

function disableContextMenu()
{
    return false;
}

function isDate(p_Expression){
	return !isNaN(new Date(p_Expression));		// <<--- this needs checking
}

function closeDatePicker()
{
	if(datePickerDivID!='' && datePickerDivID!=undefined)
	{
	  var pickerDiv = document.getElementById(datePickerDivID);
	  pickerDiv.style.visibility = "hidden";
	  pickerDiv.style.display = "none";
	  adjustiFrame();
	}
}





function lTrim(str, n){
	if (n <= 0)
	    return "";
	else if (n > String(str).length)
	    return str;
	else
	    return String(str).substring(0,n);
}
function rTrim(str, n){
    if (n <= 0)
       return "";
    else if (n > String(str).length)
       return str;
    else {
       var iLen = String(str).length;
       return String(str).substring(iLen, iLen - n);
    }
}
/*
function addToClassName(control, className)
{
    if((control)&&(className)&&(control.className))
    {
        var classNames = control.className.split(' ');
        
        for(int index = 0; index < classNames.length; index++)
        {
            if(classNames[index] == className)
            {
                // The control already has this classname.
                return;
            }
        }
        
        control.className = control.className + ' ' + className;
    
    }
}

function removeFromClassName(control, className)
{
    if((control)&&(className)&&(control.className))
    {
        var classNames = control.className.split(' ');
        var rebuiltClassName = '';
        
        for(int index = 0; index < classNames.length; index++)
        {
            if(classNames[index] != className)
            {
                rebuiltClassName = rebuiltClassName + ' ' + classNames[index];
            }
        }
        
        control.className = rebuiltClassName;
        
    }
}
*/


function selectAllText(that)
{
	that.select();
}

function padLeft(expression, length, paddingChar)
{
	var value = new String(expression);
	
	if ((paddingChar == undefined)||(paddingChar == null)||(paddingChar.length == 0))
	{
		paddingChar = ' ';
	}
		
	while(value.length < length)
	{
		value = paddingChar + value;
	}
	
	
	return value;
}


function padRight(expression, length, paddingChar)
{
	var value = new String(expression);
	
	if ((paddingChar == undefined)||(paddingChar == null)||(paddingChar.length == 0))
	{
		paddingChar = ' ';
	}
	
	while(value.length < length)
	{
		value = value + paddingChar;
	}
	
	
	return value;
}


function addEvent(elm, evType, fn, useCapture) 
{
    // cross-browser event handling for IE5+, NS6 and Mozilla
    // By Scott Andrew
    if (elm.addEventListener) 
    {
      elm.addEventListener(evType, fn, useCapture);
      return true;
    } 
    else if (elm.attachEvent) 
    {
      var r = elm.attachEvent('on' + evType, fn);
      return r;
    } 
    else 
    {
      elm['on' + evType] = fn;
    }
}


/*
    Written by Jonathan Snook, http://www.snook.ca/jonathan
    Add-ons by Robert Nyman, http://www.robertnyman.com
*/
function getElementsByClassName(oElm, strTagName, strClassName)
{
    var arrElements = (strTagName == "*" && document.all)? document.all : oElm.getElementsByTagName(strTagName);
    var arrReturnElements = new Array();
    strClassName = strClassName.replace(/\-/g, "\\-");
    var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)");
    var oElement;
    for(var i=0; i<arrElements.length; i++)
    {
        oElement = arrElements[i];      
        if(oRegExp.test(oElement.className))
        {
            arrReturnElements.push(oElement);
        }   
    }
    return (arrReturnElements)
}

var curelem = '';

MenuHover = function() 
{
    var objMenus = getElementsByClassName(document.body, "ul", "CascadeMenu");
	for (var i=0; i<objMenus.length; i++) 
	{
	    var objItems = objMenus[i].getElementsByTagName("li");
	    for (var x=0; x<objItems.length; x++) 
	    {   
		    objItems[x].onmouseenter=function() 
		    {
			    this.className+=" MenuItemHover";
		    }
		    objItems[x].onmouseleave=function() 
		    {
		    
			    //this.className=this.className.replace(new RegExp(" MenuItemHover\\b"), "");
                this.timer = new Timer(this);
                this.timer.setTimeout("out",300);			    
		    }
            objItems[x].out=function() {
                this.className=this.className.replace(new RegExp(" MenuItemHover\\b"), "");
            }		    
		}
	}
}



/* TIMER IS FOR MENU CASCADE */

// The constructor should be called with
// the parent object (optional, defaults to window).

function Timer(){
    this.obj = (arguments.length)?arguments[0]:window;
    return this;
}

// The set functions should be called with:
// - The name of the object method (as a string) (required)
// - The millisecond delay (required)
// - Any number of extra arguments, which will all be
//   passed to the method when it is evaluated.

Timer.prototype.setInterval = function(func, msec){
    var i = Timer.getNew();
    var t = Timer.buildCall(this.obj, i, arguments);
    Timer._set[i].timer = window.setInterval(t,msec);
    return i;
}
Timer.prototype.setTimeout = function(func, msec){
    var i = Timer.getNew();
    Timer.buildCall(this.obj, i, arguments);
    Timer._set[i].timer = window.setTimeout("Timer.callOnce("+i+");",msec);
    return i;
}

// The clear functions should be called with
// the return value from the equivalent set function.

Timer.prototype.clearInterval = function(i){
    if(!Timer._set[i]) return;
    window.clearInterval(Timer._set[i].timer);
    Timer._set[i] = null;
}
Timer.prototype.clearTimeout = function(i){
    if(!Timer._set[i]) return;
    window.clearTimeout(Timer._set[i].timer);
    Timer._set[i] = null;
}

// Private data

Timer._set = new Array();
Timer.buildCall = function(obj, i, args){
    var t = "";
    Timer._set[i] = new Array();
    if(obj != window){
        Timer._set[i].obj = obj;
        t = "Timer._set["+i+"].obj.";
    }
    t += args[0]+"(";
    if(args.length > 2){
        Timer._set[i][0] = args[2];
        t += "Timer._set["+i+"][0]";
        for(var j=1; (j+2)<args.length; j++){
            Timer._set[i][j] = args[j+2];
            t += ", Timer._set["+i+"]["+j+"]";
    }}
    t += ");";
    Timer._set[i].call = t;
    return t;
}
Timer.callOnce = function(i){
    if(!Timer._set[i]) return;
    eval(Timer._set[i].call);
    Timer._set[i] = null;
}
Timer.getNew = function(){
    var i = 0;
    while(Timer._set[i]) i++;
    return i;
}


function compatModalDialog(url, width, height) {
    if(window.showModalDialog)
    {
		window.showModalDialog(url,window,
			"dialogWidth:"+width+"px;dialogHeight:"+height+"px;edge:Raised;center:1;help:0;resizable:1;maximize:1;status:0");    
    }
    else
    {
		var left = screen.availWidth/2 - width/2;
		var top = screen.availHeight/2 - height/2;
		document.activeModalWin = window.open(url, "", "status=no,width="+width+",height="+height+",left="+left+",top="+top);
		window.onfocus = function(){if (document.activeModalWin.closed == false){document.activeModalWin.focus();};};    
    }
}



addEvent(window, 'load', MenuHover, false);


function submitForm(strEmail, strName, strAction) 
{
	var objEl;
	
	//Validate name if exists
	if (strName) 
	{
	    objEl = document.getElementById(strName);
		if (objEl.value == '') 
		{
			alert('Please enter your name');
			objEl.className = "TagHighLight";
			objEl.focus();
			return false;
		}
		else 
		{
		    objEl.className = "";
		}
	}	
	
	//Validate email if exists
	if (strEmail) 
	{
	    objEl = document.getElementById(strEmail);
		if (objEl.value=='' || !isEmail(objEl.value)) 
		{
			alert('Please enter a valid email address');
			objEl.className = "TagHighLight";
			objEl.focus();
			return false;
		}
		else 
		{
		    objEl.className = "";
		}		
	}


	//change action on form and submit
	if(strAction) 
	{
		document.forms[0].action = strAction;
		document.forms[0].submit();
		return false;
	}

}

// add option to select box
function appendToSelect(objselect, value, content) 
{
    var opt;
    opt = document.createElement("option");
    opt.value = value;
    opt.appendChild(document.createTextNode(content)) 
    objselect.appendChild(opt);
}

function GetXmlNodeText(node)
{ 
    if(node && node.text)
    { 
        return node.text; 
    }
    else if(node && node.textContent)
    { 
        return node.textContent; 
    } 
    else
    {
		return '';
    }
}

function getXmlString(xmlDoc)
{
	if(typeof(xmlDoc.xml) == 'string')
	{
		return xmlDoc.xml;
	}
	else
	{
		//create a new XMLSerializer
        var objXMLSerializer = new XMLSerializer();
        
        //get the XML string
        return objXMLSerializer.serializeToString(xmlDoc);
	}
	
}

function QuickSearch(strAction)
{
	document.forms[0].action=strAction;
	var d = document.getElementById('__VIEWSTATE');
	if(d) 
	{ 
	    d.parentElement.removeChild(d);
	}
	document.forms[0].submit();
}
function QuickSearchEnterSubmit(strAction) 
{
	if (window.event && window.event.keyCode == 13)
	{
		QuickSearch(strAction);
	}
	else
	{
		return true;
	}
}

/* JAVASCRIPT INCLUDE .JS FILE */
var included_files = new Array();

function include_dom(script_filename) 
{
    var html_doc = document.getElementsByTagName('head').item(0);
    var js = document.createElement('script');
    js.setAttribute('language', 'javascript');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', script_filename);
    html_doc.appendChild(js);
    return false;
}

function include_once(script_filename) 
{
    if (!in_array(script_filename, included_files)) 
    {
        included_files[included_files.length] = script_filename;
        include_dom(script_filename);
    }
}

function in_array(needle, haystack) 
{
    for (var i = 0; i < haystack.length; i++) 
    {
        if (haystack[i] == needle) 
        {
            return true;
        }
    }
    return false;

}



var mobjRepeat;
var mobjItems;
var mintCurrent = 0;
var mstrTitle = '';
var mobjTitle;

AnnouncementsPanel = function() 
{
    mobjRepeat = getElementsByClassName(document.body, "ul", "Announcements");
	for (var i=0; i<mobjRepeat.length; i++) 
	{
	    mobjItems = mobjRepeat[i].getElementsByTagName("li");
	    for (var x=0; x<mobjItems.length; x++) 
	    {   
		    mobjItems[x].style.display = 'none';	    
		}
		if(mobjItems) {
		    mobjTitle = mobjItems[0];
		    mstrTitle = mobjTitle.childNodes[0].innerHTML;
		    
		    
		}
	}
	this.timer = new Timer(this);
	this.hideAdd = function() {
	    if(mobjItems) {
	        //new Effect.Fade(mobjItems[mintCurrent], {duration: 1});
	        //new Effect.DropOut(mobjItems[mintCurrent], {duration: 2});
	        new Effect.DropOut(mobjItems[mintCurrent], {duration: 1});
	        //new Effect.SwitchOff(mobjItems[mintCurrent], {duration: 1});
	        //new Effect.Puff(mobjItems[mintCurrent], {duration: 1});
	    }
	}
	this.showAnn = function() {
	    if(mobjItems) {
	        
	        new Effect.Appear(mobjItems[mintCurrent], {duration: 1});
	        
	        mobjTitle.childNodes[0].innerHTML = mstrTitle + ' (' + mintCurrent + '/' + (mobjItems.length-1) + ')';
	        mobjItems[0].innerHTML = mobjTitle.innerHTML;
	        
	        //new Effect.BlindDown(mobjItems[mintCurrent], {duration: 1});
	        new Effect.SlideDown(mobjItems[mintCurrent], {duration: 1});
	        //new Effect.Shake(mobjItems[mintCurrent], {delay:2,duration: 1});
	        this.timer.setTimeout("nextAnn",6000);
	    }
	}
	this.nextAnn = function() {
	    if(mobjItems) {
	        this.hideAdd();
	        mintCurrent++;
	        if(mintCurrent>=mobjItems.length)
	        {
	            mintCurrent=1;
	        }
	        this.timer.setTimeout("showAnn",2000);
	    }
	}
	if(mobjItems) {
	    //always display the header (first list item)
	    mobjItems[0].style.display = 'block';
	    mintCurrent++;
	   this.timer.setTimeout("showAnn",0);
	}	

}

addEvent(window, 'load', AnnouncementsPanel, false);


//check if the previous sibling node is an element node
function get_previoussibling(n)
{
	if((typeof(n) != 'undefined')&&(n != null))
	{
		var x = n.previousSibling;
		while ((n != null)&&(x.nodeType != 1))
		{
			x = x.previousSibling;
		}
		return x;
	}
	return null;
	
	/*
	Note: Internet Explorer will skip white-space text nodes that are generated between nodes (e.g. new-line characters), while Mozilla will not. So, in the example below, we have a function that checks the node type of the previous sibling node.

	Element nodes has a nodeType of 1, so if the previous sibling node is not an element node, it moves to the previous node, and checks if this node is an element node. This continues until the previous sibling node (which must be an element node) is found. This way, the result will be correct in both Internet Explorer and Mozilla.

	*/
}


function restoreScrollPosition()
{//debugger;
	var hidScrollPositionX = document.getElementById('__SCROLLPOSITIONX');
	var hidScrollPositionY = document.getElementById('__SCROLLPOSITIONY');
	if((!blnCancelRestoreScrollPosition)&&(hidScrollPositionX)&&(hidScrollPositionY))
	{
		//window.scrollTo(hidScrollPositionX.value, hidScrollPositionY.value);
		if(scrollWindowInterval != null)
		{
			clearInterval(scrollWindowInterval);
		}

		var cypos = getCurrentYPos();
		var desty = hidScrollPositionY.value;

		scrollWindowStepSize = parseInt((desty-cypos)/25);
		scrollWindowInterval = setInterval('scrollWindow('+scrollWindowStepSize+','+desty+')',10);


	}
	/*else
	{
		window.scrollTo(0, 0);
	}*/
}

var lastYScrollPos = null;
function scrollWindow(scramount,dest)
{
	var wascypos = getCurrentYPos();
	var isAbove = (wascypos < dest);
	
	if(lastYScrollPos != null)
	{
		if(wascypos != lastYScrollPos)
		{
			//user has changed the scroll pos so quit.
			clearInterval(scrollWindowInterval);
			return;
		}
	}
	
	window.scrollTo(0,wascypos + scramount);
	
	var iscypos = getCurrentYPos();
	var isAboveNow = (iscypos < dest);
	lastYScrollPos = iscypos;
	
	if ((isAbove != isAboveNow) || (wascypos == iscypos)) 
	{
		// if we've just scrolled past the destination, or
		// we haven't moved from the last scroll (i.e., we're at the
		// bottom of the page) then scroll exactly to the link
		window.scrollTo(0,dest);
		// cancel the repeating timer
		clearInterval(scrollWindowInterval);
		// and jump to the link directly so the URL's right
		//location.hash = anchor;
	}
}

function getCurrentYPos() 
{
	if (document.body && document.body.scrollTop)
		return document.body.scrollTop;
	if (document.documentElement && document.documentElement.scrollTop)
		return document.documentElement.scrollTop;
	if (window.pageYOffset)
		return window.pageYOffset;
	return 0;
}


function newId() 
{
    try
    {
		//This is the prefered method but only works with MS Browsers.
		//The reason for begin prefered is that the microsoft aproach takes more care about being unique.
		if(ActiveXObject)
		{
			var x = new ActiveXObject("Scriptlet.TypeLib");
			if(x)
			{
				return (x.GUID).substr(1, 36);// Exclude the leading and trailing braces {}
			}			
		}			
    }
    catch (e)
    {
		//Failed to create guid.
		// Fall through to generateGuid()...
    }
    
    
	//Use alternative method.
	return generateGuid();
}


// generateGuid: Author: Richard Shears. Used by browsers other than IE to generate a guid.
function generateGuid()
{
	var strResult = '';
	
	for(var j = 0; j < 32; j++)
	{
		if( j == 8 || j == 12|| j == 16|| j == 20)
		{
			strResult = strResult + '-';
		}
		strResult = strResult + Math.floor(Math.random()*16).toString(16).toUpperCase();
	}
	
	return strResult
}

/*
var ss = {
			fixAllLinks: function() 
			{
				// Get a list of all links in the page
				var allLinks = document.getElementsByTagName('a');
				// Walk through the list
				for (var i=0;i<allLinks.length;i++) 
				{
					var lnk = allLinks[i];
					if ((lnk.href && lnk.href.indexOf('#') != -1) && 
						( (lnk.pathname == location.pathname) ||
						('/'+lnk.pathname == location.pathname) ) && 
						(lnk.search == location.search)) 
					{
						// If the link is internal to the page (begins in #)
						// then attach the smoothScroll function as an onclick
						// event handler
						ss.addEvent(lnk,'click',ss.smoothScroll);
					}
				}
			},

			smoothScroll: function(e) 
			{
				// This is an event handler; get the clicked on element,
				// in a cross-browser fashion
				if (window.event) 
				{
					target = window.event.srcElement;
				} 
				else if (e) 
				{
					target = e.target;
				} 
				else return;

				// Make sure that the target is an element, not a text node
				// within an element
				if (target.nodeType == 3) 
				{
					target = target.parentNode;
				}

				// Paranoia; check this is an A tag
				if (target.nodeName.toLowerCase() != 'a') return;

				// Find the <a name> tag corresponding to this href
				// First strip off the hash (first character)
				anchor = target.hash.substr(1);
				// Now loop all A tags until we find one with that name
				var allLinks = document.getElementsByTagName('a');
				var destinationLink = null;
				for (var i=0;i<allLinks.length;i++) 
				{
					var lnk = allLinks[i];
					if (lnk.name && (lnk.name == anchor)) 
					{
						destinationLink = lnk;
						break;
					}
				}

				// If we didn't find a destination, give up and let the browser do
				// its thing
				if (!destinationLink) return true;

				// Find the destination's position
				var destx = destinationLink.offsetLeft; 
				var desty = destinationLink.offsetTop;
				var thisNode = destinationLink;
				
				while (thisNode.offsetParent && 
						(thisNode.offsetParent != document.body)) 
				{
					thisNode = thisNode.offsetParent;
					destx += thisNode.offsetLeft;
					desty += thisNode.offsetTop;
				}

				// Stop any current scrolling
				clearInterval(ss.INTERVAL);

				cypos = ss.getCurrentYPos();

				ss_stepsize = parseInt((desty-cypos)/ss.STEPS);
				ss.INTERVAL = setInterval('ss.scrollWindow('+ss_stepsize+','+desty+',"'+anchor+'")',10);

				// And stop the actual click happening
				if (window.event) 
				{
					window.event.cancelBubble = true;
					window.event.returnValue = false;
				}
				if (e && e.preventDefault && e.stopPropagation) 
				{
					e.preventDefault();
					e.stopPropagation();
				}
			},

			scrollWindow: function(scramount,dest,anchor) 
			{
				var wascypos = ss.getCurrentYPos();
				var isAbove = (wascypos < dest);
				
				window.scrollTo(0,wascypos + scramount);
				
				var iscypos = ss.getCurrentYPos();
				var isAboveNow = (iscypos < dest);
				
				if ((isAbove != isAboveNow) || (wascypos == iscypos)) 
				{
					// if we've just scrolled past the destination, or
					// we haven't moved from the last scroll (i.e., we're at the
					// bottom of the page) then scroll exactly to the link
					window.scrollTo(0,dest);
					// cancel the repeating timer
					clearInterval(ss.INTERVAL);
					// and jump to the link directly so the URL's right
					location.hash = anchor;
				}
			},

			getCurrentYPos: function() 
			{
				if (document.body && document.body.scrollTop)
					return document.body.scrollTop;
				if (document.documentElement && document.documentElement.scrollTop)
					return document.documentElement.scrollTop;
				if (window.pageYOffset)
					return window.pageYOffset;
				return 0;
			},

			addEvent: function(elm, evType, fn, useCapture) 
			{
				// addEvent and removeEvent
				// cross-browser event handling for IE5+,  NS6 and Mozilla
				// By Scott Andrew
				if (elm.addEventListener)
				{
					elm.addEventListener(evType, fn, useCapture);
					return true;
				} 
				else if (elm.attachEvent)
				{
					var r = elm.attachEvent("on"+evType, fn);
					return r;
				} 
				else 
				{
					alert("Handler could not be removed");
				}
			} 
		}

ss.STEPS = 25;
*/
//ss.addEvent(window, 'load', ss.fixAllLinks);



/* 
	MiWeb Client Java Runtime.
	
	The following code is required by the MiWebEngine.
	

	load_page(), runOnReadyStateComplete() addEvent(window, 'load', load_page, false);
	
*/
function load_page()
{
	// This time out is to give the browser chance to load all of the .js files before we go into checking if the browser is in the complete state.
	setTimeout('runOnReadyStateComplete()', 100);
}

	

function runOnReadyStateComplete()
{
	var intRestoreScrollDelay = 10;
	
	if(blnRunOnReadyStateComplete == true)
	{
		return;
	}
	
	
	if((typeof(mstrAppPath) != 'undefined')&&(document.getElementById(mstrFilenameId)!=null)&&((typeof(document.readyState) == 'undefined')||(document.readyState == 'complete')))
	{
		if(initialise_page)
		{
			initialise_page();
		}
   
		if(typeof(restoreScrollPosition) != 'undefined')
		{
			if(typeof(mintRestoreScrollDelay) != 'undefined')
			{
				intRestoreScrollDelay = mintRestoreScrollDelay;
			}
			setTimeout('restoreScrollPosition()', intRestoreScrollDelay);
		}
	}
	else
	{
		setTimeout('runOnReadyStateComplete()', 100);
	}
}

addEvent(window, 'load', load_page, false);

/* 
	End Of Section
	MiWeb Client Java Runtime.
	
*/
var ajaxRequest;
//
function ajaxCall(strURL, xmlData, receiveFunction, blnAsync)
{
	if(typeof xmlData == 'undefined')
	{
		xmlData = null;
	}
	else if(typeof(xmlData) == 'object')
	{
		xmlData = getXmlString(xmlData);
	}
	if(typeof blnAsync == 'undefined')
	{
        blnAsync = true;
    }
	if (window.XMLHttpRequest)
	{
		// branch for native XMLHttpRequest object
		ajaxRequest = new XMLHttpRequest();
	}
	else if (window.ActiveXObject)
	{
		// branch for IE6-/Windows ActiveX version
		ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
	}
	
	if (ajaxRequest)
	{   
	
		ajaxRequest.open("POST", strURL, blnAsync);
			    
//        ajaxRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        ajaxRequest.setRequestHeader("Content-length", xmlData.length);
        ajaxRequest.setRequestHeader("Connection", "close");	

		ajaxRequest.onreadystatechange = function()
											{
												// only if req shows "complete"
												if (ajaxRequest.readyState == 4)
												{
													// only if "OK"
													if (ajaxRequest.status == 200)
													{
														if(ajaxRequest.responseXML.documentElement != null)
														{
															var objResponse  = ajaxRequest.responseXML.documentElement;		
															if((typeof(receiveFunction) != 'undefined')&&(receiveFunction != ''))
															{
																eval(receiveFunction + '(objResponse)');																
															}
														}
													}
													else
													{
														var strErrorMessage = 'Ajax request failed, code (' + ajaxRequest.status + ')';
														raiseMessage('stoperror', strErrorMessage, '');
													}
												}
											}

		ajaxRequest.send(xmlData);
	}

}


/*


Example: 
	//ajaxSimpleSendRequest('~/page/system/ajax/mypage.aspx', 'myFunction', 'action', '', new ajaxArg('arg1','arg1value'), new ajaxArg('arg2','arg2value'));
	
Args:
	strURL					Required, the page that will handle the request.
	strReceiveFunction		Required, the function that you wish to receive the response. 
	strAction				Optional, the action parameter used in the Process method in the handling ajax page. 
	strReturnFunction		Optional, the function that the receive function will use for processing different actions. 
	argArray				Optional Array, additional argument list must be in the form of ajaxArg e.g. 
										new ajaxArg('arg1','arg1value'), new ajaxArg('arg2','arg2value')

*/
function ajaxSimpleSendRequest(strURL, strReceiveFunction, strAction, strReturnFunction)
{
	var xmlDoc = createXMLDOM();                   
	var xmlTable = xmlDoc.createElement('table');
	var xmlRow = xmlDoc.createElement('row');
	xmlDoc.appendChild(xmlTable);
	xmlTable.appendChild(xmlRow);

	strReturnFunction = (typeof(strReturnFunction) == 'undefined')?'':strReturnFunction;
	strAction = (typeof(strAction) == 'undefined')?'':strAction;
	

	AddNodeToXml(xmlDoc, xmlRow, 'action', strAction);
	AddNodeToXml(xmlDoc, xmlRow, 'return_function', strReturnFunction);

	var intArgLength = arguments.length;
	for(var intIndex = 4; intIndex < intArgLength; intIndex++)
	{
		var objArg = arguments[intIndex];
		if (objArg.getType() == 'ajaxArg')
		{
			AddNodeToXml(xmlDoc, xmlRow, objArg.Name, objArg.Value);
		}
	}
	
	ajaxCall(strURL, xmlDoc, strReceiveFunction);

}

function ajaxArg(strName, strValue)
{
	this.Name = strName;
	this.Value = strValue;
	this.getType = function()
	{
		return 'ajaxArg';
	}
}
ajaxArg.prototype.Name = '';
ajaxArg.prototype.Value = '';




// This function is now defunct.
function ajaxStateHandler()
{
	// only if req shows "complete"
	if (ajaxRequest.readyState == 4)
	{
		// only if "OK"
		if (ajaxRequest.status == 200)
		{
			//debugger;
			if(ajaxRequest.responseXML.documentElement != null)
			{
				var objResponse  = ajaxRequest.responseXML.documentElement;
				var strMethod    = objResponse.getElementsByTagName('method')[0].firstChild.data;
				var objStatus    = objResponse.getElementsByTagName('status')[0];
				var objResults    = objResponse.getElementsByTagName('result');
				var intResultCount = objResults.length;
				var intStatus = 0;
				var strErrorMessage =  '';
				
				if(objStatus != null)
				{
					intStatus = GetXmlNodeText(objStatus.getElementsByTagName('status')[0]);
					strErrorMessage = GetXmlNodeText(objStatus.getElementsByTagName('error_message')[0]);
				}
				
				if(intStatus == 0)
				{
				
					for(var intResultIndex = 0; intResultIndex < intResultCount; intResultIndex++)
					{
						var objResult = objResults[intResultIndex];
						eval(strMethod + '(objResult)');
					}
				}
				else
				{
					raiseMessage('stoperror', strErrorMessage, '');
				}
			}
		}
	}
}

function createXMLDOM()
{
	var xmlDoc;
	
	// code for IE
	if (window.ActiveXObject)
	{
		xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
	}// code for Mozilla, Firefox, Opera, etc.
	else if ((document.implementation) && (document.implementation.createDocument))
	{
		xmlDoc=document.implementation.createDocument("","",null);
	}
	else
	{
		alert('Your browser cannot handle this script');
	}
	xmlDoc.async=false;
	xmlDoc.load('<?xml version="1.0" encoding="ISO-8859-1" ?>');
	return(xmlDoc);
}



function AddNodeToXml(xmlDoc, xmlRow, strName, value)
{

	var xmlNewNode = xmlDoc.createElement(strName);
	xmlNewNode.appendChild(xmlDoc.createTextNode(value));
	xmlRow.appendChild(xmlNewNode);
	
}



// JScript File
/* 
	Place custom client javascript code here.
*/

//orv
function isEmail(strEmail)
{
    // Check for valid email address
    reExp = new RegExp(/^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/);

    if (strEmail == '')
    {
        return true;
    }
    
    if (!reExp.test(strEmail)) 
    {
      return false;
    }
 
    return true;

}


function MM_openBrWindow(theURL,winName,features) {
  window.open(theURL,winName,features);
}







/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;



 
/*
	button.js
	MiWeb button support code.
*/

function setButtonState(id, cssclass, state)
{
	//debugger;
	// check to see if the browser supports document.getElementById
	if (document.getElementById)
	{
		var btn = document.getElementById(id);					// button outer div wrapper
		//var tags = btn.getElementsByTagName('span');						// all button div elements
		var tag;															// variable to hold tags
		var lp = 0;		
		var strCssclass
		var buttonStyleStandard = false;
		
		if (btn == null)
		{
			btn = document.getElementById(id);
			buttonStyleStandard = true;
		}
		
		if(cssclass == null)
		{
		    strCssclass = btn.className;
		    if ((strCssclass.length > 9)&&(strCssclass.substr(strCssclass.length - 9) == '_disabled'))
		    {
		        strCssclass = strCssclass.slice(0, strCssclass.length - 9);
		    }
		}
		else
		{
		    strCssclass = cssclass;
		}
		
															// loop variable
		if (state)
		{
			// enable button
			btn.removeAttribute('disabled');

			if (buttonStyleStandard	== false)
			{
				btn.className = strCssclass
				//
				//tag = btn.getElementsByTagName('a');
				//tag[0].removeAttribute('disabled');
				//

				tag = document.getElementById(id + '_state');
				tag.setAttribute('value', 'enabled');
				
				// set class on internal spans
				var spans = btn.getElementsByTagName('span');				// all button span elements
				for (lp = 0; lp < spans.length; lp++)
				{
					switch (spans[lp].className)
					{
						case strCssclass+'_disabled_buttonLeft':
							spans[lp].className = strCssclass+'_buttonLeft';
							break;
						case strCssclass+'_disabled_buttonRight':
							spans[lp].className = strCssclass+'_buttonRight';
							break;
					}
				}
				// set class on internal spans
				var divs = btn.getElementsByTagName('div');					// all button span elements
				for (lp = 0; lp < divs.length; lp++)
				{
					switch (divs[lp].className)
					{
						case strCssclass+'_disabled_button':
							divs[lp].className = strCssclass+'_button';
							break;
					}
				}
			}
		}
		else
		{
			btn.setAttribute('disabled', 'disabled');
			
			if (buttonStyleStandard	== false)
			{
				btn.className = strCssclass+'_disabled'
				//
				//tag = btn.getElementsByTagName('a');
				//tag[0].setAttribute('disabled', 'disabled');
				//
				//tag = btn.getElementsByTagName('input');
				//tag[0].setAttribute('value', 'disabled');
				tag = document.getElementById(id + '_state');
				tag.setAttribute('value', 'enabled');				
				
				// set class on internal spans
				var spanTags = btn.getElementsByTagName('span');				// all button span elements
				for (lp = 0; lp < spanTags.length; lp++)
				{
					switch (spanTags[lp].className)
					{
						case strCssclass+'_buttonLeft':
							spanTags[lp].className = strCssclass+'_disabled_buttonLeft';
							break;
						case strCssclass+'_buttonRight':
							spanTags[lp].className = strCssclass+'_disabled_buttonRight';
							break;
					}
				}
				// set class on internal spans
				var divTags = btn.getElementsByTagName('div');					// all button span elements
				for (lp = 0; lp < divTags.length; lp++)
				{
					switch (divTags[lp].className)
					{
						case strCssclass+'_button':
							divTags[lp].className = strCssclass+'_disabled_button';
							break;
					}
				}
			}
		}
	}
}


function setButtonText(id, text)
{
	var btn = document.getElementById(id);
	if(btn)
	{
		btn.firstChild.nextSibling.innerHTML = text;	
	}		
}


// JScript File

function fireDefaultButton(e, strDefaultButtonId, strCancelButtonId)
{
    var intKeyCode = (e.which?e.which:e.keyCode);
    
    if(intKeyCode == 13)
    {
        var btnDefaultButton = document.getElementById(strDefaultButtonId);
    
        if(btnDefaultButton != null)
        {
            if(btnDefaultButton.disabled != true)
            {
                btnDefaultButton.click();
                //location.href = btnDefaultButton.href;
            }                
            return false;
        }
    }
    else if(intKeyCode == 27)
    {
        var btnCancelButton = document.getElementById(strCancelButtonId);
        
        if(btnCancelButton != null)
        {
            if(btnCancelButton.disabled != true)
            {
                btnCancelButton.click();
                //location.href = btnCancelButton.href;
            }                
            return false;
        }
    }
        
    return true;
}
// JScript File

/*
Handles the raiseMessage Functionality or the MessageArea

*/

/* 
Declarations 
NOTE: there is only one message area per page.

*/
var messageArea = null;


function raiseMessage(messageType, message, focusClientId, proceedFunction, cancelFunction)
{
	if(!messageArea)
	{
		return false;
	}
	
    //If we are already displaying a message then do not allow another message.
    if(messageArea.messageRaised)
    {   
        return false;
    }
    
    if (messageArea.focusControl != null)
    {
        //Remove hightlight_control classname from control
        //--removeFromClassName(messageArea.focusControl, 'hightlight_control');
    }
    
    if (typeof(messageType) == 'undefined')
    {
		if(messageArea.Messages.Count() == 0)
		{
			return false;
		}
		
		messageArea.CurrentMessage = messageArea.Messages.shift();
		
		messageType = messageArea.CurrentMessage.MessageType;
		message = messageArea.CurrentMessage.Message;
		focusClientId = messageArea.CurrentMessage.FocusClientId;
		proceedFunction = messageArea.CurrentMessage.ProceedFunction;
		cancelFunction = messageArea.CurrentMessage.CancelFunction;
	}
	else
	{
		messageArea.CurrentMessage = new MessageItem(messageType, message, focusClientId, proceedFunction, cancelFunction);
    }
    
    if (typeof(proceedFunction) == 'undefined') 
    {
        proceedFunction = '';
    }

    if (typeof(cancelFunction) == 'undefined') 
    {
        cancelFunction = '';
    }
    
    if (typeof(focusClientId) == 'undefined')
    {
        messageArea.focusControl = null;
    }
    else
    {
        messageArea.focusControl = document.getElementById(focusClientId);
        //--addToClassName(messageArea.focusControl, 'hightlight_control');
    }
    
    
    messageArea.messageType = messageType;
    
    switch(messageType.toLowerCase())
    {
        case 'warning':
            messageArea.btnProceed.style.display = 'block';
            messageArea.btnCancel.style.display = 'none';
            
            setButtonState(messageArea.btnProceed.id, null, true);
            setButtonState(messageArea.btnCancel.id, null, false);
            
            //messageArea.style.border = 'double 4px rgb(0,0,250)';
            messageArea.className = 'MessageArea MessageAreaWarning';
            break;
            
        case 'info':
            messageArea.btnProceed.style.display = 'block';
            messageArea.btnCancel.style.display = 'none';
            
            setButtonState(messageArea.btnProceed.id, null, true);
            setButtonState(messageArea.btnCancel.id, null, false);            
                  
            //messageArea.style.border = 'double 4px rgb(0,250,0)';
            messageArea.className = 'MessageArea MessageAreaInfo';
            break;

        case 'validation':
            cancelFunction = 'messageArea.validateCancel()';
            messageArea.btnProceed.style.display = 'none';
            messageArea.btnCancel.style.display = 'block';  
            
            setButtonState(messageArea.btnProceed.id, null, false);
            setButtonState(messageArea.btnCancel.id, null, true);
            
            //messageArea.style.border = 'double 4px rgb(250,0,0)';
            messageArea.className = 'MessageArea MessageAreaValidation';
            break;
            
        case 'stoperror':
            messageArea.btnProceed.style.display = 'none';
            messageArea.btnCancel.style.display = 'block';
           
            setButtonState(messageArea.btnProceed.id, null, false);
            setButtonState(messageArea.btnCancel.id, null, true);
            
            //messageArea.style.border = 'double 4px rgb(250,0,0)';     
            messageArea.className = 'MessageArea MessageAreaStopError';       
            break;
            
        case 'question':
            messageArea.btnProceed.style.display = 'block';
            messageArea.btnCancel.style.display = 'block';   
            
            setButtonState(messageArea.btnProceed.id, null, true);
            setButtonState(messageArea.btnCancel.id, null, true);
            
            //messageArea.style.border = 'double 4px rgb(250,200,0)'; 
            messageArea.className = 'MessageArea MessageAreaQuestion';                      
            break;
            
        default:
        
            break;
    }            

    //Display the text
    //messageArea.txtMsg.value = message;
    messageArea.txtMsg.innerHTML = message;

    //TODO: HARD CODED THE MESSAGEBOX WIDTH HERE (400) - COULDNT FIND THE WIDTH PROGRAMMATICALLY (EASILY)
    messageArea.style.marginLeft = '-' + parseInt(400 / 2) + 'px';
      
    
    //Set the functions to call on button click.
    messageArea.proceedFunction = proceedFunction;
    messageArea.cancelFunction = cancelFunction;
    
    //Set the control id to recieve focus after the event.
    messageArea.focusClientId = focusClientId;
    
    if(typeof(formDisable) == 'function')
    {
        formDisable(true);
    }        
    
    //Display the message area
    if((messageArea.useFadeEffect == false)||(typeof(Effect) == 'undefined'))
    {
		messageArea.style.display = 'block'; 
		messageArea_Focus();
	}
	else if(messageArea.messageDisplayed != true)
	{
		new Effect.Appear(messageArea, {duration: .5, queue: 'end'});
		setTimeout('messageArea_Focus()', 505);
	}
	messageArea.messageDisplayed = true;
    
    //messageArea.style.display = 'block';
    
      
	window.scrollTo(0, 0);
    //self.scrollTo(0, messageArea.offsetTop + messageArea.scrollHeight);

    return;
}

function messageArea_Focus()
{
	try
	{
		messageArea.focus();
	}
	catch(e){}
}

function messageArea_Init(divId, txtMsgId, btnProceedId, btnProceedName, btnCancelId, btnCancelName)
{
    // Set the message area object to be the div.
    messageArea = document.getElementById(divId);
    
    if (messageArea == null)
    {
		return;
    }
    messageArea.txtMsg = document.getElementById(txtMsgId);
    messageArea.btnProceed = document.getElementById(btnProceedId);
    messageArea.btnCancel = document.getElementById(btnCancelId);
    
    //These values are used for postback.
    messageArea.btnProceedName = btnProceedName;
    messageArea.btnCancelName = btnCancelName;
    messageArea.focusControl = null;
    messageArea.messageType = '';
    messageArea.messageRaised = false;
    messageArea.messageDisplayed = false;
    messageArea.useFadeEffect = true;
    messageArea.lastResponse = false;
    
    //Initially we do not wish for the message area to be visible. This will be handled by raiseMessage().
    messageArea.style.display = 'none';
    messageArea.Messages = new MessageItemList();
    messageArea.MessageIndex = 0;
    messageArea.CurrentMessage = null;
    
    //messageArea.txtMsg.style.width = messageArea.style.width;

    
    //Create the proceed functionality.
    
    messageArea.proceedFunction = '';
        
    messageArea.proceed = function(e)
    {
        var retval = false;
        
        if((messageArea.useFadeEffect == false)||(typeof(Effect) == 'undefined'))
        {
			messageArea.style.display = 'none'; 
		}
		else
		{
			new Effect.Fade(messageArea, {duration: .2, queue: 'end'});
		}
		messageArea.messageDisplayed = false;
		
        if(messageArea.proceedFunction != '')
        {
            retval = eval(messageArea.proceedFunction);
        }
        else
        {
            retval = true;
        }
        
        messageArea.messageRaised = false;
        messageArea.lastResponse = retval;
        
        if (typeof(formDisable) == 'function')
        {
            formDisable(false);
        }            
        return;
    }
    
    //Create the cancel functionality.
    
    messageArea.cancelFunction = '';
    
    messageArea.cancel = function(e)
    {
        var retval = false;
        if((messageArea.useFadeEffect == false)||(typeof(Effect) == 'undefined'))
        {
			messageArea.style.display = 'none'; 
		}
		else
		{
			new Effect.Fade(messageArea, {duration: .2, queue: 'end'});
		}
		messageArea.messageDisplayed = false;
		
        if(messageArea.cancelFunction != '')
        {
            retval = eval(messageArea.cancelFunction);
        }
        else
        {
            retval = false;
        }
        
        messageArea.messageRaised = false;
        messageArea.lastResponse = retval;
        formDisable(false);
        
        try
        {
            if(messageArea.focusControl != null)
            {
                messageArea.focusControl.focus();
            }
        }
        catch(ex)
        {}
        return;
    }
    

    
    //Create the validation cancel functionality.
        
    messageArea.validateCancel = function(e)
    {      
        return false;
    }
    
    messageArea.nextMessage = function(e)
    {
		if (messageArea.Messages.Count > 0)
		{
			raiseMessage();
		}
    }
}


function MessageItem(messageType, message, focusClientId, proceedFunction, cancelFunction)
{
	this.MessageType = messageType;
	this.Message = message;
	this.FocusClientId = focusClientId;
	this.ProceedFunction = proceedFunction;
	this.CancelFunction = cancelFunction;
}

MessageItem.prototype.MessageType = 'info';
MessageItem.prototype.Message = '';
MessageItem.prototype.ProceedFunction = '';
MessageItem.prototype.CancelFunction = '';
MessageItem.prototype.FocusClientId = '';


function MessageItemList()
{

	this.innerList = new Array();
	

	this.Add = function(objMessageItem)
	{
		this.innerList.push(objMessageItem);
	}
	
	this.pop = function()
	{
		return this.innerList.pop();
	}
	
	this.shift = function()
	{
		return this.innerList.shift();
	}
	
	this.Item = function(index)
	{
		return this.innerList[index];
	}
	
	this.Count = function()
	{
		return this.innerList.length;
	}
}

MessageItemList.prototype.innerList = new Array();
// JScript File

function ContextMenuControl()
{

	this._innerList = new Array();

	this.Add = function(objContextMenuControlItem)
	{
		this._innerList.push(objContextMenuControlItem);
	}
	
	this.GetContextItems = function(id)
	{
		var objContextItems = new Array();
		var intTotalCount = this._innerList.length;
		
		for(var intIndex = 0; intIndex< intTotalCount; intIndex++)
		{
			var objContextMenuControlItem = this._innerList[intIndex];
			
			if(objContextMenuControlItem.Id == id)
			{
				intInnerCount = objContextMenuControlItem._innerList.length;
				
				for(var intInnerIndex = 0; intInnerIndex< intInnerCount; intInnerIndex++)
				{
					objContextItems.push(objContextMenuControlItem._innerList[intInnerIndex]);
				}
			}
			
		}
		
		return objContextItems;
	}
	
	this.GetContextMenuControlItem = function(id)
	{
		var intTotalCount = this._innerList.length;
		
		for(var intIndex = 0; intIndex< intTotalCount; intIndex++)
		{
			var objContextMenuControlItem = this._innerList[intIndex];
			
			if(objContextMenuControlItem.Id == id)
			{
				return objContextMenuControlItem;
			}
			
		}
		
		return null;
	}
	
	this.showContextMenu = function(e, id)
	{
		var oContextMenuTable = document.getElementById('ContextMenuTable');
		var isRightClick = false;
		//var oTBody = null;
		
		if(oContextMenuTable == null)
		{
		
			var main_window = window;
			var aBody = main_window.document.getElementsByTagName('body');
	
			
			oContextMenuTable = document.createElement('TABLE');
			oContextMenuTable.id = 'ContextMenuTable';		
			oContextMenuTable.className = 'ContextMenu';
			oContextMenuTable.style.position = 'absolute';
			oContextMenuTable.style.display = 'none';
			
			aBody[0].appendChild(oContextMenuTable);
			
			//oTBody = document.createElement("TBODY");
			//oContextMenuTable.appendChild(oTBody);
		}
		
		
		if(oContextMenuTable != null)
		{
			
		
			if(!e)
			{
				e = window.event;
			}
		
			if (e.which) isRightClick = (e.which == 3);
			else if (e.button) isRightClick = (e.button == 2);

			if((e.type == 'contextmenu')||(isRightClick))
			{	
				//Remove any rows 
				while(oContextMenuTable.rows.length>0)
				{
					oContextMenuTable.deleteRow(oContextMenuTable.rows.length - 1);
				}
				
				var objContextMenuControlItem = this.GetContextMenuControlItem(id);
				if(objContextMenuControlItem != null)
				{
				
					var objContextItems = this.GetContextItems(id);
					var intContextItemCount = objContextItems.length;

				
					//Add title to menu.
					var oTr = oContextMenuTable.insertRow(oContextMenuTable.rows.length);
					var oTd = oTr.insertCell(0);
					var textNode = document.createTextNode(objContextMenuControlItem.Text);
					oTd.title = objContextMenuControlItem.ToolTip;
					oTd.appendChild(textNode);
					
					

					for(var intContextItemIndex = 0; intContextItemIndex < intContextItemCount; intContextItemIndex++)
					{
						//Add new menu items for the selected control.
						oTr = oContextMenuTable.insertRow(oContextMenuTable.rows.length);
						oTd = oTr.insertCell(0);
						oAnchor = document.createElement('A');
						textNode = document.createTextNode(objContextItems[intContextItemIndex].Text);
						oAnchor.appendChild(textNode);
						oAnchor.href = objContextItems[intContextItemIndex].Href;
						oAnchor.target = objContextItems[intContextItemIndex].Target;
						oAnchor.title = objContextItems[intContextItemIndex].ToolTip;
						oTd.appendChild(oAnchor);
					}
					
				
					var menuOffset = 2
					oContextMenuTable.style.left = e.x - menuOffset;
					oContextMenuTable.style.top = e.y - menuOffset;
					oContextMenuTable.style.display = '';
					e.cancelBubble = true;
			    
					return false;
				}
			}
		}
	    
		return true;
	}
}


function ContextMenuControlItem(id, zoneId, text, toolTip, objContextItems)
{
	this.Id = id;
	this.ZoneId = zoneId;
	this.Text = text;
	this.ToolTip = toolTip;
	this._innerList = objContextItems;
}


function ContextItem(text, toolTip, href, target)
{
	this.Text = text;
	this.ToolTip = toolTip;
	this.Href = href;
	this.Target = target;
}



ContextMenuControl.prototype._innerList = null;

ContextMenuControlItem.prototype.Id = '';
ContextMenuControlItem.prototype.ZoneId = '';
ContextMenuControlItem.prototype.Text = '';
ContextMenuControlItem.prototype.ToolTip = '';
ContextMenuControlItem.prototype._innerList = null;

ContextItem.prototype.Text = '';
ContextItem.prototype.ToolTip = '';
ContextItem.prototype.Href = '';
ContextItem.prototype.Target = '';

mobjContextMenuControl = new ContextMenuControl();

