/* prototype.js */

/*  Prototype JavaScript framework, version 1.6.0.3
 *  (c) 2005-2008 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.6.0.3',

  Browser: {
    IE:     !!(window.attachEvent &&
      navigator.userAgent.indexOf('Opera') === -1),
    Opera:  navigator.userAgent.indexOf('Opera') > -1,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 &&
      navigator.userAgent.indexOf('KHTML') === -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  },

  BrowserFeatures: {
    XPath: !!document.evaluate,
    SelectorsAPI: !!document.querySelector,
    ElementExtensions: !!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement('div')['__proto__'] &&
      document.createElement('div')['__proto__'] !==
        document.createElement('form')['__proto__']
  },

  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,

  emptyFunction: function() { },
  K: function(x) { return x }
};

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions = false;


/* Based on Alex Arnell's inheritance implementation. */
var Class = {
  create: function() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      this.initialize.apply(this, arguments);
    }

    Object.extend(klass, Class.Methods);
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      var subclass = function() { };
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;

    return klass;
  }
};

Class.Methods = {
  addMethods: function(source) {
    var ancestor   = this.superclass && this.superclass.prototype;
    var properties = Object.keys(source);

    if (!Object.keys({ toString: true }).length)
      properties.push("toString", "valueOf");

    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() == "$super") {
        var method = value;
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments) };
        })(property).wrap(method);

        value.valueOf = method.valueOf.bind(method);
        value.toString = method.toString.bind(method);
      }
      this.prototype[property] = value;
    }

    return this;
  }
};

var Abstract = { };

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.isUndefined(object)) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : String(object);
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }

    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    if (Object.isElement(object)) return;

    var results = [];
    for (var property in object) {
      var value = Object.toJSON(object[property]);
      if (!Object.isUndefined(value))
        results.push(property.toJSON() + ': ' + value);
    }

    return '{' + results.join(', ') + '}';
  },

  toQueryString: function(object) {
    return $H(object).toQueryString();
  },

  toHTML: function(object) {
    return object && object.toHTML ? object.toHTML() : String.interpret(object);
  },

  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);
  },

  isElement: function(object) {
    return !!(object && object.nodeType == 1);
  },

  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },

  isHash: function(object) {
    return object instanceof Hash;
  },

  isFunction: function(object) {
    return typeof object == "function";
  },

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

  isUndefined: function(object) {
    return typeof object == "undefined";
  }
});

Object.extend(Function.prototype, {
  argumentNames: function() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^\)]*)\)/)[1]
      .replace(/\s+/g, '').split(',');
    return names.length == 1 && !names[0] ? [] : names;
  },

  bind: function() {
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this, args = $A(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat($A(arguments)));
    }
  },

  bindAsEventListener: function() {
    var __method = this, args = $A(arguments), object = args.shift();
    return function(event) {
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  curry: function() {
    if (!arguments.length) return this;
    var __method = this, args = $A(arguments);
    return function() {
      return __method.apply(this, args.concat($A(arguments)));
    }
  },

  delay: function() {
    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  },

  defer: function() {
    var args = [0.01].concat($A(arguments));
    return this.delay.apply(this, args);
  },

  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }
  },

  methodize: function() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      return __method.apply(null, [this].concat($A(arguments)));
    };
  }
});

Date.prototype.toJSON = function() {
  return '"' + this.getUTCFullYear() + '-' +
    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
    this.getUTCDate().toPaddedString(2) + 'T' +
    this.getUTCHours().toPaddedString(2) + ':' +
    this.getUTCMinutes().toPaddedString(2) + ':' +
    this.getUTCSeconds().toPaddedString(2) + 'Z"';
};

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) { }
    }

    return returnValue;
  }
};

RegExp.prototype.match = RegExp.prototype.test;

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create({
  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);
  },

  execute: function() {
    this.callback(this);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
});
Object.extend(String, {
  interpret: function(value) {
    return value == null ? '' : String(value);
  },
  specialChar: {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
  }
});

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 += String.interpret(replacement(match));
        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 = Object.isUndefined(count) ? 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 String(this);
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = Object.isUndefined(truncation) ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(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 self = arguments.callee;
    self.text.data = this;
    return self.div.innerHTML;
  },

  unescapeHTML: function() {
    var div = new Element('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return { };

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift());
        var value = pair.length > 1 ? pair.join('=') : pair[0];
        if (value != undefined) value = decodeURIComponent(value);

        if (key in hash) {
          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }
        else hash[key] = value;
      }
      return hash;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  times: function(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
      var character = String.specialChar[match[0]];
      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  },

  toJSON: function() {
    return this.inspect(true);
  },

  unfilterJSON: function(filter) {
    return this.sub(filter || Prototype.JSONFilter, '#{1}');
  },

  isJSON: function() {
    var str = this;
    if (str.blank()) return false;
    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  },

  evalJSON: function(sanitize) {
    var json = this.unfilterJSON();
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  },

  include: function(pattern) {
    return this.indexOf(pattern) > -1;
  },

  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },

  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },

  empty: function() {
    return this == '';
  },

  blank: function() {
    return /^\s*$/.test(this);
  },

  interpolate: function(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }
});

if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.stripTags().replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (Object.isFunction(replacement)) return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
};

String.prototype.parseQuery = String.prototype.toQueryParams;

Object.extend(String.prototype.escapeHTML, {
  div:  document.createElement('div'),
  text: document.createTextNode('')
});

String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);

var Template = Class.create({
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    if (Object.isFunction(object.toTemplateReplacements))
      object = object.toTemplateReplacements();

    return this.template.gsub(this.pattern, function(match) {
      if (object == null) return '';

      var before = match[1] || '';
      if (before == '\\') return match[2];

      var ctx = object, expr = match[3];
      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
      match = pattern.exec(expr);
      if (match == null) return before;

      while (match != null) {
        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
        ctx = ctx[comp];
        if (null == ctx || '' == match[3]) break;
        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
        match = pattern.exec(expr);
      }

      return before + String.interpret(ctx);
    });
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    try {
      this._each(function(value) {
        iterator.call(context, value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator, context) {
    var index = -number, slices = [], array = this.toArray();
    if (number < 1) return array;
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

  all: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator.call(context, value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result = false;
    this.each(function(value, index) {
      if (result = !!iterator.call(context, value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator.call(context, value, index));
    });
    return results;
  },

  detect: function(iterator, context) {
    var result;
    this.each(function(value, index) {
      if (iterator.call(context, value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(filter, iterator, context) {
    iterator = iterator || Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(filter);

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator.call(context, value, index));
    });
    return results;
  },

  include: function(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator, context) {
    this.each(function(value, index) {
      memo = iterator.call(context, memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator.call(context, value, index);
      if (result == null || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator, context) {
    iterator = iterator || Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator.call(context, value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator, context) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator.call(context, value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator, context) {
    return this.map(function(value, index) {
      return {
        value: value,
        criteria: iterator.call(context, 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.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (Object.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  filter:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray,
  every:   Enumerable.all,
  some:    Enumerable.any
});
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

if (Prototype.Browser.WebKit) {
  $A = function(iterable) {
    if (!iterable) return [];
    // In Safari, only use the `toArray` method if it's not a NodeList.
    // A NodeList is a function, has an function `item` property, and a numeric
    // `length` property. Adapted from Google Doctype.
    if (!(typeof iterable === 'function' && typeof iterable.length ===
        'number' && typeof iterable.item === 'function') && iterable.toArray)
      return iterable.toArray();
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  };
}

Array.from = $A;

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, length = this.length; i < 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 != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(Object.isArray(value) ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  },

  intersect: function(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  },

  toJSON: function() {
    var results = [];
    this.each(function(object) {
      var value = Object.toJSON(object);
      if (!Object.isUndefined(value)) results.push(value);
    });
    return '[' + results.join(', ') + ']';
  }
});

// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
  Array.prototype._each = Array.prototype.forEach;

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
};

if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
  var n = this.slice(0, i).reverse().indexOf(item);
  return (n < 0) ? n : i - n - 1;
};

Array.prototype.toArray = Array.prototype.clone;

function $w(string) {
  if (!Object.isString(string)) return [];
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if (Prototype.Browser.Opera){
  Array.prototype.concat = function() {
    var array = [];
    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for (var i = 0, length = arguments.length; i < length; i++) {
      if (Object.isArray(arguments[i])) {
        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  };
}
Object.extend(Number.prototype, {
  toColorPart: function() {
    return this.toPaddedString(2, 16);
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator, context) {
    $R(0, this, true).each(iterator, context);
    return this;
  },

  toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  },

  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});

$w('abs round ceil floor').each(function(method){
  Number.prototype[method] = Math[method].methodize();
});
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {

  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  return {
    initialize: function(object) {
      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    },

    _each: function(iterator) {
      for (var key in this._object) {
        var value = this._object[key], pair = [key, value];
        pair.key = key;
        pair.value = value;
        iterator(pair);
      }
    },

    set: function(key, value) {
      return this._object[key] = value;
    },

    get: function(key) {
      // simulating poorly supported hasOwnProperty
      if (this._object[key] !== Object.prototype[key])
        return this._object[key];
    },

    unset: function(key) {
      var value = this._object[key];
      delete this._object[key];
      return value;
    },

    toObject: function() {
      return Object.clone(this._object);
    },

    keys: function() {
      return this.pluck('key');
    },

    values: function() {
      return this.pluck('value');
    },

    index: function(value) {
      var match = this.detect(function(pair) {
        return pair.value === value;
      });
      return match && match.key;
    },

    merge: function(object) {
      return this.clone().update(object);
    },

    update: function(object) {
      return new Hash(object).inject(this, function(result, pair) {
        result.set(pair.key, pair.value);
        return result;
      });
    },

    toQueryString: function() {
      return this.inject([], function(results, pair) {
        var key = encodeURIComponent(pair.key), values = pair.value;

        if (values && typeof values == 'object') {
          if (Object.isArray(values))
            return results.concat(values.map(toQueryPair.curry(key)));
        } else results.push(toQueryPair(key, values));
        return results;
      }).join('&');
    },

    inspect: function() {
      return '#<Hash:{' + this.map(function(pair) {
        return pair.map(Object.inspect).join(': ');
      }).join(', ') + '}>';
    },

    toJSON: function() {
      return Object.toJSON(this.toObject());
    },

    clone: function() {
      return new Hash(this);
    }
  }
})());

Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
var ObjectRange = Class.create(Enumerable, {
  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(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (Object.isFunction(responder[callback])) {
        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 = Class.create({
  initialize: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
    else if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  }
});

Ajax.Request = Class.create(Ajax.Base, {
  _complete: false,

  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

    try {
      var response = new Ajax.Response(this);
      if (this.options.onCreate) this.options.onCreate(response);
      Ajax.Responders.dispatch('onCreate', this, response);

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
      this.transport.send(this.body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    // user-defined headers
    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (Object.isFunction(extras.push))
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    var status = this.getStatus();
    return !status || (status >= 200 && status < 300);
  },

  getStatus: function() {
    try {
      return this.transport.status || 0;
    } catch (e) { return 0 }
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + response.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(response, response.headerJSON);
      } catch (e) {
        this.dispatchException(e);
      }

      var contentType = response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

    try {
      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function() {
    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port ? ':' + location.port : ''
    }));
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name) || null;
    } catch (e) { return null }
  },

  evalResponse: function() {
    try {
      return eval((this.transport.responseText || '').unfilterJSON());
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Response = Class.create({
  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText);
      this.headerJSON   = this._getHeaderJSON();
    }

    if(readyState == 4) {
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

  status:      0,
  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: function() {
    try {
      return this.transport.statusText || '';
    } catch (e) { return '' }
  },

  getHeader: Ajax.Request.prototype.getHeader,

  getAllHeaders: function() {
    try {
      return this.getAllResponseHeaders();
    } catch (e) { return null }
  },

  getResponseHeader: function(name) {
    return this.transport.getResponseHeader(name);
  },

  getAllResponseHeaders: function() {
    return this.transport.getAllResponseHeaders();
  },

  _getHeaderJSON: function() {
    var json = this.getHeader('X-JSON');
    if (!json) return null;
    json = decodeURIComponent(escape(json));
    try {
      return json.evalJSON(this.request.options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  },

  _getResponseJSON: function() {
    var options = this.request.options;
    if (!options.evalJSON || (options.evalJSON != 'force' &&
      !(this.getHeader('Content-type') || '').include('application/json')) ||
        this.responseText.blank())
          return null;
    try {
      return this.responseText.evalJSON(options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  }
});

Ajax.Updater = Class.create(Ajax.Request, {
  initialize: function($super, container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    };

    options = Object.clone(options);
    var onComplete = options.onComplete;
    options.onComplete = (function(response, json) {
      this.updateContent(response.responseText);
      if (Object.isFunction(onComplete)) onComplete(response, json);
    }).bind(this);

    $super(url, options);
  },

  updateContent: function(responseText) {
    var receiver = this.container[this.success() ? 'success' : 'failure'],
        options = this.options;

    if (!options.evalScripts) responseText = responseText.stripScripts();

    if (receiver = $(receiver)) {
      if (options.insertion) {
        if (Object.isString(options.insertion)) {
          var insertion = { }; insertion[options.insertion] = responseText;
          receiver.insert(insertion);
        }
        else options.insertion(receiver, responseText);
      }
      else receiver.update(responseText);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  initialize: function($super, container, url, options) {
    $super(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(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (Object.isString(element))
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(Element.extend(query.snapshotItem(i)));
    return results;
  };
}

/*--------------------------------------------------------------------------*/

if (!window.Node) var Node = { };

if (!Node.ELEMENT_NODE) {
  // DOM level 2 ECMAScript Language Binding
  Object.extend(Node, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}

(function() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (Prototype.Browser.IE && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }
    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
  if (element) this.Element.prototype = element.prototype;
}).call(window);

Element.cache = { };

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 = $(element);
    element.style.display = 'none';
    return element;
  },

  show: function(element) {
    element = $(element);
    element.style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);
    content = Object.toHTML(content);
    element.innerHTML = content.stripScripts();
    content.evalScripts.bind(content).defer();
    return element;
  },

  replace: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    else if (!Object.isElement(content)) {
      content = Object.toHTML(content);
      var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content = range.createContextualFragment(content.stripScripts());
    }
    element.parentNode.replaceChild(content, element);
    return element;
  },

  insert: function(element, insertions) {
    element = $(element);

    if (Object.isString(insertions) || Object.isNumber(insertions) ||
        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
          insertions = {bottom:insertions};

    var content, insert, tagName, childNodes;

    for (var position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      insert = Element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        insert(element, content);
        continue;
      }

      content = Object.toHTML(content);

      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());

      if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }

    return element;
  },

  wrap: function(element, wrapper, attributes) {
    element = $(element);
    if (Object.isElement(wrapper))
      $(wrapper).writeAttribute(attributes || { });
    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
    else wrapper = new Element('div', wrapper);
    if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    return wrapper;
  },

  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) {
    return $(element).select("*");
  },

  firstDescendant: function(element) {
    element = $(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return $(element);
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  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) {
    if (Object.isString(selector))
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(element.parentNode);
    var ancestors = element.ancestors();
    return Object.isNumber(expression) ? ancestors[expression] :
      Selector.findElement(ancestors, expression, index);
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return element.firstDescendant();
    return Object.isNumber(expression) ? element.descendants()[expression] :
      Element.select(element, expression)[index || 0];
  },

  previous: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
    var previousSiblings = element.previousSiblings();
    return Object.isNumber(expression) ? previousSiblings[expression] :
      Selector.findElement(previousSiblings, expression, index);
  },

  next: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
    var nextSiblings = element.nextSiblings();
    return Object.isNumber(expression) ? nextSiblings[expression] :
      Selector.findElement(nextSiblings, expression, index);
  },

  select: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  adjacent: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element.parentNode, args).without(element);
  },

  identify: function(element) {
    element = $(element);
    var id = element.readAttribute('id'), self = arguments.callee;
    if (id) return id;
    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
    element.writeAttribute('id', id);
    return id;
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (Prototype.Browser.IE) {
      var t = Element._attributeTranslations.read;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name]) name = t.names[name];
      if (name.include(':')) {
        return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }
    }
    return element.getAttribute(name);
  },

  writeAttribute: function(element, name, value) {
    element = $(element);
    var attributes = { }, t = Element._attributeTranslations.write;

    if (typeof name == 'object') attributes = name;
    else attributes[name] = Object.isUndefined(value) ? true : value;

    for (var attr in attributes) {
      name = t.names[attr] || attr;
      value = attributes[attr];
      if (t.values[attr]) name = t.values[attr](element, value);
      if (value === false || value === null)
        element.removeAttribute(name);
      else if (value === true)
        element.setAttribute(name, name);
      else element.setAttribute(name, value);
    }
    return element;
  },

  getHeight: function(element) {
    return $(element).getDimensions().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    if (!element.hasClassName(className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    return element[element.hasClassName(className) ?
      'removeClassName' : 'addClassName'](className);
  },

  // 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.blank();
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (ancestor.contains)
      return ancestor.contains(element) && ancestor !== element;

    while (element = element.parentNode)
      if (element == ancestor) return true;

    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = element.cumulativeOffset();
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    style = style == 'float' ? 'cssFloat' : style.camelize();
    var value = element.style[style];
    if (!value || value == 'auto') {
      var css = document.defaultView.getComputedStyle(element, null);
      value = css ? css[style] : null;
    }
    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
    return value == 'auto' ? null : value;
  },

  getOpacity: function(element) {
    return $(element).getStyle('opacity');
  },

  setStyle: function(element, styles) {
    element = $(element);
    var elementStyle = element.style, match;
    if (Object.isString(styles)) {
      element.style.cssText += ';' + styles;
      return styles.include('opacity') ?
        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
    }
    for (var property in styles)
      if (property == 'opacity') element.setOpacity(styles[property]);
      else
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];

    return element;
  },

  setOpacity: function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = element.getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      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;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    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 (Prototype.Browser.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;
    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
    if (element._overflow !== 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName.toUpperCase() == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'absolute') return element;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    var offsets = element.positionedOffset();
    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';
    return element;
  },

  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'relative') return element;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    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;
    return element;
  },

  cumulativeScrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  getOffsetParent: 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);
  },

  viewportOffset: 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 &&
        Element.getStyle(element, 'position') == 'absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return Element._returnOffset(valueL, valueT);
  },

  clonePosition: function(element, source) {
    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 = source.viewportOffset();

    // find coordinate system to use
    element = $(element);
    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(element, 'position') == 'absolute') {
      parent = element.getOffsetParent();
      delta = parent.viewportOffset();
    }

    // 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)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
    return element;
  }
};

Element.Methods.identify.counter = 1;

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,
  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  write: {
    names: {
      className: 'class',
      htmlFor:   'for'
    },
    values: { }
  }
};

if (Prototype.Browser.Opera) {
  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
    function(proceed, element, style) {
      switch (style) {
        case 'left': case 'top': case 'right': case 'bottom':
          if (proceed(element, 'position') === 'static') return null;
        case 'height': case 'width':
          // returns '0px' for hidden elements; we want it to return null
          if (!Element.visible(element)) return null;

          // returns the border-box dimensions rather than the content-box
          // dimensions, so we subtract padding and borders from the value
          var dim = parseInt(proceed(element, style), 10);

          if (dim !== element['offset' + style.capitalize()])
            return dim + 'px';

          var properties;
          if (style === 'height') {
            properties = ['border-top-width', 'padding-top',
             'padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'padding-right', 'border-right-width'];
          }
          return properties.inject(dim, function(memo, property) {
            var val = proceed(element, property);
            return val === null ? memo : memo - parseInt(val, 10);
          }) + 'px';
        default: return proceed(element, style);
      }
    }
  );

  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
    function(proceed, element, attribute) {
      if (attribute === 'title') return element.title;
      return proceed(element, attribute);
    }
  );
}

else if (Prototype.Browser.IE) {
  // IE doesn't report offsets correctly for static elements, so we change them
  // to "relative" to get the values, then change them back.
  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
    function(proceed, element) {
      element = $(element);
      // IE throws an error if element is not in document
      try { element.offsetParent }
      catch(e) { return $(document.body) }
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);
      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    }
  );

  $w('positionedOffset viewportOffset').each(function(method) {
    Element.Methods[method] = Element.Methods[method].wrap(
      function(proceed, element) {
        element = $(element);
        try { element.offsetParent }
        catch(e) { return Element._returnOffset(0,0) }
        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);
        // Trigger hasLayout on the offset parent so that IE6 reports
        // accurate offsetTop and offsetLeft values for position: fixed.
        var offsetParent = element.getOffsetParent();
        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
          offsetParent.setStyle({ zoom: 1 });
        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );
  });

  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
    function(proceed, element) {
      try { element.offsetParent }
      catch(e) { return Element._returnOffset(0,0) }
      return proceed(element);
    }
  );

  Element.Methods.getStyle = function(element, style) {
    element = $(element);
    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
    var value = element.style[style];
    if (!value && element.currentStyle) value = element.currentStyle[style];

    if (style == 'opacity') {
      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if (value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }

    if (value == 'auto') {
      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
        return element['offset' + style.capitalize()] + 'px';
      return null;
    }
    return value;
  };

  Element.Methods.setOpacity = function(element, value) {
    function stripAlpha(filter){
      return filter.replace(/alpha\([^\)]*\)/gi,'');
    }
    element = $(element);
    var currentStyle = element.currentStyle;
    if ((currentStyle && !currentStyle.hasLayout) ||
      (!currentStyle && element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle('filter'), style = element.style;
    if (value == 1 || value === '') {
      (filter = stripAlpha(filter)) ?
        style.filter = filter : style.removeAttribute('filter');
      return element;
    } else if (value < 0.00001) value = 0;
    style.filter = stripAlpha(filter) +
      'alpha(opacity=' + (value * 100) + ')';
    return element;
  };

  Element._attributeTranslations = {
    read: {
      names: {
        'class': 'className',
        'for':   'htmlFor'
      },
      values: {
        _getAttr: function(element, attribute) {
          return element.getAttribute(attribute, 2);
        },
        _getAttrNode: function(element, attribute) {
          var node = element.getAttributeNode(attribute);
          return node ? node.value : "";
        },
        _getEv: function(element, attribute) {
          attribute = element.getAttribute(attribute);
          return attribute ? attribute.toString().slice(23, -2) : null;
        },
        _flag: function(element, attribute) {
          return $(element).hasAttribute(attribute) ? attribute : null;
        },
        style: function(element) {
          return element.style.cssText.toLowerCase();
        },
        title: function(element) {
          return element.title;
        }
      }
    }
  };

  Element._attributeTranslations.write = {
    names: Object.extend({
      cellpadding: 'cellPadding',
      cellspacing: 'cellSpacing'
    }, Element._attributeTranslations.read.names),
    values: {
      checked: function(element, value) {
        element.checked = !!value;
      },

      style: function(element, value) {
        element.style.cssText = value ? value : '';
      }
    }
  };

  Element._attributeTranslations.has = {};

  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    Object.extend(v, {
      href:        v._getAttr,
      src:         v._getAttr,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(Element._attributeTranslations.read.values);
}

else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1) ? 0.999999 :
      (value === '') ? '' : (value < 0.00001) ? 0 : value;
    return element;
  };
}

else if (Prototype.Browser.WebKit) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;

    if (value == 1)
      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };

  // Safari returns margins on body which is incorrect if the child is absolutely
  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
  // KHTML/WebKit only.
  Element.Methods.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 Element._returnOffset(valueL, valueT);
  };
}

if (Prototype.Browser.IE || Prototype.Browser.Opera) {
  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
  Element.Methods.update = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);

    content = Object.toHTML(content);
    var tagName = element.tagName.toUpperCase();

    if (tagName in Element._insertionTranslations.tags) {
      $A(element.childNodes).each(function(node) { element.removeChild(node) });
      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
        .each(function(node) { element.appendChild(node) });
    }
    else element.innerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

if ('outerHTML' in document.createElement('div')) {
  Element.Methods.replace = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) {
      element.parentNode.replaceChild(content, element);
      return element;
    }

    content = Object.toHTML(content);
    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();

    if (Element._insertionTranslations.tags[tagName]) {
      var nextSibling = element.next();
      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
      parent.removeChild(element);
      if (nextSibling)
        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
      else
        fragments.each(function(node) { parent.appendChild(node) });
    }
    else element.outerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

Element._returnOffset = function(l, t) {
  var result = [l, t];
  result.left = l;
  result.top = t;
  return result;
};

Element._getContentFromAnonymousElement = function(tagName, html) {
  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
  if (t) {
    div.innerHTML = t[0] + html + t[1];
    t[2].times(function() { div = div.firstChild });
  } else div.innerHTML = html;
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: function(element, node) {
    element.parentNode.insertBefore(node, element);
  },
  top: function(element, node) {
    element.insertBefore(node, element.firstChild);
  },
  bottom: function(element, node) {
    element.appendChild(node);
  },
  after: function(element, node) {
    element.parentNode.insertBefore(node, element.nextSibling);
  },
  tags: {
    TABLE:  ['<table>',                '</table>',                   1],
    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
    SELECT: ['<select>',               '</select>',                  1]
  }
};

(function() {
  Object.extend(this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH:    this.tags.TD
  });
}).call(Element._insertionTranslations);

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    attribute = Element._attributeTranslations.has[attribute] || attribute;
    var node = $(element).getAttributeNode(attribute);
    return !!(node && node.specified);
  }
};

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

if (!Prototype.BrowserFeatures.ElementExtensions &&
    document.createElement('div')['__proto__']) {
  window.HTMLElement = { };
  window.HTMLElement.prototype = document.createElement('div')['__proto__'];
  Prototype.BrowserFeatures.ElementExtensions = true;
}

Element.extend = (function() {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    return Prototype.K;

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    if (!element || element._extendedByPrototype ||
        element.nodeType != 1 || element == window) return element;

    var methods = Object.clone(Methods),
      tagName = element.tagName.toUpperCase(), property, value;

    // extend methods for specific tags
    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    for (property in methods) {
      value = methods[property];
      if (Object.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {
      // extend methods for all tags (Safari doesn't need this)
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

Element.hasAttribute = function(element, attribute) {
  if (element.hasAttribute) return element.hasAttribute(attribute);
  return Element.Methods.Simulated.hasAttribute(element, attribute);
};

Element.addMethods = function(methods) {
  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

  if (!methods) {
    Object.extend(Form, Form.Methods);
    Object.extend(Form.Element, Form.Element.Methods);
    Object.extend(Element.Methods.ByTag, {
      "FORM":     Object.clone(Form.Methods),
      "INPUT":    Object.clone(Form.Element.Methods),
      "SELECT":   Object.clone(Form.Element.Methods),
      "TEXTAREA": Object.clone(Form.Element.Methods)
    });
  }

  if (arguments.length == 2) {
    var tagName = methods;
    methods = arguments[1];
  }

  if (!tagName) Object.extend(Element.Methods, methods || { });
  else {
    if (Object.isArray(tagName)) tagName.each(extend);
    else extend(tagName);
  }

  function extend(tagName) {
    tagName = tagName.toUpperCase();
    if (!Element.Methods.ByTag[tagName])
      Element.Methods.ByTag[tagName] = { };
    Object.extend(Element.Methods.ByTag[tagName], methods);
  }

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    for (var property in methods) {
      var value = methods[property];
      if (!Object.isFunction(value)) continue;
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = value.methodize();
    }
  }

  function findDOMClass(tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
      "FrameSet", "IFRAME": "IFrame"
    };
    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName.capitalize() + 'Element';
    if (window[klass]) return window[klass];

    window[klass] = { };
    window[klass].prototype = document.createElement(tagName)['__proto__'];
    return window[klass];
  }

  if (F.ElementExtensions) {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
  }

  if (F.SpecificElementExtensions) {
    for (var tag in Element.Methods.ByTag) {
      var klass = findDOMClass(tag);
      if (Object.isUndefined(klass)) continue;
      copy(T[tag], klass.prototype);
    }
  }

  Object.extend(Element, Element.Methods);
  delete Element.ByTag;

  if (Element.extend.refresh) Element.extend.refresh();
  Element.cache = { };
};

document.viewport = {
  getDimensions: function() {
    var dimensions = { }, B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      if (B.WebKit && !document.evaluate) {
        // Safari <3.0 needs self.innerWidth/Height
        dimensions[d] = self['inner' + D];
      } else if (B.Opera && parseFloat(window.opera.version()) < 9.5) {
        // Opera <9.5 needs document.body.clientWidth/Height
        dimensions[d] = document.body['client' + D]
      } else {
        dimensions[d] = document.documentElement['client' + D];
      }
    });
    return dimensions;
  },

  getWidth: function() {
    return this.getDimensions().width;
  },

  getHeight: function() {
    return this.getDimensions().height;
  },

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
  }
};
/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector = Class.create({
  initialize: function(expression) {
    this.expression = expression.strip();

    if (this.shouldUseSelectorsAPI()) {
      this.mode = 'selectorsAPI';
    } else if (this.shouldUseXPath()) {
      this.mode = 'xpath';
      this.compileXPathMatcher();
    } else {
      this.mode = "normal";
      this.compileMatcher();
    }

  },

  shouldUseXPath: function() {
    if (!Prototype.BrowserFeatures.XPath) return false;

    var e = this.expression;

    // Safari 3 chokes on :*-of-type and :empty
    if (Prototype.Browser.WebKit &&
     (e.include("-of-type") || e.include(":empty")))
      return false;

    // XPath can't do namespaced attributes, nor can it read
    // the "checked" property from DOM nodes
    if ((/(\[[\w-]*?:|:checked)/).test(e))
      return false;

    return true;
  },

  shouldUseSelectorsAPI: function() {
    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;

    if (!Selector._div) Selector._div = new Element('div');

    // Make sure the browser treats the selector as valid. Test on an
    // isolated element to minimize cost of this check.
    try {
      Selector._div.querySelector(this.expression);
    } catch(e) {
      return false;
    }

    return true;
  },

  compileMatcher: function() {
    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m;

    if (Selector._cache[e]) {
      this.matcher = Selector._cache[e];
      return;
    }

    this.matcher = ["this.matcher = function(root) {",
                    "var r = root, h = Selector.handlers, c = false, n;"];

    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
            new Template(c[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.matcher.push("return h.unique(n);\n}");
    eval(this.matcher.join('\n'));
    Selector._cache[this.expression] = this.matcher;
  },

  compileXPathMatcher: function() {
    var e = this.expression, ps = Selector.patterns,
        x = Selector.xpath, le, m;

    if (Selector._cache[e]) {
      this.xpath = Selector._cache[e]; return;
    }

    this.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        if (m = e.match(ps[i])) {
          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
            new Template(x[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.xpath = this.matcher.join('');
    Selector._cache[this.expression] = this.xpath;
  },

  findElements: function(root) {
    root = root || document;
    var e = this.expression, results;

    switch (this.mode) {
      case 'selectorsAPI':
        // querySelectorAll queries document-wide, then filters to descendants
        // of the context element. That's not what we want.
        // Add an explicit context to the selector if necessary.
        if (root !== document) {
          var oldId = root.id, id = $(root).identify();
          e = "#" + id + " " + e;
        }

        results = $A(root.querySelectorAll(e)).map(Element.extend);
        root.id = oldId;

        return results;
      case 'xpath':
        return document._getElementsByXPath(this.xpath, root);
      default:
       return this.matcher(root);
    }
  },

  match: function(element) {
    this.tokens = [];

    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
    var le, p, m;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          // use the Selector.assertions methods unless the selector
          // is too complex.
          if (as[i]) {
            this.tokens.push([i, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            // reluctantly do a document-wide search
            // and look for a match in the array
            return this.findElements(document).include(element);
          }
        }
      }
    }

    var match = true, name, matches;
    for (var i = 0, token; token = this.tokens[i]; i++) {
      name = token[0], matches = token[1];
      if (!Selector.assertions[name](element, matches)) {
        match = false; break;
      }
    }

    return match;
  },

  toString: function() {
    return this.expression;
  },

  inspect: function() {
    return "#<Selector:" + this.expression.inspect() + ">";
  }
});

Object.extend(Selector, {
  _cache: { },

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: function(m) {
      m[1] = m[1].toLowerCase();
      return new Template("[@#{1}]").evaluate(m);
    },
    attr: function(m) {
      m[1] = m[1].toLowerCase();
      m[3] = m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    },
    pseudo: function(m) {
      var h = Selector.xpath.pseudos[m[1]];
      if (!h) return '';
      if (Object.isFunction(h)) return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    },
    operators: {
      '=':  "[@#{1}='#{3}']",
      '!=': "[@#{1}!='#{3}']",
      '^=': "[starts-with(@#{1}, '#{3}')]",
      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
      '*=': "[contains(@#{1}, '#{3}')]",
      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
    },
    pseudos: {
      'first-child': '[not(preceding-sibling::*)]',
      'last-child':  '[not(following-sibling::*)]',
      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
      'checked':     "[@checked]",
      'disabled':    "[(@disabled) and (@type!='hidden')]",
      'enabled':     "[not(@disabled) and (@type!='hidden')]",
      'not': function(m) {
        var e = m[6], p = Selector.patterns,
            x = Selector.xpath, le, v;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i in p) {
            if (m = e.match(p[i])) {
              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
              e = e.replace(m[0], '');
              break;
            }
          }
        }
        return "[not(" + exclusion.join(" and ") + ")]";
      },
      'nth-child':      function(m) {
        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
      },
      'nth-last-child': function(m) {
        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
      },
      'nth-of-type':    function(m) {
        return Selector.xpath.pseudos.nth("position() ", m);
      },
      'nth-last-of-type': function(m) {
        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
      },
      'first-of-type':  function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
      },
      'last-of-type':   function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
      },
      'only-of-type':   function(m) {
        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
      },
      nth: function(fragment, m) {
        var mm, formula = m[6], predicate;
        if (formula == 'even') formula = '2n+0';
        if (formula == 'odd')  formula = '2n+1';
        if (mm = formula.match(/^(\d+)$/)) // digit only
          return '[' + fragment + "= " + mm[1] + ']';
        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
          if (mm[1] == "-") mm[1] = -1;
          var a = mm[1] ? Number(mm[1]) : 1;
          var b = mm[2] ? Number(mm[2]) : 0;
          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
          "((#{fragment} - #{b}) div #{a} >= 0)]";
          return new Template(predicate).evaluate({
            fragment: fragment, a: a, b: b });
        }
      }
    }
  },

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
    },
    pseudo: function(m) {
      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
    },
    descendant:   'c = "descendant";',
    child:        'c = "child";',
    adjacent:     'c = "adjacent";',
    laterSibling: 'c = "laterSibling";'
  },

  patterns: {
    // combinators must be listed first
    // (and descendant needs to be last combinator)
    laterSibling: /^\s*~\s*/,
    child:        /^\s*>\s*/,
    adjacent:     /^\s*\+\s*/,
    descendant:   /^\s/,

    // selectors follow
    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
    id:           /^#([\w\-\*]+)(\b|$)/,
    className:    /^\.([\w\-\*]+)(\b|$)/,
    pseudo:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
    attrPresence: /^\[((?:[\w]+:)?[\w]+)\]/,
    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
  },

  // for Selector.match and Element#match
  assertions: {
    tagName: function(element, matches) {
      return matches[1].toUpperCase() == element.tagName.toUpperCase();
    },

    className: function(element, matches) {
      return Element.hasClassName(element, matches[1]);
    },

    id: function(element, matches) {
      return element.id === matches[1];
    },

    attrPresence: function(element, matches) {
      return Element.hasAttribute(element, matches[1]);
    },

    attr: function(element, matches) {
      var nodeValue = Element.readAttribute(element, matches[1]);
      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    }
  },

  handlers: {
    // UTILITY FUNCTIONS
    // joins two collections
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        a.push(node);
      return a;
    },

    // marks an array of nodes for counting
    mark: function(nodes) {
      var _true = Prototype.emptyFunction;
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = _true;
      return nodes;
    },

    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = undefined;
      return nodes;
    },

    // mark each child node with its position (for nth calls)
    // "ofType" flag indicates whether we're indexing for nth-of-type
    // rather than nth-child
    index: function(parentNode, reverse, ofType) {
      parentNode._countedByPrototype = Prototype.emptyFunction;
      if (reverse) {
        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
          var node = nodes[i];
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
        }
      } else {
        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
      }
    },

    // filters out duplicates and extends all nodes
    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (!(n = nodes[i])._countedByPrototype) {
          n._countedByPrototype = Prototype.emptyFunction;
          results.push(Element.extend(n));
        }
      return Selector.handlers.unmark(results);
    },

    // COMBINATOR FUNCTIONS
    descendant: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, node.getElementsByTagName('*'));
      return results;
    },

    child: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        for (var j = 0, child; child = node.childNodes[j]; j++)
          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
      }
      return results;
    },

    adjacent: function(nodes) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        var next = this.nextElementSibling(node);
        if (next) results.push(next);
      }
      return results;
    },

    laterSibling: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, Element.nextSiblings(node));
      return results;
    },

    nextElementSibling: function(node) {
      while (node = node.nextSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    previousElementSibling: function(node) {
      while (node = node.previousSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    // TOKEN FUNCTIONS
    tagName: function(nodes, root, tagName, combinator) {
      var uTagName = tagName.toUpperCase();
      var results = [], h = Selector.handlers;
      if (nodes) {
        if (combinator) {
          // fastlane for ordinary descendant combinators
          if (combinator == "descendant") {
            for (var i = 0, node; node = nodes[i]; i++)
              h.concat(results, node.getElementsByTagName(tagName));
            return results;
          } else nodes = this[combinator](nodes);
          if (tagName == "*") return nodes;
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName.toUpperCase() === uTagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = $(id), h = Selector.handlers;
      if (!targetNode) return [];
      if (!nodes && root == document) return [targetNode];
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (targetNode.parentNode == node) return [targetNode];
          } else if (combinator == 'descendant') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Element.descendantOf(targetNode, node)) return [targetNode];
          } else if (combinator == 'adjacent') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Selector.handlers.previousElementSibling(targetNode) == node)
                return [targetNode];
          } else nodes = h[combinator](nodes);
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node == targetNode) return [targetNode];
        return [];
      }
      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
    },

    className: function(nodes, root, className, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      return Selector.handlers.byClassName(nodes, root, className);
    },

    byClassName: function(nodes, root, className) {
      if (!nodes) nodes = Selector.handlers.descendant([root]);
      var needle = ' ' + className + ' ';
      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continue;
        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
          results.push(node);
      }
      return results;
    },

    attrPresence: function(nodes, root, attr, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var results = [];
      for (var i = 0, node; node = nodes[i]; i++)
        if (Element.hasAttribute(node, attr)) results.push(node);
      return results;
    },

    attr: function(nodes, root, attr, value, operator, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var handler = Selector.operators[operator], results = [];
      for (var i = 0, node; node = nodes[i]; i++) {
        var nodeValue = Element.readAttribute(node, attr);
        if (nodeValue === null) continue;
        if (handler(nodeValue, value)) results.push(node);
      }
      return results;
    },

    pseudo: function(nodes, name, value, root, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      if (!nodes) nodes = root.getElementsByTagName("*");
      return Selector.pseudos[name](nodes, value, root);
    }
  },

  pseudos: {
    'first-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'only-child': function(nodes, value, root) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
    },
    'nth-child':        function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root);
    },
    'nth-last-child':   function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true);
    },
    'nth-of-type':      function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, false, true);
    },
    'nth-last-of-type': function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true, true);
    },
    'first-of-type':    function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, false, true);
    },
    'last-of-type':     function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, true, true);
    },
    'only-of-type':     function(nodes, formula, root) {
      var p = Selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    },

    // handles the an+b logic
    getIndices: function(a, b, total) {
      if (a == 0) return b > 0 ? [b] : [];
      return $R(1, total).inject([], function(memo, i) {
        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
        return memo;
      });
    },

    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = Selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._countedByPrototype) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.nodeIndex == formula) results.push(node);
      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
        if (m[1] == "-") m[1] = -1;
        var a = m[1] ? Number(m[1]) : 1;
        var b = m[2] ? Number(m[2]) : 0;
        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
          for (var j = 0; j < l; j++)
            if (node.nodeIndex == indices[j]) results.push(node);
        }
      }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
    },

    'empty': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        // IE treats comments as element nodes
        if (node.tagName == '!' || node.firstChild) continue;
        results.push(node);
      }
      return results;
    },

    'not': function(nodes, selector, root) {
      var h = Selector.handlers, selectorType, m;
      var exclusions = new Selector(selector).findElements(root);
      h.mark(exclusions);
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node._countedByPrototype) results.push(node);
      h.unmark(exclusions);
      return results;
    },

    'enabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node.disabled && (!node.type || node.type !== 'hidden'))
          results.push(node);
      return results;
    },

    'disabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.disabled) results.push(node);
      return results;
    },

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.checked) results.push(node);
      return results;
    }
  },

  operators: {
    '=':  function(nv, v) { return nv == v; },
    '!=': function(nv, v) { return nv != v; },
    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
    '$=': function(nv, v) { return nv.endsWith(v); },
    '*=': function(nv, v) { return nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
     '-').include('-' + (v || "").toUpperCase() + '-'); }
  },

  split: function(expression) {
    var expressions = [];
    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    return expressions;
  },

  matchElements: function(elements, expression) {
    var matches = $$(expression), h = Selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._countedByPrototype) results.push(element);
    h.unmark(matches);
    return results;
  },

  findElement: function(elements, expression, index) {
    if (Object.isNumber(expression)) {
      index = expression; expression = false;
    }
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    expressions = Selector.split(expressions.join(','));
    var results = [], h = Selector.handlers;
    for (var i = 0, l = expressions.length, selector; i < l; i++) {
      selector = new Selector(expressions[i].strip());
      h.concat(results, selector.findElements(element));
    }
    return (l > 1) ? h.unique(results) : results;
  }
});

if (Prototype.Browser.IE) {
  Object.extend(Selector.handlers, {
    // IE returns comment nodes on getElementsByTagName("*").
    // Filter them out.
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        if (node.tagName !== "!") a.push(node);
      return a;
    },

    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node.removeAttribute('_countedByPrototype');
      return nodes;
    }
  });
}

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (Object.isUndefined(options.hash)) options.hash = true;
    var key, value, submitted = false, submit = options.submit;

    var data = elements.inject({ }, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          if (key in result) {
            // a key is already present; construct an array of values
            if (!Object.isArray(result[key])) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return options.hash ? data : Object.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, options) {
    return Form.serializeElements(Form.getElements(form), options);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('disable');
    return form;
  },

  enable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('enable');
    return form;
  },

  findFirstElement: function(form) {
    var elements = $(form).getElements().findAll(function(element) {
      return 'hidden' != element.type && !element.disabled;
    });
    var firstByIndex = elements.findAll(function(element) {
      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
    }).sortBy(function(element) { return element.tabIndex }).first();

    return firstByIndex ? firstByIndex : elements.find(function(element) {
      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().activate();
    return form;
  },

  request: function(form, options) {
    form = $(form), options = Object.clone(options || { });

    var params = options.parameters, action = form.readAttribute('action') || '';
    if (action.blank()) action = window.location.href;
    options.parameters = form.serialize(true);

    if (params) {
      if (Object.isString(params)) params = params.toQueryParams();
      Object.extend(options.parameters, params);
    }

    if (form.hasAttribute('method') && !options.method)
      options.method = form.method;

    return new Ajax.Request(action, options);
  }
};

/*--------------------------------------------------------------------------*/

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
};

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = { };
        pair[element.name] = value;
        return Object.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  setValue: function(element, value) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    Form.Element.Serializers[method](element, value);
    return element;
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    try {
      element.focus();
      if (element.select && (element.tagName.toLowerCase() != 'input' ||
          !['button', 'reset', 'submit'].include(element.type)))
        element.select();
    } catch (e) { }
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.disabled = false;
    return element;
  }
};

/*--------------------------------------------------------------------------*/

var Field = Form.Element;
var $F = Form.Element.Methods.getValue;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = {
  input: function(element, value) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element, value);
      default:
        return Form.Element.Serializers.textarea(element, value);
    }
  },

  inputSelector: function(element, value) {
    if (Object.isUndefined(value)) return element.checked ? element.value : null;
    else element.checked = !!value;
  },

  textarea: function(element, value) {
    if (Object.isUndefined(value)) return element.value;
    else element.value = value;
  },

  select: function(element, value) {
    if (Object.isUndefined(value))
      return this[element.type == 'select-one' ?
        'selectOne' : 'selectMany'](element);
    else {
      var opt, currentValue, single = !Object.isArray(value);
      for (var i = 0, length = element.length; i < length; i++) {
        opt = element.options[i];
        currentValue = this.optionValue(opt);
        if (single) {
          if (currentValue == value) {
            opt.selected = true;
            return;
          }
        }
        else opt.selected = value.include(currentValue);
      }
    }
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
};

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
  initialize: function($super, element, frequency, callback) {
    $super(callback, frequency);
    this.element   = $(element);
    this.lastValue = this.getValue();
  },

  execute: function() {
    var value = this.getValue();
    if (Object.isString(this.lastValue) && Object.isString(value) ?
        this.lastValue != value : String(this.lastValue) != String(value)) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
});

Form.Element.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = Class.create({
  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() {
    Form.getElements(this.element).each(this.registerCallback, this);
  },

  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(Abstract.EventObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) var Event = { };

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,
  KEY_INSERT:   45,

  cache: { },

  relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return Element.extend(element);
  }
});

Event.Methods = (function() {
  var isButton;

  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (Prototype.Browser.WebKit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

  } else {
    isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

    element: function(event) {
      event = Event.extend(event);

      var node          = event.target,
          type          = event.type,
          currentTarget = event.currentTarget;

      if (currentTarget && currentTarget.tagName) {
        // Firefox screws up the "click" event when moving between radio buttons
        // via arrow keys. It also screws up the "load" and "error" events on images,
        // reporting the document as the target instead of the original image.
        if (type === 'load' || type === 'error' ||
          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
            && currentTarget.type === 'radio'))
              node = currentTarget;
      }
      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
      return Element.extend(node);
    },

    findElement: function(event, expression) {
      var element = Event.element(event);
      if (!expression) return element;
      var elements = [element].concat(element.ancestors());
      return Selector.findElement(elements, expression, 0);
    },

    pointer: function(event) {
      var docElement = document.documentElement,
      body = document.body || { scrollLeft: 0, scrollTop: 0 };
      return {
        x: event.pageX || (event.clientX +
          (docElement.scrollLeft || body.scrollLeft) -
          (docElement.clientLeft || 0)),
        y: event.pageY || (event.clientY +
          (docElement.scrollTop || body.scrollTop) -
          (docElement.clientTop || 0))
      };
    },

    pointerX: function(event) { return Event.pointer(event).x },
    pointerY: function(event) { return Event.pointer(event).y },

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents")['__proto__'];
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend(Event, (function() {
  var cache = Event.cache;

  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
    arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
    return cache[id] = cache[id] || { };
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }


  // Internet Explorer needs to remove event handlers on page unload
  // in order to avoid memory leaks.
  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  // Safari has a dummy event handler on page unload so that it won't
  // use its bfcache. Safari <= 3.1 has an issue with restoring the "document"
  // object when page is returned to via the back button using its bfcache.
  if (Prototype.Browser.WebKit) {
    window.addEventListener('unload', Prototype.emptyFunction, false);
  }

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize(),
  loaded:        false
});

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards and John Resig. */

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearInterval(timer);
    document.fire("dom:loaded");
    document.loaded = true;
  }

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
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;
  },

  // 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 = Element.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 = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.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;
  },

  // Deprecation layer -- use newer Element methods now (1.5.2).

  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

/*--------------------------------------------------------------------------*/

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

/*--------------------------------------------------------------------------*/

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($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);

/*--------------------------------------------------------------------------*/

Element.addMethods();/* gui/scriptaculous.js */

// script.aculo.us scriptaculous.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Scriptaculous = {
  Version: '1.8.2',
  require: function(libraryName) {
    // inserting via DOM fails in Safari 2.0, so brute force approach
    document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
  },
  REQUIRED_PROTOTYPE: '1.6.0.3',
  load: function() {
    function convertVersionString(versionString) {
      var v = versionString.replace(/_.*|\./g, '');
      v = parseInt(v + '0'.times(4-v.length));
      return versionString.indexOf('_') > -1 ? v-1 : v;
    }

    if((typeof Prototype=='undefined') ||
       (typeof Element == 'undefined') ||
       (typeof Element.Methods=='undefined') ||
       (convertVersionString(Prototype.Version) <
        convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
       throw("script.aculo.us requires the Prototype JavaScript framework >= " +
        Scriptaculous.REQUIRED_PROTOTYPE);

    var js = /scriptaculous\.js(\?.*)?$/;
    $$('head script[src]').findAll(function(s) {
      return s.src.match(js);
    }).each(function(s) {
      var path = s.src.replace(js, ''),
      includes = s.src.match(/\?.*load=([a-z,]*)/);
      (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
       function(include) { Scriptaculous.require(path+include+'.js') });
    });
  }
};

Scriptaculous.load();/* gui/effects.js */

// script.aculo.us effects.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// 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({fontSize: (percent/100) + 'em'});
  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  return element;
};

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
};

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + .5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
    },
    pulse: function(pos, pulses) {
      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
    },
    spring: function(pos) {
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    duration:   1.0,   // seconds
    fps:        100,   // 100= assume 66fps max.
    sync:       false, // true for combining
    from:       0.0,
    to:         1.0,
    delay:      0.0,
    queue:      'parallel'
  },
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';

    element = $(element);
    $A(element.childNodes).each( function(child) {
      if (child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character),
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') ||
        Object.isFunction(element)) &&
       (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);
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(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 = Object.isString(effect.options.queue) ?
      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 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        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), 15);
  },
  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();
    for(var i=0, len=this.effects.length;i<len;i++)
      this.effects[i] && this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if (!Object.isString(queueName)) return queueName;

    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
      );
    }
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    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.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;

    this.render = (function() {
      function dispatch(effect, eventName) {
        if (effect.options[eventName + 'Internal'])
          effect.options[eventName + 'Internal'](effect);
        if (effect.options[eventName])
          effect.options[eventName](effect);
      }

      return function(pos) {
        if (this.state === "idle") {
          this.state = "running";
          dispatch(this, 'beforeSetup');
          if (this.setup) this.setup();
          dispatch(this, 'afterSetup');
        }
        if (this.state === "running") {
          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
          this.position = pos;
          dispatch(this, 'beforeUpdate');
          if (this.update) this.update(pos);
          dispatch(this, 'afterUpdate');
        }
      };
    })();

    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        '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.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ?
        '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() {
    var data = $H();
    for(property in this)
      if (!Object.isFunction(this[property])) data.set(property, this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

Effect.Parallel = Class.create(Effect.Base, {
  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.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(),
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) :
      function(value) { object[method] = value };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

Effect.Event = Class.create(Effect.Base, {
  initialize: function() {
    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if (Prototype.Browser.IE && (!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(Effect.Base, {
  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() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + '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(Effect.Base, {
  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 = width.round() + 'px';
    if (this.options.scaleY) d.height = height.round() + '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(Effect.Base, {
  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 = { };
    if (!this.options.keepBackgroundImage) {
      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+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
  scrollOffsets = document.viewport.getScrollOffsets(),
  elementOffsets = $(element).cumulativeOffset();

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()); }
  );
};

/* ------------- 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().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).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().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().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().setStyle({height: '0px'}).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().makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide().undoClipping().undoPositioned().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().undoPositioned().setStyle(oldStyle);
        }
      }, arguments[1] || { }));
};

Effect.Shake = function(element) {
  element = $(element);
  var options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}); }}); }}); }}); }}); }});
};

Effect.SlideDown = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().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.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().setStyle({height: '0px'}).show();
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || { })
  );
};

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false,
    scaleX: false,
    scaleMode: 'box',
    scaleFrom: 100,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().show();
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().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();
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping();
    }
  });
};

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().makeClipping().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'}).show();
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping().undoPositioned().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().makeClipping();
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
};

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || { },
    oldOpacity = element.getInlineOpacity(),
    transition = options.transition || Effect.Transitions.linear,
    reverser   = function(pos){
      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
    };

  return new Effect.Opacity(element,
    Object.extend(Object.extend({  duration: 2.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();
  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().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || { }));
};

Effect.Morph = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });

    if (!Object.isString(options.style)) this.style = $H(options.style);
    else {
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.style] = '';
          });
        };
      }
    }
    this.start(options);
  },

  setup: function(){
    function parseColor(color){
      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 );
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if (property == 'opacity') {
        value = parseFloat(value);
        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom: 1});
      } else if (Element.CSS_LENGTH.test(value)) {
          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
          value = parseFloat(components[1]);
          unit = (components.length == 3) ? components[2] : null;
      }

      var originalValue = this.element.getStyle(property);
      return {
        style: property.camelize(),
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      );
    });
  },
  update: function(position) {
    var style = { }, transform, i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] =
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
            (transform.unit === null ? '' : transform.unit);
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create({
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || { };
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:     track.keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
      }).flatten(),
      this.options
    );
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');

Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }

  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property, style[property]);
  });

  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
      results[property] = css[property];
      return results;
    });
    if (!styles.opacity) styles.opacity = element.getOpacity();
    return styles;
  };
}

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element);
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) {
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    };
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);/* gui/resizable.js */

/*
*    script.aculo.us resizable.js, Mon Aug 20th 2007
*
*    Orginal: http://script.aculo.us/
*
*    Scriptaculous extension "Vasil Popovski" => vas_popovski@hotmail.com
*    Extension based on Scriptaculous dragdrop.js
*
*  This extenssion is freely distributable under the terms of an MIT-style license.
*/

var Resizables = {
  resizers: [],
  observers: [],

  register: function(resizable) {
    if(this.resizers.length == 0) {
      this.eventMouseUp   = this.endResize.bindAsEventListener(this);
      this.eventMouseMove = this.updateResize.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.resizers.push(resizable);
  },

  unregister: function(resizable) {
    this.resizers = this.resizers.reject(function(r) { return r==resizable });
    if(this.resizers.length == 0) {
      Event.stopObserving(document, "mouseup", this.eventMouseUp);
      Event.stopObserving(document, "mousemove", this.eventMouseMove);
      Event.stopObserving(document, "keypress", this.eventKeypress);
    }
  },

  activate: function(resizable) {
    if(resizable.options.delay) { 
      this._timeout = setTimeout(function() { 
        Resizables._timeout = null; 
        window.focus(); 
        Resizables.activeResizable = resizable; 
      }.bind(this), resizable.options.delay); 
    } else {
      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
      this.activeResizable = resizable;
    }
  },
  deactivate: function() {
    this.activeResizable = null;
  },
  updateResize: function(event) {
    if(!this.activeResizable) return;
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
     if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
    this._lastPointer = pointer;
    this.activeResizable.updateResize(event, pointer);
  },
  endResize: function(event) {
    if(this._timeout) { 
      clearTimeout(this._timeout); 
      this._timeout = null; 
    }
    if(!this.activeResizable) return;
    this._lastPointer = null;
    this.activeResizable.endResize(event);
    this.activeResizable = null;
  },

  keyPress: function(event) {
    if(this.activeResizable)
      this.activeResizable.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, resizable, event) {  // 'onStart', 'onEnd', 'onDrag'
    if(this[eventName+'Count'] > 0)
      this.observers.each( function(o) {
        if(o[eventName]) o[eventName](eventName, resizable, event);
      });
    if(resizable.options[eventName]) resizable.options[eventName](resizable, event);
  },

  _cacheObserverCallbacks: function() {
    ['onStart','onEnd','onResize'].each( function(eventName) {
      Resizables[eventName+'Count'] = Resizables.observers.select(
        function(o) { return o[eventName]; }
      ).length;
    });
  }
}

var Resizable = Class.create();
Resizable._resizing    = {};

Resizable.prototype = {
  initialize: function(element) {
    var defaults = {
      handle: false,

      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:'_resizable', position:'end'},
          afterFinish: function(){ 
            Resizable._resizing[element] = false 
          }
        }); 
      },
      zindex: 1000,
      revert: false,
      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);
          Resizable._resizing[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'))
      this.handle = this.element.down('.'+options.handle, 0);

    if(!this.handle) this.handle = $(options.handle);
    if(!this.handle) this.handle = this.element;

    Element.makePositioned(this.element); // fix IE    
    this.delta    = this.currentDelta();
    this.options  = options;
    this.resizing = false;   

    this.eventMouseDown = this.initResize.bindAsEventListener(this);
    Event.observe(this.handle, "mousedown", this.eventMouseDown);

    Resizables.register(this);
  },
   reverteffect: function(element, horizontal, vertical) {
       var horiz = this._edim[0] - horizontal;
        var vert = this._edim[1] - vertical;
        new Effect.ReSize(element, {direction:'vert', amount:vert});
        new Effect.ReSize(element, {direction:'horizontal', amount:horiz});
    },

  destroy: function() {
    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
    Resizables.unregister(this);
  },

  currentDelta: function() {
    return([
      parseInt(Element.getStyle(this.element,'left') || '0'),
      parseInt(Element.getStyle(this.element,'top') || '0')]);
  },
  initResize: function(event) {
    if(typeof Resizable._resizing[this.element] != 'undefined' &&
      Resizable._resizing[this.element]) return;
    if(Event.isLeftClick(event)) {    
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if((tag_name = src.tagName.toUpperCase()) && (
        tag_name=='INPUT' ||
        tag_name=='SELECT' ||
        tag_name=='OPTION' ||
        tag_name=='BUTTON' ||
        tag_name=='TEXTAREA')) return;

      var pointer = [Event.pointerX(event), Event.pointerY(event)];
          this._initialX = pointer[0];
          this._initialY = pointer[1];
          var dim = Element.getDimensions(this.element);

          this._edim = [dim.width,dim.height];
          this._min = [1,1];
          this._max = [0,0];
      var pos = Position.cumulativeOffset(this.element);
      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) }); 

        if(this.options.bind == true){
            this._parentDim = Element.getDimensions(this.element.parentNode); 
            var cop = Position.cumulativeOffset(this.element.parentNode);
            var coe = Position.cumulativeOffset(this.element);
                    this.elementOffset = [coe[0]-cop[0], coe[1]-cop[1]];
        }
        if(this.options.min){
            if(this.options.min instanceof Array){
                this._min = this._min.map(function(v,i){return (this.options.min[i] > 0 ? this.options.min[i] : 1);}.bind(this));
            }
            else
                this._min = this._min.map(function(v,i){return (this.options.min > 0 ? this.options.min : 1);}.bind(this));
        }        
        if(this.options.max){
            if(this.options.max instanceof Array){
                this._max = this._max.map(function(v,i){ return (this.options.max[i] >= this._min[i]) ? this.options.max[i] : 0; }.bind(this));
            }
            else
                this._max = this._max.map(function(v,i){ return (this.options.max >= this._min[i]) ? this.options.max : 0; }.bind(this));
        }

      Resizables.activate(this);
      Event.stop(event);
    }
  },

  startResize: function(event) {
    this.resizing = 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) {
      // If element has margin-left/right/top/bottom set all sort of problems occurs regarding the elements starting position 
      // and final position especially in IE (more problems when also snap and ghosting are initiated together)
      // (similar happens in Draggable - might need fixing there as well??)
      // next few lines SOLVE the problem (works for every combination: position:absolute+offset || relative+margins etc.)
      this._clone = this.element.cloneNode(true);
      this.element.parentNode.insertBefore(this._clone, this.element);
      var style = this._clone.style;
      Position.absolutize(this._clone);
      // partial IE margin fix (if another element is below it in IE it looses its position i.e. offset on bottom is reset)
      if(navigator.appName.indexOf('Microsoft') != -1 && parseInt(Element.getStyle(this.element, 'margin-top')) > 0){
         this.element.style.top = style.marginTop;
      }
      //
      style.margin = '0px';
    }
    Resizables.notify('onStart', this, event);

    if(this.options.starteffect) this.options.starteffect(this.element);
  },
  updateResize: function(event, pointer) {
   if(!this.resizing) this.startResize(event);

    Resizables.notify('onResize', this, event);
    this.draw(pointer);
    if(this.options.change) this.options.change(this);

    Event.stop(event);
  },

  finishResize: function(event, success) {
    this.resizing = false;
    if(this.options.ghosting) {
      if(navigator.appName.indexOf('Microsoft') != -1 && parseInt(Element.getStyle(this.element, 'margin-top')) > 0)
          this.element.style.top = this._clone.style.marginTop;
      Element.remove(this._clone);
      this._clone = null;
    }
    Resizables.notify('onEnd', this, event);
    var revert = this.options.revert;
    if(revert && typeof revert == 'function') revert = revert(this.element);

    if(revert && this.reverteffect) {
        var dim = Element.getDimensions(this.element);
        this.reverteffect(this.element, dim.width, dim.height);//d[1]-this.delta[1], d[0]-this.delta[0]
    } 
    if(this.options.zindex)
      this.element.style.zIndex = this.originalZ;

    if(this.options.endeffect) 
      this.options.endeffect(this.element);

    Resizables.deactivate(this);
  },

  keyPress: function(event) {
    if(event.keyCode!=Event.KEY_ESC) return;
    this.finishResize(event, false);
    Event.stop(event);
  },

  endResize: function(event) {
    if(!this.resizing) return;
    this.finishResize(event, true);
    Event.stop(event);
  },

  draw: function(point) {
      var pos = Position.cumulativeOffset(this.element);
    var d = this.currentDelta();
            pos[0] -= d[0]; 
            pos[1] -= d[1];

        var p = [0,1].map(function(i){ 
          return (point[i]-pos[i]-this.offset[i]) 
        }.bind(this));

    var l_width = p[0] + this._edim[0] - d[0];
    var l_height = p[1] + this._edim[1] - d[1];

    p[0] = (l_width > this._min[0]) ? l_width : this._min[0];
    p[1] = (l_height > this._min[1]) ? l_height : this._min[1];

    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) {
            // IF Javascript alert activated in IE throws error if one of the snap values is 0 : [20,0]
            // or if this map functions returns 0 for i-th element
            // Same happens in Draggable (needs to be patched??)
            var dim = Math.round(v/this.options.snap[i])*this.options.snap[i];
            return (this.options.snap[i] > 0) ? ((dim > this._min[i]) ? dim : this._min[i]) : this._edim[i]; 
            }.bind(this))
          } 
          else {
            p = p.map( function(v,i) {
            var dim = Math.round(v/this.options.snap)*this.options.snap-d[i];
            return (this.options.snap > 0) ? ((dim > this._min[i]) ? dim : this._min[i]) : this._edim[i] }.bind(this))
          }
        }
    }

    if(this.options.bind){
        if(this._parentDim.width <= p[0]+this.elementOffset[0])
            p[0] = this._parentDim.width - this.elementOffset[0] - 2;
        if(this._parentDim.height <= p[1]+this.elementOffset[1])
            p[1] = this._parentDim.height - this.elementOffset[1] - 2;
    }

    if(this.options.min){
        p[0] = p[0] > this._min[0] ? p[0] : this._min[0];
        p[1] = p[1] > this._min[1] ? p[1] : this._min[1];
    }
    if(this.options.max){
        p[0] = p[0] < this._max[0] ? p[0] : (this._max[0] > 0 ? this._max[0] : p[0]);
        p[1] = p[1] < this._max[1] ? p[1] : (this._max[1] > 0 ? this._max[1] : p[1]);
    }

    var style = this.element.style;
    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
        style.width = p[0]+"px";
    if((!this.options.constraint) || (this.options.constraint=='vertical'))
         style.height = p[1]+"px";
    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  }
}

// script.aculo.us EffectResize.js

// Copyright(c) 2007 - Frost Innovation AS, http://ajaxwidgets.com
//
// EffectResize.js is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

/* Helper Effect for resizing elements...
 */
Effect.ReSize = Class.create();
Object.extend(Object.extend(Effect.ReSize.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = element;
    if(!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ amount: 100, direction: 'vert', toSize:null }, arguments[1] || {});
    if( options.direction == 'vert' )
      this.originalSize = options.originalSize || parseInt(this.element.style.height);
    else
      this.originalSize = options.originalSize || parseInt(this.element.style.width);

    if( options.toSize != null )
      options.amount = options.toSize - this.originalSize;

    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if(this.element.getStyle('display')=='none') { this.cancel(); return; }
  },
  update: function(position) {
    if( this.options.direction  == 'vert' ){
      this.element.setStyle({height: this.originalSize+(this.options.amount*position)+'px'});
    } else {
      this.element.setStyle({width: this.originalSize+(this.options.amount*position)+'px'});
    }
  },
  finish: function(){
    if( this.options.direction  == 'vert' ){
      this.element.setStyle({height: this.originalSize+this.options.amount+'px'});
    } else {
      this.element.setStyle({width: this.originalSize+this.options.amount+'px'});
    }
  }
});/* gui/controls.js */

// script.aculo.us controls.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
//           (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
// Contributors:
//  Richard Livsey
//  Rahul Bhargava
//  Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.

if(typeof Effect == 'undefined')
  throw("controls.js requires including script.aculo.us' effects.js library");

var Autocompleter = { };
Autocompleter.Base = Class.create({
  baseInitialize: function(element, update, options) {
    element          = $(element);
    this.element     = element;
    this.update      = $(update);
    this.hasFocus    = false;
    this.changed     = false;
    this.active      = false;
    this.index       = 0;
    this.entryCount  = 0;
    this.oldElementValue = this.element.value;

    if(this.setOptions)
      this.setOptions(options);
    else
      this.options = options || { };

    this.options.paramName    = this.options.paramName || this.element.name;
    this.options.tokens       = this.options.tokens || [];
    this.options.frequency    = this.options.frequency || 0.4;
    this.options.minChars     = this.options.minChars || 1;
    this.options.onShow       = this.options.onShow ||
      function(element, update){
        if(!update.style.position || update.style.position=='absolute') {
          update.style.position = 'absolute';
          Position.clone(element, update, {
            setHeight: false,
            offsetTop: element.offsetHeight
          });
        }
        Effect.Appear(update,{duration:0.15});
      };
    this.options.onHide = this.options.onHide ||
      function(element, update){ new Effect.Fade(update,{duration:0.15}) };

    if(typeof(this.options.tokens) == 'string')
      this.options.tokens = new Array(this.options.tokens);
    // Force carriage returns as token delimiters anyway
    if (!this.options.tokens.include('\n'))
      this.options.tokens.push('\n');

    this.observer = null;

    this.element.setAttribute('autocomplete','off');

    Element.hide(this.update);

    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
  },

  show: function() {
    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
    if(!this.iefix &&
      (Prototype.Browser.IE) &&
      (Element.getStyle(this.update, 'position')=='absolute')) {
      new Insertion.After(this.update,
       '<iframe id="' + this.update.id + '_iefix" '+
       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
      this.iefix = $(this.update.id+'_iefix');
    }
    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
  },

  fixIEOverlapping: function() {
    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
    this.iefix.style.zIndex = 1;
    this.update.style.zIndex = 2;
    Element.show(this.iefix);
  },

  hide: function() {
    this.stopIndicator();
    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
    if(this.iefix) Element.hide(this.iefix);
  },

  startIndicator: function() {
    if(this.options.indicator) Element.show(this.options.indicator);
  },

  stopIndicator: function() {
    if(this.options.indicator) Element.hide(this.options.indicator);
  },

  onKeyPress: function(event) {
    if(this.active)
      switch(event.keyCode) {
       case Event.KEY_TAB:
       case Event.KEY_RETURN:
         this.selectEntry();
         Event.stop(event);
       case Event.KEY_ESC:
         this.hide();
         this.active = false;
         Event.stop(event);
         return;
       case Event.KEY_LEFT:
       case Event.KEY_RIGHT:
         return;
       case Event.KEY_UP:
         this.markPrevious();
         this.render();
         Event.stop(event);
         return;
       case Event.KEY_DOWN:
         this.markNext();
         this.render();
         Event.stop(event);
         return;
      }
     else
       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;

    this.changed = true;
    this.hasFocus = true;

    if(this.observer) clearTimeout(this.observer);
      this.observer =
        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
  },

  activate: function() {
    this.changed = false;
    this.hasFocus = true;
    this.getUpdatedChoices();
  },

  onHover: function(event) {
    var element = Event.findElement(event, 'LI');
    if(this.index != element.autocompleteIndex)
    {
        this.index = element.autocompleteIndex;
        this.render();
    }
    Event.stop(event);
  },

  onClick: function(event) {
    var element = Event.findElement(event, 'LI');
    this.index = element.autocompleteIndex;
    this.selectEntry();
    this.hide();
  },

  onBlur: function(event) {
    // needed to make click events working
    setTimeout(this.hide.bind(this), 250);
    this.hasFocus = false;
    this.active = false;
  },

  render: function() {
    if(this.entryCount > 0) {
      for (var i = 0; i < this.entryCount; i++)
        this.index==i ?
          Element.addClassName(this.getEntry(i),"selected") :
          Element.removeClassName(this.getEntry(i),"selected");
      if(this.hasFocus) {
        this.show();
        this.active = true;
      }
    } else {
      this.active = false;
      this.hide();
    }
  },

  markPrevious: function() {
    if(this.index > 0) this.index--;
      else this.index = this.entryCount-1;
    this.getEntry(this.index).scrollIntoView(true);
  },

  markNext: function() {
    if(this.index < this.entryCount-1) this.index++;
      else this.index = 0;
    this.getEntry(this.index).scrollIntoView(false);
  },

  getEntry: function(index) {
    return this.update.firstChild.childNodes[index];
  },

  getCurrentEntry: function() {
    return this.getEntry(this.index);
  },

  selectEntry: function() {
    this.active = false;
    this.updateElement(this.getCurrentEntry());
  },

  updateElement: function(selectedElement) {
    if (this.options.updateElement) {
      this.options.updateElement(selectedElement);
      return;
    }
    var value = '';
    if (this.options.select) {
      var nodes = $(selectedElement).select('.' + this.options.select) || [];
      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    } else
      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');

    var bounds = this.getTokenBounds();
    if (bounds[0] != -1) {
      var newValue = this.element.value.substr(0, bounds[0]);
      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
      if (whitespace)
        newValue += whitespace[0];
      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    } else {
      this.element.value = value;
    }
    this.oldElementValue = this.element.value;
    this.element.focus();

    if (this.options.afterUpdateElement)
      this.options.afterUpdateElement(this.element, selectedElement);
  },

  updateChoices: function(choices) {
    if(!this.changed && this.hasFocus) {
      this.update.innerHTML = choices;
      Element.cleanWhitespace(this.update);
      Element.cleanWhitespace(this.update.down());

      if(this.update.firstChild && this.update.down().childNodes) {
        this.entryCount =
          this.update.down().childNodes.length;
        for (var i = 0; i < this.entryCount; i++) {
          var entry = this.getEntry(i);
          entry.autocompleteIndex = i;
          this.addObservers(entry);
        }
      } else {
        this.entryCount = 0;
      }

      this.stopIndicator();
      this.index = 0;

      if(this.entryCount==1 && this.options.autoSelect) {
        this.selectEntry();
        this.hide();
      } else {
        this.render();
      }
    }
  },

  addObservers: function(element) {
    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
  },

  onObserverEvent: function() {
    this.changed = false;
    this.tokenBounds = null;
    if(this.getToken().length>=this.options.minChars) {
      this.getUpdatedChoices();
    } else {
      this.active = false;
      this.hide();
    }
    this.oldElementValue = this.element.value;
  },

  getToken: function() {
    var bounds = this.getTokenBounds();
    return this.element.value.substring(bounds[0], bounds[1]).strip();
  },

  getTokenBounds: function() {
    if (null != this.tokenBounds) return this.tokenBounds;
    var value = this.element.value;
    if (value.strip().empty()) return [-1, 0];
    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
    var offset = (diff == this.oldElementValue.length ? 1 : 0);
    var prevTokenPos = -1, nextTokenPos = value.length;
    var tp;
    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
      if (tp > prevTokenPos) prevTokenPos = tp;
      tp = value.indexOf(this.options.tokens[index], diff + offset);
      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
    }
    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
  }
});

Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
  var boundary = Math.min(newS.length, oldS.length);
  for (var index = 0; index < boundary; ++index)
    if (newS[index] != oldS[index])
      return index;
  return boundary;
};

Ajax.Autocompleter = Class.create(Autocompleter.Base, {
  initialize: function(element, update, url, options) {
    this.baseInitialize(element, update, options);
    this.options.asynchronous  = true;
    this.options.onComplete    = this.onComplete.bind(this);
    this.options.defaultParams = this.options.parameters || null;
    this.url                   = url;
  },

  getUpdatedChoices: function() {
    this.startIndicator();

    var entry = encodeURIComponent(this.options.paramName) + '=' +
      encodeURIComponent(this.getToken());

    this.options.parameters = this.options.callback ?
      this.options.callback(this.element, entry) : entry;

    if(this.options.defaultParams)
      this.options.parameters += '&' + this.options.defaultParams;

    new Ajax.Request(this.url, this.options);
  },

  onComplete: function(request) {
    this.updateChoices(request.responseText);
  }
});

// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
//                    text only at the beginning of strings in the
//                    autocomplete array. Defaults to true, which will
//                    match text at the beginning of any *word* in the
//                    strings in the autocomplete array. If you want to
//                    search anywhere in the string, additionally set
//                    the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
//                   a partial match (unlike minChars, which defines
//                   how many characters are required to do any match
//                   at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
//                 Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.

Autocompleter.Local = Class.create(Autocompleter.Base, {
  initialize: function(element, update, array, options) {
    this.baseInitialize(element, update, options);
    this.options.array = array;
  },

  getUpdatedChoices: function() {
    this.updateChoices(this.options.selector(this));
  },

  setOptions: function(options) {
    this.options = Object.extend({
      choices: 10,
      partialSearch: true,
      partialChars: 2,
      ignoreCase: true,
      fullSearch: false,
      selector: function(instance) {
        var ret       = []; // Beginning matches
        var partial   = []; // Inside matches
        var entry     = instance.getToken();
        var count     = 0;

        for (var i = 0; i < instance.options.array.length &&
          ret.length < instance.options.choices ; i++) {

          var elem = instance.options.array[i];
          var foundPos = instance.options.ignoreCase ?
            elem.toLowerCase().indexOf(entry.toLowerCase()) :
            elem.indexOf(entry);

          while (foundPos != -1) {
            if (foundPos == 0 && elem.length != entry.length) {
              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
                elem.substr(entry.length) + "</li>");
              break;
            } else if (entry.length >= instance.options.partialChars &&
              instance.options.partialSearch && foundPos != -1) {
              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
                  foundPos + entry.length) + "</li>");
                break;
              }
            }

            foundPos = instance.options.ignoreCase ?
              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
              elem.indexOf(entry, foundPos + 1);

          }
        }
        if (partial.length)
          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
        return "<ul>" + ret.join('') + "</ul>";
      }
    }, options || { });
  }
});

// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).

// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
  setTimeout(function() {
    Field.activate(field);
  }, 1);
};

Ajax.InPlaceEditor = Class.create({
  initialize: function(element, url, options) {
    this.url = url;
    this.element = element = $(element);
    this.prepareOptions();
    this._controls = { };
    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
    Object.extend(this.options, options || { });
    if (!this.options.formId && this.element.id) {
      this.options.formId = this.element.id + '-inplaceeditor';
      if ($(this.options.formId))
        this.options.formId = '';
    }
    if (this.options.externalControl)
      this.options.externalControl = $(this.options.externalControl);
    if (!this.options.externalControl)
      this.options.externalControlOnly = false;
    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    this.element.title = this.options.clickToEditText;
    this._boundCancelHandler = this.handleFormCancellation.bind(this);
    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
    this._boundWrapperHandler = this.wrapUp.bind(this);
    this.registerListeners();
  },
  checkForEscapeOrReturn: function(e) {
    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
    if (Event.KEY_ESC == e.keyCode)
      this.handleFormCancellation(e);
    else if (Event.KEY_RETURN == e.keyCode)
      this.handleFormSubmission(e);
  },
  createControl: function(mode, handler, extraClasses) {
    var control = this.options[mode + 'Control'];
    var text = this.options[mode + 'Text'];
    if ('button' == control) {
      var btn = document.createElement('input');
      btn.type = 'submit';
      btn.value = text;
      btn.className = 'editor_' + mode + '_button';
      if ('cancel' == mode)
        btn.onclick = this._boundCancelHandler;
      this._form.appendChild(btn);
      this._controls[mode] = btn;
    } else if ('link' == control) {
      var link = document.createElement('a');
      link.href = '#';
      link.appendChild(document.createTextNode(text));
      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
      link.className = 'editor_' + mode + '_link';
      if (extraClasses)
        link.className += ' ' + extraClasses;
      this._form.appendChild(link);
      this._controls[mode] = link;
    }
  },
  createEditField: function() {
    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
    var fld;
    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
      fld = document.createElement('input');
      fld.type = 'text';
      var size = this.options.size || this.options.cols || 0;
      if (0 < size) fld.size = size;
    } else {
      fld = document.createElement('textarea');
      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
      fld.cols = this.options.cols || 40;
    }
    fld.name = this.options.paramName;
    fld.value = text; // No HTML breaks conversion anymore
    fld.className = 'editor_field';
    if (this.options.submitOnBlur)
      fld.onblur = this._boundSubmitHandler;
    this._controls.editor = fld;
    if (this.options.loadTextURL)
      this.loadExternalText();
    this._form.appendChild(this._controls.editor);
  },
  createForm: function() {
    var ipe = this;
    function addText(mode, condition) {
      var text = ipe.options['text' + mode + 'Controls'];
      if (!text || condition === false) return;
      ipe._form.appendChild(document.createTextNode(text));
    };
    this._form = $(document.createElement('form'));
    this._form.id = this.options.formId;
    this._form.addClassName(this.options.formClassName);
    this._form.onsubmit = this._boundSubmitHandler;
    this.createEditField();
    if ('textarea' == this._controls.editor.tagName.toLowerCase())
      this._form.appendChild(document.createElement('br'));
    if (this.options.onFormCustomization)
      this.options.onFormCustomization(this, this._form);
    addText('Before', this.options.okControl || this.options.cancelControl);
    this.createControl('ok', this._boundSubmitHandler);
    addText('Between', this.options.okControl && this.options.cancelControl);
    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
    addText('After', this.options.okControl || this.options.cancelControl);
  },
  destroy: function() {
    if (this._oldInnerHTML)
      this.element.innerHTML = this._oldInnerHTML;
    this.leaveEditMode();
    this.unregisterListeners();
  },
  enterEditMode: function(e) {
    if (this._saving || this._editing) return;
    this._editing = true;
    this.triggerCallback('onEnterEditMode');
    if (this.options.externalControl)
      this.options.externalControl.hide();
    this.element.hide();
    this.createForm();
    this.element.parentNode.insertBefore(this._form, this.element);
    if (!this.options.loadTextURL)
      this.postProcessEditField();
    if (e) Event.stop(e);
  },
  enterHover: function(e) {
    if (this.options.hoverClassName)
      this.element.addClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onEnterHover');
  },
  getText: function() {
    return this.element.innerHTML.unescapeHTML();
  },
  handleAJAXFailure: function(transport) {
    this.triggerCallback('onFailure', transport);
    if (this._oldInnerHTML) {
      this.element.innerHTML = this._oldInnerHTML;
      this._oldInnerHTML = null;
    }
  },
  handleFormCancellation: function(e) {
    this.wrapUp();
    if (e) Event.stop(e);
  },
  handleFormSubmission: function(e) {
    var form = this._form;
    var value = $F(this._controls.editor);
    this.prepareSubmission();
    var params = this.options.callback(form, value) || '';
    if (Object.isString(params))
      params = params.toQueryParams();
    params.editorId = this.element.id;
    if (this.options.htmlResponse) {
      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Updater({ success: this.element }, this.url, options);
    } else {
      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
      Object.extend(options, {
        parameters: params,
        onComplete: this._boundWrapperHandler,
        onFailure: this._boundFailureHandler
      });
      new Ajax.Request(this.url, options);
    }
    if (e) Event.stop(e);
  },
  leaveEditMode: function() {
    this.element.removeClassName(this.options.savingClassName);
    this.removeForm();
    this.leaveHover();
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
    if (this.options.externalControl)
      this.options.externalControl.show();
    this._saving = false;
    this._editing = false;
    this._oldInnerHTML = null;
    this.triggerCallback('onLeaveEditMode');
  },
  leaveHover: function(e) {
    if (this.options.hoverClassName)
      this.element.removeClassName(this.options.hoverClassName);
    if (this._saving) return;
    this.triggerCallback('onLeaveHover');
  },
  loadExternalText: function() {
    this._form.addClassName(this.options.loadingClassName);
    this._controls.editor.disabled = true;
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._form.removeClassName(this.options.loadingClassName);
        var text = transport.responseText;
        if (this.options.stripLoadedTextTags)
          text = text.stripTags();
        this._controls.editor.value = text;
        this._controls.editor.disabled = false;
        this.postProcessEditField();
      }.bind(this),
      onFailure: this._boundFailureHandler
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },
  postProcessEditField: function() {
    var fpc = this.options.fieldPostCreation;
    if (fpc)
      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
  },
  prepareOptions: function() {
    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
      Object.extend(this.options, defs);
    }.bind(this));
  },
  prepareSubmission: function() {
    this._saving = true;
    this.removeForm();
    this.leaveHover();
    this.showSaving();
  },
  registerListeners: function() {
    this._listeners = { };
    var listener;
    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
      listener = this[pair.value].bind(this);
      this._listeners[pair.key] = listener;
      if (!this.options.externalControlOnly)
        this.element.observe(pair.key, listener);
      if (this.options.externalControl)
        this.options.externalControl.observe(pair.key, listener);
    }.bind(this));
  },
  removeForm: function() {
    if (!this._form) return;
    this._form.remove();
    this._form = null;
    this._controls = { };
  },
  showSaving: function() {
    this._oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    this.element.addClassName(this.options.savingClassName);
    this.element.style.backgroundColor = this._originalBackground;
    this.element.show();
  },
  triggerCallback: function(cbName, arg) {
    if ('function' == typeof this.options[cbName]) {
      this.options[cbName](this, arg);
    }
  },
  unregisterListeners: function() {
    $H(this._listeners).each(function(pair) {
      if (!this.options.externalControlOnly)
        this.element.stopObserving(pair.key, pair.value);
      if (this.options.externalControl)
        this.options.externalControl.stopObserving(pair.key, pair.value);
    }.bind(this));
  },
  wrapUp: function(transport) {
    this.leaveEditMode();
    // Can't use triggerCallback due to backward compatibility: requires
    // binding + direct element
    this._boundComplete(transport, this.element);
  }
});

Object.extend(Ajax.InPlaceEditor.prototype, {
  dispose: Ajax.InPlaceEditor.prototype.destroy
});

Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
  initialize: function($super, element, url, options) {
    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
    $super(element, url, options);
  },

  createEditField: function() {
    var list = document.createElement('select');
    list.name = this.options.paramName;
    list.size = 1;
    this._controls.editor = list;
    this._collection = this.options.collection || [];
    if (this.options.loadCollectionURL)
      this.loadCollection();
    else
      this.checkForExternalText();
    this._form.appendChild(this._controls.editor);
  },

  loadCollection: function() {
    this._form.addClassName(this.options.loadingClassName);
    this.showLoadingText(this.options.loadingCollectionText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        var js = transport.responseText.strip();
        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
          throw('Server returned an invalid collection representation.');
        this._collection = eval(js);
        this.checkForExternalText();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadCollectionURL, options);
  },

  showLoadingText: function(text) {
    this._controls.editor.disabled = true;
    var tempOption = this._controls.editor.firstChild;
    if (!tempOption) {
      tempOption = document.createElement('option');
      tempOption.value = '';
      this._controls.editor.appendChild(tempOption);
      tempOption.selected = true;
    }
    tempOption.update((text || '').stripScripts().stripTags());
  },

  checkForExternalText: function() {
    this._text = this.getText();
    if (this.options.loadTextURL)
      this.loadExternalText();
    else
      this.buildOptionList();
  },

  loadExternalText: function() {
    this.showLoadingText(this.options.loadingText);
    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
    Object.extend(options, {
      parameters: 'editorId=' + encodeURIComponent(this.element.id),
      onComplete: Prototype.emptyFunction,
      onSuccess: function(transport) {
        this._text = transport.responseText.strip();
        this.buildOptionList();
      }.bind(this),
      onFailure: this.onFailure
    });
    new Ajax.Request(this.options.loadTextURL, options);
  },

  buildOptionList: function() {
    this._form.removeClassName(this.options.loadingClassName);
    this._collection = this._collection.map(function(entry) {
      return 2 === entry.length ? entry : [entry, entry].flatten();
    });
    var marker = ('value' in this.options) ? this.options.value : this._text;
    var textFound = this._collection.any(function(entry) {
      return entry[0] == marker;
    }.bind(this));
    this._controls.editor.update('');
    var option;
    this._collection.each(function(entry, index) {
      option = document.createElement('option');
      option.value = entry[0];
      option.selected = textFound ? entry[0] == marker : 0 == index;
      option.appendChild(document.createTextNode(entry[1]));
      this._controls.editor.appendChild(option);
    }.bind(this));
    this._controls.editor.disabled = false;
    Field.scrollFreeActivate(this._controls.editor);
  }
});

//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only  exists for a while,  in order to  let ****
//**** users adapt to  the new API.  Read up on the new ****
//**** API and convert your code to it ASAP!            ****

Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
  if (!options) return;
  function fallback(name, expr) {
    if (name in options || expr === undefined) return;
    options[name] = expr;
  };
  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
    options.cancelLink == options.cancelButton == false ? false : undefined)));
  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
    options.okLink == options.okButton == false ? false : undefined)));
  fallback('highlightColor', options.highlightcolor);
  fallback('highlightEndColor', options.highlightendcolor);
};

Object.extend(Ajax.InPlaceEditor, {
  DefaultOptions: {
    ajaxOptions: { },
    autoRows: 3,                                // Use when multi-line w/ rows == 1
    cancelControl: 'link',                      // 'link'|'button'|false
    cancelText: 'cancel',
    clickToEditText: 'Click to edit',
    externalControl: null,                      // id|elt
    externalControlOnly: false,
    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
    formClassName: 'inplaceeditor-form',
    formId: null,                               // id|elt
    highlightColor: '#ffff99',
    highlightEndColor: '#ffffff',
    hoverClassName: '',
    htmlResponse: true,
    loadingClassName: 'inplaceeditor-loading',
    loadingText: 'Loading...',
    okControl: 'button',                        // 'link'|'button'|false
    okText: 'ok',
    paramName: 'value',
    rows: 1,                                    // If 1 and multi-line, uses autoRows
    savingClassName: 'inplaceeditor-saving',
    savingText: 'Saving...',
    size: 0,
    stripLoadedTextTags: false,
    submitOnBlur: false,
    textAfterControls: '',
    textBeforeControls: '',
    textBetweenControls: ''
  },
  DefaultCallbacks: {
    callback: function(form) {
      return Form.serialize(form);
    },
    onComplete: function(transport, element) {
      // For backward compatibility, this one is bound to the IPE, and passes
      // the element directly.  It was too often customized, so we don't break it.
      new Effect.Highlight(element, {
        startcolor: this.options.highlightColor, keepBackgroundImage: true });
    },
    onEnterEditMode: null,
    onEnterHover: function(ipe) {
      ipe.element.style.backgroundColor = ipe.options.highlightColor;
      if (ipe._effect)
        ipe._effect.cancel();
    },
    onFailure: function(transport, ipe) {
      alert('Error communication with the server: ' + transport.responseText.stripTags());
    },
    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
    onLeaveEditMode: null,
    onLeaveHover: function(ipe) {
      ipe._effect = new Effect.Highlight(ipe.element, {
        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
        restorecolor: ipe._originalBackground, keepBackgroundImage: true
      });
    }
  },
  Listeners: {
    click: 'enterEditMode',
    keydown: 'checkForEscapeOrReturn',
    mouseover: 'enterHover',
    mouseout: 'leaveHover'
  }
});

Ajax.InPlaceCollectionEditor.DefaultOptions = {
  loadingCollectionText: 'Loading options...'
};

// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields

Form.Element.DelayedObserver = Class.create({
  initialize: function(element, delay, callback) {
    this.delay     = delay || 0.5;
    this.element   = $(element);
    this.callback  = callback;
    this.timer     = null;
    this.lastValue = $F(this.element);
    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
  },
  delayedListener: function(event) {
    if(this.lastValue == $F(this.element)) return;
    if(this.timer) clearTimeout(this.timer);
    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
    this.lastValue = $F(this.element);
  },
  onTimerEvent: function() {
    this.timer = null;
    this.callback(this.element, $F(this.element));
  }
});/* rsh.js */

/*
Copyright (c) 2007 Brian Dillard and Brad Neuberg:
Brian Dillard | Project Lead | bdillard@pathf.com | http://blogs.pathf.com/agileajax/
Brad Neuberg | Original Project Creator | http://codinginparadise.org
   
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/*
	dhtmlHistory: An object that provides history, history data, and bookmarking for DHTML and Ajax applications.
	
	dependencies:
		* the historyStorage object included in this file.

*/
window.dhtmlHistory = {
	
	/*Public: User-agent booleans*/
	isIE: false,
	isOpera: false,
	isSafari: false,
	isKonquerer: false,
	isGecko: false,
	isSupported: false,
	
	/*Public: Create the DHTML history infrastructure*/
	create: function(options) {
		
		/*
			options - object to store initialization parameters
			options.blankURL - string to override the default location of blank.html. Must end in "?"
			options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
			options.toJSON - function to override default JSON stringifier
			options.fromJSON - function to override default JSON parser
		*/

		var that = this;
		
		/*Set up the historyStorage object; pass in options bundle*/
		window.historyStorage.setup(options);
		
		/*set user-agent flags*/
		var UA = navigator.userAgent.toLowerCase();
		var platform = navigator.platform.toLowerCase();
		var vendor = navigator.vendor || "";
		if (vendor === "KDE") {
			this.isKonqueror = true;
			this.isSupported = false;
		} else if (typeof window.opera !== "undefined") {
			this.isOpera = true;
			this.isSupported = true;
		} else if (typeof document.all !== "undefined") {
			this.isIE = true;
			this.isSupported = true;
		} else if (vendor.indexOf("Apple Computer, Inc.") > -1) {
			this.isSafari = true;
			this.isSupported = (platform.indexOf("mac") > -1);
		} else if (UA.indexOf("gecko") != -1) {
			this.isGecko = true;
			this.isSupported = true;
		}

		/*Create Safari/Opera-specific code*/
		if (this.isSafari) {
			this.createSafari();
		} else if (this.isOpera) {
			this.createOpera();
		}
		
		/*Get our initial location*/
		var initialHash = this.getCurrentLocation();

		/*Save it as our current location*/
		this.currentLocation = initialHash;

		/*Now that we have a hash, create IE-specific code*/
		if (this.isIE) {
			/*Optionally override the URL of IE's blank HTML file*/
			if (typeof options !== "undefined" && options.blankURL) {
				if (options.blankURL.indexOf("?") != options.blankURL.length - 1 && historyStorage.debugMode) {
					throw new Error("Programmer error: options.blankURL must end with '?'");
				}
				this.blankURL = options.blankURL;
			}
			this.createIE(initialHash);
		}

		/*Add an unload listener for the page; this is needed for FF 1.5+ because this browser caches all dynamic updates to the
		page, which can break some of our logic related to testing whether this is the first instance a page has loaded or whether
		it is being pulled from the cache*/

		var unloadHandler = function() {
			that.firstLoad = null;
		};
		
		this.addEventListener(window,'unload',unloadHandler);		

		/*Determine if this is our first page load; for IE, we do this in this.iframeLoaded(), which is fired on pageload. We do it
		there because we have no historyStorage at this point, which only exists after the page is finished loading in IE*/
		if (this.isIE) {
			/*The iframe will get loaded on page load, and we want to ignore this fact*/
			this.ignoreLocationChange = true;
		} else {
			if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
				/*This is our first page load, so ignore the location change and add our special history entry*/
				this.ignoreLocationChange = true;
				this.firstLoad = true;
				historyStorage.put(this.PAGELOADEDSTRING, true);
			} else {
				/*This isn't our first page load, so indicate that we want to pay attention to this location change*/
				this.ignoreLocationChange = false;
				/*For browsers other than IE, fire a history change event; on IE, the event will be thrown automatically when its
				hidden iframe reloads on page load. Unfortunately, we don't have any listeners yet; indicate that we want to fire
				an event when a listener is added.*/
				this.fireOnNewListener = true;
			}
		}

		/*Other browsers can use a location handler that checks at regular intervals as their primary mechanism; we use it for IE as
		well to handle an important edge case; see checkLocation() for details*/
		var locationHandler = function() {
			that.checkLocation();
		};
		setInterval(locationHandler, 100);
	},	
	
	/*Public: Initialize our DHTML history. You must call this after the page is finished loading. Optionally, you can pass your listener in
	here so you don't need to make a separate call to addListener*/
	initialize: function(listener) {
		/*IE needs to be explicitly initialized. IE doesn't autofill form data until the page is finished loading, so we have to wait*/
		if (this.isIE) {
			/*If this is the first time this page has loaded*/
			if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
				/*For IE, we do this in initialize(); for other browsers, we do it in create()*/
				this.fireOnNewListener = false;
				this.firstLoad = true;
				historyStorage.put(this.PAGELOADEDSTRING, true);
			}
			/*Else if this is a fake onload event*/
			else {
				this.fireOnNewListener = true;
				this.firstLoad = false;   
			}
		}
		/*optional convenience to save a separate call to addListener*/
		if (listener) {
			this.addListener(listener);
		}
	},

	/*Public: Adds a history change listener. Note that only one listener is supported at this time.*/
	addListener: function(listener) {
		this.listener = listener;
		/*If the page was just loaded and we should not ignore it, fire an event to our new listener now*/
		if (this.fireOnNewListener) {
			this.fireHistoryEvent(this.currentLocation);
			this.fireOnNewListener = false;
		}
	},
	
	/*Public: Generic utility function for attaching events*/
	addEventListener: function(o,e,l) {
		if (o.addEventListener) {
			o.addEventListener(e,l,false);
		} else if (o.attachEvent) {
			o.attachEvent('on'+e,function() {
				l(window.event);
			});
		}
	},
	
	/*Public: Add a history point.*/
	add: function(newLocation, historyData) {
		
		if (this.isSafari) {
			
			/*Remove any leading hash symbols on newLocation*/
			newLocation = this.removeHash(newLocation);

			/*Store the history data into history storage*/
			historyStorage.put(newLocation, historyData);

			/*Save this as our current location*/
			this.currentLocation = newLocation;
	
			/*Change the browser location*/
			window.location.hash = newLocation;
		
			/*Save this to the Safari form field*/
			this.putSafariState(newLocation);

		} else {
			
			/*Most browsers require that we wait a certain amount of time before changing the location, such
			as 200 MS; rather than forcing external callers to use window.setTimeout to account for this,
			we internally handle it by putting requests in a queue.*/
			var that = this;
			var addImpl = function() {

				/*Indicate that the current wait time is now less*/
				if (that.currentWaitTime > 0) {
					that.currentWaitTime = that.currentWaitTime - that.waitTime;
				}
			
				/*Remove any leading hash symbols on newLocation*/
				newLocation = that.removeHash(newLocation);

				/*IE has a strange bug; if the newLocation is the same as _any_ preexisting id in the
				document, then the history action gets recorded twice; throw a programmer exception if
				there is an element with this ID*/
				if (document.getElementById(newLocation) && that.debugMode) {
					var e = "Exception: History locations can not have the same value as _any_ IDs that might be in the document,"
					+ " due to a bug in IE; please ask the developer to choose a history location that does not match any HTML"
					+ " IDs in this document. The following ID is already taken and cannot be a location: " + newLocation;
					throw new Error(e); 
				}

				/*Store the history data into history storage*/
				historyStorage.put(newLocation, historyData);

				/*Indicate to the browser to ignore this upcomming location change since we're making it programmatically*/
				that.ignoreLocationChange = true;

				/*Indicate to IE that this is an atomic location change block*/
				that.ieAtomicLocationChange = true;

				/*Save this as our current location*/
				that.currentLocation = newLocation;
		
				/*Change the browser location*/
				window.location.hash = newLocation;

				/*Change the hidden iframe's location if on IE*/
				if (that.isIE) {
					that.iframe.src = that.blankURL + newLocation;
				}

				/*End of atomic location change block for IE*/
				that.ieAtomicLocationChange = false;
			};

			/*Now queue up this add request*/
			window.setTimeout(addImpl, this.currentWaitTime);

			/*Indicate that the next request will have to wait for awhile*/
			this.currentWaitTime = this.currentWaitTime + this.waitTime;
		}
	},

	/*Public*/
	isFirstLoad: function() {
		return this.firstLoad;
	},

	/*Public*/
	getVersion: function() {
		return this.VERSIONNUMBER;
	},

	/*Get browser's current hash location; for Safari, read value from a hidden form field*/

	/*Public*/
	getCurrentLocation: function() {
		var r = (this.isSafari
			? this.getSafariState()
			: this.getCurrentHash()
		);
		return r;
	},
	
	/*Public: Manually parse the current url for a hash; tip of the hat to YUI*/
    getCurrentHash: function() {
		var r = window.location.href;
		var i = r.indexOf("#");
		return (i >= 0
			? r.substr(i+1)
			: ""
		);
    },
	
	/*- - - - - - - - - - - -*/
	
	/*Private: Constant for our own internal history event called when the page is loaded*/
	PAGELOADEDSTRING: "DhtmlHistory_pageLoaded",
	
	VERSIONNUMBER: "0.6",
	
	/*Private: URL for the blank html file we use for IE; can be overridden via the options bundle. Otherwise it must be served
	in same directory as this library*/
	blankURL: "blank.html?",
	
	/*Private: Our history change listener.*/
	listener: null,

	/*Private: MS to wait between add requests - will be reset for certain browsers*/
	waitTime: 200,
	
	/*Private: MS before an add request can execute*/
	currentWaitTime: 0,

	/*Private: Our current hash location, without the "#" symbol.*/
	currentLocation: null,

	/*Private: Hidden iframe used to IE to detect history changes*/
	iframe: null,

	/*Private: Flags and DOM references used only by Safari*/
	safariHistoryStartPoint: null,
	safariStack: null,
	safariLength: null,

	/*Private: Flag used to keep checkLocation() from doing anything when it discovers location changes we've made ourselves
	programmatically with the add() method. Basically, add() sets this to true. When checkLocation() discovers it's true,
	it refrains from firing our listener, then resets the flag to false for next cycle. That way, our listener only gets fired on
	history change events triggered by the user via back/forward buttons and manual hash changes. This flag also helps us set up
	IE's special iframe-based method of handling history changes.*/
	ignoreLocationChange: null,

	/*Private: A flag that indicates that we should fire a history change event when we are ready, i.e. after we are initialized and
	we have a history change listener. This is needed due to an edge case in browsers other than IE; if you leave a page entirely
	then return, we must fire this as a history change event. Unfortunately, we have lost all references to listeners from earlier,
	because JavaScript clears out.*/
	fireOnNewListener: null,

	/*Private: A variable that indicates whether this is the first time this page has been loaded. If you go to a web page, leave it
	for another one, and then return, the page's onload listener fires again. We need a way to differentiate between the first page
	load and subsequent ones. This variable works hand in hand with the pageLoaded variable we store into historyStorage.*/
	firstLoad: null,

	/*Private: A variable to handle an important edge case in IE. In IE, if a user manually types an address into their browser's
	location bar, we must intercept this by calling checkLocation() at regular intervals. However, if we are programmatically
	changing the location bar ourselves using the add() method, we need to ignore these changes in checkLocation(). Unfortunately,
	these changes take several lines of code to complete, so for the duration of those lines of code, we set this variable to true.
	That signals to checkLocation() to ignore the change-in-progress. Once we're done with our chunk of location-change code in
	add(), we set this back to false. We'll do the same thing when capturing user-entered address changes in checkLocation itself.*/
	ieAtomicLocationChange: null,
	
	/*Private: Create IE-specific DOM nodes and overrides*/
	createIE: function(initialHash) {
		/*write out a hidden iframe for IE and set the amount of time to wait between add() requests*/
		this.waitTime = 400;/*IE needs longer between history updates*/
		var styles = (historyStorage.debugMode
			? 'width: 800px;height:80px;border:1px solid black;'
			: historyStorage.hideStyles
		);
		var iframeID = "rshHistoryFrame";
		var iframeHTML = '<iframe frameborder="0" id="' + iframeID + '" style="' + styles + '" src="' + this.blankURL + initialHash + '"></iframe>';
		document.write(iframeHTML);
		this.iframe = document.getElementById(iframeID);
	},
	
	/*Private: Create Opera-specific DOM nodes and overrides*/
	createOpera: function() {
		this.waitTime = 400;/*Opera needs longer between history updates*/
		var imgHTML = '<img src="javascript:location.href=\'javascript:dhtmlHistory.checkLocation();\';" style="' + historyStorage.hideStyles + '" />';
		document.write(imgHTML);
	},
	
	/*Private: Create Safari-specific DOM nodes and overrides*/
	createSafari: function() {
		var formID = "rshSafariForm";
		var stackID = "rshSafariStack";
		var lengthID = "rshSafariLength";
		var formStyles = historyStorage.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
		var inputStyles = (historyStorage.debugMode
			? 'width:800px;height:20px;border:1px solid black;margin:0;padding:0;'
			: historyStorage.hideStyles
		);
		var safariHTML = '<form id="' + formID + '" style="' + formStyles + '">'
			+ '<input type="text" style="' + inputStyles + '" id="' + stackID + '" value="[]"/>'
			+ '<input type="text" style="' + inputStyles + '" id="' + lengthID + '" value=""/>'
		+ '</form>';
		document.write(safariHTML);
		this.safariStack = document.getElementById(stackID);
		this.safariLength = document.getElementById(lengthID);
		if (!historyStorage.hasKey(this.PAGELOADEDSTRING)) {
			this.safariHistoryStartPoint = history.length;
			this.safariLength.value = this.safariHistoryStartPoint;
		} else {
			this.safariHistoryStartPoint = this.safariLength.value;
		}
	},
	
	/*Private: Safari method to read the history stack from a hidden form field*/
	getSafariStack: function() {
		var r = this.safariStack.value;
		return historyStorage.fromJSON(r);
	},

	/*Private: Safari method to read from the history stack*/
	getSafariState: function() {
		var stack = this.getSafariStack();
		var state = stack[history.length - this.safariHistoryStartPoint - 1];
		return state;
	},			
	/*Private: Safari method to write the history stack to a hidden form field*/
	putSafariState: function(newLocation) {
	    var stack = this.getSafariStack();
	    stack[history.length - this.safariHistoryStartPoint] = newLocation;
	    this.safariStack.value = historyStorage.toJSON(stack);
	},

	/*Private: Notify the listener of new history changes.*/
	fireHistoryEvent: function(newHash) {
		/*extract the value from our history storage for this hash*/
		var historyData = historyStorage.get(newHash);
		/*call our listener*/
		this.listener.call(null, newHash, historyData);
	},
	
	/*Private: See if the browser has changed location. This is the primary history mechanism for Firefox. For IE, we use this to
	handle an important edge case: if a user manually types in a new hash value into their IE location bar and press enter, we want to
	to intercept this and notify any history listener.*/
	checkLocation: function() {
		
		/*Ignore any location changes that we made ourselves for browsers other than IE*/
		if (!this.isIE && this.ignoreLocationChange) {
			this.ignoreLocationChange = false;
			return;
		}

		/*If we are dealing with IE and we are in the middle of making a location change from an iframe, ignore it*/
		if (!this.isIE && this.ieAtomicLocationChange) {
			return;
		}
		
		/*Get hash location*/
		var hash = this.getCurrentLocation();

		/*Do nothing if there's been no change*/
		if (hash == this.currentLocation) {
			return;
		}

		/*In IE, users manually entering locations into the browser; we do this by comparing the browser's location against the
		iframe's location; if they differ, we are dealing with a manual event and need to place it inside our history, otherwise
		we can return*/
		this.ieAtomicLocationChange = true;

		if (this.isIE && this.getIframeHash() != hash) {
			this.iframe.src = this.blankURL + hash;
		}
		else if (this.isIE) {
			/*the iframe is unchanged*/
			return;
		}

		/*Save this new location*/
		this.currentLocation = hash;

		this.ieAtomicLocationChange = false;

		/*Notify listeners of the change*/
		this.fireHistoryEvent(hash);
	},

	/*Private: Get the current location of IE's hidden iframe.*/
	getIframeHash: function() {
		var doc = this.iframe.contentWindow.document;
		var hash = String(doc.location.search);
		if (hash.length == 1 && hash.charAt(0) == "?") {
			hash = "";
		}
		else if (hash.length >= 2 && hash.charAt(0) == "?") {
			hash = hash.substring(1);
		}
		return hash;
	},

	/*Private: Remove any leading hash that might be on a location.*/
	removeHash: function(hashValue) {
		var r;
		if (hashValue === null || hashValue === undefined) {
			r = null;
		}
		else if (hashValue === "") {
			r = "";
		}
		else if (hashValue.length == 1 && hashValue.charAt(0) == "#") {
			r = "";
		}
		else if (hashValue.length > 1 && hashValue.charAt(0) == "#") {
			r = hashValue.substring(1);
		}
		else {
			r = hashValue;
		}
		return r;
	},

	/*Private: For IE, tell when the hidden iframe has finished loading.*/
	iframeLoaded: function(newLocation) {
		/*ignore any location changes that we made ourselves*/
		if (this.ignoreLocationChange) {
			this.ignoreLocationChange = false;
			return;
		}

		/*Get the new location*/
		var hash = String(newLocation.search);
		if (hash.length == 1 && hash.charAt(0) == "?") {
			hash = "";
		}
		else if (hash.length >= 2 && hash.charAt(0) == "?") {
			hash = hash.substring(1);
		}
		/*Keep the browser location bar in sync with the iframe hash*/
		window.location.hash = hash;

		/*Notify listeners of the change*/
		this.fireHistoryEvent(hash);
	}

};

/*
	historyStorage: An object that uses a hidden form to store history state across page loads. The mechanism for doing so relies on
	the fact that browsers save the text in form data for the life of the browser session, which means the text is still there when
	the user navigates back to the page. This object can be used independently of the dhtmlHistory object for caching of Ajax
	session information.
	
	dependencies: 
		* json2007.js (included in a separate file) or alternate JSON methods passed in through an options bundle.
*/
window.historyStorage = {
	
	/*Public: Set up our historyStorage object for use by dhtmlHistory or other objects*/
	setup: function(options) {
		
		/*
			options - object to store initialization parameters - passed in from dhtmlHistory or directly into historyStorage
			options.debugMode - boolean that causes hidden form fields to be shown for development purposes.
			options.toJSON - function to override default JSON stringifier
			options.fromJSON - function to override default JSON parser
		*/
		
		/*process init parameters*/
		if (typeof options !== "undefined") {
			if (options.debugMode) {
				this.debugMode = options.debugMode;
			}
			if (options.toJSON) {
				this.toJSON = options.toJSON;
			}
			if (options.fromJSON) {
				this.fromJSON = options.fromJSON;
			}
		}		
		
		/*write a hidden form and textarea into the page; we'll stow our history stack here*/
		var formID = "rshStorageForm";
		var textareaID = "rshStorageField";
		var formStyles = this.debugMode ? historyStorage.showStyles : historyStorage.hideStyles;
		var textareaStyles = (historyStorage.debugMode
			? 'width: 800px;height:80px;border:1px solid black;'
			: historyStorage.hideStyles
		);
		var textareaHTML = '<form id="' + formID + '" style="' + formStyles + '">'
			+ '<textarea id="' + textareaID + '" style="' + textareaStyles + '"></textarea>'
		+ '</form>';
		document.write(textareaHTML);
		this.storageField = document.getElementById(textareaID);
		if (typeof window.opera !== "undefined") {
			this.storageField.focus();/*Opera needs to focus this element before persisting values in it*/
		}
	},
	
	/*Public*/
	put: function(key, value) {
		this.assertValidKey(key);
		/*if we already have a value for this, remove the value before adding the new one*/
		if (this.hasKey(key)) {
			this.remove(key);
		}
		/*store this new key*/
		this.storageHash[key] = value;
		/*save and serialize the hashtable into the form*/
		this.saveHashTable();
	},

	/*Public*/
	get: function(key) {
		this.assertValidKey(key);
		/*make sure the hash table has been loaded from the form*/
		this.loadHashTable();
		var value = this.storageHash[key];
		if (value === undefined) {
			value = null;
		}
		return value;
	},

	/*Public*/
	remove: function(key) {
		this.assertValidKey(key);
		/*make sure the hash table has been loaded from the form*/
		this.loadHashTable();
		/*delete the value*/
		delete this.storageHash[key];
		/*serialize and save the hash table into the form*/
		this.saveHashTable();
	},

	/*Public: Clears out all saved data.*/
	reset: function() {
		this.storageField.value = "";
		this.storageHash = {};
	},

	/*Public*/
	hasKey: function(key) {
		this.assertValidKey(key);
		/*make sure the hash table has been loaded from the form*/
		this.loadHashTable();
		return (typeof this.storageHash[key] !== "undefined");
	},

	/*Public*/
	isValidKey: function(key) {
		return (typeof key === "string");
	},
	
	/*Public - CSS strings utilized by both objects to hide or show behind-the-scenes DOM elements*/
	showStyles: 'border:0;margin:0;padding:0;',
	hideStyles: 'left:-1000px;top:-1000px;width:1px;height:1px;border:0;position:absolute;',
	
	/*Public - debug mode flag*/
	debugMode: false,
	
	/*- - - - - - - - - - - -*/

	/*Private: Our hash of key name/values.*/
	storageHash: {},

	/*Private: If true, we have loaded our hash table out of the storage form.*/
	hashLoaded: false, 

	/*Private: DOM reference to our history field*/
	storageField: null,

	/*Private: Assert that a key is valid; throw an exception if it not.*/
	assertValidKey: function(key) {
		var isValid = this.isValidKey(key);
		if (!isValid && this.debugMode) {
			throw new Error("Please provide a valid key for window.historyStorage. Invalid key = " + key + ".");
		}
	},

	/*Private: Load the hash table up from the form.*/
	loadHashTable: function() {
		if (!this.hashLoaded) {	
			var serializedHashTable = this.storageField.value;
			if (serializedHashTable !== "" && serializedHashTable !== null) {
				this.storageHash = this.fromJSON(serializedHashTable);
				this.hashLoaded = true;
			}
		}
	},
	/*Private: Save the hash table into the form.*/
	saveHashTable: function() {
		this.loadHashTable();
		var serializedHashTable = this.toJSON(this.storageHash);
		this.storageField.value = serializedHashTable;
	},
	/*Private: Bridges for our JSON implementations - both rely on 2007 JSON.org library - can be overridden by options bundle*/
	toJSON: function(o) {
		return o.toJSONString();
	},
	fromJSON: function(s) {
		return s.parseJSON();
	}
};
/* base64.js */

/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info/
*
**/

var Base64 = {

	// private property
	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

	// public method for encoding
	encode : function (input) {
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;

		input = Base64._utf8_encode(input);

		while (i < input.length) {

			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);

			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;

			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}

			output = output +
			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

		}

		return output;
	},

	// public method for decoding
	decode : function (input) {
		var output = "";
		var chr1, chr2, chr3;
		var enc1, enc2, enc3, enc4;
		var i = 0;

		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

		while (i < input.length) {

			enc1 = this._keyStr.indexOf(input.charAt(i++));
			enc2 = this._keyStr.indexOf(input.charAt(i++));
			enc3 = this._keyStr.indexOf(input.charAt(i++));
			enc4 = this._keyStr.indexOf(input.charAt(i++));

			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;

			output = output + String.fromCharCode(chr1);

			if (enc3 != 64) {
				output = output + String.fromCharCode(chr2);
			}
			if (enc4 != 64) {
				output = output + String.fromCharCode(chr3);
			}

		}

		output = Base64._utf8_decode(output);

		return output;

	},

	// private method for UTF-8 encoding
	_utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	},

	// private method for UTF-8 decoding
	_utf8_decode : function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;

		while ( i < utftext.length ) {

			c = utftext.charCodeAt(i);

			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			}
			else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			}
			else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}

		}

		return string;
	}

}
/* bbcode.js */

var sndBB = Class.create();

sndBB.prototype =
{
	initialize: function (s)
	{
		if (s.length > 3)
		{
			this.s	= s;
		}
		else
		{
			return (false);
		}
	},

	rep: function (re, str) 
	{
		this.s = this.s.replace(re, str);
	},
	
	html2bbcode: function()
	{
		if (!this.s)
		{
			return (null);
		}
		
		this.rep(/<a href=\"(.*?)\".*?>(.*?)<\/a>/gi,"[url]$1[/url]");
		this.rep(/<font.*?color=\"(.*?)\".*?class=\"codeStyle\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]");
		this.rep(/<font.*?color=\"(.*?)\".*?class=\"quoteStyle\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]");
		this.rep(/<font.*?class=\"codeStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[code][color=$1]$2[/color][/code]");
		this.rep(/<font.*?class=\"quoteStyle\".*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[quote][color=$1]$2[/color][/quote]");
		this.rep(/<font.*?color=\"(.*?)\".*?>(.*?)<\/font>/gi,"[color=$1]$2[/color]");
		this.rep(/<font>(.*?)<\/font>/gi,"$1");
		this.rep(/<img.*?src=\"(.*?)\".*?\/>/gi,"[img]$1[/img]");
		this.rep(/<span class=\"codeStyle\">(.*?)<\/span>/gi,"[code]$1[/code]");
		this.rep(/<span class=\"quoteStyle\">(.*?)<\/span>/gi,"[quote]$1[/quote]");
		this.rep(/<strong class=\"codeStyle\">(.*?)<\/strong>/gi,"[code][b]$1[/b][/code]");
		this.rep(/<strong class=\"quoteStyle\">(.*?)<\/strong>/gi,"[quote][b]$1[/b][/quote]");
		this.rep(/<em class=\"codeStyle\">(.*?)<\/em>/gi,"[code][i]$1[/i][/code]");
		this.rep(/<em class=\"quoteStyle\">(.*?)<\/em>/gi,"[quote][i]$1[/i][/quote]");
		this.rep(/<u class=\"codeStyle\">(.*?)<\/u>/gi,"[code][u]$1[/u][/code]");
		this.rep(/<u class=\"quoteStyle\">(.*?)<\/u>/gi,"[quote][u]$1[/u][/quote]");
		this.rep(/<\/(strong|b)>/gi,"[/b]");
		this.rep(/<(strong|b)>/gi,"[b]");
		this.rep(/<\/(em|i)>/gi,"[/i]");
		this.rep(/<(em|i)>/gi,"[i]");
		this.rep(/<\/u>/gi,"[/u]");
		this.rep(/<u>/gi,"[u]");
		this.rep(/<br \/>/gi,"\n");
		this.rep(/<br\/>/gi,"\n");
		this.rep(/<br>/gi,"\n");
		this.rep(/<p>/gi,"");
		this.rep(/<\/p>/gi,"\n");
		this.rep(/&nbsp;/gi," ");
		this.rep(/&quot;/gi,"\"");
		this.rep(/&lt;/gi,"<");
		this.rep(/&gt;/gi,">");
		this.rep(/&amp;/gi,"&");
		this.rep(/&undefined;/gi,"'");

		return (this.s); 
	},

	bbcode2html: function()
	{
		if (!this.s)
		{
			return (null);
		}
		/* NO HTML TAGS ALLOWED */
		this.s = this.s.stripTags();

		this.rep(/\n/gi,"<br />");
		this.rep(/\[b\]/gi,"<strong>");
		this.rep(/\[\/b\]/gi,"</strong>");
		this.rep(/\[i\]/gi,"<em>");
		this.rep(/\[\/i\]/gi,"</em>");
		this.rep(/\[u\]/gi,"<u>");
		this.rep(/\[\/u\]/gi,"</u>");
		this.rep(/\[url\](.*?)\[\/url\]/gi,"<a href=\"$1\">$1</a>");
		this.rep(/\[url=(.*?)\](.*?)\[\/url\]/gi,"<a href=\"$1\">$2</a>");
		this.rep(/\[img\](.*?)\[\/img\]/gi,"<img src=\"$1\" />");
		this.rep(/\[color=(.*?)\](.*?)\[\/color\]/gi,"<font color=\"$1\">$2</font>");
		this.rep(/\[code\](.*?)\[\/code\]/gi,"<div class=\"codeStyle\"><span style='font-size:0.7em'>" + lang._SND_CODE + ":</span><br />$1</div>&nbsp;");
		this.rep(/\[quote.*?\](.*?)\[\/quote\]/gi,"<div class=\"quoteStyle\"><span style='font-size:0.7em'>" + lang._SND_QUOTE + ":</span><br />$1</div>&nbsp;");


		this.rep(/\[flash,width=([0-9]+),height=([0-9]+)\](.*?)\[\/flash\]/gi, '<embed width="$1" height="$2" quality="high" src="$3" type="application/x-shockwave-flash" />');
		
		this.rep(/\[youtube\](.*?)?v=([a-zA-Z0-9]{1,12})(.*)\[\/youtube\]/gi, '<object width="425" height="355"><param name="movie" value="http://www.youtube.com/v/$2&rel=1"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/$2&rel=1" type="application/x-shockwave-flash" wmode="transparent" width="425" height="355"></embed></object>');
		return (this.s);
	}
}/* comment_ng.js */

snd_callbacks                                            	= new Array();

snd_callbacks[parseInt(lang._SND_SECTIONID_WORKSHOP)]   	= workshop_item;
snd_callbacks[parseInt(lang._SND_SECTIONID_MUSIC)]      	= music_item;
snd_callbacks[parseInt(lang._SND_SECTIONID_NEWS)]       	= news_item;
snd_callbacks[parseInt(lang._SND_SECTIONID_PROFILE)]		= user_item;
var snd_g_timer;

var sndComment = Class.create();

sndComment.prototype = 
{
	initialize: function (t, itemid)
	{
		var uid = snd_c;
		if (snd_c.uid <= 0 || !snd_c.uid)
		{
			return (false);
		}

		if ($('_itemtitle'))
		{
			this.itemtitle = $('_itemtitle').value;
		}

        	this.type = t;
        	this.itemid = itemid;
        },
	
        show: function (content)
        {      
		if (snd_c.uid <= 0 || !snd_c.uid)
		{
			return (false);
		}
		
		var options = {resize:false, width:'600px', height:'400px', maximize:false, minimize:false, close:true};
		this.pid = new sndPopup (lang._SND_COMMENT, false, options);
                this.pid.show();
		
                var input = el ('textarea');
                input.className = "sndTextarea";
                input.style.width = "99%";
                
                if (isIE)
                        input.style.height = "320px";
                else
                        input.style.height = "90%";
                
		        if (content)
		        {
		    	    input.value = content;
		        }

                this.textElm = input;
                
                var submit = el ('button');
                submit.className = "sndButtonSmall";
                submit.style.marginTop = "10px";
                submit.style.marginLeft = "40%";
                
                var txt = document.createTextNode(lang._SND_SUBMIT);
                submit.appendChild(txt);		
                submit.onclick = this.submit.bind(this);
                
                this.pid.contentElm.appendChild (input);
		this.pid.contentElm.appendChild (el('br'));
                this.pid.contentElm.appendChild (submit);
        },
        
        submit: function()
        {
                if (this.textElm)
                {
                        var data = this.textElm.value;

                   	data = new sndBB (data).bbcode2html();
	     
                        if (this.validate(data))
                        {
				var type = parseInt(this.type+500);
                                new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:type,itemtitle:this.itemtitle,cid:this.cid,o2:this.itemid,act:1,data:Base64.encode(data)}, onSuccess: this.submit_hdlr.bindAsEventListener (this) });
                        }
                        else
                        {
                                return (false);
                        }
                }
                else
                {
                        return (false);
                }
        },
        
        submit_hdlr: function (request)
        {
                if (error = detect_error (request))
                {
                        alert (error);
                        return (false);
                }
                
                if (snd_callbacks[this.type])
                {
                        snd_callbacks[this.type](this.itemid);
                }
                
                $('sndPopups').removeChild (this.pid.popElm);
        },
        
        validate: function (str)
        {
		if (!str || str == '')
			return (false);

		var start = new Date();

		if ((start.getTime() - snd_g_timer) < 6000)
		{
			alert (lang._SND_PLEASEWAIT);
			return (false);
		}

		snd_g_timer = start.getTime();

                if (str.length < 3)
                {
			alert (lang._SND_BAD_FORMAT);
			return (false);

                }

                return (true);
        },

	remove_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}

		if (snd_callbacks[this.type])
		{
			snd_callbacks[this.type](this.itemid);
		}
	},

	remove: function (cid)
	{
		if (cid > 0)
		{
			if (!confirm (lang._SND_AREYOUSURE))
			{
				return (false);
			}
			else
			{
				type = parseInt(this.type+500);
				
				new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:type,itemtitle:this.itemtitle,cid:cid,o1:2,act:1,o2:this.itemid}, onSuccess: this.remove_hdlr.bindAsEventListener(this) });
			}
		}
	},

	edit: function (cid)
	{
		this.cid = cid;
		
		this.show();
		
		this.textElm.value = new sndBB ($('comment' + cid).innerHTML.strip()).html2bbcode();
	}
}
/* events_ng.js */

var sndEvent = Class.create();

sndEvent.prototype = 
{
	initialize: function ()
	{
		this.events = new Hash();
		this.updating = false;
		this.tid = 0;
		this.valid = true;
		this.t = 0;
		this.d = {time:0,t:lang._SND_EVENTS_UPDATE,uname:snd_c.uname,uid:snd_c.uid,sid:snd_c.sid,orderby:snd_c.freshOrderby,ordermode:snd_c.freshOrderMode};
		this.ret = new Hash();
	},
	
	update: function()
	{
		if (this.valid == true && this.updating == false)
		{
			this.updating = true;
			
			this.d.time = this.t;
			this.d.orderby = snd_c.freshOrderby;
			this.d.ordermode = snd_c.freshOrderMode;
			this.d.ret = this.ret;

			var p = Base64.encode (Object.toJSON(this.d));
			new Ajax.Request ("/xchat/events.php", {method:'post', parameters:{d:p}, onFailure: this.update_fail.bindAsEventListener(this), onSuccess: this.update_hdlr.bindAsEventListener(this)});
		}
	},
	
	
	update_fail: function (request)
	{
		this.valid = true;
		this.updating = false;
			
		this.tid = setTimeout (this.update.bind(this), snd_c.event_update_interval * 4);
	},
	
	update_hdlr: function(request)
	{
		this.updating = false;

		if (error = detect_error (request))
		{
			this.update_fail(request);
		}

		if (request.responseText.indexOf('{') != 0)
		{
			this.valid = true;
			this.tid = setTimeout (this.update.bond(this), snd_c.event_update_interval);
			
			return (false);
		}
		var resp = eval ('(' + request.responseText + ')');
		
		if (resp)
		{
			if (resp.events)
			{
				this.events_response = resp.events;
				
				for (x=0; x<resp.events.length; x++)
				{
					var eid = resp.events[x].id;
					var par = resp.events[x].param;

					if (this.events[eid])
					{
						this.events[eid](par, this.ret[eid]);
					}

					this.ret[eid] = resp.events[x].param.ret;
				}
			}
			
			if (resp.t)
			{
				this.t = resp.t;
			}

			this.valid = true;
			
			this.tid = setTimeout (this.update.bind(this), snd_c.event_update_interval);
		}
		else
		{
			this.valid = false;
			snd_bad_resp (request);
			
			return (false);
		}
	},
	
	registerHdlr: function (id, hdlr)
	{
		if (id > 0 && hdlr)
		{
			this.events[id] = hdlr;

			return (true);
		}

		return (false);
	},

	unregisterHdlr: function (id)
	{
		if (this.events[id])
		{
			return (this.events.remove(id));
		}

		return (false);
	}
}
/* handlers.js */

var chats = new Hash();

function snd_online_update (param, oldparam)
{
	if (param == -1)
	{
		return (false);
	}

	var o = '';

	$('sndOnlineList').update ('');

	for (y=0; y<param.length; y++)
	{
		u = el ('a');

		if (y%2 || y==0)
			u.style.color = "#000000"
		else
			u.style.color = "#555555"

                if (param[y].status != null)
                {
                        u.innerHTML = "<span style='font-weight:bold;font-size:1.1em'>" + param[y].uname + "</font> ";
                }
                else
                {
                        u.innerHTML = param[y].uname + " ";
                }

		u.onclick = user_item.bind(this,param[y].uid);

		$('sndOnlineList').appendChild (u);
	}
}

function snd_chat_update (param, oldparam)
{
	if (param)
	{
		for (y=0; y<param.length; y++)
		{
			if (chats[param[y].sid] == true)
			{
				continue;
			}

			chats[param[y].sid] = true;

			if (!confirm (param[y].uname + lang._SND_INVITE_ASK))
			{
				new Ajax.Request ("/xchat/pms.php", {method:'post', parameters:{t:lang._SND_PMS_UPDATE,sid:param[y].sid,uname:snd_c.uname,s:lang._SND_PMS_REJECT,crc:0}, onSuccess: null});
			}
			else
			{
				new sndChat(param[y].uname, param[y].topic, param[y].sid).accept();
			}
		}
	}
	else
	{
		return (false);
	}
}

function snd_global_update (param, oldparam)
{
}

function snd_pms_update (param, oldparam)
{
	if (param[0] > 0)
	{
		$('sndNotifyMsgTop').src = "/images/soundlab/title_panel_2_left_newmsg.png";
	}
	else
	{
		$('sndNotifyMsgTop').src = "/images/soundlab/title_panel_2_left.jpg";
	}
}

function snd_notification_update (param, oldparam)
{
	if (param)
	{
		var n = el ('div');
		n.style.width = "300px";
		n.style.height = "40px";
		n.style.border = "1px solid #ff0000";
		n.innerHTML = param.text;

		$('sndNotificationBar').appendChild(n);
	}
}

function snd_fresh_update (param, oldparam)
{
	var len_music = param.music.length;
	var len_forum = param.forum.length;
    var len_demo = param.demo.length;

	var music = $('sndFreshMusicModule');
	var forum = $('sndFreshForumModule');
    var demo = $('sndFreshDemoModule');

	var music_items = music.childElements();
	var forum_items = forum.childElements();
    var demo_items  = demo.childElements();
	
	if (music)
	{
		if (len_music > 0)
		{
			for (var x=0; x<music_items.length; x++)
			{
				music.removeChild (music_items[x]);
			}

			for (var x=len_music-1; x>=0; x--)
			{
				var n = el ('div');
				n.className = "sndFreshMusicItem";
				n.onmouseover = function () { this.className = 'sndFreshMusicItemHover' };
				n.onmouseout = function () { this.className = 'sndFreshMusicItem' };
	
				var w = document.createElement ('div');
				w.style.color = "#000000";
				w.style.fontSize = "10px";
				w.style.fontWeight = "bold";
				w.style.height = "12px";
				w.style.overflow = "hidden";
				w.style.width = "200px";
				var l = el ('a');
                l.style.fontSize = "8px";
				
				Element.extend (l);
                
                var rejFont = el('span');
                rejFont.style.color = "#cd0e0e";
                
                var recFont = el('span');
                recFont.style.color = "#038c23";
                
                var rejValue = tel(param.music[x].rejected);
                var recValue = tel(param.music[x].recommended);
                var comValue = tel(param.music[x].ncomment);
                
                rejFont.appendChild (rejValue);
                recFont.appendChild (recValue);
                
				var title = el ('span');
                title.appendChild (tel (stripslashes (stripslashes (param.music[x].itemtitle.unescapeHTML()))) );
                title.style.fontSize = "10px";
                
                l.appendChild (tel('('));
                l.appendChild (comValue);
                l.appendChild (tel('/'));
                l.appendChild (recFont);
                l.appendChild (tel('/'));
                l.appendChild (rejFont);
                l.appendChild (tel(') '));
                
				l.appendChild (title);
				l.style.color = "#333333";
				l.style.fontFamily = "Verdana,Arial";
				
				l.writeAttribute ('itemid', param.music[x].itemid);
				l.onclick = function () { music_item (this.readAttribute ('itemid')) };

				w.appendChild (l);
				n.appendChild (w);

				var w = el('div');
				w.style.color = "#000000";
				w.style.height = "12px";
				w.style.overflow = "hidden";
				w.style.marginTop = "2px";

				var l = el('a');
				Element.extend (l);
				l.style.color = "#333333";
				l.style.fontFamily = "Verdana,Arial";
				l.writeAttribute ('itemid', param.music[x].authorid);
				l.onclick = function () { user_item (this.readAttribute ('itemid')) };
				l.style.cssFloat = 'right';
				l.style.styleFloat = 'right';
				l.style.marginRight = "30px";
				l.appendChild (document.createTextNode("[" + param.music[x].authorname + "]"));

				w.appendChild (l);
				
				var l = el('a');
				Element.extend (l);
				l.style.color = "#333333";
				l.writeAttribute ('itemid', param.music[x].iteminfo);
				l.onclick = function () { 	get_page_callback = 
                                function () 
								{	
									pager = new Pager('catalogItems', 15);                                         
									pager.init();                                                                  
                                    pager.showPageNav('pager', 'catalogPageNav');
                                    pager.showPage(1); 
									sorttable.makeSortable($('catalogItems'));
								}
                                get_page (window.lang._SND_PAGE_CATALOG, 1, this.readAttribute ('itemid'), 0, 0) };
				l.style.cssFloat = 'left';
				l.style.styleFloat = 'left';
				l.style.marginLeft = "6px";
				l.style.fontFamily = "Verdana,Arial";
				l.appendChild (document.createTextNode("[" + stripslashes (param.music[x].iteminfo.truncate(18)) + "]"));

				w.appendChild (l);
				n.appendChild (w);

				music.appendChild (n);
			}
		}
		else
		{
			for (var x=0; x<music_items.length; x++)
			{
				if (music_items[x].tagName == "IMG")
				{
					music.removeChild (music_items[x]);
				}
			}
		}
	}

    if (demo)
	{
		if (len_demo > 0)
		{
			for (var x=0; x<demo_items.length; x++)
			{
				demo.removeChild (demo_items[x]);
			}

			for (var x=len_demo-1; x>=0; x--)
			{
				var n = el ('div');
				n.className = "sndFreshMusicItem";
				n.onmouseover = function () { this.className = 'sndFreshMusicItemHover' };
				n.onmouseout = function () { this.className = 'sndFreshMusicItem' };
	
				var w = document.createElement ('div');
				w.style.color = "#000000";
				w.style.fontSize = "10px";
				w.style.fontWeight = "bold";
				w.style.height = "12px";
				w.style.overflow = "hidden";
				w.style.width = "200px";
				var l = el ('a');
                l.style.fontSize = "8px";
				
				Element.extend (l);
                
                var rejFont = el('span');
                rejFont.style.color = "#cd0e0e";
                
                var recFont = el('span');
                recFont.style.color = "#038c23";
                
                var rejValue = tel(param.demo[x].rejected);
                var recValue = tel(param.demo[x].recommended);
                var comValue = tel(param.demo[x].ncomment);
                
                rejFont.appendChild (rejValue);
                recFont.appendChild (recValue);
                
				var title = el ('span');
                title.appendChild (tel (stripslashes (param.demo[x].itemtitle.unescapeHTML())) );
                title.style.fontSize = "10px";
                
                l.appendChild (tel('('));
                l.appendChild (comValue);
                l.appendChild (tel('/'));
                l.appendChild (recFont);
                l.appendChild (tel('/'));
                l.appendChild (rejFont);
                l.appendChild (tel(') '));
                
				l.appendChild (title);
				l.style.color = "#333333";
				l.style.fontFamily = "Verdana,Arial";
				
				l.writeAttribute ('itemid', param.demo[x].itemid);
				l.onclick = function () { music_item (this.readAttribute ('itemid')) };

				w.appendChild (l);
				n.appendChild (w);

				var w = el('div');
				w.style.color = "#000000";
				w.style.height = "12px";
				w.style.overflow = "hidden";
				w.style.marginTop = "2px";

				var l = el('a');
				Element.extend (l);
				l.style.color = "#333333";
				l.style.fontFamily = "Verdana,Arial";
				l.writeAttribute ('itemid', param.demo[x].authorid);
				l.onclick = function () { user_item (this.readAttribute ('itemid')) };
				l.style.cssFloat = 'right';
				l.style.styleFloat = 'right';
				l.style.marginRight = "30px";
				l.appendChild (document.createTextNode("[" + param.demo[x].authorname + "]"));

				w.appendChild (l);
				
				var l = el('a');
				Element.extend (l);
				l.style.color = "#333333";
				l.writeAttribute ('itemid', param.demo[x].iteminfo);
				l.onclick = function () { 	get_page_callback = 
                                function () 
								{	
									pager = new Pager('catalogItems', 15);                                         
									pager.init();                                                                  
                                    pager.showPageNav('pager', 'catalogPageNav');
                                    pager.showPage(1); 
									sorttable.makeSortable($('catalogItems'));
								}
                                get_page (window.lang._SND_PAGE_CATALOG, 1, this.readAttribute ('itemid'), 0, 0) };
				l.style.cssFloat = 'left';
				l.style.styleFloat = 'left';
				l.style.marginLeft = "6px";
				l.style.fontFamily = "Verdana,Arial";
				l.appendChild (document.createTextNode("[" + stripslashes (param.demo[x].iteminfo.truncate(18)) + "]"));

				w.appendChild (l);
				n.appendChild (w);

				demo.appendChild (n);
			}
		}
		else
		{
			for (var x=0; x<demo_items.length; x++)
			{
				if (music_items[x].tagName == "IMG")
				{
					demo.removeChild (music_items[x]);
				}
			}
		}
	}
    
	if (forum)
	{
		if (len_forum > 0)
		{
			for (var x=0; x<forum_items.length; x++)
			{
				forum.removeChild (forum_items[x]);
			}

			for (var x=len_forum-1; x>=0; x--)
			{
				var n = el ('div');
				n.className = "sndFreshMusicItem";
				n.onmouseover = function () { this.className = 'sndFreshForumItemHover' };
				n.onmouseout = function () { this.className = 'sndFreshMusicItem' };
			
				var w = el ('div');
				w.style.color = "#000000";
				w.style.fontSize = "10px";
				w.style.fontWeight = "bold";
				w.style.height = "12px";
				w.style.overflow = "hidden";
				w.style.width = "200px";
				var l = el ('a');
	
				Element.extend (l);
				var k = stripslashes (param.forum[x].itemtitle.unescapeHTML());

				l.appendChild (document.createTextNode(k));
				l.style.color = "#000000";
				l.style.fontFamily = "Verdana,Arial";
				l.writeAttribute ('itemid', param.forum[x].itemid);
				l.href = "http://sndlab.com/forum/viewtopic.php?t=" + param.forum[x].itemid;
				l.target = "_blank";
				// l.onclick = function () { forum_item (this.readAttribute ('itemid')) };

				w.appendChild (l);
				n.appendChild (w);

				var w = el('div');
				w.style.color = "#000000";
				w.style.height = "12px";
				w.style.overflow = "hidden";
				w.style.marginTop = "2px";
	
				var l = el('a');
				Element.extend (l);
				l.style.color = "#333333";
				l.writeAttribute ('itemid', param.forum[x].authorid);
				l.onclick = function () { user_item (this.readAttribute ('itemid')) };
				l.style.cssFloat = 'right';
				l.style.styleFloat = 'right';
				l.style.marginRight = "30px";
				l.style.fontFamily = "Verdana,Arial";
				l.appendChild (document.createTextNode("[" + param.forum[x].authorname + "]"));

				w.appendChild (l);
				
				var l = el('a');
				Element.extend (l);
				l.style.color = "#333333";
				l.writeAttribute ('itemid', param.forum[x].iteminfoid);
				//l.onclick = function () { forum_item (this.readAttribute ('itemid'), 'viewforum.php?f=') };
				l.href = "http://sndlab.com/forum/viewforum.php?f=" + param.forum[x].iteminfoid;
				l.target = "_blank";
				l.style.cssFloat = 'left';
				l.style.styleFloat = 'left';
				l.style.marginLeft = "10px";
				l.style.fontFamily = "Verdana,Arial";
				l.appendChild (document.createTextNode("[" + param.forum[x].iteminfo + "]"));

				w.appendChild (l);
				n.appendChild (w);

				forum.appendChild (n);
			}
		}
		else
		{
			for (var x=0; x<forum_items.length; x++)
			{
				if (forum_items[x].tagName == "IMG")
				{
					forum.removeChild (forum_items[x]);
				}
			}
		}
	}
}/* pms.js */

var sndPms = Class.create();

sndPms.prototype =
{
	init_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		$('sndBody').update (request.responseText);
		
		Event.observe ('pmsInbox', 'click', this.inbox.bind(this));
		Event.observe ('pmsOutbox', 'click', this.outbox.bind(this));
		Event.observe ('pmsCompose', 'click', this.compose.bind(this));
		Event.observe ('pmsItemDelete', 'click', this.remove.bind(this));
		
		if (this.rcpt)
		{
			this.compose();
		}
		else
		{
			this.inbox();
		}
	},

	initialize: function (rcpt)
	{
		this.rcpt = rcpt;
		this.init = true;

		if (snd_c.uid <= 0 || !snd_c.uid)
			return (false);

		snd_loading();

		new Ajax.Request ("/index.php?option=com_pms", {method:"post",parameters:{pmsInit:1,rcpt:this.rcpt}, onSuccess:this.init_hdlr.bindAsEventListener(this)});
	},

	compose_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
	
		$('pmsContent').update (request.responseText);

		Event.observe ('pmsComposeSubmit', 'click', this.send.bind(this));
		new Ajax.Autocompleter ('pmsComposeRcpt', 'inputTagAutoComplete', '/msg/users.php', {paramName:'msgAT'});
	},

	compose: function()
	{
		snd_loading ('pmsContent');
		
		new Ajax.Request ("/index.php?option=com_pms", {method:"post",parameters:{pmsDisplayCompose:1,rcpt:this.rcpt}, onSuccess:this.compose_hdlr.bindAsEventListener(this)});
	},
	
	inbox_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		$('pmsContent').update (request.responseText);
		
		var c = $('pmsInboxItems').select ('[item="pmsInboxItem"]');

		for (var x=0; x<c.length; x++)
		{
			Event.observe (c[x], 'click', this.show.bindAsEventListener (this));
		}

		var c = $('pmsInboxItems').select ('[item="pmsItemCheck"]');

		for (var x=0; x<c.length; x++)
		{
			Event.observe (c[x], 'click', this.select.bindAsEventListener (this));
		}
		
		Event.observe ('pmsItemCheckAll', 'click', this.selectAll.bindAsEventListener(this));
		
		pager = new Pager('pmsInboxItems', 15);
		pager.init();                                                                  
	        pager.showPageNav('pager', 'pmsInboxItemsNav');
		pager.showPage(1); 
		
		sorttable.makeSortable($('pmsInboxItems'));
	},

	inbox: function()
	{
		snd_loading ('pmsContent');
		
		new Ajax.Request ("/index.php?option=com_pms", {method:"post",parameters:{pmsDisplayInbox:1}, onSuccess:this.inbox_hdlr.bindAsEventListener(this)});
	},

	outbox: function()
	{
		snd_loading ('pmsContent');
		
		new Ajax.Request ("/index.php?option=com_pms", {method:"post",parameters:{pmsDisplayOutbox:1}, onSuccess:this.inbox_hdlr.bindAsEventListener(this)});
	},

	send_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}

		$('pmsContent').update (request.responseText);
	},

	send: function ()
	{
		if (!$('pmsComposeRcpt').value || $('pmsComposeRcpt').value.length < 3 || $('pmsComposeRcpt').value.length > 12)
		{
			new Effect.Highlight ('pmsComposeRcpt', {endcolor:'#f3a3a3'});
			return (false);
		}

		if (!$('pmsComposeSubject').value || $('pmsComposeSubject').value.length < 3)
		{
			new Effect.Highlight ('pmsComposeSubject', {endcolor:'#f3a3a3'});
			return (false);
		}

		if (!$('pmsComposeMessage').value || $('pmsComposeMessage').value.length < 3)
		{
			new Effect.Highlight ('pmsComposeMessage', {endcolor:'#f3a3a3'});
			return (false);
		}

		var msg = new sndBB($('pmsComposeMessage').value).bbcode2html();

		new Ajax.Request ("/index.php?option=com_pms", {method:"post",parameters:{
			pmsSend:1,
			pmsComposeRcpt:$('pmsComposeRcpt').value,
			pmsComposeSubject:$('pmsComposeSubject').value,
			pmsComposeMessage:msg,
			pmsComposeFlag:$('pmsComposeFlag').value}, onSuccess:this.send_hdlr.bindAsEventListener(this)});

		snd_loading ('pmsContent');
	},

	show_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}

		$('pmsContent').update (request.responseText);

		this.handlerCache = this.reply.bind(this);

		Event.observe ('pmsComposeSubmit', 'click', this.handlerCache);
	},

	show: function (elm)
	{
		var e = Event.element (elm);
		var cid = e.id;

		new Ajax.Request ("/index.php?option=com_pms", {method:"post",parameters:{
			pmsDisplayItem:1,
			cid:cid,t:$('t').value}, onSuccess: this.show_hdlr.bindAsEventListener(this)});

		snd_loading ('pmsContent');
	},

	reply: function()
	{
		$('pmsComposeSubmit').value = lang._SND_SUBMIT;
		$('pmsComposeRcpt').disabled = false;
		$('pmsComposeSubject').disabled = false;

		if ($('pmsComposeSubject').value.indexOf ('RE: ') != 0)
		{
			$('pmsComposeSubject').value = 'RE: ' + $('pmsComposeSubject').value;
		}

		$('pmsMessageBody').hide();
		$('pmsComposeMessage').show();
		$('pmsComposeMessage').value = new sndBB($('pmsMessageBody').innerHTML).html2bbcode();
		$('pmsComposeFlagImg').show();
		
		Event.stopObserving ('pmsComposeSubmit', 'click', this.handlerCache);

		Event.observe ('pmsComposeSubmit', 'click', this.send.bind(this));

		new Ajax.Autocompleter ('pmsComposeRcpt', 'inputTagAutoComplete', '/msg/users.php', {paramName:'msgAT'});
	},

	select: function (elm)
	{
		var e = Event.element (elm);
		var row = e.up(1);
		
		if (e.checked == true)
		{
			row.style._backgroundColor = row.style.backgroundColor;
			row.setStyle ({backgroundColor:"#ffffcc"});
			row.writeAttribute ("selected", 1);
		}
		else
		{
			row.setStyle ({backgroundColor:row.style._backgroundColor});
			row.writeAttribute ("selected", 0);
		}
	},
	
	selectAll: function (elm)
	{
		var e = Event.element (elm);
		var c = $('pmsInboxItems').select ('[item="pmsInboxItemRow"]');
		
		if (e.checked == true)
		{
			for (var x=0; x<c.length; x++)
			{
				c[x].style._backgroundColor = c[x].style.backgroundColor;
				c[x].setStyle ({backgroundColor:"#ffffcc"});
				c[x].writeAttribute ("selected", 1);
			}
		}
		else
		{
			for (var x=0; x<c.length; x++)
			{
				c[x].setStyle ({backgroundColor:c[x].style._backgroundColor});
				c[x].writeAttribute ("selected", 0);
			}
		}
	},
	
	remove: function()
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var c = $('pmsInboxItems').select ('[item="pmsInboxItemRow"]');
		if (c.length <= 0)
		{
			return (false);
		}
		var items = new Array();
		
		for (var x=0; x<c.length; x++)
		{
			if (c[x].readAttribute("selected") == 1)
			{
				items[x] = c[x].id;
			}
		}
		
		var i = items.toJSON();
		
		new Ajax.Request ("/index.php?option=com_pms", {method:"post",parameters:{
			pmsDeleteItems:1,
			section:$('pmsSection').value,
			items:i}, onSuccess: this.inbox_hdlr.bindAsEventListener(this)});
	}
}/* music.js */

var mus = 30;

var mus_main = 0;
var mus_cntnr = 1;
var mus_file = 2;

var mus_cntnr_skilled = 2;
var mus_cntnr_novice = 1;
var mus_cntnr_demo = 6;
var mus_cntnr_runners = 5;

var mus_file_down = 0;
var mus_file_edit = 1;

var sndMusicList = Class.create();

sndMusicList.prototype = 
{
	initialize: function()
	{
		snd_loading();
		
		new Ajax.Request ("/index.php?option=com_music", {method:"get",parameters:{next:0,by:this.orderby}, onSuccess: this.change.bindAsEventListener(this)});
	},
	
	change: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
	
		$('sndBody').update (request.responseText);
		
		var pageLimit = $('pageLimit').value;
		var pageTotal = $('pageTotal').value;
		var pageCurrent = $('pageCurrent').value;
		
		var nextpage = $('nextItemPage').value;
		var prevpage = $('prevItemPage').value;
		var orderby = $('orderBySelected').value;
		
		for (var x=0; x<pageTotal; x++)
		{
			var k = el('a');
			if ((x+1) == pageCurrent)
			{
				k.style.color = "#000000";
				k.style.fontSize = '14px';
			}
			k.onclick = this.page.bind(this, ((x*pageLimit)+1));
			k.update(' ' + (x+1) + ' ');
			
			$('pageJumpLinks').appendChild (k);
		}
		
		Event.observe ('nextPageLink', 'click', this.page.bind(this,nextpage));
		Event.observe ('prevPageLink', 'click', this.page.bind(this,prevpage));
		Event.observe ('orderBy', 'change', this.changeOrder.bindAsEventListener(this,pageCurrent));
		
		$('orderBy').selectedIndex = orderby;
		$('orderBy').value = orderby;
	},
	
	page: function (p)
	{
		snd_loading();
		
		new Ajax.Request ("/index.php?option=com_music", {method:"get",parameters:{next:p,by:this.orderby}, onSuccess: this.change.bindAsEventListener(this)});
	},
	
	changeOrder: function (elm, p)
	{
		var e = Event.element (elm);
		this.orderby = e.value;
		snd_loading();
		
		new Ajax.Request ("/index.php?option=com_music", {method:"get",parameters:{next:p,by:this.orderby}, onSuccess: this.change.bindAsEventListener(this)});
	}
}

function music_cntnr_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}

	var main = document.getElementById('sndBody');	
	main.innerHTML = request.responseText;
	
	Event.observe ('sndOrderby', 'change', music_cntnr_orderby);
	main = null;
}

function music_cntnr(page)
{
	snd_loading();
	
	var o = $('sndOrderby').value;
	
	new Ajax.Request (snd_ajax_url, {method:'get',parameters:{t:30,g:mus_cntnr,next:page,by:o}, onSuccess: music_cntnr_hdlr});	
}


function music_cntnr_orderby()
{
	music_cntnr();
}

function music_item_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		snd_stop_loading();

		return (false);
	}

	var main = document.getElementById ('sndBody')
	main.innerHTML = request.responseText;

    createSinglePlayer ('jwSinglePlayer', this.itemId);
	main = null;
}

function music_item (id)
{
	if (id > 0)
	{
		snd_loading();
		
		snd_history_add ("#p:" + lang._SND_PAGE_MUSIC + ";cid:" + id, {handler:function () { music_item (this.cid) },cid:id});
        this.itemId = id;
		new Ajax.Request (snd_ajax_url, {method:'get',parameters:{t:lang._SND_SECTIONID_MUSIC,cid:id}, onSuccess: music_item_hdlr.bindAsEventListener(this)});
	}
}

function music_item_sid (sid)
{
    if (sid != null)
	{
		snd_loading();
		
		snd_history_add ("#p:" + lang._SND_PAGE_MUSIC + ";sid:" + sid, {handler:function () { music_item_sid (this.sid) },sid:sid});

		new Ajax.Request (snd_ajax_url, {method:'get',parameters:{t:lang._SND_SECTIONID_MUSIC,sid:sid}, onSuccess: music_item_hdlr});
	}
}

function music_delete_comment_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}
}

function music_delete_comment (id)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
	}

	new Ajax.Request (snd_ajax_url, {method:'get',parameters:{act:1,t:snd_m_delete_comment,cid:id}, onSuccess: music_delete_comment_hdlr});
	return (true);
}

function music_recommend_file_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}
	
	ret = eval ('(' + request.responseText + ')');
	
	new Effect.Puff ('recommendIcon', {afterFinish:function() { $('recommendIcon').setStyle({ display: 'block'}); } });
	
    val = $('recommendedCount').innerHTML;
    val++;
    $('recommendedCount').update (val);
    
	return (false);
}
function music_recommend_file (id)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
	}
	
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{act:1,t:lang._SND_SECTIONID_RECOMMEND,cid:id,o:1}, onSuccess: music_recommend_file_hdlr});
}

function music_reject_file_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}
	
	ret = eval ('(' + request.responseText + ')');
	
	new Effect.Puff ('rejectIcon', { afterFinish:function() { $('rejectIcon').setStyle({ display: 'block'}); } });
	val = $('rejectedCount').innerHTML;
    val++;
    $('rejectedCount').update (val);
	return (false);
}

function music_reject_file (id)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
	}
	
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{act:1,t:lang._SND_SECTIONID_RECOMMEND,cid:id,o:2}, onSuccess: music_reject_file_hdlr});
}

function music_delete_file_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}
	
	new sndPanel (snd_c.uid, snd_c.uname).show();
}

function music_delete_file (id)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
	}
	new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{uid:snd_c.uid,file_id:id,deleteFile:1},onSuccess: music_delete_file_hdlr} );	
}

function music_edit_file_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}

	var resp = eval ('(' + request.responseText + ')');

	if (resp)
	{
		$('sndBody').innerHTML = Base64.decode (resp.template);

		document.getElementById('upload_genre').value = resp.file.genre;
		document.getElementById('upload_category').value = resp.file.containerid;
		document.getElementById('upload_title').value = resp.file.filetitle;
		document.getElementById('upload_description').value = new sndBB (resp.file.description).html2bbcode();
		
		document.getElementById('upload_size').value = resp.file.filesize;
		document.getElementById('upload_url').value = resp.file.url;
		document.getElementById('upload_duration').value = resp.file.duration;
		
		document.getElementById('upload_sid').value = resp.file.sid;
	}
}

function music_edit_file (id)
{
	snd_loading();
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_PAGE_NEW,cid:id}, onSuccess: function (request) { music_edit_file_hdlr (request, id) } });
}

function music_new()
{
	if (snd_c.uid > 0)
	{
		snd_loading();
		new sndUpload();
	}
	else
	{
		return (false);
	}
}/* page.js */

var get_page_callback = null;

function get_page_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		snd_stop_loading();
	
		return (false);
	}

	if (snd_state == snd_s_wide)
	{
		snd_set_state(snd_s_narrow, false);
		var body = $('sndBody');
	}
	else
	{
		var body = $('sndBody');
	}

	body.update (request.responseText);
	
	if (get_page_callback)
	{
		setTimeout (get_page_callback, 250);
	}

	get_page_callback = null;
	request = null;
}

function get_page (t, cid, opt1, opt2, act, i)
{
	if (!opt1)
		opt1 = '';
	if (!opt2)
		opt2 = '';
	if (!cid)
		cid = 'f';

	if (!i)
		snd_history_add ("#p:" + t + ";c:" + cid + ";o1:" + opt1 + ";o2:" + opt2, {t:t,cid:cid,opt1:opt1,opt2:opt2,act:act,handler:function() { get_page (this.t, this.cid, this.opt1, this.opt2, this.act) }});

	snd_loading();
	
	new Ajax.Request ("/index.php", {method:'post',parameters:{opt:'com_ajax',t:t,cid:cid,o1:opt1,o2:opt2,act:act}, onSuccess: get_page_hdlr});
}

function snd_getpage (options)
{
	var cid		= options.cid;
	var t		= options.t;
	var o1		= options.o1;
	var o2		= options.o2;
	var act		= options.act;
	var method 	= options.method;
	var url		= options.url;
	var history	= options.history;
	var next	= options.next;

	if (!cid)
		cid= 'f';

	if (history)
		snd_history_add ("#p:" + t + ";c:" + cid + ";n:" + next + ";act:" + act + ";o1" + o1, {options:options,handler:function(){snd_getpage (this.options)} } );

	snd_loading ();

	new Ajax.Request (url, {method:method,parameters:{t:t,cid:cid,act:act,o1:o1,o2:o2,next:next}, onSuccess:get_page_hdlr });
}/* panel.js */

var sndPanel = Class.create();

sndPanel.prototype =
{
	initialize: function(uid, uname)
	{
		if (uid <= 0 || uname == null)
		{
			this.valid = false;
			return (false);
		}
		
		this.uid = uid;
		this.uname = uname;
		this.valid = true;
		this.managerTree = false;
		
		this.playlistImage  = false;
		snd_history_add ("#p:" + lang._SND_SECTIONID_MUSIC + ";cid:" + uid, {handler:function () { music_item (this.uid) },cid:uid});
		
		this.show();
	},

	show_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}

		if (request)
		{			
			$('sndBody').innerHTML = request.responseText;
			
			var tabberOptions = {'manualStartup':true,onTabDisplay:this.tabChange.bind(this)};
			this.topTabs = tabberAutomatic(tabberOptions)

			Event.observe ('panelEditAvatar', 'mouseover', this.highlight.bindAsEventListener ($('panelEditAvatar')));
			Event.observe ('panelEditAvatar', 'click', this.editAvatar.bindAsEventListener (this));
			
			Event.observe ('panelEditInfo', 'mouseover', this.highlight.bindAsEventListener ($('panelEditInfo')));
			Event.observe ('panelEditInfo', 'click', this.editInfo.bindAsEventListener (this));
			Event.observe ('panelSendAdditionalInfo', 'click', this.sendAdditional.bindAsEventListener (this));
		}
	},

	show: function()
	{
		if (this.pid)
		{
			document.getElementById('sndPopups').removeChild (this.pid.popElm);
			this.pid= false;
		}
		
		if (!this.valid)
			return (false);

		snd_loading();
		new Ajax.Request (snd_ajax_url, {method:"post", parameters:{t:lang._SND_PANEL,act:0,cid:snd_c.uid}, onSuccess: this.show_hdlr.bind(this)} );
	},
	
	highlight: function()
	{
		this.style.cursor = 'pointer';
		Effect.Pulsate (this);
	},
	
	editAvatar: function()
	{
		if (this.pid)
		{
			document.getElementById('sndPopups').removeChild (this.pid.popElm);
			this.pid = false;
		}
		
		var options = {resize:false, width:'500px', height:'180px', maximize:true, minimize:true, close:true, position:'fixed'};
		this.pid = new sndPopup (lang._SND_EDIT_AVATAR, false, options);
		var thisObj = this;
		this.pid.closeHdlr = function () { thisObj.pid = false };
		this.pid.show();
		
		
		var w = document.createElement ('div');
		w.style.fontSize = "14px";
		w.style.margin = "10px";
		
		w.appendChild (document.createTextNode (lang._SND_AVATAR_UPLOAD_INFO));
		
		var f = document.createElement ('form');
		f.style.margin = "20px";
		f.target = "avatarUploadTarget";
		f.action = "/index.php?option=com_panel";
		f.method = "POST";
		f.encoding  = "multipart/form-data";
		f.name = "avatarForm";
		
		var u = document.createElement ("div");
		u.id = "uploadbuttons";
		u.style.width = "420px";
		
		this.i = document.createElement ('input');
		this.i.type = "file";
		this.i.name = "avatarFileName";
		this.i.style.opacity = "0";
		this.i.style.filter = "alpha(opacity=0)";
		this.i.style.position = "absolute";
		this.i.style.top = "0pt";
		this.i.style.right = "0px";
		this.i.style.zIndex = "2";
		this.i.style.cursor = "pointer";
		
		Event.observe (this.i, 'change', this.avatarFilenameChanged.bindAsEventListener(this));
		
		var b = document.createElement ('button');
		var t = document.createTextNode (lang._SND_BROWSE);
		b.appendChild (t);
		b.className = "sndButtonSmall";
		b.name = "avatarSubmit";
		b.setAttribute ('id', "avatarSubmit");
		b.value = lang._SND_BROWSE;
		b.style.position = "absolute";
		b.style.zIndex = "1";
		b.style.top = "0pt";
		b.style.right = "0px";
		b.style.cursor = "pointer";
		
		var s = document.createElement ('button');
		var t = document.createTextNode (lang._SND_SUBMIT);
		s.appendChild (t);
		s.className = "sndButtonSmall";
		s.style.marginLeft = "10px";
		s.name = "avatarSubmit";
		s.setAttribute ('id', "avatarSubmit");
		s.value = lang._SND_SUBMIT;
		s.style.cssFloat = "left";
		s.style.styleFloat  = "left";
		
		this.dis = document.createElement ('div');
		
		u.appendChild (s);
		u.appendChild (this.i);
		u.appendChild (b);
		f.appendChild (u);
		w.appendChild (f);
		w.appendChild (this.dis);
		
		this.pid.contentElm.appendChild (w);
		var thisObj = this;
		
		Event.observe ('avatarUploadTarget', 'load', this.show.bind(this));
		
		if (isIE)
		{
			Event.observe ('avatarSubmit', 'click', function () {  f.submit(); document.getElementById('sndPopups').removeChild (thisObj.pid.popElm);thisObj.pid = false} );
		}
		else
		{
			Event.observe ('avatarSubmit', 'click', function () {  f.submit();} );
		}
	},
	
	avatarFilenameChanged: function()
	{
		if (this.dis)
		{
			if (this.dis.firstChild)
			{
				this.dis.removeChild (this.dis.firstChild);
			}
			
			var c = document.createElement ('div');
			c.appendChild (document.createTextNode(this.i.value));
			
			this.dis.appendChild (c);
		}
	},
	
	editInfo: function()
	{
		if (this.pid)
		{
			document.getElementById('sndPopups').removeChild (this.pid.popElm);
			this.pid = false;
		}
		
		var options = {resize:true, width:'600px', height:'480px', maximize:true, minimize:true, close:true, position:'fixed'};
		this.pid = new sndPopup (lang._SND_EDIT_AVATAR, false, options);
		var thisObj = this;
		this.pid.closeHdlr = function () { thisObj.pid = false };
		this.pid.show();
		
		var t = document.createElement ('table');
		t.width = "99%";
		
		var tbody = document.createElement("tbody");
		t.appendChild (tbody);
		
		var tr = document.createElement ('tr');
		var td = document.createElement ('td');
		td.width = "100%"
		td.style.height = "300px";
		
		tr.appendChild (td);
		tbody.appendChild (tr);
		
		this.infoInp = document.createElement ('textarea');
		this.infoInp.className = 'sndTextarea';
		this.infoInp.style.width = "100%";
		this.infoInp.style.height = "400px";

		this.infoInp.value = new sndBB($('infoContent').innerHTML).html2bbcode();		
		
		var snd = document.createElement ('button');
		snd.className = "sndButtonSmall";
		snd.setAttribute ('id', 'infoSend');
		snd.appendChild (document.createTextNode (lang._SND_SUBMIT));
		
		td.appendChild (this.infoInp);
		
		this.pid.contentElm.appendChild (t);
		this.pid.contentElm.appendChild (snd);
		
		Event.observe ('infoSend', 'click', this.sendInfo.bind(this));
	},
	
	sendInfo: function()
	{
		if (this.infoInp.value.length > 3)
		{
			var bb = new sndBB(this.infoInp.value).bbcode2html();

			new Ajax.Request (snd_ajax_url, {method:"post", parameters:{t:lang._SND_PANEL,act:1,cid:snd_c.uid,info:Base64.encode(bb)}, onSuccess: this.show.bind(this)});
		}
	},
	
	tabChange_hdlr: function(request, id)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var d = el ('div');
		d.style.margin = "0px";
		d.style.padding = "0px";
		
		d.innerHTML = request.responseText;
		
		var v = $(id + 'Content');
		
		var c = $(id + 'Content').childElements();
		
		if (c)
		{
			for (var x=0; x<c.length; x++)
			{
				Element.remove (c[x]);
			}
		}
		
		$(id + 'Content').appendChild (d);
		$(id).writeAttribute ("loaded", "1");
		
		if (id == 'tabManager')
		{
			var tabberOptions = {'manualStartup':true};
			tabberAutomatic(tabberOptions)
			d.remove();
			
			var ret = eval('('+request.responseText+')');
			var data = ret.data;
			var template = ret.template;
			
			$(id + 'Content').className = "tree";
			$(id + 'Content').update (Base64.decode(template));
			
			if (data)
			{
				for (var x=0; x<data.length; x++)
				{
					var t = data[x].t;
					data[x].onClickCaption = this.managerContainerElm.bind (this, 0, 0, t, '');
					
					for (var y=0; y<data[x].children.length; y++)
					{
						var k = data[x].children[y].id;
						var d = data[x].children[y].description;
						
						data[x].children[y].onClickCaption = this.managerContainerElm.bind (this, 1, k, t, d);
					}
				}
			}
			
			this.managerContainerData 	= data;
			this.managerTree 		= new Bs_Tree();
			this.managerTree.imageDir 	= "/js/gui/tree/img/win98/";
			
			this.managerTree.initByArray(this.managerContainerData);
			this.managerTree.drawInto('managerTree');
			
			Event.observe ('managerContainerDelete', 'click', this.managerContainerDelete.bind(this));
			Event.observe ('managerContainerSubmit', 'click', this.managerContainerSubmit.bind(this));
		}

		
		if (id == 'tabContent')
		{
			pager = null;
			pager = new Pager('contentItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'contentPageNavPosition');
        		pager.showPage(1);
			try { sorttable.makeSortable ($('contentItems')) } catch (e) { alert (e) };
			
			var c = $('contentItems').select('[title="contentItemDelete"]');
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.contentDelete.bindAsEventListener(this);
				}
			}
			
			c = $('contentItems').select('[title="contentItemEdit"]');
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.contentEdit.bindAsEventListener(this);
				}
			}
			
			Event.observe ('contentSubmit', 'click', this.contentSubmit.bindAsEventListener (this));
		}
		
		if (id == 'tabWorkshop')
		{
			pager = null;
			pager = new Pager('workshopItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'workshopPageNavPosition');
        		pager.showPage(1);
			try { sorttable.makeSortable ($('workshopItems')) } catch (e) { alert (e) };

			var c = $('workshopItems').select('[title="workshopItemDelete"]');
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.workshopDelete.bindAsEventListener(this);
				}
			}


			var c = $('workshopItems').select('[title="workshopItemEdit"]');
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.workshopEdit.bindAsEventListener(this);
				}
			}

			Event.observe ('workshopFile', 'change', this.workshopBrowseFile.bindAsEventListener(this));
			Event.observe ('workshopSubmit', 'click', this.workshopSubmit.bind(this));
		}

		if (id == 'tabTracks')
		{
			pager = null;
			pager = new Pager('trackItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'tracksPageNavPosition');
        		pager.showPage(1);
			try { sorttable.makeSortable ($('trackItems')) } catch (e) { alert (e) };
			
			var c = $('trackItems').select('[title="trackItemDelete"]');
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.trackDelete.bindAsEventListener(this);
				}
			}
			
			var c = $('trackItems').select('[title="trackItemEdit"]');
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.trackEdit.bindAsEventListener(this);
				}
			}
		}
		
		if (id == 'tabPlaylists')
		{
			new Ajax.Autocompleter ('playlistAuthorSearch', 'inputTagAutoComplete', '/msg/tracks.php', {paramName:'author', indicator:'playlistSearchIndicator'});
			new Ajax.Autocompleter ('playlistTitleSearch', 'inputTagAutoComplete', '/msg/tracks.php', {paramName:'title',indicator:'playlistSearchIndicator'});
			
			Event.observe ('playlistAddItem', 'click', this.playlistAddItem.bindAsEventListener (this));
			Event.observe ('playlistSubmit', 'click', this.playlistSubmit.bind(this));

			var c = $('playlistButtons').childElements();

			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "IMG")
				{
					Event.observe (c[x], 'click', this.playlistSelectImage.bindAsEventListener(this));
				}
			}
			
			var k = $('playlistItems2').select('[title="playlistItemDelete"]');

			for (var x=0; x<k.length; x++)
			{
				if (k[x].tagName == "A")
				{
					k[x].onclick = this.playlistDelete.bindAsEventListener(this);
				}
			}
		}
		
		if (id == 'tabLinks')
		{
			pager = null;
			pager = new Pager('linkItems', 15);
			pager.init();
	        	pager.showPageNav('pager', 'linksPageNavPosition');
        		pager.showPage(1);
			try { sorttable.makeSortable ($('linkItems')) } catch (e) { alert (e) };
			var c = $('linkItems').select('[title="linkItemDelete"]');
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.linkDelete.bindAsEventListener(this);
				}
			}
			
			Event.observe ('linkSubmit', 'click', this.linkSubmit.bindAsEventListener (this));
		}
		
		if (id == 'tabFavorites')
		{
			pager = null;
			pager = new Pager('favoriteItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'favoritesPageNavPosition');
        		pager.showPage(1);
			try { sorttable.makeSortable ($('favoriteItems')) } catch (e) { alert (e) }

			var c = $('favoriteItems').select('[title="favoriteItemDelete"]');
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.favoriteDelete.bindAsEventListener(this);
				}
			}
		}
	
		if (id == 'tabFriends')
		{
			pager = null;
			pager = new Pager('friendItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'friendsPageNavPosition');
        		pager.showPage(1);
			try { sorttable.makeSortable ($('friendItems')) } catch (e) { alert (e) }

			var c = $('friendItems').select('[title="friendItemDelete"]');
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.friendDelete.bindAsEventListener(this);
				}
			}
		}

		if (id.indexOf ('tabApprovals') == 0)
		{
			switch (id)
			{
				case 'tabApprovals':
					var t = lang._SND_SECTIONID_NEWS;
					break;
				case 'tabApprovalsWorkshop':
					var t = lang._SND_SECTIONID_WORKSHOP;
					break;
				case 'tabApprovalsPlaylist':
					var t = lang._SND_SECTIONID_PLAYLIST;
					break;
				case 'tabApprovalsLink':
					var t = lang._SND_SECTIONID_LINKS;
					break;
				case 'tabApprovalsLabel':
					var t = lang._SND_SECTIONID_LABEL;
					break;
				default:
					var t = lang._SND_SECTIONID_UNKNOWN;
					break;
			}
			
			pager = null;
			pager = new Pager('approveItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'approvalsPageNavPosition');
        		pager.showPage(1);
			
			var c = $('approveItems').select('[title="approveItemDis"]');

			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.rejectItem.bindAsEventListener(this, t);
				}
			}

			var c = $('approveItems').select('[title="approveItemAcc"]');

			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.approveItem.bindAsEventListener(this, t);
				}
			}
		}

		if (id =='tabManagerUsers')
		{
			pager = null;
			pager = new Pager('userManagerItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'userManagerNavPosition');
        		pager.showPage(1);

			try { sorttable.makeSortable ($('userManagerItems')) } catch (e) { alert (e) };
			
			var c = $('userManagerItems').select('[title="userManagerItemLock"]');

			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.blockUser.bindAsEventListener(this);
				}
			}
		}

		if (id =='tabManagerTracks')
		{
			pager = null;
			pager = new Pager('trackManagerItems', 15);
			pager.init();
        		pager.showPageNav('pager', 'trackManagerNavPosition');
        		pager.showPage(1);

			try { sorttable.makeSortable ($('trackManagerItems')) } catch (e) { alert (e) };
			
			var c = $('trackManagerItems').select('[title="trackManagerDelete"]');

			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "A")
				{
					c[x].onclick = this.managerDeleteTrack.bindAsEventListener(this);
				}
			}
		}
	},

	tabChange: function (tab)
	{
		if (tab.index > 0)
		{
			id = tab.tabber.tabs[tab.index].div.id;
			
			Element.extend (tab.tabber.tabs[tab.index].div);
			
			if (tab.tabber.tabs[tab.index].div.readAttribute ("loaded") > 0)
			{
			
				if (id == 'tabContent')
				{
					pager = null;
					pager = new Pager('contentItems', 15);
					pager.init();
	        			pager.showPageNav('pager', 'contentPageNavPosition');
        				pager.showPage(1);
				}
			
				if (id == 'tabWorkshop')
				{
					pager = null;
					pager = new Pager('workshopItems', 15);
					pager.init();
        				pager.showPageNav('pager', 'workshopPageNavPosition');
        				pager.showPage(1);
				}

				if (id == 'tabFavorites')
				{
					pager = null;
					pager = new Pager('favoriteItems', 15);
					pager.init();
        				pager.showPageNav('pager', 'favortiesPageNavPosition');
        				pager.showPage(1);
				}

				if (id == 'tabTracks')
				{
					pager = null;
					pager = new Pager('trackItems', 15);
					pager.init();
        				pager.showPageNav('pager', 'tracksPageNavPosition');
        				pager.showPage(1);
				}
		
				if (id == 'tabLinks')
				{
					pager = null;
					pager = new Pager('linkItems', 15);
					pager.init();
	        			pager.showPageNav('pager', 'linksPageNavPosition');
        				pager.showPage(1);
				}
				
				if (id == 'tabManagerTracks')
				{
					pager = null;
					pager = new Pager('trackManagerItems', 15);
					pager.init();
        				pager.showPageNav('pager', 'trackManagerNavPosition');
        				pager.showPage(1);
					
					try { sorttable.makeSortable ($('trackManagerItems')) } catch (e) { alert (e) };
				}
				
				if (id == 'tabManagerUsers')
				{
					pager = null;
					pager = new Pager('userManagerItems', 15);
					pager.init();
        				pager.showPageNav('pager', 'userManagerNavPosition');
        				pager.showPage(1);

					try { sorttable.makeSortable ($('userManagerItems')) } catch (e) { alert (e) };
				}
				
				if (id == 'tabPlaylists')
				{	
					new Ajax.Autocompleter ('playlistAuthorSearch', 'playlistAutocomplete', '/msg/tracks.php', {paramName:'author'});
					new Ajax.Autocompleter ('playlistTitleSearch', 'playlistAutocomplete', '/msg/tracks.php', {paramName:'title'});
				}
				return (false);
			}
			
			snd_loading (tab.tabber.tabs[tab.index].div.id + 'Content');
		
			new Ajax.Request (snd_ajax_url, {method:"post", parameters:{t:lang._SND_PANEL,act:0,cid:0,o1:tab.tabber.tabs[tab.index].div.id}, onSuccess: this.tabChange_hdlr.bindAsEventListener (this, tab.tabber.tabs[tab.index].div.id)});
		}
	},

	linkSubmit_hdlr: function (request, item)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}

		var resp 	= eval ('(' + request.responseText + ')');
		if (!resp)
		{
			return (false);
		}
		
		item.id	= 'linkItem' + resp.linkid;
		var time = item.select ('[id="itemTime"]')[0];
		time.update (resp.time);
		
		var cat  = item.select ('[id="itemCategory"]')[0];
		cat.update (resp.categoryname);
		
		var del  = item.select ('[id="itemDelete"]')[0];
		Event.observe (del, 'click', this.linkDelete.bindAsEventListener(this));
		del.id = resp.linkid;
		
		$('linkItems').tBodies[0].appendChild (item);
		
		$('linkForm').reset();
		new Effect.Appear (item);
	},

	linkSubmit: function()
	{
		if ($('linkTitle').value.length < 1 || $('linkTitle').value.length > 30)
		{
			new Effect.Highlight ('linkTitle');
			return (false);
		}
		
		if ($('linkUrl').value.length < 1 || !is_url ($('linkUrl').value))
		{
			new Effect.Highlight ('linkUrl');
			return (false);
		}
			
		if ($('linkCategory').value == 0)
                {
                        new Effect.Highlight ('linkCategory');
                        return (false);
                }
		
		var item = $('linkItemPrototype').cloneNode (true);
		Element.extend (item);
		
		
		var link = item.select ('[id="itemLink"]')[0];
		link.href = $('linkUrl').value;
		link.target = "_blank";
		link.update ($('linkTitle').value);
		
		var desc = item.select ('[id="itemDescription"]')[0];
		desc.update (new sndBB($('linkDescription').value).bbcode2html());
		
		new Ajax.Request ("/index.php?option=com_panel", {
						method:"post",parameters:{
								linkSubmit:1,
								linkTitle:$('linkTitle').value,
								linkUrl:$('linkUrl').value,
								linkCategory:$('linkCategory').value,
								linkDescription:$('linkDescription').value
							},
						onSuccess:this.linkSubmit_hdlr.bindAsEventListener(this, item)
					});
	},

	linkDelete_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		$('linkItem' + ret.linkid).remove();
	},
	
	linkDelete: function(elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{linkDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.linkDelete_hdlr.bind(this)} );
	},
	
	contentDelete_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		$('contentItem' + ret.contentid).remove();
	},
	
	contentDelete: function (elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{contentDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.contentDelete_hdlr.bind(this)} );
	},
	
	contentEdit_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		var item = ret.item;
		
		if (item)
		{
			$('contentTitle').value = item.itemtitle;
			$('contentIntro').value = item.introtext;
			$('contentText').value = item.description;
			$('contentItemImage').src = "/xchat/content_img.php?img=" + item.image;
			$('cid').value = item.id;
			$('contentAddForm').show();
			
			if (item.image.length > 1)
			{
				var s = snd_filename (item.image);
				var k = s.split(".");
				
				$('contentSid').value = k[0];
			}
		}
	},
	
	contentEdit: function(elm)
	{
		var e = Event.element (elm);
		var cid = e.id;
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{contentEdit:1,cid:cid}, onSuccess: this.contentEdit_hdlr.bind(this)} );
	},
	
	contentSubmit_hdlr: function(request, item)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var resp	= eval ('(' + request.responseText + ')');
		
		if (resp.edit)
		{
			$('contentForm').reset();
			$('contentAddForm').hide();
			return (false);
		}
		
		if (!resp)
		{
			return (false);
		}
		
		item.id	= 'contentItem' + resp.contentid;
		var time = item.select ('[id="itemTime"]')[0];
		time.update (resp.time);
		
		var cat  = item.select ('[id="itemCategory"]')[0];
		cat.update (resp.categoryname);
		
		var edit = item.select ('[id="itemEdit"]')[0];
		Event.observe (edit, 'click', this.contentEdit.bindAsEventListener(this));
		edit.id	= resp.contentid;
		
		var del  = item.select ('[id="itemDelete"]')[0];
		Event.observe (del, 'click', this.contentDelete.bindAsEventListener(this));
		del.id = resp.contentid;
		
		var link = item.select ('[id="itemLink"]')[0];
		link.id = resp.contentid;
		
		$('contentItems').tBodies[0].appendChild (item);
		
		$('contentForm').reset();
		$('contentAddForm').hide();
		
		new Effect.Appear (item);
	},

	contentSubmit: function()
	{
		if ($('contentTitle').value.length < 3)
		{
			scroll (0,0);
			new Effect.Highlight ('contentTitle');
			
			return (false);
		}
		
		if ($('contentIntro').value.length < 3)
		{
			scroll (0,0);
			new Effect.Highlight ('contentIntro');
			
			return (false);
		}
		
		if ($('contentText').value.length < 3)
		{
			new Effect.Highlight ('contentText');
			return (false);
		}
		
		var contentSid = $('contentSid').value;

		var item = $('contentItemPrototype').cloneNode (true);
		Element.extend (item);
		
		var link = item.select ('[id="itemLink"]')[0];
		link.update ($('contentTitle').value);
		
		Event.observe (link, 'click', this.contentItem.bindAsEventListener(this));
		
		var t = new sndBB($('contentIntro').value).bbcode2html();
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{cid:$('cid').value,contentSid:contentSid,contentSubmit:1,contentTitle:$('contentTitle').value,contentIntro:$('contentIntro').value,contentText:$('contentText').value,contentCategory:$('contentCategory').value}, onSuccess: this.contentSubmit_hdlr.bindAsEventListener(this, item)} );
	},
	
	favoriteDelete_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		$('favoriteItem' + ret.favoriteid).remove();
	},
	
	favoriteDelete: function(elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{favoriteDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.favoriteDelete_hdlr.bind(this)} );
	},

	friendDelete_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		$('friendItem' + ret.friendid).remove();
	},

	friendDelete: function(elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{friendDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.friendDelete_hdlr.bind(this)} );
	},


	trackDelete_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		$('trackItem' + ret.trackid).remove();
	},
	
	trackDelete: function(elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{trackDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.trackDelete_hdlr.bind(this)} );
	},

 	sendAdditional_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}

		new Effect.Highlight ('addCity',{endcolor:'#a3f3ae'});
		new Effect.Highlight ('addUIN',{endcolor:'#a3f3ae'});
		new Effect.Highlight ('addHomepage',{endcolor:'#a3f3ae'});
		new Effect.Highlight ('addFullname',{endcolor:'#a3f3ae'});
	},
	sendAdditional_hdlrE: function (request)
	{
		new Effect.Highlight ('addCity',{endcolor:'#f3a3a3'});
                new Effect.Highlight ('addUIN',{endcolor:'#f3a3a3'});
                new Effect.Highlight ('addHomepage',{endcolor:'#f3a3a3'});
		new Effect.Highlight ('addFullname',{endcolor:'#f3a3a3'});
	},

	sendAdditional: function()
	{
		var city = $('addCity').value;
		var uin = $('addUIN').value;
		var page = $('addHomepage').value;
		var name = $('addFullname').value;
		new Ajax.Request (snd_ajax_url, {method:"post", parameters:{t:lang._SND_PANEL,cid:snd_c.uid,act:1,city:city,uin:uin,page:page,name:name}, onSuccess: this.sendAdditional_hdlr.bind(this)} );
	},
	
	playlistAddItem: function()
	{
		var islocal = false;
		var source = false;
		
		if ($('playlistAuthorSearch').value.length > 3)
		{
			var islocal = 1;
			var source = $('playlistAuthorSearch').value;
		}
		else if ($('playlistTitleSearch').value.length > 3)
		{
			var islocal = 1;
			var source = $('playlistTitleSearch').value;
		}
		else if (is_url($('playlistURL').value))
		{
			var islocal = 0;
			var source = $('playlistURL').value;
		}
		else
		{
			new Effect.Highlight ('playlistItemTitle');
			new Effect.Highlight ('playlistAuthorSearch');
			new Effect.Highlight ('playlistTitleSearch');
			new Effect.Highlight ('playlistURL');
		}
		if (source && $('playlistItemTitle').value.length > 3)
		{
			var i = $('playlistItem').cloneNode(true);
			i.show();
			
			i.writeAttribute ('islocal', islocal);
			i.writeAttribute ('location', source);
			i.writeAttribute ('itemtitle', $('playlistItemTitle').value);
			var c = i.childElements();
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "SPAN")
				{	
					c[x].update ($('playlistItemTitle').value);
				}
				if (c[x].tagName == "IMG")
				{
					Event.observe (c[x], 'click', this.playlistDeleteItem.bindAsEventListener(this));
				}
			}
			$('playlistItems').appendChild (i);
			
			$('playlistAuthorSearch').value 	= "";
			$('playlistTitleSearch').value 		= "";
			$('playlistURL').value 			= "";
			$('playlistItemTitle').value 		= "";
		}
	},

	playlistDeleteItem: function (e)
	{
		var el = Event.element(e);
		Element.extend (el);
		Element.remove (el.up());
	},

	playlistSubmit_hdlr: function(request,item)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var resp 	= eval ('(' + request.responseText + ')');
		
		if (!resp)
		{
			return (false);
		}
		
		item.id	= 'playlistItem' + resp.playlistid;
		var time = item.select ('[id="itemTime"]')[0];
		time.update (resp.time);
		
		var edit = item.select ('[id="itemEdit"]')[0];
		Event.observe (edit, 'click', this.playlistEdit.bindAsEventListener(this));
		edit.id	= resp.playlistid;
		
		var del  = item.select ('[id="itemDelete"]')[0];
		Event.observe (del, 'click', this.playlistDelete.bindAsEventListener(this));
		del.id = resp.playlistid;
		
		var link = item.select ('[id="itemLink"]')[0];
		link.id = resp.playlistid;
		
		$('playlistItems2').tBodies[0].appendChild (item);
		
		$('playlistForm').reset();
		new Effect.Appear (item);
	},

	playlistSubmit: function()
	{
		if ($('playlistItems').childElements().length >= 3 && $('playlistTitle').value.length > 3)
		{
			var c = $('playlistItems').childElements();
			var items = new Array();
			
			for (var x=0; x<c.length; x++)
			{
				if (c[x].tagName == "DIV")
				{
					items[x] = {itemtitle:c[x].readAttribute('itemtitle'),islocal:c[x].readAttribute('islocal'),location:c[x].readAttribute('location')};
				}
			}
			
			var item = $('playlistItemPrototype').cloneNode (true);
			Element.extend (item);
		
			var link = item.select ('[id="itemLink"]')[0];
			link.update ($('playlistTitle').value);
			Event.observe (link, 'click', this.playlistItem.bindAsEventListener(this));
		
			$('playlistForm').request ({onComplete: this.playlistSubmit_hdlr.bindAsEventListener(this,item), method:'post', parameters:{t:lang._SND_SECTIONID_PLAYLIST,act:1,items:items.toJSON()} });
		}
		else
		{
			if ($('playlistTitle').value.length <= 3)
			{
				new Effect.Highlight ('playlistTitle');
			}
			
			new Effect.Highlight ('playlistItems');
		}
	},

	playlistSelectImage: function(el)
	{
		var e = Event.element (el);

		if (this.playlistImage)
		{
			this.playlistImage.style.border = "0px solid #ff0000";
		}

		$('playlistImage').value = e.src;

		this.playlistImage = e;
		this.playlistImage.style.border = "1px solid #f3a3a3";
	},

	playlistDelete_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		$('playlistItem' + ret.playlistid).remove();
	},

	playlistDelete: function(el)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (el);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{playlistDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.playlistDelete_hdlr.bind(this)} );
	},

	approval_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}

		var resp = eval ('(' + request.responseText + ')');
		
		if (resp.itemid)
		{
			$('approveItem' + resp.itemid).remove();
		}
	},

	approveItem: function(elm, t)
	{
		if (!confirm (lang._SND_AREYOUSURE))
                {
                        return (false);
                }

		var e = Event.element (elm);
		var cid = e.readAttribute ('id');

		new Ajax.Request ("/index.php?option=com_panel", {method:"post",parameters:{approveItem:1,t:t,cid:cid}, onSuccess:this.approval_hdlr.bindAsEventListener (this)});
	},

	rejectItem: function(elm, t)
	{
		if (!confirm (lang._SND_AREYOUSURE))
                {
                        return (false);
                }
		else
		{
			var reason = prompt (lang._SND_REJECT_REASON);
			var e = Event.element (elm);
			var cid = e.readAttribute ('id');

			new Ajax.Request ("/index.php?option=com_panel", {method:"post",parameters:{rejectItem:1,t:t,cid:cid,reason:reason}, onSuccess:this.approval_hdlr.bindAsEventListener (this)});
		}
	},

	workshopBrowseFile: function(elm)
	{
		var e = Event.element (elm);

		$('workshopFilename').update (snd_filename(e.value));
	},

	workshopSubmit_hdlr: function(request, item)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var resp	= eval ('(' + request.responseText + ')');

		if (!resp)
		{
			return (false);
		}
		if (resp.edit == 1)
		{
			$('workshopForm').reset();
			$('workshopAddForm').hide();

			return (true);
		}
		item.id	= 'workshopItem' + resp.workshopid;
		var time = item.select ('[id="itemTime"]')[0];
		time.update (resp.time);
		
		var cat  = item.select ('[id="itemCategory"]')[0];
		cat.update (resp.categoryname);
		
		var edit = item.select ('[id="itemEdit"]')[0];
		Event.observe (edit, 'click', this.workshopEdit.bindAsEventListener(this));
		edit.id	= resp.workshopid;
		
		var del  = item.select ('[id="itemDelete"]')[0];
		Event.observe (del, 'click', this.workshopDelete.bindAsEventListener(this));
		del.id = resp.workshopid;
		
		var link = item.select ('[id="itemLink"]')[0];
		link.id = resp.workshopid;
		
		$('workshopItems').tBodies[0].appendChild (item);
		
		$('workshopForm').reset();
		$('workshopAddForm').hide();

		new Effect.Appear (item);
	},

	workshopSubmit: function()
	{
		if ($('workshopTitle').value.length < 3)
		{
			scroll (0,0);
			new Effect.Highlight ('workshopTitle');
			
			return (false);
		}
		
		if ($('workshopIntro').value.length < 3)
		{
			scroll (0,0);
			new Effect.Highlight ('workshopIntro');
			
			return (false);
		}
		
		if ($('workshopText').value.length < 3)
		{
			new Effect.Highlight ('workshopText');
			return (false);
		}
		
		var item = $('workshopItemPrototype').cloneNode (true);
		Element.extend (item);
		
		var link = item.select ('[id="itemLink"]')[0];
		link.update ($('workshopTitle').value);
		Event.observe (link, 'click', this.workshopItem.bindAsEventListener(this));
		var cid = $('workshopCid').value;

		new Ajax.Request ("/index.php?option=com_panel", 
					{method:"post", parameters:
						{workshopSubmit:1,cid:cid,
							workshopTitle:$('workshopTitle').value,
							workshopIntro:$('workshopIntro').value,
							workshopText:$('workshopText').value,
							workshopCategory:$('workshopCategory').value
						}, onSuccess: this.workshopSubmit_hdlr.bindAsEventListener(this, item)} );
	},

	workshopDelete_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		$('workshopItem' + ret.workshopid).remove();
	},
	
	workshopDelete: function(elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{workshopDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.workshopDelete_hdlr.bind(this)} );
	},

	workshopItem: function(elm)
	{		
		var e = Event.element (elm);
		
		workshop_item(e.id);
	},
	
	contentItem: function(elm)
	{
		var e = Event.element (elm);
		
		content_item(e.id);
	},
	
	workshopEdit_hdlr: function(request)
	{
		if (error = detect_error (request))
                {       
                        alert (error);
                        return (false);
                }

		var ret = eval ('(' + request.responseText + ')');
		var item = ret.item;
		
		if (item)
		{
			$('workshopTitle').value = item.itemtitle;
			$('workshopIntro').value = item.introtext;
			$('workshopText').value = item.description;
			$('workshopCid').value = item.id;
			$('workshopAddForm').show();
		}
	},

	workshopEdit: function(elm)
	{
		var e = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{workshopEdit:1,cid:e.id}, onSuccess: this.workshopEdit_hdlr.bind(this)} );
	},

	trackEdit: function(elm)
	{
		var e = Event.element (elm);

		snd_loading();
		new sndUpload(e.id);
	},
	
	playlistEdit: function(id)
	{
	},
	
	playlistItem: function(elm)
	{
	},
	
	managerContainerSubmit_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		var data = ret.data;
		
		if (data)
		{
			for (var x=0; x<data.length; x++)
			{
				var t = data[x].t;
				data[x].onClickCaption = this.managerContainerElm.bind (this, 0, 0, t, '');
				
				for (var y=0; y<data[x].children.length; y++)
				{
					var k = data[x].children[y].id;
					var d = data[x].children[y].description;
					
					data[x].children[y].onClickCaption = this.managerContainerElm.bind (this, 1, k, t, d);
				}
			}
		}
			
		this.managerContainerData 	= data;
		this.managerTree 		= new Bs_Tree();
		this.managerTree.imageDir 	= "/js/gui/tree/img/win98/";
			
		this.managerTree.initByArray(this.managerContainerData);
		this.managerTree.drawInto('managerTree');
		
		this.managerTree		= false;
		this.managerTree 		= new Bs_Tree();
		this.managerTree.imageDir 	= "/js/gui/tree/img/win98/";
		this.managerTree.initByArray(this.managerContainerData);
		this.managerTree.drawInto('managerTree');

		$('managerContainerName').value = '';
		$('managerContainerDesc').value = '';
	},
	
	managerContainerSubmit: function()
	{
		if ($('managerContainerName').value.length < 3)
		{
			new Effect.Highlight ('managerContainerName');
			return (false);
		}
		
		if ($('managerContainerDesc').value.length < 3)
		{
			new Effect.Highlight ('managerContainerDesc');
			return (false);
		}
		
		var desc = new sndBB($('managerContainerDesc').value).bbcode2html();
		var name = $('managerContainerName').value;
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{manager:1,
										containerTitle:name,
										containerDesc:desc,
										containerSubmit:1,
										t:$('t').value,
										cid:$('cid').value}, onSuccess: this.managerContainerSubmit_hdlr.bind(this)} );
	},
	
	managerContainerDelete_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var ret = eval ('(' + request.responseText + ')');
		
		if (this.managerContainerData)
		{
			for (var x=0; x<this.managerContainerData.length; x++)
			{
				if (this.managerContainerData[x].t == ret.type)
				{
					for (var y=0; y<this.managerContainerData[x].children.length; y++)
					{
						if (this.managerContainerData[x].children[y].id == ret.itemid)
						{
							this.managerContainerData[x].children.splice (y,1);
							break;
						}
					}	
				}
				else
				{
					continue;
				}
			}
		}
		
		this.managerTree		= false;
		this.managerTree 		= new Bs_Tree();
		this.managerTree.imageDir 	= "/js/gui/tree/img/win98/";
		this.managerTree.initByArray(this.managerContainerData);
		this.managerTree.drawInto('managerTree');

		$('managerContainerName').value = '';
		$('managerContainerDesc').value = '';
	},
	
	managerContainerDelete: function()
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		var t = $('t').value;
		var cid = $('cid').value;
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{manager:1,containerDelete:1,cid:cid,t:t}, onSuccess: this.managerContainerDelete_hdlr.bind(this)} );
	},
	
	managerContainerElm: function(node_type, id, t, desc, elm)
	{
		if (node_type == 0)
		{			
			$('managerContainerEdit').show();
			$('managerContainerDelete').hide();
			
			$('managerContainerName').value = '';
			$('managerContainerDesc').value = '';
			
			$('t').value = t;
			$('cid').value = 0;
		}
		if (node_type == 1)
		{
			var parent = elm.parent.caption;
			var itemname = elm.caption;
			
			$('managerContainerEdit').show();
			$('managerContainerName').value = itemname;
			$('managerContainerDesc').value = new sndBB(desc).html2bbcode();
			
			$('managerContainerDelete').show();
			
			$('t').value = t;
			$('cid').value = id;
		}
	},
	
	blockUser_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var resp = eval ('(' + request.responseText + ')');
		
		if (resp)
		{
			
			var uid = resp.userid;
			var prev_state = resp.previous;

			if (prev_state == 1)
			{
				$(''+uid).src = "/images/soundlab/actions/account_normal_32x32.png";
			}
			else
			{
				$(''+uid).src = "/images/soundlab/actions/account_locked_32x32.png";
			}
		}
	},
	
	blockUser: function(elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{manager:1,blockUser:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.blockUser_hdlr.bind(this)} );
	},
	
	managerDeleteTrack_hdlr: function(request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var resp = eval ('(' + request.responseText + ')');
		
		if (resp)
		{
			if (resp.errors)
			{
				alert (resp.errors);
			}
			
			$('trackManagerItem' + resp.trackid).remove();
		}
	},
	
	managerDeleteTrack: function(elm)
	{
		if (!confirm (lang._SND_AREYOUSURE))
		{
			return (false);
		}
		
		var d = Event.element (elm);
		
		new Ajax.Request ("/index.php?option=com_panel", {method:"post", parameters:{manager:1,trackDelete:1,cid:d.id,uid:snd_c.uid}, onSuccess: this.managerDeleteTrack_hdlr.bind(this)} );
	}
}/* catalog.js */

var sndCatalog = Class.create();

sndCatalog.prototype = 
{
    initHandler: function(request)
	{
    	if (error = detect_error (request))
		{
			return (false);
		}
		
		$('sndBody').update(request.responseText);
    },
    
    initialize: function()
    {
    },
    
    show: function()
    {
        snd_loading();
        new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_PAGE_CATALOG,cid:'f'},onSuccess:this.initHandler.bindAsEventListener(this)});
    }
}

var sndMusicCatalog = new sndCatalog();
/* popup_ng.js */

var counter = 0;
var sndPopup = Class.create();

sndPopup.prototype = 
{
	initialize: function (title, content, options)
	{
		if (options == null)
		{
			this.options		= new Object();
			this.options.width 	= "300px";
			this.options.height 	= "200px";
			this.options.posX 	= "30px";
			this.options.posY 	= "30px";
			this.options.overflow  = "hidden";
			this.options.maximize	= true;
			this.options.minimize	= true;
			this.options.close	= true;
			this.options.resize	= true;
		}
		else
		{
			this.options = options;
		}
		
		if (!title)
		{
			this.title = "";
		}
		else
		{
			this.title = title;
		}
		
		if (!content)
		{
			this.content = false;
		}
		else
		{
			this.content = content;
		}
		this.popElm = false;
  	},
	
	close: function()
	{
		if (this.closeHdlr)
		{
			this.closeHdlr();
		}

		document.getElementById('sndPopups').removeChild (this.popElm);
	},
	
	minimize: function()
	{
		var popElm = this.parentNode.parentNode;
		
		popElm.style.display 		= "none";
		
		var trayElm 			= el ('img');
                trayElm.src 			= "/images/soundlab/mod_menu/popup_maximize.gif";
                trayElm.className 		= "trans";
                trayElm.style.cssFloat 		= "right";
                trayElm.style.display 		= "inline";
		
                trayElm.onclick = function ()
                {
                        popElm.style.display 	= "block";
                        snd_tray_del (trayElm);
                }
		
		trayElm.parentElm = popElm;
                snd_tray_add (trayElm);
	},
	
	maximize: function()
	{
		var popElm = this.parentNode.parentNode;
        
                if (popElm.maximized == false)
                {                
                        popElm.oldWidth 		= popElm.style.width;
                        popElm.oldHeight 		= popElm.style.height;
                        popElm.oldTop 			= popElm.style.top;
                        popElm.oldLeft 			= popElm.style.left;
                        popElm.style.width 		= (getPageWidth()) + 'px';
                        popElm.style.height 		= "100%";
                        popElm.style.top 		= '0px';
                        popElm.style.left 		= '0px';
                
                        popPos 				= findPos(popElm);
                        
                        popElm.contentElm.style.height = (popPos[3] - 30) + 'px';
                        popElm.maximized 		= true;
                        
                        if (this.maximizeHdlr)
                        {
                                this.maximizeHdlr(true);
                        }
                        
                        return (false);
                }
		
		if (popElm.maximized == true)
                {
                        popElm.style.width 		= popElm.oldWidth;
                        popElm.style.height 		= popElm.oldHeight;
                        popElm.style.top 		= popElm.oldTop;
                        popElm.style.left 		= popElm.oldLeft;
                        popElm.contentElm.style.height 	= (parseInt(popElm.oldHeight)-30) + 'px';
                        popElm.maximized 		= false;
                        
                        if (this.maximizeHdlr)
                        {
                                this.maximizeHdlr(false);
                        }
                        
                        return (false);
                }
	},
	
	hide: function()
	{
		if (this.popElm.closeHdlr)
		{
			this.popElm.closeHdlr();
		}

		this.popElm.hide();
		this.popElm.valid = false;
	},

	position: function (x, y)
	{
		this.popElm.style.top		= y;
		this.popElm.style.left		= x;
	},

	resize: function (thisObj)
	{
		thisObj.contentElm.style.height	= (parseInt(thisObj.popElm.style.height) - 46) + 'px';
	},

  	show: function(x,y) 
	{
		this.clearElm 			= el('div');
        	this.clearElm.className 	= "clearFloatC";
	
		this.popElm 			= el('div');
        	this.popElm.className 		= "sndPopup";
		
		if (this.options.posY)
			this.popElm.style.top		= this.options.posY;
		else if (y)
			this.popElm.style.top		= y;
		
		if (this.options.posX)
			this.popElm.style.left		= this.options.posX;
		else if (x)
			this.popElm.style.left		= x;
			
		if (this.options.overflow)
			this.popElm.style.overflow 	= this.options.overflow;
		
		this.popElm.style.width	= this.options.width;
		this.popElm.style.height	= this.options.height;
		
		this.menuElm 			= el('div');
        	this.menuElm.className 		= "sndPopupMenu";
		
		this.titleElm 			= el('div');
        	this.titleElm.className 	= "sndPopupMenuText";
        	this.titleElm.innerHTML 	= this.title;
		
        	this.closeElm 			= el('div');
	        this.closeElm.onclick 		= this.close.bind(this);
	        this.closeElm.title 		= lang._SND_CLOSE;
	        this.closeElm.className 	= "sndPopupClose";
		
		this.minimizeElm 		= el('div');
        	this.minimizeElm.onclick 	= this.minimize;
        	this.minimizeElm.title 		= lang._SND_MINIMIZE;
        	this.minimizeElm.className 	= "sndPopupMinimize";
        
        	this.maximizeElm 		= el('div');
        	this.maximizeElm.onclick 	= this.maximize;
        	this.maximizeElm.title 		= lang._SND_MAXIMIZE;
        	this.maximizeElm.className 	= "sndPopupMaximize";
		
		if (this.options.close)
			this.menuElm.appendChild (this.closeElm);
			
		if (this.options.minimize)
			this.menuElm.appendChild (this.minimizeElm);
			
		if (this.options.maximize)
			this.menuElm.appendChild (this.maximizeElm);
			
		this.resizeElm			= el('div');
		this.resizeElm.style.border	= '1px solid #ff0009';
		this.resizeElm.style.cssFloat	= 'right';
		this.resizeElm.style.width	= "16px";
		this.resizeElm.style.height	= "16px";
	
		this.menuElm.appendChild (this.titleElm);
        	this.menuElm.appendChild (this.clearElm);
		
		this.contentElm 		= el('div');
        	this.contentElm.className 	= "sndPopupContent";
        	this.contentElm.style.width 	= "100%";
	        this.contentElm.style.height 	= (parseInt(this.options.height) - 46) + 'px';
		
		if (this.content)
			this.contentElm.innerHTML = this.content;
		
		this.popElm.appendChild (this.menuElm);
        	this.popElm.appendChild (this.contentElm);
		

		this.popElm.style.display 	= 'block';
		if (isIE)
		{
			this.popElm.style.position	= 'absolute';
			scroll (0,0);
		}
		else
			this.popElm.style.position	= 'fixed';
        	this.popElm.maximized 		= false;
		this.popElm.pid 		= counter;
		this.popElm.valid		= true;
		this.popElm.contentElm		= this.contentElm;
		
		new Draggable (this.popElm, {handle:this.menuElm,starteffect:false,endeffect:false});
		
		if (this.options.resize)
		{
			this.popElm.appendChild (this.resizeElm);
			var thisObj = this;
			new Draggable (this.resizeElm, {revert:true,onDrag: function () { thisObj.resize (thisObj) }});
			new Resizable (this.popElm, {starteffect:false,endeffect:false,handle:this.resizeElm, onDrag: function () { thisObj.resize (thisObj) }});
		}
		
		$('sndPopups').appendChild (this.popElm);
		counter++;
	}
};/* preview.js */

var sndPreview = Class.create();

sndPreview.prototype =
{
	initialize: function(data, elm)
	{
		if (elm)
		{
			this.elm 	= elm;
		}
		else
		{
			this.elm	= false;
		}
		if (!data || data.length < 3)
		{
			return (false);
		}
		else
		{
			this.data 	= data;
		}
	},
	
	show: function()
	{
		if (this.data)
		{
			var options 		= {resize:false, width:'600px', height:'480px', maximize:false, minimize:true, close:true, position:'fixed'};
			this.pid 		= new sndPopup (lang._SND_PREVIEW, false, options);
			this.pid.show();
			
			var c			= document.createElement ('div');
			c.style.margin		= "10px";
			c.style.width 		= "575px";
			c.style.height		= "430px";
			c.style.overflow	= "auto";
			c.style.border		= "1px solid #cfe0e0";
			c.style.textAlign	= "left";
			var res 		= new sndBB(this.data).bbcode2html();
			
			c.innerHTML 		= res;
			this.pid.contentElm.appendChild (c);
		}
		else
		{
			return (false);
		}
	}
}/* search.js */

var sndSearch = Class.create();

sndSearch.prototype =
{
	initialize: function (str)
	{
		if (str && str.length < 3)
		{
			new Effect.Pulsate ('searchStr');
			
			return (false);
		}

		if (!str || str == lang._SND_SEARCH)
		{
			return (false);
		}
		
		this.query = str;
	},

	search: function()
	{
		if (!this.query)
			return (false);

		snd_loading();
		
		new Ajax.Request(snd_ajax_url, {method:'post',parameters:{t:lang._SND_SECTIONID_SEARCH,cid:1,o1:this.query}, onSuccess: this.search_hdlr.bindAsEventListener(this)} );
	},

	search_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		$('sndBody').innerHTML = request.responseText;

		pager = new Pager('searchResults', 15);
		pager.init();                                                                  
	        pager.showPageNav('pager', 'searchPageNav');
		pager.showPage(1); 
		sorttable.makeSortable($('searchResults'));
	}		
}/* tags.js */

var sndTags = Class.create();

sndTags.prototype =
{
	initialize: function(id)
	{
		if (id > 0)
		{
			this.id 	= id;
		}
		else
		{
			return (false);
		}
	},

	popup_hdlr: function (request)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var tags = eval ('(' + request.responseText + ')');
		var tag = '';
		
		if (this.t.firstChild)
		{
			this.t.removeChild (this.t.firstChild);
		}
		
		if (tags.taglist)
		{
			for (x=0; x<tags.taglist.length; x++)
			{
				tag 			= el ('a');
				Element.extend (tag);
				size 			= 1/(3.3 / tags.taglist[x].count);
				
				if (size < 0.8)
					size 	= 0.8;
				
				if (size > 3.3)
					size = 3.3;
					
				tag.style.fontSize	= size + 'em';
				tag.id			= 'taG' + tags.taglist[x].tagname;
				tag.writeAttribute ('tagid', tags.taglist[x].tagid);
				
				tag.onclick		= tag_item.bindAsEventListener(this);
				
				tag.appendChild (document.createTextNode (tags.taglist[x].tagname + " "));
				this.t.appendChild (tag);
			}
		}
		
		if (!isIE)
		{
			new Ajax.Autocompleter ('inputTag', 'inputTagAutoComplete', '/msg/tags.php', {paramName:'tagAT', indicator:'tagsLoading'});
		}
		else
		{
			new Ajax.Autocompleter ('inputTag', 'inputTagAutoComplete', '/msg/tags.php', {paramName:'tagAT'});
		}
	},
	
	popup: function()
	{
		var options 		= {resize:false, width:'370px', height:'530px', maximize:false, minimize:true, close:true, position:'fixed'};
		this.pid 		= new sndPopup (lang._SND_NAME_TAGS, false, options);
		this.pid.show();
		this.pid.contentElm.style.textAlign = "left";
		
		this.i			= el ('input');
		this.i.type		= "text";
		this.i.className	= "uploadInputLarge";
		this.i.style.zIndex	= "4";
		this.i.style.position	= "absolute";
		this.i.style.top	= "35px";
		this.i.style.left	= "20px";
		this.i.id		= "inputTag";

		var ia			= el ('div');
		ia.id			= "inputTagAutoComplete";		
		ia.className		= "sndAutocomplete";
		ia.style.zIndex		= "6";
		ia.style.display	= "block";
		
		var il			= el ('img');
		il.src			= "/images/soundlab/ajax-loader.gif";
		il.id			= "tagsLoading";
		il.style.position	= "absolute";
		il.style.zIndex		= "5";
		il.style.display	= "none";
		il.style.top		= "42px";
		il.style.left		= "305px";
		
		var s			= el ('img');
		s.src			= "/images/soundlab/actions/tag_32x32.png";
		s.title			= lang._SND_ACTION_TAG;
		s.alt			= lang._SND_ACTION_TAG;
		
		s.className		= "trans";
		s.style.width		= "32px";
		s.style.height		= "32px";
		s.style.position	= "absolute";
		s.style.zIndex		= "5";
		s.style.top		= "37px";
		s.style.left		= "270px";
		s.style.opacity		= "0.79";
		s.style.cursor		= "pointer";
		
		s.onclick		= this.addTag.bind(this);
		
		var d			= el ('div');
		d.style.position	= 'absolute';
		d.style.background	= "url('/images/soundlab/face_tags.jpg')"
		d.style.width		= "370px";
		d.style.height		= "500px";
		d.style.zIndex		= "1";
		
		var cover			= el ('div');
		cover.style.position		= 'absolute';
		cover.style.background		= "#000000";
		cover.style.opacity		= "0.5";
		cover.style.filter		= "alpha(opacity=50)";
		cover.style.width		= "370px";
		cover.style.height		= "500px";
		cover.style.zIndex		= "2";

		this.t	 		= el ('div');
		this.t.style.position	= "absolute";
		this.t.style.width	= "350px";
		this.t.style.right	= "10px";
		this.t.style.height 	= "410px";
		this.t.style.zIndex	= "3";
		this.t.style.top	= "90px";
		this.t.style.color	= "#f9f9f9";
		this.t.style.overflow	= "auto";
		this.t.style.textAlign = "justify";
		
		
		var tags 		= el ('div');
	
		tags.appendChild (this.t);
		tags.appendChild (d);
		tags.appendChild (cover);
		tags.appendChild (this.i);
		tags.appendChild (il);
		tags.appendChild (s);
		
		this.pid.contentElm.appendChild (tags);

		snd_loading (this.t, "/images/soundlab/ajax-loader.gif");
		
		new Ajax.Request (snd_ajax_url, {method:"post", parameters:{t:lang._SND_TAGS,act:0,cid:this.id}, onSuccess: this.popup_hdlr.bindAsEventListener (this) } );
	},

	
	getHTML: function()
	{
		return ('');
	},
	
	addTag_hdlr: function (request, tagname)
	{
		if (error = detect_error (request))
		{
			alert (error);
			return (false);
		}
		
		var r = eval ('(' + request.responseText + ')');
		
		if (r)
		{
			var tagid = r.tagid;
		}
		else
		{
			var tagid = 0;
		}
		
		if ($('taG' + tagname))
		{
			var newsize = parseFloat ($('taG' + tagname).getStyle('font-size'));
			if (newsize > 3.4)
				newsize = 3.3;

			newsize = (newsize + 0.1) + 'em';
		
			$('taG' + tagname).setStyle ({fontSize: newsize});
		}	
		else
		{
			tag 			= el ('a');
			Element.extend (tag);
			tag.onclick		= tag_item;
			tag.id			= 'taG' + tagname;
			tag.style.margin	= "5px";
			tag.style.fontSize	= '0.8em'
			tag.writeAttribute ('tagid', tagid);
			
			var col 		= Math.floor(Math.random()*70);
		
			tag.appendChild (document.createTextNode (tagname + " "));
			this.t.appendChild (tag);
		}	

		$('tagsLoading').style.display = 'none';
	},
	
	addTag: function()
	{
		if (this.i.value.length < 1 || !snd_c.uid || snd_c.uid <= 0)
		{
			return (false);
		}
		
		var tagname 	= this.i.value;
		this.i.value	= '';
		
		$('tagsLoading').style.display = 'block';
		
		new Ajax.Request (snd_ajax_url, {method:"post", parameters:{t:lang._SND_TAGS,act:1,cid:this.id,o1:tagname}, onSuccess: this.addTag_hdlr.bindAsEventListener (this, tagname) } );
	}
}

function tag_item_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}
	
	$('sndBody').innerHTML = request.responseText;
}

function tag_item (e)
{
	if (typeof(e) == "object")
	{
		var elm = Event.element (e);
		
		var id = elm.readAttribute ('tagid');
	}
	if (typeof(e) == "number")
	{
		var id = e;
	}
	if (typeof(e) == "string")
	{
		var id = e;
	}
	
	snd_history_add ("#p:" + lang._SND_TAGS + ";c:" + id, {handler:tag_item,cid:id});
	
	snd_loading();
	
	new Ajax.Request (snd_ajax_url, {method:"post", parameters:{t:lang._SND_TAGS_SEARCH,act:0,cid:id}, onSuccess: tag_item_hdlr} );
	
	return (false);
}/* upload_ng.js */

var sndUpload = Class.create();

sndUpload.prototype = 
{
    initHandler: function(request)
	{
		if (error = detect_error (request))
		{
			return (false);
		}
		
		$('sndBody').update(request.responseText);
        $('uploadNextStep').observe('click', this.nextStep.bindAsEventListener(this));
        $('uploadPrevStep').observe('click', this.prevStep.bindAsEventListener(this));
        $('uploadSave').observe('click', this.save.bindAsEventListener(this));
        
        $('locationLocal').selected = false;
        $('locationRemote').selected = false;
        
        $('locationRemoteImg').observe('click', this.toggleLocation.bindAsEventListener(this));
        $('locationLocalImg').observe('click', this.toggleLocation.bindAsEventListener(this));
        
        new Ajax.Autocompleter("trackTags", "tagAutocomplete", "/index.php?option=com_autocomplete&type=tag", {paramName:'param', tokens: ','});
        new Ajax.Autocompleter("trackGenre", "genreAutocomplete", "/index.php?option=com_autocomplete&type=genre", {paramName:'param'});
        new Ajax.Autocompleter("trackPlugins", "pluginAutocomplete", "/index.php?option=com_autocomplete&type=plugin", {paramName:'param', tokens: ','});
        new Ajax.Autocompleter("trackDaw", "dawAutocomplete", "/index.php?option=com_autocomplete&type=plugin", {paramName:'param'});
        
        $('uploadWizardStep2').hide();
        $('uploadWizardStep3').hide();
        $('uploadTechnicals').hide();
        $('uploadCollapse').hide();
        $('uploadError').hide();
        $('uploadWait').hide();
        
        this.trackTitle          = $('trackTitle').value;
        this.trackTags           = $('trackTags').value;
        this.trackGenre          = $('trackGenre').value;
        this.trackDescription    = $('trackDescription').value;
        this.trackCategory       = $('trackCategory').value;
        this.trackUrl            = $('trackUrl').value;
        this.trackSize           = $('trackSize').value;
        this.trackType           = $('trackType').value;
        this.source              = $('trackSourceIndicator').value;
        this.trackSid            = $('trackSid').value;
        this.trackDaw            = $('trackDaw').value;
        this.trackPlugins        = $('trackPlugins').value;
        this.trackDawVersion     = $('trackDawVersion').value;
        this.trackTechnical      = $('trackTechnicalOthers').value;
        this.trackDuration       = $('trackDuration').value;
        this.trackLicense        = $('trackLicense').value;
        
        if (this.source == 1)
        {
            this.source = true;
            this._toggleSelect ($('locationRemote'), true);
            this._toggleSelect ($('locationLocal'), false);
        }
        else if (this.source == -1)
        {
            this.source = false;
            this._toggleSelect ($('locationRemote'), false);
            this._toggleSelect ($('locationLocal'), true);
        }
        else
        {
            this.source = null;
            this._toggleSelect ($('locationRemote'), false);
            this._toggleSelect ($('locationLocal'), false);
        }
        
        if (this.cid == 0)
        {
            $('uploadSave').hide();
        }
    },
    
	initialize: function(cid)
	{
		if (!cid || cid == 0)
        {
			this.cid = 0;
        }
		else
        {
			this.cid = cid;
        }
	
        this.source             = null;
        this.uploadFinishedOnce = false;
        this.step               = 1;
        
		new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_PAGE_NEW,cid:this.cid,step:this.step},onSuccess:this.initHandler.bindAsEventListener(this)});
	},
    
    saveHdlr: function (request)
    {
        if (error = detect_error (request))
        {
            alert (error);
            return;
        }
        else
        {
            var resp = eval ('(' + request.responseText + ')');
            if (resp)
            {
                music_item_sid (resp.sid);
            }
        }
    },
    
    save: function(event)
    {
        var s = null;
        
        if (this.source == true)
        {
            this.trackUrl               = $('trackUrl').value;
            this.trackDuration          = $('trackDuration').value;
            this.trackSize              = $('trackSize').value;
            this.trackType              = $('trackType').value;
            
            s                            = "URL [" + this.trackUrl + "]";
        }
        if (this.source == false)
        {
            s = "Plik z dysku";
        }
        if (this.source == null)
        {
            s = "Niezdefiniowane";
        }
        
        if (!this.validateData())
        {
            return;
        }
    
        if ( confirm ("Czy napewno chcesz zapisaÄ‡ dane:\nTytuÅ‚: " + this.trackTitle + "\nGatunek: " + this.trackGenre + "\nZrodlo: " + s))
        {
            new Ajax.Request (snd_ajax_url, {method:'post',parameters:{   
                                                                            t:lang._SND_PAGE_NEW,
                                                                            cid:this.cid,
                                                                            step:this.step,
                                                                            trackTags:this.trackTags,
                                                                            trackTitle:this.trackTitle,
                                                                            trackGenre:this.trackGenre,
                                                                            trackDescription:this.trackDescription,
                                                                            trackUrl:this.trackUrl,
                                                                            source:this.source,
                                                                            trackUrl:this.trackUrl,
                                                                            trackDaw:this.trackDaw,
                                                                            trackPlugins:this.trackPlugins,
                                                                            trackDawVersion:this.trackDawVersion,
                                                                            trackTechnical:this.trackTechnical,
                                                                            trackCategory:this.trackCategory,
                                                                            trackLicense:this.trackLicense,
									    trackDuration:this.trackDuration,
                                                                            sid:this.trackSid,
                                                                            save:1
                                                                        }, onSuccess:this.saveHdlr.bindAsEventListener(this)});
        }        
        else
        {
            return;
        }
    },
    
    prevStep: function(event)
    {        
        this.step = 1;
        $('uploadWizardStep2').hide();
        $('uploadWizardStep3').hide();
        $('uploadWizardStep1').show();
        $('uploadNextStep').show();
    },
    
    nextStep: function(event)
    {
        if (this.step == 1)
        {
            if (this.source == true)
            {
                $('uploadWizardStep3').show();
            }
            else if (this.source == false)
            {
                $('uploadWizardStep2').show();
            }
            else if (this.source == null)
            {
                alert ("Musiz wybraÄ‡ ÅºrÃ³dÅ‚o utworu");
                return;
            }
         
            $('uploadNextStep').hide();
            $('uploadWizardStep1').hide();
            
            this.trackTitle          = $('trackTitle').value;
            this.trackTags           = $('trackTags').value;
            this.trackGenre          = $('trackGenre').value;
            this.trackDescription    = $('trackDescription').value;
            this.trackCategory       = $('trackCategory').value;
            this.trackUrl            = $('trackUrl').value;
            this.trackSize           = $('trackSize').value;
            this.trackType           = $('trackType').value;
            this.trackSid            = $('trackSid').value;
            this.trackDaw            = $('trackDaw').value;
            this.trackPlugins        = $('trackPlugins').value;
            this.trackDawVersion     = $('trackDawVersion').value;
            this.trackTechnical      = $('trackTechnicalOthers').value;
            this.trackDuration       = $('trackDuration').value;
            this.trackLicense        = $('trackLicense').value;
            
            if (this.source == false)
            {
                this.step = 2;
                
                $('uploadSave').show();
                $('uploadWizardStep2').show();
                $('uploadWizardStep3').hide();
                $('uploadWizardStep1').hide();
            }
            else if (this.source == true)
            {
                this.step = 3;
                $('uploadWizardStep2').hide();
                $('uploadWizardStep3').show();
                $('uploadWizardStep1').hide();
                $('uploadSave').show();
            }
        }
    },
    
    toggleLocation: function(event)
    {
        debugger;
        e = event.target;
        
        if (e.id == 'locationRemoteImg')
        {
            this.source = true;
            
            this._toggleSelect ($('locationRemote'), true);
            this._toggleSelect ($('locationLocal'), false);
        }
        
        if (e.id == 'locationLocalImg')
        {
            this.source = false;
            
            this._toggleSelect ($('locationRemote'), false);
            this._toggleSelect ($('locationLocal'), true);
        }
    },
    
    _toggleSelect: function (e, selected)
    {        
        if (selected)
        {
            e.style.backgroundColor = '#74ffff';
        }
        else
        {
            e.style.backgroundColor= '#f9f9f9';
        }
    },
    
    flashUploadFinished: function(res)
    {
        /* res holds the server response */
        
        var request = new Object();
        request.responseText = res;
        
        if (error = detect_error (request))
        {
            alert (error);
        }
        else
        {
            var file = eval ('(' + request.responseText + ')');
            this.trackUpdateFileInfo (file);
        }
        
        $('uploadWait').hide();
        $('uploadSave').show();
    },
    
    flashUploadFailed: function()
    {
        $('uploadWait').hide();
        $('uploadSave').hide();
        $('uploadError').show();
    },
    
    flashUploadStarted: function()
    {
        $('uploadError').hide();
        $('uploadWait').show();
        $('uploadSave').hide();
    },
    
    trackUpdateFileInfo: function (file)
    {
        $('trackFileMimeIcon').src = "/images/soundlab/mimetypes/" + file.fformat + ".png";
        $('trackFileFormat').update (file.fformat);
        $('trackFileDuration').update (file.fduration);
        $('trackFileSize').update (file.fsize);
        $('trackFileTime').update (file.ftime);
        $('trackFileBitrate').update (file.fbitrate);
        $('trackFileInfoView').show();
    },
    
    validateData: function()
    {
        if (this.trackTitle.length < 3)
        {
            alert ("Brak tytuÅ‚u utrowy (minimum 3 znaki)");
            return (false);
        }
        if (this.trackDescription.length < 10)
        {
            alert ("Brak opisu utworu (minimum 10 znakÃ³w)");
            return (false);
        }
        if (this.trackGenre.length < 3)
        {
            alert ("Brak gatunku (minimum 3 znaki)");
            return (false);
        }
        if (this.source == null)
        {
            alert ("Musisz wybraÄ‡ ÅºrÃ³dÅ‚o utworu");
            return (false);
        }
        if (this.source == true)
        {
            if (this.trackUrl.length < 6)
            {
                alert ("NieprawidÅ‚owy URL pliku");
                return (false);
            }
        }
        return (true);
    }
}

/* called from flash uploader */
function uploadFinished(response)
{
    res = Base64.decode(response);
    sndUpload.prototype.flashUploadFinished(res);
}

function uploadFailed()
{
    sndUpload.prototype.flashUploadFailed();
}

function uploadStarted()
{
    sndUpload.prototype.flashUploadStarted();
}

/*
 * textarea BBcode stuff
 */
function getSelection(ta)
{ 
    var bits = [ta.value,'','','']; 
    if(document.selection)
    { 
        var vs = '#$%^%$#';
        var tr=document.selection.createRange()
        if(tr.parentElement()!=ta) return null;
        bits[2] = tr.text;
        tr.text = vs;
        fb = ta.value.split(vs);
        tr.moveStart('character',-vs.length);
        tr.text = bits[2];
        bits[1] = fb[0];
        bits[3] = fb[1];
    }
    else
    { 
        if(ta.selectionStart == ta.selectionEnd) return null;
            bits=(new RegExp('([\x00-\xff]{'+ta.selectionStart+'})([\x00-\xff]{'+(ta.selectionEnd - ta.selectionStart)+'})([\x00-\xff]*)')).exec(ta.value);
    }

    return bits;
}

function matchPTags(str)
{
    str = ' ' + str + ' ';
    ot = str.split(/\[[B|U|I].*?\]/i);
    ct = str.split(/\[\/[B|U|I].*?\]/i);
    return ot.length==ct.length;
}

function addPTag(ta, pTag)
{
    bits = getSelection(ta);
    if(bits)
    { 
        if(!matchPTags(bits[2]))
        {
            alert('NieprawidÅ‚owe zaznaczenie\n\nW zaznaczonym bloku znajdujÄ… siÄ™ niezamkniÄ™te tagi.');
            return;
        }
        
        ta.value = bits[1] + '[' + pTag + ']' + bits[2] + '[/' + pTag + ']' + bits[3];
    }
}
/* user.js */

var snd_a_favorite = 402;
var snd_a_friend = 401;
var snd_a_enemy = 403;

function snd_hover_user (elm)
{
	if (menu.valid)
		menu.hide();
	
	menu.show(elm);
} 

function user_item (id)
{
	get_page_callback = snd_user;
	get_page (lang._SND_PAGE_PROFILE_USER,id,0,0,0,1);
	
	snd_history_add ("#p:" + lang._SND_PAGE_PROFILE_USER + ";c:" + id, {t:lang._SND_PAGE_PROFILE_USER,cid:id,handler:function() { user_item (id) }});
}

function snd_user()
{
	var tabberOptions = {'manualStartup':true};
	tabberAutomatic(tabberOptions)
}

function snd_new_login(obj)
{
	var s = obj.value;
	
	if (snd_unicode (s) != -1)
	{
		new Effect.Highlight (obj, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		return (false);
	}
	
	if (s.length < 3 || s.length > 12 || !snd_specialchars(obj.value, "_"))
	{
		new Effect.Highlight (obj, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		return (false);
	}
	else
	{
		new Effect.Highlight (obj, {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});
		return (true);
	}
}

function snd_new_realname(obj)
{
	var s = obj.value;
	
	if (s.length < 3 || s.length > 128 || !snd_specialchars(obj.value, " "))
	{
		new Effect.Highlight (obj, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		return (false);
	}
	else
	{
		new Effect.Highlight (obj, {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});
		return (true);
	}
}

function snd_new_email(obj)
{
	var s = obj.value;
	
	if (!snd_email (s))
	{
		new Effect.Highlight (obj, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		return (false);
	}
	else
	{
		new Effect.Highlight (obj, {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});
		return (true);
	}
}

function snd_new_password(obj)
{
}

function snd_new_submit_hdlr(request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}
	
	$('sndBody').innerHTML = request.responseText;
}

function snd_new_submit()
{
	
	var login = $('new_username');
	var name = $('new_realname');
	var email = $('new_email');
	var pass = $('new_password');
	var pass2 = $('new_password2');
	var sid = $('sid');
	var token = $('new_token');
	var language = $('new_language');
	var city = $('new_city');
	var uin = $('new_uin');
	var homepage = $('new_homepage');
	
	if (pass.value.length < 4 || pass2.value.length < 4)
	{
		alert (lang._SND_U_DATAMISSING);
		
		new Effect.Highlight (pass, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		new Effect.Highlight (pass2, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		
		return (false);
	}
	else
	{
		new Effect.Highlight (pass, {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});
		new Effect.Highlight (pass2, {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});
	}
	
	if (pass.value != pass2.value)
	{
		alert (lang._SND_VPASS);
		
		new Effect.Highlight (pass, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		new Effect.Highlight (pass2, {endcolor:'#f3a3a3',restorecolor:'#f3a3a3'});
		
		return (false);
	}
	else
	{
		new Effect.Highlight (pass, {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});
		new Effect.Highlight (pass2, {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});
	}
	
	if (!snd_new_email (email))
	{
		return (false);
	}
	
	if (!snd_new_login (login))
	{
		return (false);
	}
		
	if (!snd_new_realname (name))
	{
		return (false);
	}
	
	snd_loading();
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_PAGE_NEWUSER,act:1,cid:0,lang:language.value,sid:sid.value,secret:token.value,uname:login.value,realname:name.value,email:email.value,pass:pass.value,pass2:pass2.value,city:city.value,homepage:homepage.value,uin:uin.value},onSuccess:snd_new_submit_hdlr});
}

var _snd_user_cloud = Class.create();

_snd_user_cloud.prototype = 
{
	initialize: function (e, parent)
	{
		this.x = 0;
		this.e = e;
		this.p = parent;
		
	}
}


function snd_user_cloud_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}

	var ret = eval ('(' + request.responseText + ')');
	
	if (ret)
	{
		if (ret.users)
		{
			this.i			= document.createElement ('input');
			this.i.type		= "text";
			this.i.className	= "uploadInputLarge";
			this.i.style.zIndex	= "4";
			this.i.style.top	= "35px";
			this.i.style.left	= "20px";
			this.i.style.cssFloat   = "left";
			this.i.style.styleFloat = "left";
			this.i.id		= "inpUserName";

			var ia			= document.createElement ('div');
			ia.id			= "inputTagAutoComplete";		
			ia.className		= "sndAutocomplete";
			ia.style.zIndex		= "6";
			ia.style.display	= "none";
		
			var il			= document.createElement ('img');
			il.src			= "/images/soundlab/ajax-loader.gif";
			il.id			= "tagsLoading";
			il.style.zIndex		= "5";
			il.style.display	= "none";
			il.style.top		= "42px";
			il.style.left		= "305px";

			var k			= document.createElement ('img');
			k.src			= "/images/soundlab/mod_menu/profile.png";
		
			k.className		= "trans";
			k.style.width		= "32px";
			k.style.height		= "32px";
			k.style.zIndex		= "5";
			k.style.top		= "37px";
			k.style.left		= "270px";
			k.style.cursor		= "pointer";
			k.style.cssFloat	= "left";
			k.style.styleFloat	= "left";
			k.style.marginLeft	= "5px";
			k.style.marginRight	= "10px";
			k.onclick		= snd_user_cloud_search;

			var t = document.createElement ('table');
			t.cellPadding = "0";
			t.cellSpacing = "0";
			t.width = "100%";

			var tTr = t.insertRow(0);

			var c = tTr.insertCell (0);
			c.className = "cornerTopLeft";

			c = tTr.insertCell (1);
			c.className = "cornerTopCenter";

			c = tTr.insertCell (2);
			c.className = "cornerTopRight";

			tTr = t.insertRow (1);

			c = tTr.insertCell (0);
			c.className = "cornerLeft";

			c = tTr.insertCell (1);
			c.className = "cornerCenter";

			var d = document.createElement ('div');
			d.appendChild (i);
			d.appendChild (ia);
			d.appendChild (k);

			d.style.textAlign = 'justify';
			
			for (var x=0; x<ret.users.length; x++)
			{
				var s = el ('span');
			
				Element.extend (s);
			
				s.style.fontSize = (9 + Math.floor(Math.random() * 12)) + "px";
				s.appendChild (document.createTextNode (ret.users[x].username+ " "));
				s.id = ret.users[x].username;
				s.writeAttribute ('userid', ret.users[x].id);
				s.onclick = function () { user_item(this.readAttribute('userid')) } ;
				s.onmouseover = function () { this.style.cursor = "pointer" };
				
				d.appendChild (s);
			}

			c.appendChild (d);
			
			c = tTr.insertCell (2);
			c.className = "cornerRight";
			
			tTr = t.insertRow (2);
		
			c = tTr.insertCell (0);
			c.className = "cornerBottomLeft";

			c = tTr.insertCell (1);
			c.className = "cornerBottomCenter";

			c = tTr.insertCell (2);
			c.className = "cornerBottomRight";
			
			$('sndBody').innerHTML = '';
			$('sndBody').appendChild (t);

			if (!isIE)
			{
				new Ajax.Autocompleter ('inpUserName', 'inputTagAutoComplete', '/msg/users.php', {paramName:'msgAT'});
			}
			else
			{
				new Ajax.Autocompleter ('inpUserName', 'inputTagAutoComplete', '/msg/users.php', {paramName:'msgAT'});
			}
		}
	}	
}

function snd_user_cloud ()
{
	snd_loading();	
	
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_PAGE_CLOUD,act:0,cid:0}, onSuccess:snd_user_cloud_hdlr} );
}

function snd_user_cloud_search()
{
	var u = $('inpUserName');

	if (u.value.length >= 3)
	{
		var user = $(u.value);
		Element.extend (user);
		if (user)
		{
			user_item (user.readAttribute('userid'));
		}
	}
}/* user_utils.js */

function snd_add_favorite_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		
		new Effect.Puff ('favoriteAddIcon');
		return (false);
	}

	new Effect.Puff ('favoriteAddIcon');
}

function snd_add_favorite (type, id, title)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
	}
    
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_ADD_FAVORITE,type:type,cid:id,act:1,o1:title},onSuccess: snd_add_favorite_hdlr});
}

function snd_del_favorite_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}	

	new sndPanel(snd_c.uid, snd_c.uname).show();
}

function snd_del_favorite (id)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
	}
	
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_DEL_FAVORITE,cid:id,act:1},onSuccess: snd_del_favorite_hdlr});
}

function snd_accept_friend_hdlr (request)
{
        if (error = detect_error (request))
        {
                alert (error);
		msg_inbox();

                return (false);
        }
}

function snd_accept_friend (id, inviteid)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
	}

	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_ADD_FRIEND,cid:id,act:1},onSuccess: snd_accept_friend_hdlr});
}

function snd_del_friend_hdlr (request)
{
        if (error = detect_error (request))
        {
                alert (error);
                return (false);
        }

	new sndPanel(snd_c.uid, snd_c.uname).show();
}

function snd_del_friend (id)
{
	if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
		
	}

	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_DEL_FRIEND,cid:id,act:1},onSuccess: snd_del_friend_hdlr});
}

function snd_invite_friend_hdlr (request)
{
        if (error = detect_error (request))
        {
                alert (error);

		new Effect.Puff ('friendAddIcon');
                return (false);
        }

	new Effect.Puff ('friendAddIcon');
}

function snd_invite_friend (id)
{
        if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
		
	}

	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_INVITE_FRIEND,cid:id,act:1},onSuccess: snd_invite_friend_hdlr});
}

function snd_reject_friend_hdlr (request)
{
        if (error = detect_error (request))
        {
                alert (error);
		msg_inbox();

                return (false);
        }
}

function snd_reject_friend (id)
{
        if (!confirm (lang._SND_AREYOUSURE))
	{
		return (false);
		
	}
	
	new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_REJECT_FRIEND,cid:id,act:1},onSuccess: snd_reject_friend_hdlr});
}
function snd_check_username_avail_hdlr (request)
{
	if (request.responseText == "<ul><li><span class='informal'>no results</span></li></ul>")
	{
		new Effect.Highlight ('new_username', {endcolor:'#a3f3ae',restorecolor:'#a3f3ae'});	
		return (false);
	}
	else
	{
		new Effect.Highlight ('new_username', {endcolor:'#f34848',restorecolor:'#f34848'});
		return (false);
	}
}
function snd_check_username_avail()
{
	var u = $('new_username').value;
	if (u.length >= 3 && snd_new_login ($('new_username')) )
	{
		new Ajax.Request ("/msg/users.php", {method:'post',parameters:{msgAT:u},onSuccess:snd_check_username_avail_hdlr});
	}
}

function snd_reminder_hdlr (request)
{
	if (error = detect_error (request))
	{
		alert (error);
		return (false);
	}

	$('sndBody').update (request.responseText);
}

function snd_reminder (uname)
{
	if (uname.length > 3)
	{
		new Ajax.Request ("/xchat/reminder.php", {method:'post',parameters:{uname:uname},onSuccess:snd_reminder_hdlr});
	}
}
/* utils.js */

var snd_s_narrow = 0;
var snd_s_wide = 1;
var snd_state = 0;
var oninit_hdlr = false;
/* 1 - live , 0 - static */
var snd_act = 1;
var snd_upload_act = false;
var ERROR = 'unknown error / nieznany blad';
var snd_ajax_url = "/index.php?opt=com_ajax";

var snd_events = false;
var snd_player = false;
var snd_xlog = false;
var snd_last_played_item = 0;
var snd_current_music_fresh = 0; /* 0 = music 1 = demo */
var pager;
/* reserver for a successful upload and a new ID */
var snd_global_newitem = -1;

function el(element)
{
	return (document.createElement (element));
}

function tel(element)
{
	return (document.createTextNode (element));
}

function snd_ajax_error(type)
{
	$('sndBody').update();
	d = document.createElement ('div');
	d.style.width = "531px";
	d.style.height = "400px";

	i = document.createElement ('img');
	i.src = "/images/soundlab/uvod/nyarly_87x137.png";
	i.className = "trans";
	i.style.width = "87px";
	i.style.height = "137px";
	i.style.display = "inline";
	i.style.cssFloat = "left";
	i.style.styleFloat = "left";
	
	m = document.createElement ('div');
	m.style.background = "url('/images/soundlab/event_error.png')";
	m.style.width = "300px";
	m.style.height = "76px";
	m.style.cssFloat = "left";
	m.style.styleFloat = "left";
	m.style.marginTop= "30px";
	m.style.marginLeft = "20px";
	m.style.textAlign = "center";
	m.style.font = "bold 18px Helvetica";
	m.style.paddingTop = "20px";
	m.style.color = "#ff0000";
	
	m.innerHTML = type;
	
	d.appendChild (i);
	d.appendChild (m);
	$('sndBody').appendChild (d);
}

function snd_ajax (ajax_request, xrequest, response)
{
	if (xrequest.status == 404 || xrequest.status == 500 || xrequest.status == 400 || xrequest.status == 403 || xrequest.status == 503)
	{
		snd_ajax_error(xrequest.status);
	}
}

Ajax.Responders.register({onComplete: snd_ajax,onFailure: snd_ajax});

Event.observe (window, 'load', snd_init);

function snd_init ()
{	
	dhtmlHistory.initialize(snd_history_change);	
	snd_events = new sndEvent();
	
	if (snd_events)
	{
		snd_events.registerHdlr (lang._SND_EVENTS_ONLINE, snd_online_update);
		snd_events.registerHdlr (lang._SND_EVENTS_CHAT, snd_chat_update);
		snd_events.registerHdlr (lang._SND_EVENTS_PMS, snd_pms_update);
		snd_events.registerHdlr (lang._SND_EVENTS_GLOBAL, snd_global_update);
		snd_events.registerHdlr (lang._SND_EVENTS_NOTIFY, snd_notification_update);
		snd_events.registerHdlr (lang._SND_EVENTS_FRESH, snd_fresh_update);
		snd_events.update();
	}

	var SND_SECTIONID_MUSIC = lang._SND_SECTIONID_MUSIC;

	var str = window.location;

	snd_location (str);
	$('sndPreload').hide();
}

function snd_history_change (newLocation, historyData)
{
	if (historyData)
	{
		if (historyData.handler)
		{
			historyData.handler();
		}
	}
}

function snd_history_add (location, data)
{
	if (!data.handler)
	{
		return (false);
	}
	
	dhtmlHistory.add (Base64.encode (location), data);
}

function basename (path) 
{ 
	if (path)
	{
		return path.replace( /.*\//, "" ); 
	}
}

function _n()
{
}

function is_url (s)
{
	if (s)
	{
		if (s.length > 5)
		{
			try
			{
				var j = new RegExp("^[A-Za-z]+://[A-Za-z0-9-]+\.[A-Za-z0-9]+");
			}
			catch (e)
			{
				alert (e);
			}
			return (j.test(s));
		}
		else
		{
			return (false);
		}
	}
	else
	{
		return (false);
	}
	
	return (false);
}

function valid_url(url) {
    var v = new RegExp();
    v.compile("^[A-Za-z]+://[A-Za-z0-9-_]+\\.[\#A-Za-z0-9-_%&\?\/.=]+$");
    if (!v.test(url)) {
        return false;
    }
    return (true);
} 

function detect_error (request)
{
	if (typeof (request) == "object")
	{
		if (request.responseText)
		{
			if (request.responseText.indexOf('{status:1,') == 0)
			{
				var status = eval ('(' + request.responseText + ')');
	
				if (status.error.length == 0)
				{
					return (ERROR);
				}
				else
				{
					return (status.error ? status.error : ERROR);
				}
			}
			else
			{
				return (0);
			}
		}
	}
	else
	{
		return (ERROR);
	}
}

function snd_loading(id, img)
{
	if (!id)
	{
        var main = 'sndBody';
		
		var c = document.createElement ('center');
		var i = document.createElement ('img');
		
		if (img)
		{
			i.src = img;
		}
		else
		{
			i.src = "/images/soundlab/loading.gif";
		}
		
		i.style.marginTop	= "40px";
		i.style.marginBottom	= "40px";
		
		c.appendChild (i);
		
		$(main).innerHTML = ''
		
		$(main).appendChild (c);
	}
	else
	{
		var c = el ('center');
		var i = el ('img');
		
		if (img)
		{
			i.src = img;
		}
		else
		{
			i.src = "/images/soundlab/loading.gif";
		}
		
		i.style.marginTop	= "40px";
		i.style.marginBottom	= "40px";
		
		c.appendChild (i);
		
		var d = $(id).childElements();

		if (d)
		{
			for (var x=0; x<d.length; x++)
			{
				Element.remove (d[x]);
			}
		}
		
		$(id).appendChild (c);
	}
}

function snd_stop_loading(id)
{
	if (!id)
	{
		var main = document.getElementById('sndBody');
		main.innerHTML = "";
		main = null;
	}
	else
	{
		var main = document.getElementById(id);
		main.innerHTML = "";
		main = null;
	}
}

if (navigator.userAgent.toLowerCase().indexOf("msie") == -1)
{
	isIE = false;
}
else
{
	isIE = true;
}

if (navigator.userAgent.toLowerCase().indexOf("opera") == -1)
{
	isOpera = false;
}
else
{
	isOpera = true;
}

if (navigator.userAgent.toLowerCase().indexOf("mozilla") == -1)
{
	isMozilla = false;
}
else
{
	isMozilla = true;
}

if (navigator.userAgent.toLowerCase().indexOf("khtml") == -1)
{
	isKHTML = false;
}
else
{
	isKHTML = true;
}

function findPos(obj)
{
	var curleft = curtop = width  = height = 0;
	width = obj.offsetWidth;
	height = obj.offsetHeight;

	if (obj.offsetParent) 
	{
		curleft = obj.offsetLeft
		curtop = obj.offsetTop

		while (obj = obj.offsetParent)
		{
			curleft += obj.offsetLeft
			curtop += obj.offsetTop
		}
	}
	
	return [curleft, curtop, width, height];
}

function getPageWidth()
{  
	if (isIE == false && isOpera == false)
		return(document.width);

	if (isIE == true && isOpera == false)
		return(document.body.scrollWidth);  

	if (isOpera == true)
		return (window.innerWidth - 16);

	return(-1);
}

function getPageHeight()
{
	if (isIE == false && isOpera == false)
		return(window.screen.availHeight);  

	if (isIE == true && isOpera == false)
		return(document.body.scrollHeight);  

	if (isOpera == true)
		return (window.innerHeight);

	return(-1);
}

function snd_notify (ind)
{
	if (!ind)
	{
		new Effect.Appear ('sndNotificationBar', { duration: 1.0 });
		setTimeout ("snd_notify(1)", 2000);
	}
	else
	{
		new Effect.Fade ('sndNotificationBar', { duration: 1.0});
	}
}

function snd_set_state (stateid, flag)
{
	if (stateid == snd_s_narrow && snd_state == snd_s_wide)
	{
		var cont = document.getElementById('sndBodyContainer');
		var wide = document.getElementById('sndBodyWide');
	
		cont.style.display = "block";
		wide.style.display = "none";

		snd_state = snd_s_narrow;

		return (false);
	}
	if (stateid == snd_s_wide && snd_state == snd_s_narrow)
	{
		var cont = document.getElementById('sndBodyContainer');
		var wide = document.getElementById('sndBodyWide');
		
		wide.style.display = "block";
		cont.style.display = "none";

		snd_state = snd_s_wide;

		return (false);
	}

	return (true);
}

function snd_user_profile (id)
{
	if (snd_forum_i == false)
	{
		snd_hover_user (0);
                get_page_callback = snd_user;

		get_page (lang._SND_PAGE_PROFILE_USER, id);
	}
	
	if (snd_forum_i == true)
	{
		snd_forum_i = false;

		get_page_callback = snd_user;
		
		get_page (lang._SND_PAGE_PROFILE_USER, id);
	}
}

function snd_specialchars (str, allow)
{
	var ich = "!@#$%^&*()+=-[]\\\';,./{}|\":<>? _";

	for (var x=0; x<str.length; x++)
	{
		if (ich.indexOf(str.charAt(x)) != -1)
		{
			if (allow.indexOf(str.charAt(x)) != -1)
			{
				continue;
			}
			return (false);
		}
	}
	
	return (true);
}

function snd_unicode (str)
{
	var u = escape (str);
	
	return (u.indexOf ("%u"));
}

function snd_email(str)
{
	if (str)
	{
		if (str.length > 3)
		{
			var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/;
			if (filter.test(str))
			{
				return (true);
			}
			else
			{
				return (false);
			}
		}
		return (false);
	}
	return (false);
}

function snd_tray_add (elm)
{
	var tray = document.getElementById('sndTray');

	if (tray)
	{
		tray.appendChild (elm);
	}
	else
	{
		return (false);
	}
}

function snd_tray_del (elm)
{
	var tray = document.getElementById('sndTray');

	if (tray)
	{
		tray.removeChild (elm);
	}
	else
	{
		return (false);
	}
}

function news_item (id)
{
        get_page (lang._SND_PAGE_NEWS, id);
}

function stripslashes(str) 
{
	str=str.replace(/\\'/g,'\'');
	str=str.replace(/\\"/g,'"');
	str=str.replace(/\\\\/g,'\\');
	str=str.replace(/\\0/g,'\0');
	return str;
}

function snd_bad_resp (request)
{
	alert (lang._SND_BADRESPONSE + " [" + request.responseText + "]");
}

function checkTime(i)
{
	if (i<10)
	{
		i="0" + i;
	}

	return i;
}

function trim(stringToTrim)
{
	return stringToTrim.replace(/^\s+|\s+$/g,"");
}

function snd_online_list()
{
	var o 				= $('sndOnlineList');
	var i 				= $('sndOnlineListTrigger');
	
	if (o.style.display == 'block')
	{
		o.style.display		= 'none';
		i.src			= '/images/soundlab/com_music/downloads.gif';
		return (false);
	}
	
	if (o.style.display == 'none')
	{
		o.style.display 	= 'block';
		i.src			= '/images/soundlab/com_music/uploads.gif';
		return (false);
	}
	
	i = o = null;
}

function rnd(scale) 
{
	var dd=new Date();      
	return((Math.round(Math.abs(Math.sin(dd.getTime()))*1000000000)%scale)); 
}

function snd_filename (path)
{
	if (path)
	{
		if (path.length > 2)
		{
			if (!isOpera)
			{
				var m = path.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);
	        		return (m[2]);
			}
			else
			{
				return (path);
			}
		}
		return (false);
	}
	return (false);
}

function snd_fileext (path)
{
	path = path.replace(/^\s|\s$/g, "");
	if (/\.\w+$/.test(path)) 
	{
		var m = path.match(/([^\/\\]+)\.(\w+)$/);
		if (m)
		{
			return (m[2]);
		}
	}
}

function snd_isimage (path)
{
	var ext = snd_fileext (path);
	
	if (ext != 'jpg' && ext != 'png' && ext != "PNG" && ext != "GIF" && ext != 'gif' && ext != "JPG" && ext != "jpeg" && ext != "JPEG")
	{
		return (false);
	}
	
	return (true);
}

function snd_dispatcher (type, id)
{
	
	switch (type)
	{
		case lang._SND_SECTIONID_MUSIC:
			music_item (id);
			
			break;
	
		case lang._SND_SECTIONID_NEWS:
			news_item (id);
			
			break;

		case lang._SND_SECTIONID_WORKSHOP:
			workshop_item (id);
			break;
			
		case lang._SND_SECTIONID_PROFILE:
			get_page_callback = snd_user;

		        get_page (lang._SND_PAGE_PROFILE_USER,id,0,0,0,1);

			break;
		
		case lang._SND_SECTIONID_TAGS:
			tag_item (id);
			
			break;
			
		case lang._SND_PAGE_STATIC:
			get_page (lang._SND_PAGE_STATIC, id);
			
			break;
		
		case lang._SND_SECTIONID_FORUM:
			forum_item (id);

			break;
	
		default:
			break;
	}
}

function link_cntnr_hdlr (request)
{
	if (error = detect_error (request))
	{
		return (false);
	}

	$('sndBody').innerHTML = request.responseText;
}

function link_cntnr (id)
{
	if (id > 0)
	{
		snd_loading();
		new Ajax.Request (snd_ajax_url, {method:'post',parameters:{t:lang._SND_PAGE_LINKS,cid:id}, onSuccess: link_cntnr_hdlr});
	}
}

function snd_toggle_player()
{
	if ($('sndPlayer').visible())
	{
		$('sndPlayer').hide();
		return (false);
	}
	else
	{
		$('sndPlayer').show();
		return (false);
	}
}

function snd_format_unit (val)
{
	if (val < 1024)
	{
		return (val + 'b');
	}
	if (val > 1024 && val < 1048576)
	{
		var s = val / 1024;
		return (s.toFixed(2) + 'kb');
	}
	if (val >= 1048576)
	{
		var s = val / 1048576;
		return (s.toFixed(2) + 'Mb');
	}
}

function snd_page (t, p, opt1, opt2)
{
	snd_loading();

	var o = new Object();

	o.cid = 0;
	o.act = 0;
	o.t = t;
	o.next = p;
	o.o1 = opt1;
	o.o2 = opt2;
	o.method = "get";
	o.history = true;
	o.url = snd_ajax_url;

	snd_getpage (o);
}

function genre_item (genre)
{

	get_page_callback = function () 
	{	
		pager = new Pager('catalogItems', 15);                                         
		pager.init();                                                                  
		pager.showPageNav('pager', 'catalogPageNav');
		pager.showPage(1); 
		sorttable.makeSortable($('catalogItems'));
	}
	
	var o 		= new Object();
	o.t 		= lang._SND_PAGE_CATALOG;
	o.cid		= genre;
	o.next		= 0;
	o.o1		= genre;
	o.o2		= 0;
	o.act		= 0;
	o.url		= snd_ajax_url;
	o.history	= true;
	o.method	= "post";
	
	snd_getpage (o);
}

function news_cntnr(id)
{
	get_page_callback = function()
	{
		pager = null;
		pager = new Pager('searchResults', 15);
		pager.init();
		pager.showPageNav('pager', 'searchResultsNav');
		pager.showPage(1);
			
		try { sorttable.makeSortable ($('searchResults')) } catch (e) { alert (e) };
	};
	get_page (window.lang._SND_PAGE_FRONT, id, '', 'container')
}

function content_item (id)
{
	get_page (window.lang._SND_PAGE_FRONT, id);
}

function snd_location (s)
{
	if (s.hash)
	{
		var d = Base64.decode (s.hash).substr(1); /* remove the # */
		var k = d.split(";");

		if (k[0])
			var p = k[0].split (":")[1];
		if (k[1])
			var c = k[1].split (":")[1];
		if (k[2])
			var o1 = k[2].split (":")[1];
		if (k[3])
			var o2 = k[3].split (":")[1];

		if (p == lang._SND_PAGE_PROFILE_USER)
		{
			user_item (c);
			return (false);
		}
		get_page (p,c,o1,o2);
	}
}

function toggle_order_mode()
{
	if (snd_c.freshOrderMode == 0)
	{
		$('orderModeArrow').update("&uarr;");
		snd_c.freshOrderMode = 1;
		return;
	}
	if (snd_c.freshOrderMode == 1)
	{
		$('orderModeArrow').update ("&darr;");
		snd_c.freshOrderMode = 0;
		return;
	}
}

function change_table_class (tableId, newClassPrefix)
{
    if (snd_last_played_item != 0)
    {
        _i = snd_last_played_item;
        snd_last_played_item = 0
        
        change_table_class (_i, "S");
    }
    
    $$('#'+tableId+' td').each (function (item)
    {
        cornerPosition = item.className.indexOf('corner');
        if (cornerPosition > 0)
        {
            part = item.className.split ("corner");
            tablePart = part[1];
            
            c = newClassPrefix + "corner" + tablePart;
            item.className = c;
            
            snd_last_played_item = tableId;
        }
    });
}

function snd_toggle_music_fresh()
{
    if (snd_current_music_fresh)
    {
        /* is demo set to  music */
        $('sndFreshMusicHeader').style.background = "url(/images/soundlab/fresh_header_music.png)";
        snd_current_music_fresh = 0;
        
        $('sndFreshMusicModule').style.display = "block";
        $('sndFreshDemoModule').style.display = "none";
    }
    else
    {
        /* is music set to demo */
        $('sndFreshMusicHeader').style.background = "url(/images/soundlab/fresh_header_demo.png)";
        snd_current_music_fresh = 1;
        
        $('sndFreshMusicModule').style.display = "none";
        $('sndFreshDemoModule').style.display = "block";
    }
}

/* workshop.js */

function workshop_item_hdlr(request)
{
	if (error = detect_error (request))
	{
		alert (error);
		snd_stop_loading();

		return (false);
	}

	$('sndBody').update(request.responseText);
}

function workshop_item(id)
{
	snd_loading();

	new Ajax.Request ('/index.php?option=com_workshop', {method:'post',parameters:{cid:id}, onSuccess: workshop_item_hdlr});
}

function workshop_cntnr(id)
{
	snd_loading();

	new Ajax.Request ('/index.php?option=com_workshop', {method:'post',parameters:{o2:'container',cid:id}, onSuccess: workshop_item_hdlr});
}

function workshop_edit (id)
{
	snd_loading();

	new Ajax.Request ('/index.php?option=com_workshop', {method:'post',parameters:{cid:id}, onSuccess: workshop_item_hdlr});
}/* swfobject.js */

/*	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();/* player.js */

var snd_track_playing = -1;

function createPlayer(playerDiv)
{
    var flashvars = 
    {
        autostart:"true",
        file:"",
        playlist:"bottom",
        playlistsize:"24",
        skin:"/jw/silverywhite.swf",
        backcolor:"e4e7e8"
    }
    
    var params = 
    {
        allowfullscreen:"true", 
        allowscriptaccess:"always"
    }
    
    var attributes = 
    {
        id:"jwPlayer",
        name:"jwPlayer"
    }

    swfobject.embedSWF("/jw/player.swf", playerDiv, "430", "78", "9.0.115", false, flashvars, params, attributes);
}

function createSinglePlayer(playerDiv, itemId)
{
    var flashvars = 
    {
        autostart:"false",
        file:"/listen/" + itemId + "/1.mp3",
        playlist:"bottom",
        playlistsize:"0",
        skin:"/jw/modieus.swf",
        backcolor:"000000"
    }
    
    var params = 
    {
        allowfullscreen:"true", 
        allowscriptaccess:"always"
    }
    
    var attributes = 
    {
        id:playerDiv,
        name:playerDiv
    }

    swfobject.embedSWF("/jw/player.swf", playerDiv, "450", "32", "9.0.115", false, flashvars, params, attributes);
}

function playItem (id)
{
    if (!player)
    {
        return;
    }
    else
    {
        if ($('playTrack_'+id))
        {
            if (snd_track_playing == id)
            {
                snd_track_playing = -1;
                /* pause is pressed, stop and change button to play */
                
                $('playTrack_'+id).src = '/images/soundlab/com_music/play.gif';
                player.sendEvent ("STOP");
            }
            else
            {
                snd_track_playing = id;
                /* play is pressed, play and change to pause */
                
                $('playTrack_'+id).src = '/images/soundlab/com_music/stop.gif';
                player.sendEvent ("LOAD", "/get/" + id + "/jw");
            }
        }
    }
}

function playerListener(p)
{
}

function addListeners()
{
    if (player) 
    { 
        player.addModelListener("STATE", "playerListener");
	} 
    else 
    {
		setTimeout("addListeners()",100);
	}
}
    
var player = null; 
function playerReady(thePlayer)
{
    player = document.getElementById(thePlayer.id);
    addListeners();
}/* gui/tree/Bs_Tree.class.js */

/********************************************************************************************
* BlueShoes Framework; This file is part of the php application framework.
* NOTE: This code is stripped (obfuscated). To get the clean documented code goto 
*       www.blueshoes.org and register for the free open source *DEVELOPER* version or 
*       buy the commercial version.
*       
*       In case you've already got the developer version, then this is one of the few 
*       packages/classes that is only available to *PAYING* customers.
*       To get it go to www.blueshoes.org and buy a commercial version.
* 
* @copyright www.blueshoes.org
* @author    Samuel Blume <sam at blueshoes dot org>
* @author    Andrej Arn <andrej at blueshoes dot org>
*/
if (!Bs_Objects) {var Bs_Objects = [];};function Bs_Tree() {
this._id;this._objectId;this.autoCollapse      = false;this.lookAhead = 2;this.rememberState = false;this.captionBgColor    = "yellow";this.linkStyle;this.divStyle = 'font-family: Arial, Helvetica, sans-serif; font-size: 11px;';this.showPseudoElement = false;this.useCheckboxSystem = false;this.checkboxSystemWalkTree = 3;this.checkboxSystemIfPartlyThenFull = true;this.checkboxSystemImgDir;this.checkboxSystemGuiNochange;this.useRadioButton = false;this.radioButtonName;this.imageDir = '/_bsJavascript/components/tree/img/win98/';this.imageHeight = 16;this.useFolderIcon = true;this.useLeaf = true;this.walkTree = true;this.useAutoSequence = true;this.draggable = false;this._clearingHouse = new Array;this._pseudoElement;this._currentActiveElement;this._elementSequence = 0;this._errorArray;this.stopWatch;this.simple = false;this._constructor = function() {
this._id = Bs_Objects.length;Bs_Objects[this._id] = this;this._objectId = "Bs_Tree_" + this._id;var a = [];a['id']               = 'pseudoElement001';a['caption']          = "root";a['url']              = "";a['target']           = "";a['isOpen']           = true;this._pseudoElement = this._createTreeElement(a, 0);}
this.loadSkin = function(skinName) {
switch (skinName) {
case 'win2k':
case 'win98':
this.imageDir = '/_bsJavascript/components/tree/img/win98/';this.imageHeight = 16;break;case 'winxp':
this.imageDir = '/_bsJavascript/components/tree/img/winXp/';this.imageHeight = 17;break;case 'bobby-blue':
this.imageDir      = '/_bsJavascript/components/tree/img/bobby/blue/';this.imageHeight   = 16;this.useFolderIcon = false;break;default:
return false;}
return true;}
this.initByArray = function(arr) {
for (var i=0, n=arr.length; i<n; i++) {
var e = this._createTreeElement(arr[i], 1);if (e == false) {
return false;}
this._pseudoElement.addChild(e);}
return true;}
this.getActiveElement = function() {
if (typeof(this._currentActiveElement) != 'undefined') return this._currentActiveElement;return false;}
this.setActiveElement = function(treeElement) {
this._currentActiveElement = treeElement;}
this._createTreeElement = function(arr, level) {
if (typeof(level) == 'undefined') level = 1;var e = new Bs_TreeElement();var status = e.initByArray(arr, this, level);if (!status) {
this._addError(e.getLastError());return false;}
this._clearingHouse[e.id] = e;if (arr['children']) {
if ((this.useCheckboxSystem && (this.checkboxSystemWalkTree >= 2)) || e.isOpen || ((this.lookAhead +2) > level) || (this.lookAhead == -1) || ((typeof(e.parent) == 'object') && (e.parent.isOpen))) {
for (var i=0, n=arr['children'].length; i<n; i++) {
var newE = this._createTreeElement(arr['children'][i], level +1);if (!newE) return false;e.addChild(newE);}
} else {
e._undoneChildren = arr['children'];}
}
return e;}
this.getElement = function(elementId) {
if (elementId == 0) return this._pseudoElement;if (typeof(this._clearingHouse[elementId]) == 'object') {
return this._clearingHouse[elementId];} else {
return false;}
}
this.getElementByCaptionPath = function(data) {
var elm = this._pseudoElement;for (var i=0, n=data.length; i<n; i++) {
var newElm = null;for (var j=0, jn=elm._children.length; j<jn; j++) {
if (elm._children[j].caption == data[i]) {
newElm = elm._children[j];elm = newElm;if (typeof(elm._undoneChildren) == 'object') {
for (var k=0, kn=elm._undoneChildren.length; k<kn; k++) {
var newE = this._createTreeElement(elm._undoneChildren[k], elm._level +1);elm.addChild(newE);}
elm._undoneChildren = false;}
break;}
}
if (newElm == null) return false;}
return newElm;}
this.removeElement = function(elementId) {
debugger;
if (typeof(this._clearingHouse[elementId]) == 'undefined') return false;var elm = this._clearingHouse[elementId];if ((typeof(elm.parent) == 'object') && (typeof(elm.parent._children) == 'object')) {
for (var i=0, n=elm.parent._children.length; i<n; i++) {
if (elm.parent._children[i].id == elementId) {
elm.parent._children.deleteItem(i);break;}
}
}
this._clearingHouse.deleteItemHash(elementId);for (var i=0, n=elm._children.length; i<n; i++) {
this._clearingHouse.deleteItemHash(elm._children[i].id);}
if ((typeof(elm.parent) == 'object') && (elm.parent._isOutrendered)) {
elm.parent.render(true, true);}
return true;}
this.draw = function() {
if (this.simple) {
var content = this._pseudoElement.renderSimple();} else {
var content = this._pseudoElement.render();}
document.writeln(content[0]);eval(content[1]);}
this.toHtml = function() {
if (this.simple) {
return this._pseudoElement.renderSimple();} else {
return this._pseudoElement.render();}
}
this.executeOnElement = function(id, func, params) {
if (this._clearingHouse[id]) {
if (this._clearingHouse[id][func]) {
if (params) {
switch (params.length) {
case 1:
return this._clearingHouse[id][func](params[0]);break;case 2:
return this._clearingHouse[id][func](params[0], params[1]);break;case 3:
return this._clearingHouse[id][func](params[0], params[1], params[2]);break;case 4:
return this._clearingHouse[id][func](params[0], params[1], params[2], params[3]);break;}
} else {
return this._clearingHouse[id][func]();}
}
}
return;}
this.getJavascriptCode = function() {
return this._pseudoElement.getJavascriptCode('a', true);}
this.elementToggleOpenClose = function(id) {
this._clearingHouse[id].toggleOpenClose();}
this.elementOpenWalkUp = function(id) {
if (typeof(this._clearingHouse[id]) != 'undefined') {
var elm = this._clearingHouse[id];elm.open(true);if (typeof(elm.parent) != 'undefined') this.elementOpenWalkUp(elm.parent.id);} else {
return false;}
return true;}
this.elementCloseWalkUp = function(id) {
if (typeof(this._clearingHouse[id]) != 'undefined') {
var elm = this._clearingHouse[id];elm.close(true);if (typeof(elm.parent) != 'undefined') this.elementCloseWalkUp(elm.parent.id);} else {
return false;}
return true;}
this.elementCloseWalkDown = function(id) {
if (typeof(id) == 'undefined') {
var elm = this._pseudoElement;} else if (typeof(this._clearingHouse[id]) != 'undefined') {
var elm = this._clearingHouse[id];elm.close(true);} else {
return false;}
if (typeof(elm._children) != 'undefined') {
for (var i=0; i<elm._children.length; i++) {
this.elementCloseWalkDown(elm._children[i].id);}
}
return true;}
this.elementOpen = function(id) {
if (typeof(this._clearingHouse[id]) != 'undefined') {
this._clearingHouse[id].open();}
}
this.elementClose = function(id) {
this._clearingHouse[id].close();}
this.openPath = function(data, valueType) {
var elm = this.getElementByCaptionPath(data);if (elm == false) return false;this.elementOpenWalkUp(elm.id);return true;}
this.elementCheckboxEvent = function(id, value) {
this._clearingHouse[id].checkboxEvent(value);}
this.applyState = function() {
if (typeof(getCookie) == 'undefined') {
alert('Webmaster: please make sure core/lang/Bs_Cookie.lib.js is included for the rememberState/applyState feature.');return false;}
var name = this._objectId;var data = getCookie(name);for (treeElementId in data) {
var treeElm = this.getElement(treeElementId);for (action in data[treeElementId]) {
if (data[treeElementId][action]) {
treeElm.open();} else {
treeElm.close();}
}
}
return true;}
this._updateStateCookie = function(treeElementId, action, value) {
if (typeof(setCookie) == 'undefined') {
alert('Webmaster: please make sure core/lang/Bs_Cookie.lib.js is included for the rememberState/applyState feature.');return false;}
var name = this._objectId;var data = getCookie(name);if ((typeof(data) != 'object') || (data == null)) data = new Object();if ((typeof(data[treeElementId]) != 'object') || (typeof(data[treeElementId]) == null)) {
data[treeElementId] = new Object();}
if (typeof(data[treeElementId][action]) == 'undefined') {
data[treeElementId][action] = value;} else {
if (data[treeElementId][action] != value) {
delete data[treeElementId][action];delete data[treeElementId];}
}
setCookie(name, data);return true;}
this.debugDumpTree = function(elm, indent) {
if (typeof(elm) == 'undefined') {
elm    = this._pseudoElement;indent = '';var firstCall = true;}
var ret = '';if (typeof(elm._children) == 'object') {
for (var i=0; i<elm._children.length; i++) {
ret += indent + i + ': ' + elm._children[i].id + ': ' + elm._children[i].caption + "\n";ret += this.debugDumpTree(elm._children[i], indent + '  ');}
}
if (firstCall) {
alert(ret);} else {
return ret;}
}
this._addError = function(str) {
if (typeof(this._errorArray) == 'undefined') {
this._errorArray = new Array(str);} else {
this._errorArray[this._errorArray.length] = str;}
}
this.getLastError = function() {
if (typeof(this._errorArray) != 'undefined') {
if (this._errorArray.length > 0) {
return this._errorArray[this._errorArray.length -1];}
}
return false;}
this.old_drawInto = function(id) {
if (this.simple) {
var content = this._pseudoElement.renderSimple();} else {
var content = this._pseudoElement.render();}
var e       = document.getElementById(id);if (e) {
e.innerHTML = content[0];if ('' != content[1]) eval(content[1]);}
}
this._imgPreload = function() {
var id = this.globalId;var e  = document.getElementById(id);var ii = 0;var outTemp = new Array();outTemp[ii++] = '<img src="' + this.imageDir + 'line1.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'line2.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'line3.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'minus1.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'minus2.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'minus3.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'plus1.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'plus2.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'plus3.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'line3.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'empty.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'leaf.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'folderClosed.gif" border="0" style="display:none;">';outTemp[ii++] = '<img src="' + this.imageDir + 'folderOpen.gif" border="0" style="display:none;">';if (e) e.innerHTML = outTemp.join('');}
this._afterImgPreload = function() {
var id = this.globalId;if (this.simple) {
var content = this._pseudoElement.renderSimple();} else {
var content = this._pseudoElement.render();}
var e       = document.getElementById(id);if (e) {
e.innerHTML = content[0];if ('' != content[1]) {
eval(content[1]);}
}
}
this.drawInto = function(id) {
this.globalId = id;setTimeout('Bs_Objects['+this._id+']._imgPreload()', 0);setTimeout('Bs_Objects['+this._id+']._afterImgPreload()',500);}
this._constructor();}
/* gui/tree/Bs_TreeElement.class.js */

/********************************************************************************************
* BlueShoes Framework; This file is part of the php application framework.
* NOTE: This code is stripped (obfuscated). To get the clean documented code goto 
*       www.blueshoes.org and register for the free open source *DEVELOPER* version or 
*       buy the commercial version.
*       
*       In case you've already got the developer version, then this is one of the few 
*       packages/classes that is only available to *PAYING* customers.
*       To get it go to www.blueshoes.org and buy a commercial version.
* 
* @copyright www.blueshoes.org
* @author    Samuel Blume <sam at blueshoes dot org>
* @author    Andrej Arn <andrej at blueshoes dot org>
*/
function Bs_TreeElement() {
this.id;this.parent;this._tree;this.caption;this.url;this.target;this.linkStyle;this.divStyle;this.onClick;this.isOpen = false;this.visible = true;this.isChecked = 0;this.checkboxName;this.radioButtonSelected;this._checkboxObject;this._level = 0;this._children = new Array;this._undoneChildren;this.imageDir;this.imageHeight;this.icon;this.beforeIconSpan;this.beforeCaptionSpan;this.afterCaptionSpan;this.dataContainer;this._attachedEvents;this._isOutrendered = false;this._errorArray;this.getThis = function() {
return this;}
this.addChild = function(treeElement) {
treeElement.parent = this;if (typeof(this._children) != 'object') this._children = new Array;if (this._children.push) {
this._children.push(treeElement);} else {
this._children[this._children.length] = treeElement;}
treeElement._level = this._level +1;this._updateLevelAndParent(treeElement);this._tree._clearingHouse[treeElement.id] = treeElement;if (this._isOutrendered) {
this.render(true, true);}
}
this.addChildByArray = function(elementData) {
var treeElement = this._tree._createTreeElement(elementData);this.addChild(treeElement);return treeElement;}
this.isChild = function(elementId, bubble) {
for (var i=0, n=this._children.length; i<n; i++) {
if (this._children[i].id == elementId) return true;if (bubble) {
if (this._children[i].isChild(elementId, true)) return true;}
}
return false;}
this.setCaption = function(caption) {
this.caption = caption;if (this._isOutrendered) {
var span = document.getElementById(this._tree._objectId + '_e_' + this.id + '_caption2');if (span) span.innerHTML = caption;}
}
this.render = function(omitDivTags, putIntoPage, lookAhead) {
if (typeof(this._tree.stopWatch) == 'object') this._tree.stopWatch.takeTime('Bs_TreeElement.render() for id: ' + this.id + ' in level: ' + this._level);if (typeof(lookAhead) == 'undefined') {
lookAhead = this._tree.lookAhead;}
if ((this._tree._pseudoElement == this) && !this._tree.showPseudoElement && (lookAhead != -1)) {
lookAhead++;}
var imageDir    = this._getVar('imageDir');var imageHeight = this._getVar('imageHeight');var out      = new Array();var outI     = 0;var evalStr  = '';if (!omitDivTags) {
out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '"';out[outI++] = ' style="';if (!this.visible) {
out[outI++] = 'display:none;';}
out[outI++] = '">';}
if ((this._level) > 0 || (this._tree.showPseudoElement)) {
out[outI++] = '<nobr>';out[outI++] = '<div style="float:none;"';out[outI++] = ' id="' + this._tree._objectId + '_e_' + this.id + '_drag"';if (this._tree.draggable) {
out[outI++] = ' onDragStart="Bs_Objects['+this._tree._id+'].executeOnElement(\'' + this.id + '\', \'fireEvent\', Array(\'onDragStart\'));"';out[outI++] = ' onDragEnter="Bs_Objects['+this._tree._id+'].executeOnElement(\'' + this.id + '\', \'fireEvent\', Array(\'onDragEnter\'));"';out[outI++] = ' onDragOver="Bs_Objects['+this._tree._id+'].executeOnElement(\'' + this.id + '\', \'fireEvent\', Array(\'onDragOver\'));"';out[outI++] = ' onDrop="Bs_Objects['+this._tree._id+'].executeOnElement(\'' + this.id + '\', \'fireEvent\', Array(\'onDrop\'));"';}
out[outI++] = '>';out[outI++] = '<div style="overflow:visible; height:' + imageHeight + '; ' + this._getVar('divStyle') + '">';var level = this._level;if (!this._tree.showPseudoElement) --level;var obj = this;var outTemp = '';for (var i=0; i<level; i++) {
obj = obj.parent;if (obj.hasSiblingsDown(true)) {
var img = 'line1';} else {
var img = 'empty';}
outTemp = '<img src="' + imageDir + img + '.gif" height="' + imageHeight + '" border="0" align="top">' + outTemp;}
out[outI++] = outTemp;if (this.hasSiblingsDown()) {
var imgNumber = 3;} else {
var imgNumber = 2;}
if (this.hasVisibleChildren()) {
if ((this._level == 0) || (!this._tree.showPseudoElement && (this._level == 1) && ((this._tree.useAutoSequence && (this.id == 1)) || (!this._tree.useAutoSequence && true)))) {
if (this.hasSiblingsDown()) {
imgNumber++;} else {
imgNumber--;}
}
if (this.isOpen) {
var plusImg = 'minus' + imgNumber;var onClick = 'Close';} else {
var plusImg = 'plus' + imgNumber;var onClick = 'Open';}
} else {
var plusImg = 'line' + imgNumber;var onClick = false;}
if (onClick) {
var onClickStr = 'onClick="Bs_Objects['+this._tree._id+'].elementToggleOpenClose(\'' + this.id + '\');"';} else {
var onClickStr = '';}
if (this.onClick) {
var onClick = this.onClick;        onClick = onClick.replace(/__this\.id__/g, this.id); //replace the string __this.id__ with the actual id (int).
out[outI++] = '<span style="cursor:pointer; cursor:hand;" onClick="' + onClick + '">';}
out[outI++] = '<img id="' + this._tree._objectId + '_e_' + this.id + '_openClose" src="' + imageDir + plusImg + '.gif" height="' + imageHeight + '" border="0" ' + onClickStr + ' align="middle"';out[outI++] = ' style="vertical-align:' + ((imageHeight > 16) ? 'middle' : 'top') + '">';if (this.beforeIconSpan) {
out[outI++] = "<span>" + this.beforeIconSpan + "</span>";}
if (this.url) {
var hRef = '<a href="' + this.url + '"';if (this.target) {
hRef += ' target="' + this.target + '"';hRef += ' style="'  + this._getLinkStyle() + '"';}
hRef += '>';}
var folderIconId = this._tree._objectId + '_e_' + this.id + '_folder';if (this._getVar('useFolderIcon')) {
if (hRef) out[outI++] = hRef;switch (typeof(this.icon)) {
case 'undefined':
if (this._tree.useLeaf && !this.hasChildren()) {
var folderImg = 'leaf';} else {
var folderImg = 'folder';folderImg += (this.isOpen) ? 'Open' : 'Closed';}
out[outI++] = '<img id="' + folderIconId + '" src="' + imageDir + folderImg + '.gif" height="' + imageHeight + '" border="0" align="top">';break;case 'bool':
case 'boolean':
break;case 'string':
if (this.icon != 'false') {
out[outI++] = '<img id="' + folderIconId + '" src="';if (!this._iconHasPath(this.icon)) out[outI++] = imageDir;out[outI++] = this.icon;if (!this._iconHasExtension(this.icon)) out[outI++] = '.gif';out[outI++] = '" height="' + imageHeight + '" border="0" align="top">';}
}
if (hRef) out[outI++] = '</a>';}
if (this.beforeCaptionSpan) {
out[outI++] = "<span>" + this.beforeCaptionSpan + "</span>";}
if (this._tree.useRadioButton) {
out[outI++] = '<input type="radio"';if (this._tree.radioButtonName) {
out[outI++] = ' name="' + this._tree.radioButtonName + '"';} else {
out[outI++] = ' name="' + 'bsTreeRad_' + this._tree._objectId + '"';}
if (true) {
out[outI++] = ' style="height:16px;"';}
if (this.radioButtonSelected) {
out[outI++] = ' checked';}
out[outI++] = '>';}
if (this._tree.useCheckboxSystem) {
var checkboxSpan = this.checkboxName + 'Span';var checkboxObj  = this.checkboxName + 'Obj';out[outI++] = '&nbsp;<span id="' + checkboxSpan + '">';var t = new Bs_Checkbox();t.objectName = checkboxObj;t.checkboxName = this.checkboxName;t.value = this.isChecked;if (this._getVar('checkboxSystemGuiNochange')) {
t.guiNochange = true;}
var chkImagDir = this._getVar('checkboxSystemImgDir');if (chkImagDir) {
t.imgDir = chkImagDir;} else {
t.imgDir     = "/_bsJavascript/components/checkbox/img/win2k_noBorder/";}
t.imgWidth   = '13';t.imgHeight  = '13';if (this._tree.checkboxSystemWalkTree) {
t.attachOnClick('Bs_Objects['+this._tree._id+'].elementCheckboxEvent(\'' + this.id + '\', ' + checkboxObj + '.value);');}
eval(checkboxObj + ' = t;');this._checkboxObject = t;evalStr += checkboxObj + ".draw('" + checkboxSpan + "');";out[outI++] = '</span>';}
out[outI++] = '&nbsp;';out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '_caption"';if (this.onClick || this.hasEventAttached('onClickCaption')) {
out[outI++] = ' style="cursor:pointer; cursor:hand;"';} else {
out[outI++] = ' style="cursor:default;"';}
out[outI++] = ' onClick="Bs_Objects['+this._tree._id+'].executeOnElement(\'' + this.id + '\', \'fireEvent\', Array(\'onClickCaption\'));">';if (hRef) out[outI++] = hRef;out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '_caption2">' + this.caption + '</span>';if (hRef) out[outI++] = '</a>';out[outI++] = '</span>';if (this.onClick) {
out[outI++] = '</span>';}
out[outI++] = '</div>';if (this.afterCaptionSpan) {
out[outI++] = '<div style="overflow:visible;">' + this.afterCaptionSpan + '</div>';} else {
}
out[outI++] = '</div>';out[outI++] = '</nobr>';}
out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '_children"';if (!this.isOpen) {
out[outI++] = ' style="display:none;"';}
out[outI++] = '>';if (this.isOpen || (lookAhead > 0) || (lookAhead == -1)) {
for (var i=0, n=this._children.length; i<n; i++) {
if (lookAhead == -1) {
var newLookAhead = -1;} else {
if (this.isOpen) {
var newLookAhead = lookAhead;} else {
var newLookAhead = lookAhead -1;}
}
var t = this._children[i].render(false, false, newLookAhead);out[outI++] = t[0];evalStr    += t[1];}
}
out[outI++] = '</span>';if (!omitDivTags) {
out[outI++] = '</span>';}
out[outI++] = "\n";this._isOutrendered = true;var content = new Array(out.join(''), evalStr);if (putIntoPage) {
var doc = document.getElementById(this._tree._objectId + '_e_' + this.id);if (doc != null) {
doc.innerHTML = content[0];if (content[1] != '') {
eval(content[1]);}
return true;} else {
return false;}
} else {
return content;}
}
this.renderSimple = function(omitDivTags, putIntoPage, lookAhead) {
if (typeof(this._tree.stopWatch) == 'object') this._tree.stopWatch.takeTime('Bs_TreeElement.renderSimple() for id: ' + this.id + ' in level: ' + this._level);if (typeof(lookAhead) == 'undefined') {
lookAhead = this._tree.lookAhead;}
if ((this._tree._pseudoElement == this) && !this._tree.showPseudoElement && (lookAhead != -1)) {
lookAhead++;}
var imageDir    = this._getVar('imageDir');var imageHeight = this._getVar('imageHeight');var out      = new Array;var outI     = 0;var evalStr  = new Array;if (!omitDivTags) {
out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '"';out[outI++] = ' style="';if (!this.visible) {
out[outI++] = 'display:none;';}
out[outI++] = '">';}
if ((this._level) > 0 || (this._tree.showPseudoElement)) {
out[outI++] = '<nobr>';out[outI++] = '<div style="float:none;"';out[outI++] = ' id="' + this._tree._objectId + '_e_' + this.id + '_drag"';out[outI++] = '>';out[outI++] = '<div style="overflow:visible; height:' + imageHeight + '; ' + this._getVar('divStyle') + '">';var level = this._level;if (!this._tree.showPseudoElement) --level;var obj = this;var outTemp = '';for (var i=0; i<level; i++) {
obj = obj.parent;if (obj.hasSiblingsDown(true)) {
var img = 'line1';} else {
var img = 'empty';}
outTemp = '<img src="' + imageDir + img + '.gif" height="' + imageHeight + '" border="0" align="top">' + outTemp;}
out[outI++] = outTemp;if (this.hasSiblingsDown()) {
var imgNumber = 3;} else {
var imgNumber = 2;}
if (this.hasVisibleChildren()) {
if ((this._level == 0) || (!this._tree.showPseudoElement && (this._level == 1) && ((this._tree.useAutoSequence && (this.id == 1)) || (!this._tree.useAutoSequence && true)))) {
if (this.hasSiblingsDown()) {
imgNumber++;} else {
imgNumber--;}
}
if (this.isOpen) {
var plusImg = 'minus' + imgNumber;var onClick = 'Close';} else {
var plusImg = 'plus' + imgNumber;var onClick = 'Open';}
} else {
var plusImg = 'line' + imgNumber;var onClick = false;}
if (onClick) {
var onClickStr = 'onClick="Bs_Objects['+this._tree._id+'].elementToggleOpenClose(\'' + this.id + '\');"';} else {
var onClickStr = '';}
out[outI++] = '<img id="' + this._tree._objectId + '_e_' + this.id + '_openClose" src="' + imageDir + plusImg + '.gif" height="' + imageHeight + '" border="0" ' + onClickStr + ' align="middle"';out[outI++] = ' style="vertical-align:' + ((imageHeight > 16) ? 'middle' : 'top') + '">';if (this.url) {
var hRef = '<a href="' + this.url + '"';if (this.target) {
hRef += ' target="' + this.target + '"';hRef += ' style="'  + this._getLinkStyle() + '"';}
hRef += '>';}
var folderIconId = this._tree._objectId + '_e_' + this.id + '_folder';if (this._getVar('useFolderIcon')) {
if (hRef) out[outI++] = hRef;switch (typeof(this.icon)) {
case 'undefined':
if (this._tree.useLeaf && !this.hasChildren()) {
var folderImg = 'leaf';} else {
var folderImg = 'folder';folderImg += (this.isOpen) ? 'Open' : 'Closed';}
out[outI++] = '<img id="' + folderIconId + '" src="' + imageDir + folderImg + '.gif" height="' + imageHeight + '" border="0" align="top">';break;case 'bool':
case 'boolean':
break;case 'string':
if (this.icon != 'false') {
out[outI++] = '<img id="' + folderIconId + '" src="';if (!this._iconHasPath(this.icon)) out[outI++] = imageDir;out[outI++] = this.icon;if (!this._iconHasExtension(this.icon)) out[outI++] = '.gif';out[outI++] = '" height="' + imageHeight + '" border="0" align="top">';}
}
if (hRef) out[outI++] = '</a>';}
out[outI++] = '&nbsp;';out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '_caption"';if (this.onClick) {
out[outI++] = ' style="cursor:pointer; cursor:hand;"';} else {
out[outI++] = ' style="cursor:default;"';}
out[outI++] = ' onClick="Bs_Objects['+this._tree._id+'].executeOnElement(\'' + this.id + '\', \'fireEvent\', Array(\'onClickCaption\'));">';if (hRef) out[outI++] = hRef;out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '_caption2">' + this.caption + '</span>';if (hRef) out[outI++] = '</a>';out[outI++] = '</span>';out[outI++] = '</div>';out[outI++] = '</div>';out[outI++] = '</nobr>';}
if (typeof(this._tree.stopWatch) == 'object') this._tree.stopWatch.takeTime('Bs_TreeElement.renderSimple() 3');out[outI++] = '<span id="' + this._tree._objectId + '_e_' + this.id + '_children"';if (!this.isOpen) {
out[outI++] = ' style="display:none;"';}
out[outI++] = '>';if (this.isOpen || (lookAhead > 0) || (lookAhead == -1)) {
for (var i=0, n=this._children.length; i<n; i++) {
if (lookAhead == -1) {
var newLookAhead = -1;} else {
if (this.isOpen) {
var newLookAhead = lookAhead;} else {
var newLookAhead = lookAhead -1;}
}
var t = this._children[i].renderSimple(false, false, newLookAhead);out[outI++]             = t[0];evalStr[evalStr.length] = t[1];}
}
out[outI++] = '</span>';if (!omitDivTags) {
out[outI++] = '</span>';}
out[outI++] = "\n";this._isOutrendered = true;var content = new Array(out.join(''), evalStr.join(''));if (putIntoPage) {
var doc = document.getElementById(this._tree._objectId + '_e_' + this.id);if (doc != null) {
doc.innerHTML = content[0];if (content[1] != '') {
eval(content[1]);}
return true;} else {
return false;}
} else {
return content;}
}
this.reset = function() {
this.caption           = null;this.url               = null;this.target            = null;this.onClick           = null;this.isOpen            = false;this.isChecked         = 0;this.checkboxName      = null;this.beforeIconSpan    = null;this.beforeCaptionSpan = null;this.afterCaptionSpan  = null;this.linkStyle         = null;this.divStyle          = null;}
this.initByArray = function(a, tree, level) {
this._tree   = tree;this._level  = level;if (typeof(this._tree.stopWatch) == 'object') this._tree.stopWatch.takeTime('Bs_TreeElement.initByArray()');if (this._tree.useAutoSequence && (level > 0)) {
this.id      = ++this._tree._elementSequence;} else {
if (typeof(a['id']) == 'undefined') {
this._addError('tree error: useAutoSequence is set to false, but for an array element there is no id defined.');return false;}
this.id = a['id'];}
if (typeof(a['caption'])             != 'undefined') this.caption             = a['caption'];if (typeof(a['url'])                 != 'undefined') this.url                 = a['url'];if (typeof(a['target'])              != 'undefined') this.target              = a['target'];if (typeof(a['isOpen'])              != 'undefined') this.isOpen              = a['isOpen'];if (!this._tree.simple) {
if (typeof(a['linkStyle'])           != 'undefined') this.linkStyle           = a['linkStyle'];if (typeof(a['divStyle'])            != 'undefined') this.divStyle            = a['divStyle'];if (typeof(a['onClick'])             != 'undefined') this.onClick             = a['onClick'];if (typeof(a['isChecked'])           != 'undefined') this.isChecked           = parseInt(a['isChecked']);if (typeof(a['visible'])             != 'undefined') this.visible             = a['visible'];if (typeof(a['icon'])                != 'undefined') this.icon                = a['icon'];if (typeof(a['imageDir'])            != 'undefined') this.imageDir            = a['imageDir'];if (typeof(a['beforeIconSpan'])      != 'undefined') this.beforeIconSpan      = a['beforeIconSpan'];if (typeof(a['beforeCaptionSpan'])   != 'undefined') this.beforeCaptionSpan   = a['beforeCaptionSpan'];if (typeof(a['afterCaptionSpan'])    != 'undefined') this.afterCaptionSpan    = a['afterCaptionSpan'];if (typeof(a['radioButtonSelected']) != 'undefined') this.radioButtonSelected = a['radioButtonSelected'];if (typeof(a['dataContainer'])       != 'undefined') this.dataContainer       = a['dataContainer'];if (typeof(a['checkboxName']) != 'undefined') {
this.checkboxName  = a['checkboxName'];} else {
if (this._tree.useCheckboxSystem) {
this.checkboxName = 'bsTreeChk_' + this._tree._objectId + '_' + this.id;}
}
if (typeof(a['onClickCaption'])    != 'undefined') {
this.attachEvent('onClickCaption', a['onClickCaption']);}
if (typeof(a['onChangeCheckbox'])    != 'undefined') {
this.attachEvent('onChangeCheckbox', a['onChangeCheckbox']);}
if (typeof(a['events']) != 'undefined') {
for (ev in a['events']) {
this.attachEvent(ev, a['events'][ev]);}
}
}
return true;}
this.exportAsArray = function(withChildren) {
var ret = new Array();if (typeof(this.id)                       != 'undefined') ret['id']                       = this.id;if (typeof(this.caption)                  != 'undefined') ret['caption']                  = this.caption;if (typeof(this.url)                      != 'undefined') ret['url']                      = this.url;if (typeof(this.target)                   != 'undefined') ret['target']                   = this.target;if (typeof(this.onClick)                  != 'undefined') ret['onClick']                  = this.onClick;if (typeof(this.isOpen)                   != 'undefined') ret['isOpen']                   = this.isOpen;if (typeof(this.isChecked)                != 'undefined') ret['isChecked']                = this.isChecked;if (typeof(this.visible)                  != 'undefined') ret['visible']                  = this.visible;if (typeof(this.icon)                     != 'undefined') ret['icon']                     = this.icon;if (typeof(this.imageDir)                 != 'undefined') ret['imageDir']                 = this.imageDir;if (typeof(this.beforeIconSpan)           != 'undefined') ret['beforeIconSpan']           = this.beforeIconSpan;if (typeof(this.afterCaptionSpan)         != 'undefined') ret['afterCaptionSpan']         = this.afterCaptionSpan;if (typeof(this.radioButtonSelected)      != 'undefined') ret['radioButtonSelected']      = this.radioButtonSelected;if (typeof(this.dataContainer)            != 'undefined') ret['dataContainer']            = this.dataContainer;if (typeof(this.checkboxName)             != 'undefined') ret['checkboxName']             = this.checkboxName;if (typeof(this.beforeCaptionSpan)        != 'undefined') ret['beforeCaptionSpan']        = this.beforeCaptionSpan;if (typeof(this.linkStyle)                != 'undefined') ret['linkStyle']                = this.linkStyle;if (typeof(this.divStyle)                 != 'undefined') ret['divStyle']                 = this.divStyle;if (withChildren) {
ret['children'] = new Array();for (var i=0; i<this._children.length; i++) {
ret['children'][ret['children'].length] = this._children[i].exportAsArray(true);}
}
return ret;}
this.updateObjectByArray = function(a) {
this.reset();if (a['caption'])            this.caption            = a['caption'];if (a['url'])                this.url                = a['url'];if (a['target'])             this.target             = a['target'];if (a['onClick'])            this.onClick            = a['onClick'];if (a['isOpen'])             this.isOpen             = a['isOpen'];if (a['isChecked'])          this.isChecked          = a['isChecked'];if (a['imageDir'])           this.imageDir           = a['imageDir'];if (a['checkboxName']) {
this.checkboxName  = a['checkboxName'];} else {
if (this._tree.useCheckboxSystem) {
this.checkboxName = 'bsTreeCheckbox' + this.id;}
}
if (a['beforeIconSpan'])     this.beforeIconSpan     = a['beforeIconSpan'];if (a['beforeCaptionSpan'])  this.beforeCaptionSpan  = a['beforeCaptionSpan'];if (a['afterCaptionSpan'])   this.afterCaptionSpan   = a['afterCaptionSpan'];}
this.getJavascriptCode = function(varName, recursive) {
var ret = "";if (
(this._tree.useAutoSequence && (this.id > 1))
|| (!this._tree.useAutoSequence && !this.parent)
) {
} else {
ret += varName + " = new Array();\n";if (!this._tree.useAutoSequence) {
ret += varName + "['id'] = \"" + this.id + "\";\n";}
if (this.caption)           ret += varName + "['caption']            = \"" + this.caption            + "\";\n";if (this.url)               ret += varName + "['url']                = \"" + this.url                + "\";\n";if (this.target)            ret += varName + "['target']             = \"" + this.target             + "\";\n";if (this.onClick) {
        //var onClick = this.onClick.replace(/'/g,  "\'");
        var onClick = this.onClick.replace(/"/g,  '\"');
ret += varName + "['onClick']            = \"" + onClick            + "\";\n";}
if (this.imageDir)          ret += varName + "['imageDir']           = \"" + this.imageDir           + "\";\n";if (this.isOpen)            ret += varName + "['isOpen']             = '" + this.isOpen              + "';\n";if (this.isChecked)         ret += varName + "['isChecked']          = '" + this.isChecked           + "';\n";if (this.checkboxName)      ret += varName + "['checkboxName']       = '" + this.checkboxName        + "';\n";if (this.icon)              ret += varName + "['icon']               = \"" + this.icon               + "\";\n";if (this.beforeIconSpan)    ret += varName + "['beforeIconSpan']     = \"" + this.beforeIconSpan     + "\";\n";if (this.beforeCaptionSpan) ret += varName + "['beforeCaptionSpan']  = \"" + this.beforeCaptionSpan  + "\";\n";if (this.afterCaptionSpan)  ret += varName + "['afterCaptionSpan']   = \"" + this.afterCaptionSpan   + "\";\n";if (this.linkStyle)         ret += varName + "['linkStyle']          = \"" + this.linkStyle          + "\";\n";if (this.divStyle)          ret += varName + "['divStyle']           = \"" + this.divStyle           + "\";\n";varName += "['children']";}
if (recursive) {
if (this._children.length > 0) {
ret += varName + " = new Array();\n";for (var i=0; i<this._children.length; i++) {
ret += this._children[i].getJavascriptCode(varName + "[" + i + "]", recursive);}
}
}
return ret;}
this.setActive = function() {
var activeElement = this._tree.getActiveElement();if (activeElement != false) {
activeElement.unsetActive();}
this._tree.setActiveElement(this);this._highlight();}
this._highlight = function() {
var elmSetActive = document.getElementById(this._tree._objectId + '_e_' + this.id + '_caption');if (elmSetActive != null) {
elmSetActive.style.backgroundColor = this._getVar('captionBgColor');} else {
setTimeout("Bs_Objects["+this._tree._id+"].executeOnElement('" + this.id + "', '_highlight');", 800);}
}
this.unsetActive = function() {
var e = document.getElementById(this._tree._objectId + '_e_' + this.id + '_caption');if (e != null) e.style.backgroundColor = 'transparent';}
this.toggleOpenClose = function() {
if (this.isOpen) {
if (this.hasEventAttached('onBeforeClose')) {
var status = this.fireEvent('onBeforeClose');if (status != true) return;}
this.close();if (this.hasEventAttached('onAfterClose')) this.fireEvent('onAfterClose');} else {
if (this.hasEventAttached('onBeforeOpen')) {
var status = this.fireEvent('onBeforeOpen');if (status != true) return;}
this.open();if (this.hasEventAttached('onAfterOpen')) this.fireEvent('onAfterOpen');}
}
this.open = function(checkParents) {
if (this.isOpen) return;this.isOpen = true;if (this.hasVisibleChildren()) {
var lookAhead = this._tree.lookAhead;var doRender  = false;for (var i=0; i<this._children.length; i++) {
if (!this._children[i]._isOutrendered) {
doRender = true;break;}
}
if (doRender) this.render(true, true, lookAhead);}
if (true || !doRender) {
if (this._isOutrendered) {
var d = document.getElementById(this._tree._objectId + '_e_' + this.id + '_children');if (d) d.style.display = 'block';this._switchIconsOnToggleOpenClose();} else {
if (checkParents) {
this._renderParentsUp();}
this.render(true, true);}
if (this._tree.rememberState) this._tree._updateStateCookie(this.id, 'open', true);}
if (this._tree.autoCollapse) {
var sib = this.getSiblings();for (var i=0, n=sib.length; i<n; i++) {
if (sib[i].id != this.id) {
sib[i].close();}
}
}
if (this.hasVisibleChildren()) {
var lookAhead = this._tree.lookAhead;var treeElm     = this;for (var j=0, n=treeElm._children.length; j<n; j++) {
if (typeof(treeElm._children[j]._undoneChildren) == 'object') {
for (var k=0, kn=treeElm._children[j]._undoneChildren.length; k<kn; k++) {
var newE = this._tree._createTreeElement(treeElm._children[j]._undoneChildren[k], treeElm._children[j]._level +1);treeElm._children[j].addChild(newE);}
treeElm._children[j]._undoneChildren = false;}
if (treeElm._children[j].hasVisibleChildren()) {
var doRender = false;for (var k=0, kn=treeElm._children[j]._children.length; k<kn; k++) {
if (!treeElm._children[j]._children[k]._isOutrendered) {
var doRender = true;break;}
}
if (doRender) {
treeElm._children[j].render(true, true, lookAhead);}
}
}
}
}
this._renderParentsUp = function() {
if (typeof(this.parent) == 'undefined') this.parent._renderParentsUp();if (this._isOutrendered) return;this.render(true, true);}
this.close = function() {
if (!this.isOpen) return;this.isOpen = false;if (this._isOutrendered) {
var d = document.getElementById(this._tree._objectId + '_e_' + this.id + '_children');d.style.display = 'none';this._switchIconsOnToggleOpenClose();} else {
this.render(true, true);}
if (this._tree.rememberState) this._tree._updateStateCookie(this.id, 'open', false);}
this._switchIconsOnToggleOpenClose = function() {
var openClose = document.getElementById(this._tree._objectId + '_e_' + this.id + '_openClose');if (openClose) openClose.src = this._getSourceOpenCloseIcon();if (this._getVar('useFolderIcon')) {
var folderIconId = this._tree._objectId + '_e_' + this.id + '_folder';var fIcon = document.getElementById(folderIconId);if (fIcon) {
fIcon.src = this._getSourceFolderIcon();}
}
}
this._getSourceOpenCloseIcon = function() {
if (this.hasSiblingsDown()) {
var imgNumber = 3;} else {
var imgNumber = 2;}
if (this.hasVisibleChildren()) {
if ((this._level == 0) || (!this._tree.showPseudoElement && (this._level == 1) && ((this._tree.useAutoSequence && (this.id == 1)) || (!this._tree.useAutoSequence && true)))) {
if (this.hasSiblingsDown()) {
imgNumber++;} else {
imgNumber--;}
}
if (this.isOpen) {
var plusImg = 'minus' + imgNumber;var onClick = 'Close';} else {
var plusImg = 'plus' + imgNumber;var onClick = 'Open';}
} else {
var plusImg = 'line' + imgNumber;}
var imageDir = this._getVar('imageDir');return imageDir + plusImg + '.gif';}
this._getSourceFolderIcon = function() {
var imageDir = this._getVar('imageDir');switch (typeof(this.icon)) {
case 'undefined':
if (this._tree.useLeaf && !this.hasChildren()) {
var folderImg = 'leaf';} else {
var folderImg = 'folder';folderImg += (this.isOpen) ? 'Open' : 'Closed';}
return imageDir + folderImg + '.gif';break;case 'bool':
case 'boolean':
break;case 'string':
if (this.icon != 'false') {
var ret = '';if (!this._iconHasPath(this.icon)) ret += imageDir;ret += this.icon;if (!this._iconHasExtension(this.icon)) ret += '.gif';return ret;}
}
return '';}
this.hasChildren = function() {
return (this._children.length > 0);}
this.hasVisibleChildren = function() {
if (!this._children || !(this._children.length > 0)) {
this._hasVisibleChildren = false;return false;}
for (var i=0, n=this._children.length; i<n; i++) {
if (this._children[i].visible) {
this._hasVisibleChildren = true;return true;}
}
this._hasVisibleChildren = false;return false;}
this.numChildren = function() {
return this._children.length;}
this.childPos = function(id) {
for (var i=0, n=this._children.length; i<n; i++) {
if (this._children[i].id == id) return ++i;}
return false;}
this.hasSiblings = function() {
}
this.hasSiblingsDown = function(ignoreCache) {
if (false && !ignoreCache) {
if (typeof(this._hasSiblingsDown) != 'undefined') return this._hasSiblingsDown;}
try {
var tot = this.parent.numChildren();var pos = this.parent.childPos(this.id);this._hasSiblingsDown = (pos < tot);return this._hasSiblingsDown;} catch (e) {
this._hasSiblingsDown = false;return false;}
}
this.hasSiblingsAbove = function() {
}
this.getSiblings = function() {
try {
return this.parent.getChildren();} catch(e) {
return new Array;}
}
this.getChildren = function() {
return this._children;}
this.getParentId = function() {
try {
return this.parent.id;} catch (e) {
return false;}
}
this.hasParent = function() {
return (this.parent);}
this.attachEvent = function(trigger, yourEvent) {
if (typeof(this._attachedEvents) == 'undefined') {
this._attachedEvents = new Array();}
if (typeof(this._attachedEvents[trigger]) == 'undefined') {
this._attachedEvents[trigger] = new Array(yourEvent);} else {
this._attachedEvents[trigger][this._attachedEvents[trigger].length] = yourEvent;}
}
this.hasEventAttached = function(trigger) {
return ((typeof(this._attachedEvents) != 'undefined') && (typeof(this._attachedEvents[trigger]) != 'undefined'));}
this.fireEvent = function(trigger) {
var ret = true;if (trigger == 'onClickCaption') {
this.setActive();}
if ((typeof(this._attachedEvents) != 'undefined') && (typeof(this._attachedEvents[trigger]) != 'undefined')) {
var e = this._attachedEvents[trigger];if ((typeof(e) == 'string') || (typeof(e) == 'function')) {
e = new Array(e);}
for (var i=0, n=e.length; i<n; i++) {
if (typeof(e[i]) == 'function') {
var status = e[i](this);if (status == false) ret = false;} else if (typeof(e[i]) == 'string') {
        	var ev = e[i].replace(/__this\.id__/g, this.id); //replace the string __this.id__ with the actual id.
        	//ev = ev.replace(/__this__/g, 'this'); //replace the string __this__ with 'this'.
eval(ev);}
}
}
return ret;}
this._addError = function(str) {
if (typeof(this._errorArray) == 'undefined') {
this._errorArray = new Array(str);} else {
this._errorArray[this._errorArray.length] = str;}
}
this.getLastError = function() {
if (typeof(this._errorArray) != 'undefined') {
if (this._errorArray.length > 0) {
return this._errorArray[this._errorArray.length -1];}
}
return false;}
this._getVar = function(varName) {
if (typeof(this[varName]) != 'undefined') {
return this[varName];} else {
if (this._tree.walkTree && (typeof(this.parent) != 'undefined')) {
return this.parent._getVar(varName);} else if (typeof(this._tree[varName]) != 'undefined') {
return this._tree[varName];} else {
return null;}
}
}
this.onMouseOver = function() {
var img = document.getElementById(this._spanId + 'icon');if (!img.swapOver0) {
img.swapOver0 = new Image();img.swapOver0.src = this.imgDir + 'enabled_0_over.gif';img.swapOver1 = new Image();img.swapOver1.src = this.imgDir + 'enabled_1_over.gif';img.swapOver2 = new Image();img.swapOver2.src = this.imgDir + 'enabled_2_over.gif';img.swapOut0 = new Image();img.swapOut0.src = this.imgDir + 'enabled_0.gif';img.swapOut1 = new Image();img.swapOut1.src = this.imgDir + 'enabled_1.gif';img.swapOut2 = new Image();img.swapOut2.src = this.imgDir + 'enabled_2.gif';}
img.src = img['swapOver' + this.value].src;}
this.onMouseOut = function() {
var img = document.getElementById(this._spanId + 'icon');img.src = img['swapOut' + this.value].src;}
this.setCheckboxValue = function(value, fireEvents, doWalk) {
if (typeof(fireEvents) == 'undefined') fireEvents = true;if (typeof(doWalk)     == 'undefined') doWalk     = true;if (!this.hasChildren()) {
value = (value) ? 2 : 0;} else {
if (this.isChecked == 0) {
if (this._tree.checkboxSystemWalkTree && (this._tree.checkboxSystemWalkTree != 2) && (this._tree.checkboxSystemWalkTree != 3) && this.hasChildren()) {
value = 1;}
}
}
this.isChecked = value;this._checkboxObject.setTo(value, true);if (fireEvents) {
if (this.hasEventAttached('onChangeCheckbox')) this.fireEvent('onChangeCheckbox');}
if (doWalk) {
if ((this._tree.checkboxSystemWalkTree == 3) || (this._tree.checkboxSystemWalkTree == 1) || (this._tree.checkboxSystemWalkTree == 4)) {
this.parent.updateCheckboxFromChild();}
if ((this._tree.checkboxSystemWalkTree == 3) || (this._tree.checkboxSystemWalkTree == 2) || ((this._tree.checkboxSystemWalkTree == 4) && (value == 0))) {
this.checkboxUpdateDown(value);}
}
}
this.checkboxEvent = function(value) {
if (!this.hasChildren()) {
value = (value) ? 2 : 0;} else {
if (this.isChecked == 1) {
if ((!this._tree.checkboxSystemIfPartlyThenFull) || ((this._tree.checkboxSystemWalkTree) && (this._tree.checkboxSystemWalkTree != 2) && (this._tree.checkboxSystemWalkTree != 3))) {
value = 0;} else {
value = 2;}
} else if (this.isChecked == 0) {
if (this._tree.checkboxSystemWalkTree && (this._tree.checkboxSystemWalkTree != 2) && (this._tree.checkboxSystemWalkTree != 3) && this.hasChildren()) {
value = 1;}
}
}
this.isChecked = value;this._checkboxObject.setTo(value, true);if (this.hasEventAttached('onChangeCheckbox')) this.fireEvent('onChangeCheckbox');if ((this._tree.checkboxSystemWalkTree == 3) || (this._tree.checkboxSystemWalkTree == 1) || (this._tree.checkboxSystemWalkTree == 4)) {
this.parent.updateCheckboxFromChild();}
if ((this._tree.checkboxSystemWalkTree == 3) || (this._tree.checkboxSystemWalkTree == 2) || ((this._tree.checkboxSystemWalkTree == 4) && (value == 0))) {
this.checkboxUpdateDown(value);}
}
this.checkboxUpdateDown = function(value) {
for (var i=0; i<this._children.length; i++) {
this._children[i]._updateCheckboxFromParent(value, true);}
}
this.updateCheckboxVisually = function() {
if (typeof(this._checkboxObject) == 'object') {
try {
this._checkboxObject.setTo(this.isChecked);} catch (e) {
}
}
}
this._updateCheckboxFromParent = function(newValue, recursiveDown) {
var backupValue = this.isChecked;this.isChecked = (newValue) ? 2 : 0;var hasChanged = (this.isChecked != backupValue);if (hasChanged) {
this.updateCheckboxVisually();if (this.hasEventAttached('onChangeCheckbox')) this.fireEvent('onChangeCheckbox');}
if (recursiveDown) this.checkboxUpdateDown(newValue, true);}
this.updateCheckboxFromChild = function() {
var backupIsChecked = this.isChecked;var numYes   = 0;var numNo    = 0;var isPartly = false;for (var i=0, n=this._children.length; i<n; i++) {
if (this._children[i].isChecked == 1) {
isPartly = true;this.isChecked = 1;break;} else if (this._children[i].isChecked) {
numYes++;} else {
numNo++;}
if ((numYes > 0) && (numNo > 0)) {
break;}
}
if (!isPartly) {
if ((numYes > 0) && (numNo > 0)) {
this.isChecked = 1;} else if (numYes > 0) {
this.isChecked = 2;} else {
this.isChecked = 0;}
}
if (backupIsChecked != this.isChecked) {
this.updateCheckboxVisually();if (this.hasEventAttached('onChangeCheckbox')) this.fireEvent('onChangeCheckbox');}
if (typeof(this.parent) == 'object') {
this.parent.updateCheckboxFromChild();}
}
this._updateLevelAndParent = function(treeElement) {
if ((typeof(treeElement._children) == 'object') && (treeElement._children.length > 0)) {
for (var i=0, n=treeElement._children.length; i<n; i++) {
treeElement._children[i].parent = treeElement;treeElement._children[i]._level = treeElement._level +1;this._updateLevelAndParent(treeElement._children[i]);}
}
}
this._getLinkStyle = function() {
if (typeof(this.linkStyle)       != 'undefined') return this.linkStyle;if (typeof(this._tree.linkStyle) != 'undefined') return this._tree.linkStyle;return '';}
this._iconHasExtension = function(iconStr) {
var iconLower = iconStr.toLowerCase();var iconPos   = iconLower.lastIndexOf('.');if (iconPos > -1) {
var iconExt = iconLower.substr(iconPos +1);if ((iconExt != 'gif') && (iconExt != 'png') && (iconExt != 'jpg') && (iconExt != 'jpeg')) {
return false;}
} else {
return false;}
return true;}
this._iconHasPath = function(iconStr) {
if (iconStr.indexOf('://') > -1) return true;if (iconStr.substr(0, 1) == '/') return true;return false;}
}
/* gui/shortcut.js */

/**
 * http://www.openjs.com/scripts/events/keyboard_shortcuts/
 * Version : 2.01.A
 * By Binny V A
 * License : BSD
 */
shortcut = {
	'all_shortcuts':{},//All the shortcuts are stored in this array
	'add': function(shortcut_combination,callback,opt) {
		//Provide a set of default options
		var default_options = {
			'type':'keydown',
			'propagate':false,
			'disable_in_input':false,
			'target':document,
			'keycode':false
		}
		if(!opt) opt = default_options;
		else {
			for(var dfo in default_options) {
				if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
			}
		}

		var ele = opt.target
		if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
		var ths = this;
		shortcut_combination = shortcut_combination.toLowerCase();

		//The function to be called at keypress
		var func = function(e) {
			e = e || window.event;
			
			if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
				var element;
				if(e.target) element=e.target;
				else if(e.srcElement) element=e.srcElement;
				if(element.nodeType==3) element=element.parentNode;

				if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
			}
	
			//Find Which key is pressed
			if (e.keyCode) code = e.keyCode;
			else if (e.which) code = e.which;
			var character = String.fromCharCode(code).toLowerCase();
			
			if(code == 188) character=","; //If the user presses , when the type is onkeydown
			if(code == 190) character="."; //If the user presses , when the type is onkeydown
	
			var keys = shortcut_combination.split("+");
			//Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
			var kp = 0;
			
			//Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
			var shift_nums = {
				"`":"~",
				"1":"!",
				"2":"@",
				"3":"#",
				"4":"$",
				"5":"%",
				"6":"^",
				"7":"&",
				"8":"*",
				"9":"(",
				"0":")",
				"-":"_",
				"=":"+",
				";":":",
				"'":"\"",
				",":"<",
				".":">",
				"/":"?",
				"\\":"|"
			}
			//Special Keys - and their codes
			var special_keys = {
				'esc':27,
				'escape':27,
				'tab':9,
				'space':32,
				'return':13,
				'enter':13,
				'backspace':8,
	
				'scrolllock':145,
				'scroll_lock':145,
				'scroll':145,
				'capslock':20,
				'caps_lock':20,
				'caps':20,
				'numlock':144,
				'num_lock':144,
				'num':144,
				
				'pause':19,
				'break':19,
				
				'insert':45,
				'home':36,
				'delete':46,
				'end':35,
				
				'pageup':33,
				'page_up':33,
				'pu':33,
	
				'pagedown':34,
				'page_down':34,
				'pd':34,
	
				'left':37,
				'up':38,
				'right':39,
				'down':40,
	
				'f1':112,
				'f2':113,
				'f3':114,
				'f4':115,
				'f5':116,
				'f6':117,
				'f7':118,
				'f8':119,
				'f9':120,
				'f10':121,
				'f11':122,
				'f12':123
			}
	
			var modifiers = { 
				shift: { wanted:false, pressed:false},
				ctrl : { wanted:false, pressed:false},
				alt  : { wanted:false, pressed:false},
				meta : { wanted:false, pressed:false}	//Meta is Mac specific
			};
                        
			if(e.ctrlKey)	modifiers.ctrl.pressed = true;
			if(e.shiftKey)	modifiers.shift.pressed = true;
			if(e.altKey)	modifiers.alt.pressed = true;
			if(e.metaKey)   modifiers.meta.pressed = true;
                        
			for(var i=0; k=keys[i],i<keys.length; i++) {
				//Modifiers
				if(k == 'ctrl' || k == 'control') {
					kp++;
					modifiers.ctrl.wanted = true;

				} else if(k == 'shift') {
					kp++;
					modifiers.shift.wanted = true;

				} else if(k == 'alt') {
					kp++;
					modifiers.alt.wanted = true;
				} else if(k == 'meta') {
					kp++;
					modifiers.meta.wanted = true;
				} else if(k.length > 1) { //If it is a special key
					if(special_keys[k] == code) kp++;
					
				} else if(opt['keycode']) {
					if(opt['keycode'] == code) kp++;

				} else { //The special keys did not match
					if(character == k) kp++;
					else {
						if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
							character = shift_nums[character]; 
							if(character == k) kp++;
						}
					}
				}
			}

			if(kp == keys.length && 
						modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
						modifiers.shift.pressed == modifiers.shift.wanted &&
						modifiers.alt.pressed == modifiers.alt.wanted &&
						modifiers.meta.pressed == modifiers.meta.wanted) {
				callback(e);
	
				if(!opt['propagate']) { //Stop the event
					//e.cancelBubble is supported by IE - this will kill the bubbling process.
					e.cancelBubble = true;
					e.returnValue = false;
	
					//e.stopPropagation works in Firefox.
					if (e.stopPropagation) {
						e.stopPropagation();
						e.preventDefault();
					}
					return false;
				}
			}
		}
		this.all_shortcuts[shortcut_combination] = {
			'callback':func, 
			'target':ele, 
			'event': opt['type']
		};
		//Attach the function with the event
		if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
		else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
		else ele['on'+opt['type']] = func;
	},

	//Remove the shortcut - just specify the shortcut and I will remove the binding
	'remove':function(shortcut_combination) {
		shortcut_combination = shortcut_combination.toLowerCase();
		var binding = this.all_shortcuts[shortcut_combination];
		delete(this.all_shortcuts[shortcut_combination])
		if(!binding) return;
		var type = binding['event'];
		var ele = binding['target'];
		var callback = binding['callback'];

		if(ele.detachEvent) ele.detachEvent('on'+type, callback);
		else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
		else ele['on'+type] = false;
	}
}/* gui/tabber.js */

/*==================================================
  $Id: tabber.js,v 1.9 2006/04/27 20:51:51 pat Exp $
  tabber.js by Patrick Fitzgerald pat@barelyfitz.com

  Documentation can be found at the following URL:
  http://www.barelyfitz.com/projects/tabber/

  License (http://www.opensource.org/licenses/mit-license.php)

  Copyright (c) 2006 Patrick Fitzgerald

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation files
  (the "Software"), to deal in the Software without restriction,
  including without limitation the rights to use, copy, modify, merge,
  publish, distribute, sublicense, and/or sell copies of the Software,
  and to permit persons to whom the Software is furnished to do so,
  subject to the following conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
  ==================================================*/

function tabberObj(argsObj)
{
  var arg; /* name of an argument to override */

  /* Element for the main tabber div. If you supply this in argsObj,
     then the init() method will be called.
  */
  this.div = null;

  /* Class of the main tabber div */
  this.classMain = "tabber";

  /* Rename classMain to classMainLive after tabifying
     (so a different style can be applied)
  */
  this.classMainLive = "tabberlive";

  /* Class of each DIV that contains a tab */
  this.classTab = "tabbertab";

  /* Class to indicate which tab should be active on startup */
  this.classTabDefault = "tabbertabdefault";

  /* Class for the navigation UL */
  this.classNav = "tabbernav";

  /* When a tab is to be hidden, instead of setting display='none', we
     set the class of the div to classTabHide. In your screen
     stylesheet you should set classTabHide to display:none.  In your
     print stylesheet you should set display:block to ensure that all
     the information is printed.
  */
  this.classTabHide = "tabbertabhide";

  /* Class to set the navigation LI when the tab is active, so you can
     use a different style on the active tab.
  */
  this.classNavActive = "tabberactive";

  /* Elements that might contain the title for the tab, only used if a
     title is not specified in the TITLE attribute of DIV classTab.
  */
  this.titleElements = ['h2','h3','h4','h5','h6'];

  /* Should we strip out the HTML from the innerHTML of the title elements?
     This should usually be true.
  */
  this.titleElementsStripHTML = true;

  /* If the user specified the tab names using a TITLE attribute on
     the DIV, then the browser will display a tooltip whenever the
     mouse is over the DIV. To prevent this tooltip, we can remove the
     TITLE attribute after getting the tab name.
  */
  this.removeTitle = true;

  /* If you want to add an id to each link set this to true */
  this.addLinkId = false;

  /* If addIds==true, then you can set a format for the ids.
     <tabberid> will be replaced with the id of the main tabber div.
     <tabnumberzero> will be replaced with the tab number
       (tab numbers starting at zero)
     <tabnumberone> will be replaced with the tab number
       (tab numbers starting at one)
     <tabtitle> will be replaced by the tab title
       (with all non-alphanumeric characters removed)
   */
  this.linkIdFormat = '<tabberid>nav<tabnumberone>';

  /* You can override the defaults listed above by passing in an object:
     var mytab = new tabber({property:value,property:value});
  */
  for (arg in argsObj) { this[arg] = argsObj[arg]; }

  /* Create regular expressions for the class names; Note: if you
     change the class names after a new object is created you must
     also change these regular expressions.
  */
  this.REclassMain = new RegExp('\\b' + this.classMain + '\\b', 'gi');
  this.REclassMainLive = new RegExp('\\b' + this.classMainLive + '\\b', 'gi');
  this.REclassTab = new RegExp('\\b' + this.classTab + '\\b', 'gi');
  this.REclassTabDefault = new RegExp('\\b' + this.classTabDefault + '\\b', 'gi');
  this.REclassTabHide = new RegExp('\\b' + this.classTabHide + '\\b', 'gi');

  /* Array of objects holding info about each tab */
  this.tabs = new Array();

  /* If the main tabber div was specified, call init() now */
  if (this.div) {

    this.init(this.div);

    /* We don't need the main div anymore, and to prevent a memory leak
       in IE, we must remove the circular reference between the div
       and the tabber object. */
    this.div = null;
  }
}


/*--------------------------------------------------
  Methods for tabberObj
  --------------------------------------------------*/


tabberObj.prototype.init = function(e)
{
  /* Set up the tabber interface.

     e = element (the main containing div)

     Example:
     init(document.getElementById('mytabberdiv'))
   */

  var
  childNodes, /* child nodes of the tabber div */
  i, i2, /* loop indices */
  t, /* object to store info about a single tab */
  defaultTab=0, /* which tab to select by default */
  DOM_ul, /* tabbernav list */
  DOM_li, /* tabbernav list item */
  DOM_a, /* tabbernav link */
  aId, /* A unique id for DOM_a */
  headingElement; /* searching for text to use in the tab */

  /* Verify that the browser supports DOM scripting */
  if (!document.getElementsByTagName) { return false; }

  /* If the main DIV has an ID then save it. */
  if (e.id) {
    this.id = e.id;
  }

  /* Clear the tabs array (but it should normally be empty) */
  this.tabs.length = 0;

  /* Loop through an array of all the child nodes within our tabber element. */
  childNodes = e.childNodes;
  for(i=0; i < childNodes.length; i++) {

    /* Find the nodes where class="tabbertab" */
    if(childNodes[i].className &&
       childNodes[i].className.match(this.REclassTab)) {
      
      /* Create a new object to save info about this tab */
      t = new Object();
      
      /* Save a pointer to the div for this tab */
      t.div = childNodes[i];
      
      /* Add the new object to the array of tabs */
      this.tabs[this.tabs.length] = t;

      /* If the class name contains classTabDefault,
	 then select this tab by default.
      */
      if (childNodes[i].className.match(this.REclassTabDefault)) {
	defaultTab = this.tabs.length-1;
      }
    }
  }

  /* Create a new UL list to hold the tab headings */
  DOM_ul = document.createElement("ul");
  DOM_ul.className = this.classNav;
  
  /* Loop through each tab we found */
  for (i=0; i < this.tabs.length; i++) {

    t = this.tabs[i];

    /* Get the label to use for this tab:
       From the title attribute on the DIV,
       Or from one of the this.titleElements[] elements,
       Or use an automatically generated number.
     */
    t.headingText = t.div.title;

    /* Remove the title attribute to prevent a tooltip from appearing */
    if (this.removeTitle) { t.div.title = ''; }

    if (!t.headingText) {

      /* Title was not defined in the title of the DIV,
	 So try to get the title from an element within the DIV.
	 Go through the list of elements in this.titleElements
	 (typically heading elements ['h2','h3','h4'])
      */
      for (i2=0; i2<this.titleElements.length; i2++) {
	headingElement = t.div.getElementsByTagName(this.titleElements[i2])[0];
	if (headingElement) {
	  t.headingText = headingElement.innerHTML;
	  if (this.titleElementsStripHTML) {
	    t.headingText.replace(/<br>/gi," ");
	    t.headingText = t.headingText.replace(/<[^>]+>/g,"");
	  }
	  break;
	}
      }
    }

    if (!t.headingText) {
      /* Title was not found (or is blank) so automatically generate a
         number for the tab.
      */
      t.headingText = i + 1;
    }

    /* Create a list element for the tab */
    DOM_li = document.createElement("li");

    /* Save a reference to this list item so we can later change it to
       the "active" class */
    t.li = DOM_li;

    /* Create a link to activate the tab */
    DOM_a = document.createElement("a");
    DOM_a.appendChild(document.createTextNode(t.headingText));
    DOM_a.href = "javascript:void(null);";
    DOM_a.title = t.headingText;
    DOM_a.onclick = this.navClick;

    /* Add some properties to the link so we can identify which tab
       was clicked. Later the navClick method will need this.
    */
    DOM_a.tabber = this;
    DOM_a.tabberIndex = i;

    /* Do we need to add an id to DOM_a? */
    if (this.addLinkId && this.linkIdFormat) {

      /* Determine the id name */
      aId = this.linkIdFormat;
      aId = aId.replace(/<tabberid>/gi, this.id);
      aId = aId.replace(/<tabnumberzero>/gi, i);
      aId = aId.replace(/<tabnumberone>/gi, i+1);
      aId = aId.replace(/<tabtitle>/gi, t.headingText.replace(/[^a-zA-Z0-9\-]/gi, ''));

      DOM_a.id = aId;
    }

    /* Add the link to the list element */
    DOM_li.appendChild(DOM_a);

    /* Add the list element to the list */
    DOM_ul.appendChild(DOM_li);
  }

  /* Add the UL list to the beginning of the tabber div */
  e.insertBefore(DOM_ul, e.firstChild);

  /* Make the tabber div "live" so different CSS can be applied */
  e.className = e.className.replace(this.REclassMain, this.classMainLive);

  /* Activate the default tab, and do not call the onclick handler */
  this.tabShow(defaultTab);

  /* If the user specified an onLoad function, call it now. */
  if (typeof this.onLoad == 'function') {
    this.onLoad({tabber:this});
  }

  return this;
};


tabberObj.prototype.navClick = function(event)
{
  /* This method should only be called by the onClick event of an <A>
     element, in which case we will determine which tab was clicked by
     examining a property that we previously attached to the <A>
     element.

     Since this was triggered from an onClick event, the variable
     "this" refers to the <A> element that triggered the onClick
     event (and not to the tabberObj).

     When tabberObj was initialized, we added some extra properties
     to the <A> element, for the purpose of retrieving them now. Get
     the tabberObj object, plus the tab number that was clicked.
  */

  var
  rVal, /* Return value from the user onclick function */
  a, /* element that triggered the onclick event */
  self, /* the tabber object */
  tabberIndex, /* index of the tab that triggered the event */
  onClickArgs; /* args to send the onclick function */

  a = this;
  if (!a.tabber) { return false; }

  self = a.tabber;
  tabberIndex = a.tabberIndex;

  /* Remove focus from the link because it looks ugly.
     I don't know if this is a good idea...
  */
  a.blur();

  /* If the user specified an onClick function, call it now.
     If the function returns false then do not continue.
  */
  if (typeof self.onClick == 'function') {

    onClickArgs = {'tabber':self, 'index':tabberIndex, 'event':event};

    /* IE uses a different way to access the event object */
    if (!event) { onClickArgs.event = window.event; }

    rVal = self.onClick(onClickArgs);
    if (rVal === false) { return false; }
  }

  self.tabShow(tabberIndex);

  return false;
};


tabberObj.prototype.tabHideAll = function()
{
  var i; /* counter */

  /* Hide all tabs and make all navigation links inactive */
  for (i = 0; i < this.tabs.length; i++) {
    this.tabHide(i);
  }
};


tabberObj.prototype.tabHide = function(tabberIndex)
{
  var div;

  if (!this.tabs[tabberIndex]) { return false; }

  /* Hide a single tab and make its navigation link inactive */
  div = this.tabs[tabberIndex].div;

  /* Hide the tab contents by adding classTabHide to the div */
  if (!div.className.match(this.REclassTabHide)) {
    div.className += ' ' + this.classTabHide;
  }
  this.navClearActive(tabberIndex);

  return this;
};


tabberObj.prototype.tabShow = function(tabberIndex)
{
  /* Show the tabberIndex tab and hide all the other tabs */

  var div;

  if (!this.tabs[tabberIndex]) { return false; }

  /* Hide all the tabs first */
  this.tabHideAll();

  /* Get the div that holds this tab */
  div = this.tabs[tabberIndex].div;

  /* Remove classTabHide from the div */
  div.className = div.className.replace(this.REclassTabHide, '');

  /* Mark this tab navigation link as "active" */
  this.navSetActive(tabberIndex);

  /* If the user specified an onTabDisplay function, call it now. */
  if (typeof this.onTabDisplay == 'function') {
    this.onTabDisplay({'tabber':this, 'index':tabberIndex});
  }

  return this;
};

tabberObj.prototype.navSetActive = function(tabberIndex)
{
  /* Note: this method does *not* enforce the rule
     that only one nav item can be active at a time.
  */

  /* Set classNavActive for the navigation list item */
  this.tabs[tabberIndex].li.className = this.classNavActive;

  return this;
};


tabberObj.prototype.navClearActive = function(tabberIndex)
{
  /* Note: this method does *not* enforce the rule
     that one nav should always be active.
  */

  /* Remove classNavActive from the navigation list item */
  this.tabs[tabberIndex].li.className = '';

  return this;
};


/*==================================================*/


function tabberAutomatic(tabberArgs)
{
  /* This function finds all DIV elements in the document where
     class=tabber.classMain, then converts them to use the tabber
     interface.

     tabberArgs = an object to send to "new tabber()"
  */
  var
    tempObj, /* Temporary tabber object */
    divs, /* Array of all divs on the page */
    i; /* Loop index */

  if (!tabberArgs) { tabberArgs = {}; }

  /* Create a tabber object so we can get the value of classMain */
  tempObj = new tabberObj(tabberArgs);

  /* Find all DIV elements in the document that have class=tabber */

  /* First get an array of all DIV elements and loop through them */
  divs = document.getElementsByTagName("div");
  for (i=0; i < divs.length; i++) {
    
    /* Is this DIV the correct class? */
    if (divs[i].className &&
	divs[i].className.match(tempObj.REclassMain)) {
      
      /* Now tabify the DIV */
      tabberArgs.div = divs[i];
      divs[i].tabber = new tabberObj(tabberArgs);
    }
  }
  
  return this;
}


/*==================================================*/


function tabberAutomaticOnLoad(tabberArgs)
{
  /* This function adds tabberAutomatic to the window.onload event,
     so it will run after the document has finished loading.
  */
  var oldOnLoad;

  if (!tabberArgs) { tabberArgs = {}; }

  /* Taken from: http://simon.incutio.com/archive/2004/05/26/addLoadEvent */

  oldOnLoad = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = function() {
      tabberAutomatic(tabberArgs);
    };
  } else {
    window.onload = function() {
      oldOnLoad();
      tabberAutomatic(tabberArgs);
    };
  }
}


/*==================================================*/


/* Run tabberAutomaticOnload() unless the "manualStartup" option was specified */

if (typeof tabberOptions == 'undefined') {

    tabberAutomaticOnLoad();

} else {

  if (!tabberOptions['manualStartup']) {
    tabberAutomaticOnLoad(tabberOptions);
  }

}
/* gui/sorttable.js */

/*
  SortTable
  version 2
  7th April 2007
  Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/
  
  Instructions:
  Download this file
  Add <script src="sorttable.js"></script> to your HTML
  Add class="sortable" to any table you'd like to make sortable
  Click on the headers to sort
  
  Thanks to many, many people for contributions and suggestions.
  Licenced as X11: http://www.kryogenix.org/code/browser/licence.html
  This basically means: do what you want with it.
*/

 
var stIsIE = /*@cc_on!@*/false;

sorttable = {
  init: function() {
    // quit if this function has already been called
    if (arguments.callee.done) return;
    // flag this function so we don't do the same thing twice
    arguments.callee.done = true;
    // kill the timer
    if (_timer) clearInterval(_timer);
    
    if (!document.createElement || !document.getElementsByTagName) return;
    
    sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/;
    
    forEach(document.getElementsByTagName('table'), function(table) {
      if (table.className.search(/\bsortable\b/) != -1) {
        sorttable.makeSortable(table);
      }
    });
    
  },
  
  makeSortable: function(table, c) {
    if (table.getElementsByTagName('thead').length == 0) {
      // table doesn't have a tHead. Since it should have, create one and
      // put the first table row in it.
      the = document.createElement('thead');
      the.appendChild(table.rows[0]);
      table.insertBefore(the,table.firstChild);
    }
    // Safari doesn't support table.tHead, sigh
    if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0];
    
    if (table.tHead.rows.length != 1) return; // can't cope with two header rows
    
    // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as
    // "total" rows, for example). This is B&R, since what you're supposed
    // to do is put them in a tfoot. So, if there are sortbottom rows,
    // for backwards compatibility, move them to tfoot (creating it if needed).
    sortbottomrows = [];
    for (var i=0; i<table.rows.length; i++) {
      if (table.rows[i].className.search(/\bsortbottom\b/) != -1) {
        sortbottomrows[sortbottomrows.length] = table.rows[i];
      }
    }
    if (sortbottomrows) {
      if (table.tFoot == null) {
        // table doesn't have a tfoot. Create one.
        tfo = document.createElement('tfoot');
        table.appendChild(tfo);
      }
      for (var i=0; i<sortbottomrows.length; i++) {
        tfo.appendChild(sortbottomrows[i]);
      }
      delete sortbottomrows;
    }
    // work through each column and calculate its type
    headrow = table.tHead.rows[0].cells;
    for (var i=0; i<headrow.length; i++) {
      // manually override the type with a sorttable_type attribute
      if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col
        mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/);
        if (mtch) { override = mtch[1]; }
	      if (mtch && typeof sorttable["sort_"+override] == 'function') {
	        headrow[i].sorttable_sortfunction = sorttable["sort_"+override];
	      } else {
	        headrow[i].sorttable_sortfunction = sorttable.guessType(table,i);
	      }
	      // make it clickable to sort
	      headrow[i].sorttable_columnindex = i;
	      headrow[i].sorttable_tbody = table.tBodies[0];
	      dean_addEvent(headrow[i],"click", function(e) {
	
		pager.showAll()
		
          if (this.className.search(/\bsorttable_sorted\b/) != -1) {
            // if we're already sorted by this column, just 
            // reverse the table, which is quicker
            sorttable.reverse(this.sorttable_tbody);
            this.className = this.className.replace('sorttable_sorted',
                                                    'sorttable_sorted_reverse');
            this.removeChild(document.getElementById('sorttable_sortfwdind'));
            sortrevind = document.createElement('span');
            sortrevind.id = "sorttable_sortrevind";
            sortrevind.innerHTML = stIsIE ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
            this.appendChild(sortrevind);
	    
	    	pager.showPage (pager.currentPage);
		
            return;
          }
          if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) {
            // if we're already sorted by this column in reverse, just 
            // re-reverse the table, which is quicker
            sorttable.reverse(this.sorttable_tbody);
            this.className = this.className.replace('sorttable_sorted_reverse',
                                                    'sorttable_sorted');
            this.removeChild(document.getElementById('sorttable_sortrevind'));
            sortfwdind = document.createElement('span');
            sortfwdind.id = "sorttable_sortfwdind";
            sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
            this.appendChild(sortfwdind);
	    
		pager.showPage (pager.currentPage);
		
            return;
          }
          
          // remove sorttable_sorted classes
          theadrow = this.parentNode;
          forEach(theadrow.childNodes, function(cell) {
            if (cell.nodeType == 1) { // an element
              cell.className = cell.className.replace('sorttable_sorted_reverse','');
              cell.className = cell.className.replace('sorttable_sorted','');
            }
	    
          });
          sortfwdind = document.getElementById('sorttable_sortfwdind');
          if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); }
          sortrevind = document.getElementById('sorttable_sortrevind');
          if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); }
          
          this.className += ' sorttable_sorted';
          sortfwdind = document.createElement('span');
          sortfwdind.id = "sorttable_sortfwdind";
          sortfwdind.innerHTML = stIsIE ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
          this.appendChild(sortfwdind);

	        // build an array to sort. This is a Schwartzian transform thing,
	        // i.e., we "decorate" each row with the actual sort key,
	        // sort based on the sort keys, and then put the rows back in order
	        // which is a lot faster because you only do getInnerText once per row
	        row_array = [];
	        col = this.sorttable_columnindex;
	        rows = this.sorttable_tbody.rows;
	        for (var j=0; j<rows.length; j++) {
	          row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]];
	        }
	        /* If you want a stable sort, uncomment the following line */
	        //sorttable.shaker_sort(row_array, this.sorttable_sortfunction);
	        /* and comment out this one */
	        row_array.sort(this.sorttable_sortfunction);
	        
	        tb = this.sorttable_tbody;
	        for (var j=0; j<row_array.length; j++) {
	          tb.appendChild(row_array[j][1]);
	        }
	        
	        delete row_array;pager.showPage (pager.currentPage);
	      });
	    }
    }
  },
  
  guessType: function(table, column) {
    // guess the type of a column based on its first non-blank row
    sortfn = sorttable.sort_alpha;
    for (var i=0; i<table.tBodies[0].rows.length; i++) {
      text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]);
      if (text != '') {
        if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) {
          return sorttable.sort_numeric;
        }
        // check for a date: dd/mm/yyyy or dd/mm/yy 
        // can have / or . or - as separator
        // can be mm/dd as well
        possdate = text.match(sorttable.DATE_RE)
        if (possdate) {
          // looks like a date
          first = parseInt(possdate[1]);
          second = parseInt(possdate[2]);
          if (first > 12) {
            // definitely dd/mm
            return sorttable.sort_ddmm;
          } else if (second > 12) {
            return sorttable.sort_mmdd;
          } else {
            // looks like a date, but we can't tell which, so assume
            // that it's dd/mm (English imperialism!) and keep looking
            sortfn = sorttable.sort_ddmm;
          }
        }
      }
    }
    return sortfn;
  },
  
  getInnerText: function(node) {
    // gets the text we want to use for sorting for a cell.
    // strips leading and trailing whitespace.
    // this is *not* a generic getInnerText function; it's special to sorttable.
    // for example, you can override the cell text with a customkey attribute.
    // it also gets .value for <input> fields.
    
    hasInputs = (typeof node.getElementsByTagName == 'function') &&
                 node.getElementsByTagName('input').length;
    
    /*if (node.getAttribute("sorttable_customkey") != null) {
      return node.getAttribute("sorttable_customkey");
    }*/
    if (typeof node.textContent != 'undefined' && !hasInputs) {
      return node.textContent.replace(/^\s+|\s+$/g, '');
    }
    else if (typeof node.innerText != 'undefined' && !hasInputs) {
      return node.innerText.replace(/^\s+|\s+$/g, '');
    }
    else if (typeof node.text != 'undefined' && !hasInputs) {
      return node.text.replace(/^\s+|\s+$/g, '');
    }
    else {
      switch (node.nodeType) {
        case 3:
          if (node.nodeName.toLowerCase() == 'input') {
            return node.value.replace(/^\s+|\s+$/g, '');
          }
        case 4:
          return node.nodeValue.replace(/^\s+|\s+$/g, '');
          break;
        case 1:
        case 11:
          var innerText = '';
          for (var i = 0; i < node.childNodes.length; i++) {
            innerText += sorttable.getInnerText(node.childNodes[i]);
          }
          return innerText.replace(/^\s+|\s+$/g, '');
          break;
        default:
          return '';
      }
    }
  },
  
  reverse: function(tbody) {
    // reverse the rows in a tbody
    newrows = [];
    for (var i=0; i<tbody.rows.length; i++) {
      newrows[newrows.length] = tbody.rows[i];
    }
    for (var i=newrows.length-1; i>=0; i--) {
       tbody.appendChild(newrows[i]);
    }
    delete newrows;
  },
  
  /* sort functions
     each sort function takes two parameters, a and b
     you are comparing a[0] and b[0] */
  sort_numeric: function(a,b) {
    aa = parseFloat(a[0].replace(/[^0-9.-]/g,''));
    if (isNaN(aa)) aa = 0;
    bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); 
    if (isNaN(bb)) bb = 0;
    return aa-bb;
  },
  sort_alpha: function(a,b) {
    if (a[0]==b[0]) return 0;
    if (a[0]<b[0]) return -1;
    return 1;
  },
  sort_ddmm: function(a,b) {
    mtch = a[0].match(sorttable.DATE_RE);
    y = mtch[3]; m = mtch[2]; d = mtch[1];
    if (m.length == 1) m = '0'+m;
    if (d.length == 1) d = '0'+d;
    dt1 = y+m+d;
    mtch = b[0].match(sorttable.DATE_RE);
    y = mtch[3]; m = mtch[2]; d = mtch[1];
    if (m.length == 1) m = '0'+m;
    if (d.length == 1) d = '0'+d;
    dt2 = y+m+d;
    if (dt1==dt2) return 0;
    if (dt1<dt2) return -1;
    return 1;
  },
  sort_mmdd: function(a,b) {
    mtch = a[0].match(sorttable.DATE_RE);
    y = mtch[3]; d = mtch[2]; m = mtch[1];
    if (m.length == 1) m = '0'+m;
    if (d.length == 1) d = '0'+d;
    dt1 = y+m+d;
    mtch = b[0].match(sorttable.DATE_RE);
    y = mtch[3]; d = mtch[2]; m = mtch[1];
    if (m.length == 1) m = '0'+m;
    if (d.length == 1) d = '0'+d;
    dt2 = y+m+d;
    if (dt1==dt2) return 0;
    if (dt1<dt2) return -1;
    return 1;
  },
  
  shaker_sort: function(list, comp_func) {
    // A stable sort function to allow multi-level sorting of data
    // see: http://en.wikipedia.org/wiki/Cocktail_sort
    // thanks to Joseph Nahmias
    var b = 0;
    var t = list.length - 1;
    var swap = true;

    while(swap) {
        swap = false;
        for(var i = b; i < t; ++i) {
            if ( comp_func(list[i], list[i+1]) > 0 ) {
                var q = list[i]; list[i] = list[i+1]; list[i+1] = q;
                swap = true;
            }
        } // for
        t--;

        if (!swap) break;

        for(var i = t; i > b; --i) {
            if ( comp_func(list[i], list[i-1]) < 0 ) {
                var q = list[i]; list[i] = list[i-1]; list[i-1] = q;
                swap = true;
            }
        } // for
        b++;

    } // while(swap)
  }  
}

/* ******************************************************************
   Supporting functions: bundled here to avoid depending on a library
   ****************************************************************** */

// Dean Edwards/Matthias Miller/John Resig

/* for Mozilla/Opera9 */
if (document.addEventListener) {
    document.addEventListener("DOMContentLoaded", sorttable.init, false);
}

/* for Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
    document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
    var script = document.getElementById("__ie_onload");
    script.onreadystatechange = function() {
        if (this.readyState == "complete") {
            sorttable.init(); // call the onload handler
        }
    };
/*@end @*/

/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
    var _timer = setInterval(function() {
        if (/loaded|complete/.test(document.readyState)) {
            sorttable.init(); // call the onload handler
        }
    }, 10);
}

/* for other browsers */
window.onload = sorttable.init;

// written by Dean Edwards, 2005
// with input from Tino Zijdel, Matthias Miller, Diego Perini

// http://dean.edwards.name/weblog/2005/10/add-event/

function dean_addEvent(element, type, handler) {
	if (element.addEventListener) {
		element.addEventListener(type, handler, false);
	} else {
		// assign each event handler a unique ID
		if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++;
		// create a hash table of event types for the element
		if (!element.events) element.events = {};
		// create a hash table of event handlers for each element/event pair
		var handlers = element.events[type];
		if (!handlers) {
			handlers = element.events[type] = {};
			// store the existing event handler (if there is one)
			if (element["on" + type]) {
				handlers[0] = element["on" + type];
			}
		}
		// store the event handler in the hash table
		handlers[handler.$$guid] = handler;
		// assign a global event handler to do all the work
		element["on" + type] = handleEvent;
	}
};
// a counter used to create unique IDs
dean_addEvent.guid = 1;

function removeEvent(element, type, handler) {
	if (element.removeEventListener) {
		element.removeEventListener(type, handler, false);
	} else {
		// delete the event handler from the hash table
		if (element.events && element.events[type]) {
			delete element.events[type][handler.$$guid];
		}
	}
};

function handleEvent(event) {
	var returnValue = true;
	// grab the event object (IE uses a global event object)
	event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
	// get a reference to the hash table of event handlers
	var handlers = this.events[event.type];
	// execute each event handler
	for (var i in handlers) {
		this.$$handleEvent = handlers[i];
		if (this.$$handleEvent(event) === false) {
			returnValue = false;
		}
	}
	return returnValue;
};

function fixEvent(event) {
	// add W3C standard event methods
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
};
fixEvent.preventDefault = function() {
	this.returnValue = false;
};
fixEvent.stopPropagation = function() {
  this.cancelBubble = true;
}

// Dean's forEach: http://dean.edwards.name/base/forEach.js
/*
	forEach, version 1.0
	Copyright 2006, Dean Edwards
	License: http://www.opensource.org/licenses/mit-license.php
*/

// array-like enumeration
if (!Array.forEach) { // mozilla already supports this
	Array.forEach = function(array, block, context) {
		for (var i = 0; i < array.length; i++) {
			block.call(context, array[i], i, array);
		}
	};
}

// generic enumeration
Function.prototype.forEach = function(object, block, context) {
	for (var key in object) {
		if (typeof this.prototype[key] == "undefined") {
			block.call(context, object[key], key, object);
		}
	}
};

// character enumeration
String.forEach = function(string, block, context) {
	Array.forEach(string.split(""), function(chr, index) {
		block.call(context, chr, index, string);
	});
};

// globally resolve forEach enumeration
var forEach = function(object, block, context) {
	if (object) {
		var resolve = Object; // default
		if (object instanceof Function) {
			// functions have a "length" property
			resolve = Function;
		} else if (object.forEach instanceof Function) {
			// the object implements a custom forEach method so use that
			object.forEach(block, context);
			return;
		} else if (typeof object == "string") {
			// the object is a string
			resolve = String;
		} else if (typeof object.length == "number") {
			// the object is array-like
			resolve = Array;
		}
		resolve.forEach(object, block, context);
	}
};

/* gui/paging.js */

function Pager(tableName, itemsPerPage) {
    this.tableName = tableName;
    this.itemsPerPage = itemsPerPage;
    this.currentPage = 1;
    this.pages = 0;
    this.inited = false;
    
    this.showAll = function ()
    {
    	var rows = document.getElementById(tableName).rows;
        // i starts from 1 to skip table header row
        for (var i = 1; i < rows.length; i++) {
		rows[i].style.display = '';
	}
    }
    this.showRecords = function(from, to) {   
        var rows = document.getElementById(tableName).rows;
        // i starts from 1 to skip table header row
        for (var i = 1; i < rows.length; i++) {
            if (i < from || i > to)  
                rows[i].style.display = 'none';
            else
                rows[i].style.display = '';
        }
    }
    
    this.showPage = function(pageNumber) {
    	if (! this.inited) {
    		alert("not inited");
    		return;
    	}

        var oldPageAnchor = document.getElementById('pg'+this.currentPage);
	if (oldPageAnchor)
	{
        	oldPageAnchor.className = 'pg-normal';
	}
        
        this.currentPage = pageNumber;
        var newPageAnchor = document.getElementById('pg'+this.currentPage);
	if (newPageAnchor)
	{
        	newPageAnchor.className = 'pg-selected';
	}
        
        var from = (pageNumber - 1) * itemsPerPage + 1;
        var to = from + itemsPerPage - 1;
	
        this.showRecords(from, to);
    }   
    
    this.prev = function() {
        if (this.currentPage > 1)
            this.showPage(this.currentPage - 1);
    }
    
    this.next = function() {
        if (this.currentPage < this.pages) {
            this.showPage(this.currentPage + 1);
        }
    }                        
    
    this.init = function() {
	try {
	        var rows = document.getElementById(tableName).rows;
	}
	catch (e)
	{
		alert (e);
	}
        var records = (rows.length - 1); 
        this.pages = Math.ceil(records / itemsPerPage);
        this.inited = true;
    }

    this.showPageNav = function(pagerName, positionId) {
    	if (! this.inited) {
    		alert("not inited");
    		return;
    	}
    	var element = document.getElementById(positionId);
    	
	if (!element)
	{
		return (false);
	}
    	var pagerHtml = '<span style="cursor: pointer" onclick="' + pagerName + '.prev();" class="pg-normal"> &#171 Prev </span> | ';
        for (var page = 1; page <= this.pages; page++) 
            pagerHtml += '<span style="cursor: pointer" id="pg' + page + '" class="pg-normal" onclick="' + pagerName + '.showPage(' + page + ');">' + page + '</span> | ';
        pagerHtml += '<span style="cursor: pointer" onclick="'+pagerName+'.next();" class="pg-normal"> Next &#187;</span>';            
        
	if (element)
	{
		if (element.firstChild)
		{
			element.removeChild (element.firstChild);
		}
	}
	
	var d = document.createElement ('div');
	d.innerHTML = pagerHtml;
	
        element.appendChild (d);
    }
}

/* gui/dragdrop.js */

// script.aculo.us dragdrop.js v1.8.2, Tue Nov 18 18:30:58 +0100 2008

// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//           (c) 2005-2008 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

if(Object.isUndefined(Effect))
  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(Object.isArray(containment)) {
        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 drop, affected = [];

    this.drops.each( function(drop) {
      if(Droppables.isAffected(point, element, drop))
        affected.push(drop);
    });

    if(affected.length>0)
      drop = Droppables.findDeepestChild(affected);

    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
    if (drop) {
      Position.within(drop.element, point[0], point[1]);
      if(drop.onHover)
        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));

      if (drop != this.last_active) 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);
        return true;
      }
  },

  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({
  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 = Object.isNumber(element._opacity) ? 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,
      quiet: 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] || Object.isUndefined(arguments[1].endeffect))
      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 && Object.isString(options.handle))
      this.handle = this.element.down('.'+options.handle, 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.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(!Object.isUndefined(Draggable._dragging[this.element]) &&
      Draggable._dragging[this.element]) return;
    if(Event.isLeftClick(event)) {
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if((tag_name = src.tagName.toUpperCase()) && (
        tag_name=='INPUT' ||
        tag_name=='SELECT' ||
        tag_name=='OPTION' ||
        tag_name=='BUTTON' ||
        tag_name=='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.delta)
      this.delta = this.currentDelta();

    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);
      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
      if (!this._originallyAbsolute)
        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);

    if(!this.options.quiet){
      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 + Position.deltaX;
        p[1] += this.options.scroll.scrollTop + Position.deltaY;
        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(Prototype.Browser.WebKit) window.scrollBy(0,0);

    Event.stop(event);
  },

  finishDrag: function(event, success) {
    this.dragging = false;

    if(this.options.quiet){
      Position.prepare();
      var pointer = [Event.pointerX(event), Event.pointerY(event)];
      Droppables.show(pointer, this.element);
    }

    if(this.options.ghosting) {
      if (!this._originallyAbsolute)
        Position.relativize(this.element);
      delete this._originallyAbsolute;
      Element.remove(this._clone);
      this._clone = null;
    }

    var dropped = false;
    if(success) {
      dropped = Droppables.fire(event, this.element);
      if (!dropped) dropped = false;
    }
    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
    Draggables.notify('onEnd', this, event);

    var revert = this.options.revert;
    if(revert && Object.isFunction(revert)) revert = revert(this.element);

    var d = this.currentDelta();
    if(revert && this.options.reverteffect) {
      if (dropped == 0 || revert != 'failure')
        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);
      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(Object.isFunction(this.options.snap)) {
        p = this.options.snap(p[0],p[1],this);
      } else {
      if(Object.isArray(this.options.snap)) {
        p = p.map( function(v, i) {
          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
      } else {
        p = p.map( function(v) {
          return (v/this.options.snap).round()*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 };
  }
});

Draggable._dragging = { };

/*--------------------------------------------------------------------------*/

var SortableObserver = Class.create({
  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.toUpperCase() != "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){
    element = $(element);
    var s = Sortable.sortables[element.id];

    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,
      quiet:       false,
      scroll:      false,
      scrollSensitivity: 20,
      scrollSpeed: 15,
      format:      this.SERIALIZE_RULE,

      // these take arrays of elements or ids and can be
      // used for better initialization performance
      elements:    false,
      handles:     false,

      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,
      quiet:       options.quiet,
      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
    };

    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);
    }

    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
      var handle = options.handles ? $(options.handles[i]) :
        (options.handle ? $(e).select('.' + 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) Sortable._marker.hide();
  },

  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') || Element.extend(document.createElement('DIV'))).
          hide().addClassName('dropmarker').setStyle({position:'absolute'});
      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
    }
    var offsets = Position.cumulativeOffset(dropon);
    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});

    if(position=='after')
      if(sortable.overlap == 'horizontal')
        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
      else
        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});

    Sortable._marker.show();
  },

  _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: [],
        position: parent.children.length,
        container: $(children[i]).down(options.treeTag)
      };

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

  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: [],
      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) {
  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
};/* xchat.js */

/*--------------------------------------------------------------------------*/

function chat_string_create_urls(input)
{
    return input.replace(/(ftp|http|https|file):\/\/[\S]+(\b|$)/gim, '<a href="$&" class="my_link" target="_blank">$&</a>').replace(/([^\/])(www[\S]+(\b|$))/gim, '$1<a href="http://$2" class="my_link" target="_blank">$2</a>');
}

Element.addMethods();var xchat = false;
var xrun = false;
var xsid = 0;
var xlines = 15;
var xtime = 4000;
var xpass = 'dupa.8';

var xChat = Class.create();

xChat.prototype = 
{
	initialize: function()
	{
		this.crc = 0;

		this.out = $('out');
		this.inp = $('inp');

		Event.observe (this.inp, "keydown", this.send.bindAsEventListener(this));

		new Ajax.Request ('/xchat/xchat.php', {method:"post", parameters:{sid:snd_c.sid,crc:this.crc}, onSuccess: this.update_hdlr.bindAsEventListener(this)});
	},
	
	emote: function (str)
	{
		str = chat_string_create_urls (str);
		return (str);
		str = str.replace (/:\)/, '<img src=/images/soundlab/gmail/nose_smile.gif style="display:inline" />');
		str = str.replace (/:\(/, '<img src=/images/soundlab/gmail/frown.gif style="display:inline" />');
		str = str.replace (/<3/, '<img src=/images/soundlab/gmail/heart.gif style="display:inline" />');
		str = str.replace (/:D/, '<img src=/images/soundlab/gmail/grin.gif style="display:inline" />');
		str = str.replace (/x-\(/, '<img src=/images/soundlab/gmail/angry.gif style="display:inline" />');
		str = str.replace (/B-\)/, '<img src=/images/soundlab/gmail/cool.gif style="display:inline" />');
		str = str.replace (/:P/, '<img src=/images/soundlab/gmail/tongue.gif style="display:inline" />');
		str = str.replace (/:-\//, '<img src=/images/soundlab/gmail/slant.gif style="display:inline" />');
		str = str.replace (/=\)/, '<img src=/images/soundlab/gmail/equal_smile.gif style="display:inline" />');
		str = chat_string_create_urls (str);
		return (str);
	},

	update_hdlr: function(request)
	{
		var o = eval ('(' + request.responseText + ')');
		if (o.xchat && o.xchat.length > 4)
		{
			var c = this.out.childElements();

			this.crc = o.crc;

			for (var x=0; x<c.length; x++)
				this.out.removeChild (c[x]);

			for (var x=0; x<o.xchat.length; x++)
			{
				var k = document.createElement ('div');
				k.style.width = "100%";
				k.style.height = "13px";
				k.style.border = "0px solid #e9e9e9";
				var str = stripslashes (this.emote (o.xchat[xlines-x].str));
				k.innerHTML = "[" + o.xchat[xlines-x].uname + " - "+o.xchat[xlines-x].time+"]: " + str;

				this.out.appendChild (k);

				if (x == xlines)
					break;
			}
		}

		setTimeout (this.update.bind(this), xtime);
	},

	update_err: function (request)
	{
		setTimeout (this.update.bind(this), xtime);
	},
	
	update: function()
	{
		if (xrun)
		{
			new Ajax.Request ('/xchat/xchat.php', {method:"post", parameters:{sid:snd_c.sid,crc:this.crc}, onFailure: this.update_err.bindAsEventListener (this), onSuccess: this.update_hdlr.bindAsEventListener(this)});
		}
		else
		{
			setTimeout (this.update.bind(this), xtime);
			xrun=true;
		}
	},
	
	send_hdlr: function(request)
	{
	},
	
	send: function(e)
	{
		if (e.keyCode != 13)
			return (false);

		var msg = this.inp.value;
		if (msg.length < 1)
		{
			return (false);
		}

		this.inp.value = '';
		new Ajax.Request ("/xchat/xchat.php", {method:"post", parameters:{crc:this.crc,msg:msg,sid:snd_c.sid}, onSuccess: this.send_hdlr.bindAsEventListener(this)});
	}
}

function stripslashes(str) 
{
	str=str.replace(/\\'/g,'\'');
	str=str.replace(/\\"/g,'"');
	str=str.replace(/\\\\/g,'\\');
	str=str.replace(/\\0/g,'\0');
	return str;
}

function xinit()
{
	xchat = new xChat();
}

shortcut.add("Alt+Shift+X",parent.snd_toggle_xchat);
shortcut.add("Ctrl+Shift+X",parent.snd_toggle_xchat);

Event.observe (window, 'load', xinit);

