/*  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();// script.aculo.us scriptaculous.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007

// Copyright (c) 2005-2007 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.7.1_beta3',
  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.5.1',
  load: function() {
    function convertVersionString(versionString){
      var r = versionString.split('.');
      return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
    }
 
    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);
    
    $A(document.getElementsByTagName("script")).findAll( function(s) {
      return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
    }).each( function(s) {
      var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
      var 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();// script.aculo.us builder.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)
//
// 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/

var Builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();

    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;

    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];

    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);

    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1])
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        }

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return $(element);
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e);
        else
          if(Builder._isStringOrNumber(e))
            element.appendChild(Builder._text(e));
      });
    } else
      if(Builder._isStringOrNumber(children))
        element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    $(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) {
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope

    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);

    tags.each( function(tag){
      scope[tag] = function() {
        return Builder.node.apply(Builder, [tag].concat($A(arguments)));
      };
    });
  }
};// 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);/**
 * SWFObject v1.5: Flash Player detection and embed - http://blog.deconcept.com/swfobject/
 *
 * SWFObject is (c) 2007 Geoff Stearns and is released under the MIT License:
 * http://www.opensource.org/licenses/mit-license.php
 *
 */
if(typeof deconcept=="undefined"){var deconcept=new Object();}if(typeof deconcept.util=="undefined"){deconcept.util=new Object();}if(typeof deconcept.SWFObjectUtil=="undefined"){deconcept.SWFObjectUtil=new Object();}deconcept.SWFObject=function(_1,id,w,h,_5,c,_7,_8,_9,_a){if(!document.getElementById){return;}this.DETECT_KEY=_a?_a:"detectflash";this.skipDetect=deconcept.util.getRequestParameter(this.DETECT_KEY);this.params=new Object();this.variables=new Object();this.attributes=new Array();if(_1){this.setAttribute("swf",_1);}if(id){this.setAttribute("id",id);}if(w){this.setAttribute("width",w);}if(h){this.setAttribute("height",h);}if(_5){this.setAttribute("version",new deconcept.PlayerVersion(_5.toString().split(".")));}this.installedVer=deconcept.SWFObjectUtil.getPlayerVersion();if(!window.opera&&document.all&&this.installedVer.major>7){deconcept.SWFObject.doPrepUnload=true;}if(c){this.addParam("bgcolor",c);}var q=_7?_7:"high";this.addParam("quality",q);this.setAttribute("useExpressInstall",false);this.setAttribute("doExpressInstall",false);var _c=(_8)?_8:window.location;this.setAttribute("xiRedirectUrl",_c);this.setAttribute("redirectUrl","");if(_9){this.setAttribute("redirectUrl",_9);}};deconcept.SWFObject.prototype={useExpressInstall:function(_d){this.xiSWFPath=!_d?"expressinstall.swf":_d;this.setAttribute("useExpressInstall",true);},setAttribute:function(_e,_f){this.attributes[_e]=_f;},getAttribute:function(_10){return this.attributes[_10];},addParam:function(_11,_12){this.params[_11]=_12;},getParams:function(){return this.params;},addVariable:function(_13,_14){this.variables[_13]=_14;},getVariable:function(_15){return this.variables[_15];},getVariables:function(){return this.variables;},getVariablePairs:function(){var _16=new Array();var key;var _18=this.getVariables();for(key in _18){_16[_16.length]=key+"="+_18[key];}return _16;},getSWFHTML:function(){var _19="";if(navigator.plugins&&navigator.mimeTypes&&navigator.mimeTypes.length){if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","PlugIn");this.setAttribute("swf",this.xiSWFPath);}_19="<embed type=\"application/x-shockwave-flash\" src=\""+this.getAttribute("swf")+"\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\"";_19+=" id=\""+this.getAttribute("id")+"\" name=\""+this.getAttribute("id")+"\" ";var _1a=this.getParams();for(var key in _1a){_19+=[key]+"=\""+_1a[key]+"\" ";}var _1c=this.getVariablePairs().join("&");if(_1c.length>0){_19+="flashvars=\""+_1c+"\"";}_19+="/>";}else{if(this.getAttribute("doExpressInstall")){this.addVariable("MMplayerType","ActiveX");this.setAttribute("swf",this.xiSWFPath);}_19="<object id=\""+this.getAttribute("id")+"\" classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\""+this.getAttribute("width")+"\" height=\""+this.getAttribute("height")+"\" style=\""+this.getAttribute("style")+"\">";_19+="<param name=\"movie\" value=\""+this.getAttribute("swf")+"\" />";var _1d=this.getParams();for(var key in _1d){_19+="<param name=\""+key+"\" value=\""+_1d[key]+"\" />";}var _1f=this.getVariablePairs().join("&");if(_1f.length>0){_19+="<param name=\"flashvars\" value=\""+_1f+"\" />";}_19+="</object>";}return _19;},write:function(_20){if(this.getAttribute("useExpressInstall")){var _21=new deconcept.PlayerVersion([6,0,65]);if(this.installedVer.versionIsValid(_21)&&!this.installedVer.versionIsValid(this.getAttribute("version"))){this.setAttribute("doExpressInstall",true);this.addVariable("MMredirectURL",escape(this.getAttribute("xiRedirectUrl")));document.title=document.title.slice(0,47)+" - Flash Player Installation";this.addVariable("MMdoctitle",document.title);}}if(this.skipDetect||this.getAttribute("doExpressInstall")||this.installedVer.versionIsValid(this.getAttribute("version"))){var n=(typeof _20=="string")?document.getElementById(_20):_20;n.innerHTML=this.getSWFHTML();return true;}else{if(this.getAttribute("redirectUrl")!=""){document.location.replace(this.getAttribute("redirectUrl"));}}return false;}};deconcept.SWFObjectUtil.getPlayerVersion=function(){var _23=new deconcept.PlayerVersion([0,0,0]);if(navigator.plugins&&navigator.mimeTypes.length){var x=navigator.plugins["Shockwave Flash"];if(x&&x.description){_23=new deconcept.PlayerVersion(x.description.replace(/([a-zA-Z]|\s)+/,"").replace(/(\s+r|\s+b[0-9]+)/,".").split("."));}}else{if(navigator.userAgent&&navigator.userAgent.indexOf("Windows CE")>=0){var axo=1;var _26=3;while(axo){try{_26++;axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+_26);_23=new deconcept.PlayerVersion([_26,0,0]);}catch(e){axo=null;}}}else{try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");}catch(e){try{var axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");_23=new deconcept.PlayerVersion([6,0,21]);axo.AllowScriptAccess="always";}catch(e){if(_23.major==6){return _23;}}try{axo=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");}catch(e){}}if(axo!=null){_23=new deconcept.PlayerVersion(axo.GetVariable("$version").split(" ")[1].split(","));}}}return _23;};deconcept.PlayerVersion=function(_29){this.major=_29[0]!=null?parseInt(_29[0]):0;this.minor=_29[1]!=null?parseInt(_29[1]):0;this.rev=_29[2]!=null?parseInt(_29[2]):0;};deconcept.PlayerVersion.prototype.versionIsValid=function(fv){if(this.major<fv.major){return false;}if(this.major>fv.major){return true;}if(this.minor<fv.minor){return false;}if(this.minor>fv.minor){return true;}if(this.rev<fv.rev){return false;}return true;};deconcept.util={getRequestParameter:function(_2b){var q=document.location.search||document.location.hash;if(_2b==null){return q;}if(q){var _2d=q.substring(1).split("&");for(var i=0;i<_2d.length;i++){if(_2d[i].substring(0,_2d[i].indexOf("="))==_2b){return _2d[i].substring((_2d[i].indexOf("=")+1));}}}return "";}};deconcept.SWFObjectUtil.cleanupSWFs=function(){var _2f=document.getElementsByTagName("OBJECT");for(var i=_2f.length-1;i>=0;i--){_2f[i].style.display="none";for(var x in _2f[i]){if(typeof _2f[i][x]=="function"){_2f[i][x]=function(){};}}}};if(deconcept.SWFObject.doPrepUnload){if(!deconcept.unloadSet){deconcept.SWFObjectUtil.prepUnload=function(){__flash_unloadHandler=function(){};__flash_savedUnloadHandler=function(){};window.attachEvent("onunload",deconcept.SWFObjectUtil.cleanupSWFs);};window.attachEvent("onbeforeunload",deconcept.SWFObjectUtil.prepUnload);deconcept.unloadSet=true;}}if(!document.getElementById&&document.all){document.getElementById=function(id){return document.all[id];};}var getQueryParamValue=deconcept.util.getRequestParameter;var FlashObject=deconcept.SWFObject;var SWFObject=deconcept.SWFObject;/*******************************************/
/****   PAGE MANAGER                    ****/
/*******************************************/

/** 
 * Common starting point for all page initialization.
 *
 * Page initialization has two "hooks" to use:
 * - pageLoadInit()     : For things that needs to be done as soon as possible
 * - pageLoadLateInit() : For everything else
 * 
 * Every page that needs JavaScript functionality should make a call like this:
 *      DNV_PageManager.setPage( <PAGE_CONTROLLER> );
 * In the case of the Global home page, it looks like this:
 *      DNV_PageManager.setPage( new DNV_HomePage() );
 */
var DNV_PageManager = {
	page:null,
	initCalled:false,
	lateInitCalled:false,
	
	/** Set the page controller object to use */
	setPage:function( page ) {
		this.page = page;
	},
	
	initSiteSelector:function() {
		var p = DNV_PageManager.page;
		if( p == null ) { return false; }
		if( p.initSiteSelector == null ) {
			p = p.basePage;
			if( p.initSiteSelector == null ) { return false; }
		}
		p.initSiteSelector();
	},
	
	pageLoadInit:function() {
		if( DNV_PageManager.initCalled ) { return false; }
		var p = DNV_PageManager.page;
		if( p == null ) { return false; }
		if( p.pageLoadInit == null ) { return false; }
		var isReady = p.isReadyForPageLoadInit();
		if( !isReady ) { return false; }
		DNV_PageManager.initCalled = true;
		p.pageLoadInit();
		return true;
	},
	
	pageLoadLateInit:function() {
		if( DNV_PageManager.lateInitCalled ) { return false; }
		var p = DNV_PageManager.page;
		if( p == null || p.isReadyForPageLoadInit == null ) { return false; }
		p.pageLoadLateInit();
	},
	
	handleDocumentOnContentLoaded:function() {
		DNV_PageManager.pageLoadInit();
	},
	
	handleWindowOnLoad:function() {
		DNV_PageManager.pageLoadInit();
		window.setTimeout( DNV_PageManager.handleLoadTimeout, 1 );
	},
	
	handleLoadTimeout:function() {
		DNV_PageManager.pageLoadInit();
		DNV_PageManager.pageLoadLateInit();
	},
	
	handleWindowOnUnLoad:function() {
		var p = DNV_PageManager.page;
		if( p == null || p.pageUnloadCleanup == null ) { return false; }
		p.pageUnloadCleanup();
		DNV_PageManager.page = null;
		DNV_PageManager = null;
	}
	
}

document.observe( "contentloaded", DNV_PageManager.handleDocumentOnContentLoaded );
Event.observe( window, "load", DNV_PageManager.handleWindowOnLoad );
Event.observe( window, "unload", DNV_PageManager.handleWindowOnUnLoad );



/*******************************************/
/****   BASE PAGE CLASS                 ****/
/*******************************************/

var DNV_BasePage = Class.create();
DNV_BasePage.prototype = {
	
	siteSelectorDropdown:null,

	initialize : function() {
	},
	
	isReadyForPageLoadInit : function() {
		return $("RootContainer") != null && $("SiteSelector") != null;
	},
	
	pageLoadInit:function() {
		this.initJavaScriptMarker();
		this.checkScreenWidth();
		this.initSiteSelector();
	},
	
	pageLoadLateInit:function() {
		this.initPagedComponents();
		this.initLinkedComponentContent();
		this.initServiceSelectors();
	},
	
	pageUnloadCleanup:function() {
		this.siteSelectorDropdown.unload();
		this.siteSelectorDropdown = null;
		DNV_PagedComponentBehavior.unload();
		DNV_LinkedComponentContentBehavior.unload();
		DNV_ServiceSelector.unload();
	},
	
	/**
	 * Add a meta class name to RootContainer to indicate to CSS that user has javascript.
	 */
	initJavaScriptMarker:function() {
		$("RootContainer").addClassName( "userHasJavaScript" );
	},
	
	/** 
	 * Check if screen is more than 1024px wide, and apply meta class to RootContainer.
	 */
	checkScreenWidth:function() {
		if( screen.availWidth > 1024 ) {
			$("RootContainer").addClassName( "screenWiderThan1024" );
		}
	},
	
	initSiteSelector:function() {
		if( this.siteSelectorDropdown == null ) {
			this.siteSelectorDropdown = new DropdownControl( $("SiteSelector"), null, 22 );
                                                this.siteSelectorDropdown.setSelected(-1);
		}
	},
	
	initPagedComponents:function() {
		DNV_PagedComponentBehavior.findAndInit();
	},
	
	initLinkedComponentContent:function() {
		DNV_LinkedComponentContentBehavior.findAndInit();
	},
	
	initServiceSelectors:function() {
		DNV_ServiceSelector.findAndInit();
	}
}



/*******************************************/
/****   HOME PAGE CLASS                 ****/
/*******************************************/


var DNV_HomePage = Class.create();
DNV_HomePage.prototype = {
	
	basePage:null,
	
	HOT_TOPICS_PAGE_COOKIE_NAME:"hotTopicsPage",
	HOT_TOPICS_PAGE_COOKIE_EXPIRES_DAYS:10,
	
	initialize : function() {
		this.basePage = new DNV_BasePage();
	},
	
	isReadyForPageLoadInit : function() {
		return this.basePage.isReadyForPageLoadInit() && $("HomePageNavigationContainer") != null;
	},

	pageLoadInit:function() {
		this.basePage.pageLoadInit();
		this.adjustNavigationHeights();	
		this.adjustNavigationWidths();	
		var componentColumns = new DNV_ComponentColumnsContainer();
		this.adjustRightComponentHeight(componentColumns);
	},
	
	pageLoadLateInit:function() {
		this.basePage.pageLoadLateInit();
		this.initRotatingHotTopics();
		this.initFlashBanner();
	},
	
	pageUnloadCleanup:function() {
		this.basePage.pageUnloadCleanup();
		this.basePage = null;
	},

	initFlashBanner:function() {
		var banner = DNV_FlashBanner.instance;
		if( banner != null ) {
			banner.delayedRender();
		}
	},
	
	/**
	 * Adjust height of all ul elements int the navigation to match the tallest one.
	 */
	adjustNavigationHeights : function() {
		var height = 0;
		var container = $("HomePageNavigationContainer");
		var elements = container.getElementsBySelector("ul");
		elements.each( function( ul ) { 
			var h = ul.getHeight();
			if( h > height ) { height = h };
		} );
		elements.each( function( ul ) { 
			ul.setStyle( { height: height + "px" } );
		} );
	},

	/**
	 * Adjust height of all .section elements in the navigation to fill the width of the container.
	 */
	adjustNavigationWidths:function() {
		var container = $("HomePageNavigationContainer");
		var containerWidth = container.getWidth() - 30;
		var sections = container.getElementsBySelector(".section");
		var sectionWidth = Math.floor( containerWidth / sections.length );
		sections.each( function(section) {
			section.style.width = sectionWidth + "px";
		} );
	},
	
	/** 
	 * @param componentColumns Reference to the controller for the components at the bottom of the page
	 */
	adjustRightComponentHeight:function(componentColumns) {	
		var container = $("HomePageRightComponentContainer");
		if( container == null ) { return; }
		var pos = Position.page(container);
		var otherComponents = document.getElementsByClassName("ComponentColumns")[0];
		if( otherComponents == null ) { return; }
		var otherPos = Position.page(otherComponents);
		var posDiff = otherPos[1] - pos[1];
		var minHeight = componentColumns.getBodyHeight() + posDiff;
		var height = 0;
		var footerHeight = 0;
		var footer = container.getElementsByClassName("footer")[0];
		if( footer != null ) { footerHeight = $(footer).getHeight(); }
		var elements = $A(container.getElementsByClassName("body"));
		elements.each( function( element ) { 
			element.setStyle( {display:"block"} );
			var h = element.getHeight();
			if( h > height ) { height = h };
			var contentHeight = 0;
			var contents = $A(element.getElementsByClassName("content"));
			contents.each(function(content){
				contentHeight += content.getHeight();
			});
			if( contentHeight > height ) {
				height = contentHeight;
			}
			element.setStyle( {display:""} );
		} );
		height += footerHeight * 2;
		if( height < minHeight ) {
			height = Math.max( height, minHeight );
		} else {
			//componentColumns.adjustHeights(height-minHeight);
		}
		elements.each( function( element ) { 
			element.setStyle( { height: height + "px" } );
		} );
	},
	
	/** 
	 * Assume the Hot Topics component is the first paged, numbered component on the page.
	 */
	initRotatingHotTopics:function() {
		var comp = DNV_PagedComponentBehavior.getFirstPagedNumberedComponent();
		if (comp) {
			var paging = comp.getPagingControl();
			currPage = this.getStoredHotTopicsPage();
			paging.gotoPage( this.getStoredHotTopicsPage() );
			this.storeHotTopicsPage( paging.getCurrentPageIndex() + 1 );
		}
	},
	
	getStoredHotTopicsPage:function() {
		var index = DNV_CookieManager.getCookie( this.HOT_TOPICS_PAGE_COOKIE_NAME );
		if( index == null ) {
			return 0;
		} else {
			return index;
		}
	},
	
	storeHotTopicsPage:function( pageIndex ) {
		DNV_CookieManager.setCookie( this.HOT_TOPICS_PAGE_COOKIE_NAME, pageIndex, this.HOT_TOPICS_PAGE_COOKIE_EXPIRES_DAYS );
	}
}



/*******************************************/
/****   SUB PAGE CLASS                  ****/
/*******************************************/


var DNV_SubPage = Class.create();
DNV_SubPage.prototype = {
	
	basePage:null,
	contentContainer:null,
	comboMenu:null,
	linkedContentItems:null,

	initialize : function() {
		this.basePage = new DNV_BasePage();
	},
	
	isReadyForPageLoadInit : function() {
		return this.basePage.isReadyForPageLoadInit() && $("ContentContainer") != null;
	},
	
	pageLoadInit:function() {
		this.contentContainer = $("ContentContainer");
		this.basePage.pageLoadInit();
		this.initContentTools();
	},
	
	pageLoadLateInit:function() {
		this.contentContainer = $("ContentContainer");
		this.basePage.pageLoadLateInit();
		this.initNavigation();
		this.initContent();
		this.initLinkedContentItems();
	},
	
	pageUnloadCleanup:function() {
		this.basePage.pageUnloadCleanup();
                                if(this.comboMenu != null)
		    this.comboMenu.unload();
		this.comboMenu = null;
		this.linkedContentItems.each(function(lc){lc.unload()});
		this.linkedContentItems = null;
	},
	
	initNavigation:function() {
		this.comboMenu = new ComboMenu( $("ComboMenuContainer") );
	},
	
	initContent:function() {
		if( this.contentContainer == null ) { return; }
		this.initPagedContent();
		$A(this.contentContainer.getElementsByClassName("faq")).each( function(faq){ new DNV_FaqContent( faq ) } );
		$A(this.contentContainer.getElementsByClassName("contactUs")).each( function(contactUs){ new DNV_ContactUsContent( contactUs ) } );
	},
	
	initPagedContent:function() {
		if( this.contentContainer == null ) { return; }
		var collections = this.contentContainer.getElementsByClassName("pages");
		if( collections.length != 1 ) { return; }
		var pages = collections[0].getElementsByClassName("page");
		if( pages != null && pages.length > 1 ) {
			new DNV_PagedContent( this.contentContainer );
		}
	},
	
	initLinkedContentItems:function() {
		if(this.contentContainer==null){return false;}
		var _lc = this.linkedContentItems = [];
		var tStart = (new Date()).getTime();
		var items = this.contentContainer.getElementsByClassName("item");
		// var tOne = (new Date()).getTime() - tStart;
		var count = items.length;
		var isIE6 = (
			window.external &&
			typeof window.XMLHttpRequest == "undefined"
		);

            // Why is the IE 6 line below even here... causes trouble => commented out (Mats Mortensen)
		//if(isIE6) { count = Math.min(10,count); }

		for( var i=0; i < count; i++ ) {
			_lc.push(new DNV_LinkedContentBehavior(items[i]));
		}
		// var tTwo = (new Date()).getTime() - tStart;
		// alert( tOne + ", " + tTwo );
	},

	initContentTools:function() {
		if( this.contentContainer == null ) { return; }
		var tools = this.contentContainer.getElementsByClassName("tools")[0];
		if( tools != null ) { 
			var fontSize = tools.down(".fontSize");
			if( fontSize != null ) {
				new DNV_ContentFontSizeTools( fontSize );
			}
			var print = tools.down( ".print" );
			if( print != null ) {
				new DNV_ContentPrintButton( print );
			}
			var emailButton = tools.down( ".email" );
			var emailForm = $("EmailAFriend");
			if( emailButton != null && emailForm != null ) {
				new DNV_ContentEmailFriend( emailButton, emailForm );
			}
		}
	},
	
	toggleUnitDetails: function(item){	
		$(item).up().up().next().toggle(); 
		if ($(item).up().up().next().visible()){			
			$(item).up().up('div').removeClassName('closed').addClassName('open');
		} else {
			$(item).up().up('div').removeClassName('open').addClassName('closed');
		}
		return false;
	}
	
};





/*******************************************/
/****   ENTRY PAGE CLASS                ****/
/*******************************************/

var DNV_EntryPage = Class.create();
DNV_EntryPage.prototype = {
	
	basePage:null,
	linkedContentItemsIn3rdColumn:null,
	
	initialize : function() {
		this.basePage = new DNV_SubPage();
	},
	
	isReadyForPageLoadInit : function() {
		return this.basePage.isReadyForPageLoadInit();
	},
	
	pageLoadInit:function() {
		this.basePage.pageLoadInit();
		this.adjustHeights();
	},
	
	pageLoadLateInit:function() {
		this.basePage.pageLoadLateInit();
		this.initLinkedContentItemsIn3rdColumn();
	},
	
	pageUnloadCleanup:function() {
		this.basePage.pageUnloadCleanup();
		this.linkedContentItemsIn3rdColumn.each(function(lc){lc.unload()});
	},
	
	initLinkedContentItemsIn3rdColumn:function() {
		var container = $("EntryPageComponentContainer");
		if(container==null){return false;}
		var _lc = this.linkedContentItemsIn3rdColumn = [];
		var items = container.getElementsBySelector(".group .item");
		items.each(
			function(item){ _lc.push(new DNV_LinkedContentBehavior( item ));}
		);
	},
	
	adjustHeights:function() {
		var contentContainer = $("ContentContainer");
		// Check if page is flexible -> do not adjust heights
		if (contentContainer.hasClassName('flexible')) 
		{
			
		} 
		else 
		{
			var primaryContent = contentContainer.getElementsByClassName("primary")[0];
			var secondaryContent = contentContainer.getElementsByClassName("secondary")[0];
			var height = 0;
			height = Math.max( height, $("EntryPageComponentContainer").getHeight() );
			height = Math.max( height, primaryContent.getHeight() );
			height = Math.max( height, secondaryContent.getHeight() );
			primaryContent.setStyle({height:height+'px'});
		}
	}
	
}

/*******************************************/
/****   PAGED CONTENT                   ****/
/*******************************************/


var DNV_PagedContent = Class.create();
DNV_PagedContent.prototype = {

	target:null,
	pages:null,
	pageNavigationContainer:null,
	pagingControl:null,
	
	/** Constructor */
	initialize:function( target ) {
		this.target = target;
		this.initPaging();
	},
	
	initPaging:function() {
		this.target.down(".pages").addClassName("enabled");
		this.target.down(".pageNavigation").addClassName("enabled");
		this.pages = this.target.getElementsBySelector( ".page" );
		this.buildPageNavigationContainer();
		var previousButton = this.target.getElementsBySelector( ".button.previous" )[0];
		var nextButton = this.target.getElementsBySelector( ".button.next" )[0];
		this.pagingControl = new PagingControl( this.pages, this.pageNavigationContainer, previousButton, nextButton );
		this.pagingControl.gotoPage(0);
		this.pagingControl.setScrollToElement( document.body );
	},
	
	buildPageNavigationContainer:function() {
		var c = this.target.getElementsBySelector(".pageNavigation .list")[0];
		var ol = Builder.node( "ol" );
		c.appendChild( ol );
		this.pageNavigationContainer = ol;
	}	
}



/*******************************************/
/****   FAQ CONTENT                     ****/
/*******************************************/


var DNV_FaqContent = Class.create();
DNV_FaqContent.prototype = {

	target:null,
	toggleAllVisible:true,
	
	/** Constructor */
	initialize:function( target ) {
		this.target = target;
		this.toggleAllButton = this.target.getElementsByClassName("toggleAllButton")[0];
		this.initEvents();
		this.toggleAll();
	},
	
	/** Use one event listener for all click events */
	initEvents:function() {
		Event.observe( this.target, "click", this.handleClick.bindAsEventListener(this) );
	},
	
	/** Handle click for all elements inside target container. */
	handleClick:function( event ) {
		var element = Event.element( event );
		if( element == this.toggleAllButton ) {
			Event.stop(event);
			this.toggleAll();
		} else if( element.tagName.toLowerCase() == "dt" ) {
			Event.stop(event);
			this.toggleOne( element );
		}
	},
	
	toggleAll:function() {
		if( this.toggleAllVisible ) {
			this.target.getElementsBySelector("dd").each( Element.hide );
			this.toggleAllVisible = false;
		} else {
			this.target.getElementsBySelector("dd").each( Element.show );
			this.toggleAllVisible = true;
		}
	},
	
	toggleOne:function( element ) {
		var dd = null;
		if( element.tagName.toLowerCase() == "dt" ) {
			dd = element.next("dd");
		} else if( element.tagName.toLowerCase() == "dd" ) {
			dd = element;
		}
		if( dd != null ) {
			dd.toggle();
		}
	}

}



/*******************************************/
/****   CONTACT US                      ****/
/*******************************************/


var DNV_ContactUsContent = Class.create();
DNV_ContactUsContent.prototype = {
	target:null,
	/** Constructor */
	initialize:function( target ) {
		this.target = target;
		this.initEvents();
		this.units = $A(this.target.getElementsByClassName("unit"));
		this.update();
	},
	initEvents:function() {
		Event.observe( this.target, "click", this.handleClick.bindAsEventListener(this) );
	},
	handleClick:function( event ) {
		if( "INPUT" != event.target.tagName ) { return; }
		this.update();
	},
	update:function() {
		this.units.each( function( unit ) {
			var radio = unit.down("input");
			if( radio.checked ) {
				unit.addClassName("selectedUnit");
			} else {
				unit.removeClassName("selectedUnit");
			}
		} );
	}
}



/*******************************************/
/****   CONTENT PRINT BUTTON            ****/
/*******************************************/

var DNV_ContentPrintButton = Class.create();
DNV_ContentPrintButton.prototype = {
	initialize:function( target ) {
		Event.observe( target, "click", this.handleClick.bindAsEventListener(this) );
	},
	handleClick:function( event ) {
		Event.stop( event );
		window.print();
		Event.element( event ).blur();
	}
}

/*******************************************/
/****   CONTENT FONT SIZE TOOLS         ****/
/*******************************************/

var DNV_ContentFontSizeTools = Class.create();
DNV_ContentFontSizeTools.prototype = {
	
	PREFERENCE_COOKIE_NAME:"fontSize",
	PREFERENCE_COOKIE_EXPIREDAYS:10,
	ALLOWED_FONTSIZE_NAMES:["normal","larger","largest"],
	target:null,
	buttons:null,
	
	initialize:function( target ) {
		if( !this.verifyTarget( target ) ) { return; }
		
		this.target = target;
		this.initButtonProperties();
		this.initSelected();
		this.initEvents();
		this.updateButtonClassNames();
	},
	
	verifyTarget:function( target ) {
		if( target == null ) { return false; }
		if( target.down(".button") == null ) { return false; }
		return true;
	},
	
	initButtonProperties:function() {
		var b = this.buttons = this.target.getElementsBySelector("div");
		b[0]._fontSize = "normal";
		b[0]._hovered = false;
		b[0]._selected = false;
		b[1]._fontSize = "larger";
		b[1]._hovered = false;
		b[1]._selected = false;
		b[2]._fontSize = "largest";
		b[2]._hovered = false;
		b[2]._selected = false;
	},
	
	updateButtonClassNames:function() {
		var len = this.buttons.length;
		for( var i = 0; i < len; i++ ) {
			var b = this.buttons[i];
			this.removeClassNames( b );
			var className = b._fontSize;
			if( b._selected ) {
				className += "Selected";
			} else if( b._hovered ) {
				className += "Hovered";
			}
			b.addClassName( className );
		}
	},
	
	removeClassNames:function( button ) {
		button.removeClassName( button._fontSize );
		button.removeClassName( button._fontSize + "Selected" );
		button.removeClassName( button._fontSize + "Hovered" );
	},
	
	initSelected:function() {
		var size = this.getStoredPreference();
		this.setFontSize( size );
		this.buttons.each( function(b){ 
			b._selected = b.className.indexOf( size ) != -1; 
		} );
	},
	
	initEvents:function() {
		var len = this.buttons.length;
		for( var i = 0; i < len; i++ ) {
			Event.observe( this.buttons[i], "mouseover", this.handleMouseOver.bindAsEventListener(this) );
			Event.observe( this.buttons[i], "mouseout", this.handleMouseOut.bindAsEventListener(this) );
			Event.observe( this.buttons[i], "click", this.handleClick.bindAsEventListener(this) );
		}
	},
	
	handleClick:function( event ) {
		var element = Event.element( event );
		if( this.buttons.include( element ) ) {
			this.buttons.each( function(b){ b._selected = false; } );
			element._selected = true;
		}
		this.updateButtonClassNames();
		this.setFontSize( element._fontSize );
	},
	
	handleMouseOver:function( event ) {
		var element = Event.element( event );
		if( this.buttons.include( element ) ) {
			this.buttons.each( function(b){ b._hovered = false; } );
			element._hovered = true;
		}
		this.updateButtonClassNames();
	},
	
	handleMouseOut:function( event ) {
		var element = Event.element( event );
		if( this.buttons.include( element ) ) {
			element._hovered = false;
		}
		this.updateButtonClassNames();
	},
	
	/**
	 * If name is accepted return it unchanged.
	 * If name is accepted, but wrong case, return correct case.
	 * If name is not accepted, return default size name
	 */
	normalizeSizeName:function( name ) {
		if( name == null ) {
			name = this.ALLOWED_FONTSIZE_NAMES[0];
		} else {
			name = name.toLowerCase();
			if( !this.ALLOWED_FONTSIZE_NAMES.include( name ) ) {
				name = this.ALLOWED_FONTSIZE_NAMES[0];
			}
		}
		return name;
	},
	
	setFontSize:function( fontSize ) {
		fontSize = this.normalizeSizeName( fontSize );
		this.storePreference( fontSize );
		var b = $(document.body);
		b.removeClassName( "normalFontSize" );
		b.removeClassName( "largerFontSize" );
		b.removeClassName( "largestFontSize" );
		b.addClassName( fontSize + "FontSize" );
	},
	
	getStoredPreference:function() {
		var stored = DNV_CookieManager.getCookie( this.PREFERENCE_COOKIE_NAME );
		return this.normalizeSizeName( stored );
	},
	
	storePreference:function( preferredFontSize ) {
		preferredFontSize = this.normalizeSizeName( preferredFontSize );
		DNV_CookieManager.setCookie( this.PREFERENCE_COOKIE_NAME, preferredFontSize, this.PREFERENCE_COOKIE_EXPIREDAYS );
	}
}



/*******************************************/
/****   EMAIL A FRIEND                 *****/
/*******************************************/

var DNV_ContentEmailFriend = Class.create();
DNV_ContentEmailFriend.prototype = {
	
	button:null,
	form:null,
	
	initialize:function(button,form) {
		this.button = $(button);
		this.form = $(form);
		this.initEvents();
	},
	
	initEvents:function() {
		Event.observe( this.button, "click", this.handleButtonClick.bindAsEventListener(this) );
		Event.observe( this.form.down(".cancel"), "click", this.handleCancelClick.bindAsEventListener(this) );
		Event.observe( this.form, "submit", this.handleSubmit.bindAsEventListener(this) );
	},
	
	handleButtonClick:function(event) {
		Event.stop(event);
		this.toggleForm();
	},

	handleCancelClick:function(event) {
		this.toggleForm();
	},
	
	toggleForm:function() {
		this.form.toggle();
		
		var selectArr = $A(document.getElementsByTagName("select"));
		
		if( this.form.visible() ) {
			this.form.down(".fields").show();
			this.form.down(".message").hide();
			this.form.elements[0].focus();
			this.freezeHeight();
			
			selectArr.each(function(item){
				$(item).hide();
			});
		}else{
			selectArr.each(function(item){
				$(item).show();
			});
		}
	},
	
	freezeHeight:function() {
		this.form.style.height = (this.form.getHeight()-40) + "px";
	},
	
	handleSubmit:function(event) {
		Event.stop(event);
		if( this.validateForm() ) {
			this.showMessage("WAIT");
			new Ajax.Request(
				this.form.action,
				{	method:this.form.method,
					parameters:this.form.serialize(true),
					onSuccess:this.handleSubmitSuccess.bind(this),
					onFailure:this.handleSubmitFailure.bind(this) } );
		}
	},
	
	validateForm:function() {
		var hasErrors = true;
		var elements = this.form.serialize(true);
		hasErrors = ( elements.from.indexOf("@") == -1 || elements.to.indexOf("@") == -1 );
		if( hasErrors ) {
			this.form.down(".validationError").show();
		} else {
			this.form.down(".validationError").hide();
		}
		return !hasErrors;
	},
	
	handleSubmitSuccess:function(transport) {
		this.showMessage(transport.responseText);
		new Effect.Fade( this.form, {delay:1} );
	},
	
	handleSubmitFailure:function(transport) {
		this.showMessage("ERROR");
	},
	
	showMessage:function(message) {
		this.form.down(".fields").hide();
		var container = this.form.down(".message");
		container.show();
		container.getElementsBySelector("p").each(function(el){$(el).hide()});
		switch(message) {
			case "WAIT" : container.down(".wait").show(); break;
			case "ERROR" : container.down(".error").show(); break;
			default: 
				container.down(".result").show();
				container.down(".result").update(message);
				break;
		}
	}
	
}







/*******************************************/
/****   FLASH BANNER                    ****/
/*******************************************/


var DNV_FlashBanner = Class.create();

DNV_FlashBanner.prototype = {
	
	REQUIRED_FLASH_VERSION:"8",
	pathToSwf:null,
	debug:false,
	messages:[],
	
	COOKIE_NAME:"flashMessageIndex",
	COOKIE_EXPIRES_DAYS:10,
	RENDER_DELAY:200,
	renderTargetId:null,
	
	initialize:function( pathToSwf ) {
		this.pathToSwf = pathToSwf;
		DNV_FlashBanner.instance = this;
	},
	
	/** Place the flash content in the container */
	renderInElement:function( targetId ) {
		this.renderTargetId = targetId;
		$(targetId).update("");
	},
	
	delayedRender:function() {
		var targetId = this.renderTargetId;
		var target = $(targetId);
		var so = new SWFObject( this.pathToSwf, targetId, target.getWidth(), target.getHeight(), this.REQUIRED_FLASH_VERSION, "#FFFFFF" );
		so.addParam( "wmode", "transparent" );
		so.addParam( "menu", "false" );
		so.addVariable( "debug", this.debug ? "true" : "false" );
		for( var i = 0; i < this.messages.length; i++ ) {
			var message = this.messages[i];
			so.addVariable( "template-"+i, message.getTemplate() );
			so.addVariable( "title-"+i, message.getTitle() );
			so.addVariable( "text-"+i, message.getText() );
			so.addVariable( "color-"+i, message.getColor() );
			so.addVariable( "linkUrl-"+i, message.getLinkUrl() );
			so.addVariable( "imageUrl-"+i, message.getImageUrl() );
			so.addVariable( "animation-"+i, message.getAnimation() );
		}
		var index = this.getStoredMessageIndex();
		if( index >= this.messages.length ) { index = 0; }
		this.storeMessageIndex( index + 1 );
		so.addVariable( "startIndex", index );
		so.write( targetId );
	},
	
	setDebugMode:function( mode ) {
		this.debug = mode;
	},
	
	addMessage:function() {
		var message = new DNV_FlashBannerMessage();
		this.messages.push( message );
		return message;
	},
	
	getStoredMessageIndex:function() {
		var index = parseInt(DNV_CookieManager.getCookie( this.COOKIE_NAME ));
		if( index == null || isNaN( index ) ) {
			return 0;
		} else {
			return index;
		}
	},
	
	storeMessageIndex:function( index ) {
		DNV_CookieManager.setCookie( this.COOKIE_NAME, index, this.COOKIE_EXPIRES_DAYS );
	}
	
}

/** Banner message value object **/
var DNV_FlashBannerMessage = Class.create();
DNV_FlashBannerMessage.prototype = {
	
	template:"",
	title:"",
	text:"",
	color:"",
	linkUrl:"",
	imageUrl:"",
	animation:"",
	
	initialize:function() {},
	
	setTemplate:function( template ) { this.template = template; },
	getTemplate:function() { return this.template; },
	setTitle:function( title ) { this.title = title; },
	getTitle:function() { return this.title; },
	setText:function( text ) { this.text = text; },
	getText:function() { return this.text; },
	setColor:function( color ) { this.color = color; },
	getColor:function() { return this.color; },
	setLinkUrl:function( url ) { this.linkUrl = url; },
	getLinkUrl:function() { return this.linkUrl; },
	setImageUrl:function( url ) { this.imageUrl = url; },
	getImageUrl:function() { return this.imageUrl; },
	setAnimation:function( animation ) { this.animation = animation; },
	getAnimation:function() { return this.animation; }
	
}



/*******************************************/
/****   FLV PLAYER                      ****/
/*******************************************/


var DNV_FlvPlayer = Class.create();
DNV_FlvPlayer.prototype = {
	
	videoWidth:null,
	videoHeight:null,
	videoUrl:null,
	stillUrl:null,
	playerUrl:null,
	text:{},
	
	initialize:function( playerUrl ) {
		this.playerUrl = playerUrl;
	},
	renderInElement:function( targetId ) {
		if(this.validate()) {
			var playerSize = this.getPlayerSize();
			var so = new SWFObject(this.playerUrl,"flvplayer",playerSize.width,playerSize.height,"8","#FFFFFFF");
			so.addParam("wmode","transparent");
			so.addParam("menu","false");
			so.addVariable("videoUrl",this.videoUrl);
			so.addVariable("stillUrl",this.stillUrl);
			for( var t in this.text ) {
				so.addVariable("text."+t,this.text[t]);
			}
			so.write(targetId);
		}
	},
	validate:function() {
		var valid = 
			this.videoWidth != null &&
			this.videoHeight != null &&
			this.videoUrl   != null &&
			this.stillUrl  != null
			;
		return valid;
	},
	getPlayerSize:function() {
		var size = {};
		size.padding = 4;
		size.controlsHeight = 34;
		size.width  = this.videoWidth  + (size.padding*2);
		size.height = this.videoHeight + (size.padding*3) + size.controlsHeight;	
		return size;
	}
	
};


Element.addMethods({
    onmouseenter: function(element,observer) {
        element = $(element);
        element.observe('mouseover',function(evt,currentTarget) {
            var relatedTarget = $(evt.relatedTarget || evt.fromElement);
            if( relatedTarget && relatedTarget!=currentTarget &&
relatedTarget.childOf(currentTarget)==false ) {
                observer();
            }
        }.bindAsEventListener({},element));
        return element;
    },
    onmouseleave: function(element,observer) {
        element = $(element);
        element.observe('mouseout',function(evt,currentTarget) {
            var relatedTarget = $(evt.relatedTarget || evt.toElement);
            if( relatedTarget && relatedTarget!=currentTarget &&
relatedTarget.childOf(currentTarget)==false ) {
                observer();
            }
        }.bindAsEventListener({},element));
        return element;
    }
});

var DNV_NewHomePage = Class.create();
DNV_NewHomePage.prototype = {
    
	basePage:null,
	target:null,	
    banner:null,
	hotTopics:null, 
	focusAreas:null,
	linkedHotTopics:null,
	linkedFocusAreas:null,
			
	initialize : function(target) {
		this.target = target;
		this.basePage = new DNV_BasePage();		
		this.initEvents();			
	},
	
	isReadyForPageLoadInit : function() {
		return this.basePage.isReadyForPageLoadInit() && $("HomePageContainer") != null;
	},
	
	pageLoadInit:function() {				
		this.basePage.pageLoadInit();		
		this.hotTopics = $("HotTopics");
		this.focusAreas = $$(".selector.featured");
		this.initLinkedHotTopics();				
		this.initLinkedFocusAreas();		
		this.initBanner();			
		this.initSelectors();		
        this.initStaffSearch();
	},
	
	pageLoadLateInit:function() {		
		this.basePage.pageLoadLateInit();		
	},
	
	pageUnloadCleanup:function() {	
		this.basePage.pageUnloadCleanup();
		this.basePage = null;
		this.linkedHotTopics.each(function(lc){lc.unload()});
		this.linkedHotTopics = null;	
		this.linkedFocusAreas.each(function(lc){lc.unload()});
		this.linkedFocusAreas = null;		
	},
	
	initSelectors : function(){
		$$('.submenu li.level1').each(function(item){				
			Element.onmouseenter(item, function() {							
				if (item.hasClassName('sfhover') == false){					
					item.addClassName('sfhover');				
					var submenu = item.down('ul');				
					if (submenu){										
						submenu.setStyle( {top: item.offsetTop + 'px'});                        
					}
				}
			});
		});	
		
		$$('.submenu li.level1').each(function(item){		
			Element.onmouseleave(item, function() {				
				$(item).removeClassName('sfhover');				
			});
		});		
	},
	
	initEvents:function() {
		Event.observe( this.target, "click", this.handleClick.bindAsEventListener(this) );
	},
	
	initBanner: function(){
		this.banner = new Banner('#BannerContainer .bannerItems', '#BannerContainer .bannerThumbnails', '#BannerContainer .bannerThumbnails-items', 
                    '#BannerContainer .bannerThumbnails-control', '#BannerContainer .bannerThumbnails-background', {
			delay: 8000,
			effect: 'scroll',
			bannerDuration: 1.0,
			thumbDuration: 1.0
		})
	},
    
    initStaffSearch: function(){
        $$('.watermark').each(function(item){
            item.value = item.title;
            $('watermarkRuler').innerHTML = item.title;                
            item.setStyle({'backgroundPosition': ($('watermarkRuler').offsetWidth + 20) + 'px 8px'});                
                            
            item.observe('focus', function() {
                removeWatermark(item);
            });
            
            item.observe('keydown', function() {
                removeWatermark(item);
            });
            
            item.observe('blur', function() {                    
                if (item.value === ""){                    
                    item.addClassName('watermark-on');
                    item.value = item.title;
                }
            });                
        });  
      
        
        if ($('staffSearchForm') != null){
			Event.observe('staffSearchForm', 'submit', function() {
			   $$('.watermark').each(function(item){
					removeWatermark(item);
			   });
			});
        }


        function removeWatermark(item){
            item.removeClassName('watermark-on');                                               
            
            if (item.value === item.title){                        
                item.value = '';
            }
        }
    },
	
	initLinkedHotTopics:function() {
		if(this.hotTopics==null){return false;}
		var _lc = this.linkedHotTopics = [];		
		var items = this.hotTopics.getElementsByClassName("linked");		
		var count = items.length;
		
		for( var i=0; i < count; i++ ) {
			_lc.push(new DNV_LinkedContentBehavior(items[i]));
		}				
	},
	
	initLinkedFocusAreas:function() {
		if(this.focusAreas == null){return false;}
		var _lc = this.linkedFocusAreas = [];		
        if (this.focusAreas && this.focusAreas.length > 0){
            this.focusAreas.each(function(elem){
                var items = elem.getElementsByClassName("linked");		
                var count = items.length;
                
                for( var i=0; i < count; i++ ) {
                    _lc.push(new DNV_LinkedContentBehavior(items[i]));
                }				
            });
        }		
	},
	
	toggleContactInfo: function(item){
				
		$(item).up().next().toggle(); 
		if ($(item).up().next().visible()){			
			$(item).up('li').removeClassName('closed').addClassName('open');
		} else {
			$(item).up('li').removeClassName('open').addClassName('closed');
		}
		return false;
	},
	
	/** Handle click for all elements inside target container. */
	handleClick:function( event ) {
		var element = Event.element( event );		
		if (!element.className || (element.className && (element.className.indexOf('dropDown') == -1 || element.className && element.className.indexOf('dropDownTrigger') >= 0))) {
			$$('.dropDown').each(function(item){
				item.hide();
			});
		}		
		if( element.className && element.className.indexOf('dropDownTrigger') >= 0 ) {
			Event.stop(event);			
			if (element.className.indexOf('selector-label') >= 0 || element.className.indexOf('selector-desc') >= 0){
					$(element).up('a').next('.dropDown').show();
			}
			else {
				$(element).next('.dropDown').show();
			}
		}
		if( element.className && element.className.indexOf('dropDownClose') >= 0 ) {
			Event.stop(event);			
			$(element).up('.dropDown').hide();
		}
	}		
};

var DNV_NewEntryPage = Class.create();
DNV_NewEntryPage.prototype = {

	linkedContentItemsIn3rdColumn:null,
	comboMenu:null,
	basePage:null,
	target:null,	
	primaryArticles: null,
	secondaryArticles: null,
	rightColumn: null,
	linkedPrimaryArticles:null,
	linkedSecondaryArticles:null,
	linkedRightColumnContent: null,
			
	initialize : function(target) {
		this.target = target;
		this.basePage = new DNV_BasePage();		
		this.initEvents();			
	},
	
	isReadyForPageLoadInit : function() {
		return this.basePage.isReadyForPageLoadInit() && $("EntryPageContainer") != null;
	},
	
	pageLoadInit:function() {				
		this.basePage.pageLoadInit();	
		this.primaryArticles = $("PrimaryArticleList");
		this.secondaryArticles = $("SecondaryArticles");
		this.rightColumn = $("RightColumnContainer");
		this.initLinkedPrimaryArticles();				
		this.initLinkedSecondaryArticles();	
		this.initLinkedRightColumnContent();
	},
	
	pageLoadLateInit:function() {		
		this.basePage.pageLoadLateInit();
		this.initNavigation();
		this.initLinkedContentItemsIn3rdColumn();
	},
	
	pageUnloadCleanup:function() {	
		this.basePage.pageUnloadCleanup();
		this.basePage = null;
		this.linkedPrimaryArticles.each(function(lc){lc.unload()});
		this.linkedPrimaryArticles = null;	
		this.linkedSecondaryArticles.each(function(lc){lc.unload()});
		this.linkedSecondaryArticles = null;	
		this.linkedRightColumnContent.each(function(lc){lc.unload()});
		this.linkedRightColumnContent = null;
		if(this.comboMenu != null)
		    this.comboMenu.unload();
		this.comboMenu = null;
		this.linkedContentItemsIn3rdColumn.each(function(lc){lc.unload()});
	},
		
	initEvents:function() {
		Event.observe( this.target, "click", this.handleClick.bindAsEventListener(this) );
	},
	
	
	initNavigation:function() {
		this.comboMenu = new ComboMenu( $("ComboMenuContainer") );
	},
	
		
	initLinkedPrimaryArticles:function() {
		if(this.primaryArticles==null){return false;}
		var _lc = this.linkedPrimaryArticles = [];		
		var items = this.primaryArticles.getElementsByClassName("linked");		
		var count = items.length;
		
		for( var i=0; i < count; i++ ) {
			_lc.push(new DNV_LinkedContentBehavior(items[i]));
		}				
	},
	
	initLinkedSecondaryArticles:function() {
		if(this.secondaryArticles==null){return false;}
		var _lc = this.linkedSecondaryArticles = [];		
		var items = this.secondaryArticles.getElementsByClassName("linked");		
		var count = items.length;
		
		for( var i=0; i < count; i++ ) {
			_lc.push(new DNV_LinkedContentBehavior(items[i]));
		}				
	},
	
	initLinkedRightColumnContent:function() {
		if(this.rightColumn==null){return false;}
		var _lc = this.linkedRightColumnContent = [];		
		var items = this.rightColumn.getElementsByClassName("linked");		
		var count = items.length;
		
		for( var i=0; i < count; i++ ) {
			_lc.push(new DNV_LinkedContentBehavior(items[i]));
		}				
	},
	
	initLinkedContentItemsIn3rdColumn:function() {
		var container = $("EntryPageContainer");
		if(container==null){return false;}
		var _lc = this.linkedContentItemsIn3rdColumn = [];
		var items = container.getElementsBySelector(".group .item");
		items.each(
			function(item){ _lc.push(new DNV_LinkedContentBehavior( item ));}
		);
	},
	
	/** Handle click for all elements inside target container. */
	handleClick:function( event ) {
		
	}		
};
var Banner = Class.create()
Banner.prototype = {	
	initialize: function (bannerItemsContainer, thumbContainer, thumbItemsContainer, thumbNavControls, thumbBackground, options) {
		this.bannerItemsContainer = $$(bannerItemsContainer)[0] ? $$(bannerItemsContainer)[0] : null;		
		this.thumbItemsContainer = $$(thumbItemsContainer).length > 0 ? $$(thumbItemsContainer)[0] : null;		
		this.thumbContainer = $$(thumbContainer).length > 0 ? $$(thumbContainer)[0] : null;	
        this.thumbBackground = $$(thumbBackground).length > 0 ? $$(thumbBackground)[0] : null;	
		this.thumbNavControls = $$(thumbNavControls);
		this.bannerIndex = 0;
		this.direction = 'left';		
		this.bannerItems = [];
		this.thumbWidth = 0;
		this.thumbItemsContainerWidth = 0;
		this.changing = false;
						
		if (this.bannerItemsContainer) {
			this.bannerItems = this.bannerItemsContainer.childElements();			
            Element.onmouseenter(this.bannerItemsContainer.up(), (function() {                
                this.stop();
            }).bind(this));
            Element.onmouseleave(this.bannerItemsContainer.up(), (function() {                
                this.timer = setTimeout(this.periodicallyUpdate.bind(this), this.options.delay);
            }).bind(this));
		}
		
		if (this.bannerItems && this.bannerItems.length > 0){
			this.bannerItems.each(function(item){
				item.hide();
			});
			this.bannerItems[0].show();	
		}
						
		if (this.thumbItemsContainer){
			this.thumbItems = this.thumbItemsContainer.childElements();			
			this.thumbItemsContainerClone = this.thumbItemsContainer.cloneNode(true);				
			this.thumbItemsContainer.up().insert(this.thumbItemsContainerClone);	
            
			if (this.thumbItems && this.thumbItems.length > 0){
				this.thumbWidth = this.thumbItems[0].getWidth();	
				this.thumbItemsContainerWidth = this.thumbWidth * this.thumbItems.length;				
				this.thumbItemsContainer.setStyle({left: this.thumbWidth + 'px' });
				this.thumbItemsContainerClone.setStyle({left: '-' + this.thumbWidth * (this.thumbItems.length-1) + 'px' });				
                this.thumbItemsClone = this.thumbItemsContainerClone.childElements();
				this.initThumbItems(this.thumbItems);
                this.initThumbItems(this.thumbItemsClone);
			}			
		}
		
		if (this.thumbNavControls) {			
            this.thumbNavControls.each(function(item){
                item.setOpacity(0.7);
            });
            this.thumbNavControls.invoke('observe', 'click', this.click.bind(this));
        }
        
        if (this.thumbBackground) {
            this.thumbBackground.setOpacity(0.7);
        }
		
		this.options = Object.extend({
            delay: 5000,
			effect: 'scroll',
			bannerDuration: 0.5,
			thumbDuration: 0.5
        }, options || {});
		
		this.start();
	},
    
    initThumbItems: function(thumbItems){
        thumbItems.invoke('observe', 'click', this.click.bind(this));
        thumbItems.each(function(item){
            item.setOpacity(0.6);
            Element.onmouseenter(item, function(){                
                item.appear({duration:0.2});
            });
            Element.onmouseleave(item, function(){
                if (!item.hasClassName('current')){                    
                    item.fade({duration:0.2, to: 0.6});
                }
            });
        });
        thumbItems[0].addClassName('current');
        thumbItems[0].setOpacity(1.0);
    },
	
	click: function (e) {	
		e.preventDefault ? e.preventDefault() : e.returnValue = false;
		if (!this.changing) {
			var element = e.findElement('a');			
			var doChange = false;			
			var clickPos = element.cumulativeOffset()[0] - this.thumbContainer.cumulativeOffset()[0] - this.thumbNavControls[0].getWidth();			
			element.blur();									
			if (element.hasClassName('prev') || clickPos === 0){
				this.direction = 'right';
				doChange = true;
			} else if (element.hasClassName('next') || clickPos === this.thumbWidth*2) {
				this.direction = 'left';			
				doChange = true;
			}										
			if (doChange) {
				this.changeBanner();
				this.changeThumb();
			}			
		}
	},
	
	changeThumb: function(){	
                    if (this.thumbItemsContainer){
                        this.moveThumbsContainer(this.thumbItemsContainer);				
                        this.moveThumbsContainer(this.thumbItemsContainerClone);
                    }
	},
	
	moveThumbsContainer: function(container) {		
		this.changing = true;
		var thumbs = container.childElements();
		thumbs.each(function(item){
			item.removeClassName("current");
            item.setOpacity(0.6);
		});
		thumbs[this.bannerIndex].addClassName('current');		
        thumbs[this.bannerIndex].setOpacity(1.0);
		if (this.direction === 'left') {	
			if (this.thumbItemsContainer.positionedOffset()[0] <= -this.thumbItemsContainerWidth) {					
				this.thumbItemsContainer.setStyle({left: this.thumbItemsContainerWidth + 'px'});						
			}	
			if (this.thumbItemsContainerClone.positionedOffset()[0] <= -this.thumbItemsContainerWidth) {					
				this.thumbItemsContainerClone.setStyle({left: this.thumbItemsContainerWidth + 'px'});						
			}
			container.morph('left: ' + (container.positionedOffset()[0] - this.thumbWidth) + 'px', {duration:this.options.thumbDuration, transition: Effect.Transitions.easeInOutCubic, afterFinish: (function(){
				if (container.positionedOffset()[0] <= -this.thumbItemsContainerWidth) {					
					container.setStyle({left: this.thumbItemsContainerWidth + 'px'});						
				}	
				this.changing = false;				
				}).bind(this)
			});		
		}
		else {			
			if (this.thumbItemsContainer.positionedOffset()[0] >= this.thumbItemsContainerWidth) {
				this.thumbItemsContainer.setStyle({left: -this.thumbItemsContainerWidth + 'px'});						
			}
			if (this.thumbItemsContainerClone.positionedOffset()[0] >= this.thumbItemsContainerWidth) {
				this.thumbItemsContainerClone.setStyle({left: -this.thumbItemsContainerWidth + 'px'});						
			}
			container.morph('left: ' + (container.positionedOffset()[0] + this.thumbWidth) +'px', {duration:this.options.thumbDuration, transition: Effect.Transitions.easeInOutCubic, afterFinish: (function(){
					if (container.positionedOffset()[0] >= this.thumbItemsContainerWidth) {
						container.setStyle({left: -this.thumbItemsContainerWidth + 'px'});						
					}
					this.changing = false;					
				}).bind(this)
			});	
		}
	},
	
	changeBanner: function () {		
		if (this.direction === 'left') {
			newIndex = this.bannerIndex >= this.bannerItems.length-1 ? 0 : this.bannerIndex + 1;			
		}		
		else {
			newIndex = this.bannerIndex === 0 ? this.bannerItems.length-1 : this.bannerIndex - 1;
		}
		if (this.options.effect === 'scroll') {
			var positionSign = this.direction === 'left' ? '' : '-';
			var morphSign = this.direction === 'left' ? '-' : '';			
			this.bannerItems[this.bannerIndex].setStyle({display:'block', opacity:'1', 'z-index':'2'});
			this.bannerItems[newIndex].setStyle({left: positionSign + this.bannerItems[newIndex].getWidth()+'px', display:'block', opacity:'1'});
			this.bannerItems[newIndex].morph('left:0px', {duration:this.options.bannerDuration, transition: Effect.Transitions.easeInOutCubic});
			this.bannerItems[this.bannerIndex].morph('left:' + morphSign + this.bannerItems[newIndex].getWidth()+'px', {duration:this.options.bannerDuration, transition: Effect.Transitions.easeInOutCubic});
		} 
		else if (this.options.effect === 'fade'){
			this.bannerItems[newIndex].appear({duration:this.options.bannerDuration});
			this.bannerItems[this.bannerIndex].fade({duration:this.options.bannerDuration});
		}
		this.bannerIndex = newIndex;		
	},
	
	stop: function () {	        
		if (this.timer) {
			clearTimeout(this.timer);
		}
	},

	start: function () { 		
        this.periodicallyUpdate();
    },
	
	periodicallyUpdate: function () {		
		if (this.timer != null) {
			clearTimeout(this.timer);
			if (!this.changing) {
				this.direction = 'left';
				this.changeBanner();			
				this.changeThumb();
			}
		}
		this.timer = setTimeout(this.periodicallyUpdate.bind(this), this.options.delay);		
    }
};

var DNV_ComponentColumnsContainer = Class.create();
DNV_ComponentColumnsContainer.prototype = {
	
	bodyHeight : 100,
	
	initialize : function() {
		this.initHeights();
	},
		
	getBodyHeight : function() {
		return this.bodyHeight;
	},
	
	/**
	 * Adjust height of all component body elements to match the tallest one.
	 */
	initHeights : function() {
		var height = 0;
		var container = document.getElementsByClassName("ComponentColumns")[0];
		var footerHeight = 0;
		var footer = container.getElementsByClassName("footer")[0];
		if( footer != null ) { footerHeight = $(footer).getHeight(); }
		var elements = $A(container.getElementsByClassName("body"));
		elements.each( function( element ) { 
			element.setStyle( {display:"block"} );
			var h = element.getHeight();
			if( h > height ) { height = h };
			var contentHeight = 0;
			var contents = $A(element.getElementsByClassName("content"));
			contents.each(function(content){
				contentHeight += content.getHeight();
			});
			if( contentHeight > height ) {
				height = contentHeight;
			}
			element.setStyle( {display:""} );
		} );
		height += footerHeight * 2;
		
		var firstAncestor = null;
		elements.each( function( element ) { 
			var ancestor = element.ancestors()[0];
			if (ancestor && ancestor.className == "component contentList") {
				if (ancestor != firstAncestor) { // First element in new ancestor - add classname first
					firstAncestor = ancestor;
					element.addClassName("first");
				} 
				element.setStyle( { height: "auto" } ); // Autoadjust height
			} else {
				// mamort - This causes problems. Seems to work better without it
				//element.setStyle( { height: height + "px" } );
			}
		} );
		this.bodyHeight = height;
	},
	
	/**
	 * Adjust height of all component body elements by a delta value.
	 */
	adjustHeights:function(deltaHeight){
		var elements = document.getElementsByClassName("ComponentColumns")[0].getElementsByClassName("body");
		elements.each( function( element ) { 
			element.setStyle( {display:"block"} );
			var oldHeight = element.getHeight();
			element.setStyle( {display:"", height: (oldHeight+deltaHeight) + "px"} );
		} );
	}
}


/**
 * Component behavior that adds navigation to a component that has multiple bodies.
 */
var DNV_PagedComponentBehavior = Class.create();
DNV_PagedComponentBehavior._behaviors = [];
DNV_PagedComponentBehavior.findAndInit = function() {
	var containers = $A(document.getElementsByClassName( "ComponentContainer" ));
	containers.each( function( container ) {
		container.getElementsBySelector(".component.paged").each( 
			function( comp ) { 
				var b = new DNV_PagedComponentBehavior( comp ) ;
				DNV_PagedComponentBehavior._behaviors.push(b);
			}
		);
	} );
}
DNV_PagedComponentBehavior.getFirstPagedNumberedComponent = function() {
	var containers = $A(document.getElementsByClassName( "ComponentContainer" ));
	var componentToReturn = null;
	containers.each( function( container ) {
		container.getElementsBySelector(".component.paged").each( 
			function( comp ) { 
				if( comp.getElementsByClassName("numbers").length > 0 ) {
					componentToReturn = comp;
					return;
				}
			}
		);
	} );
	return componentToReturn;
}
DNV_PagedComponentBehavior.unload = function() {
	DNV_PagedComponentBehavior._behaviors.each(function(behavior){ behavior.unload() });
}

DNV_PagedComponentBehavior.prototype = {
	
	target:null,
	pages:null,
	navigationContainer:null,
	pagingControl:null,
	
	initialize:function( target ) {
		this.target = target;
		this.pages = this.target.getElementsByClassName( "body" );
		this.initNavigation();	
		this.createAccessorMethod();
	},
	
	unload:function() {
		this.target.getPagingControl = null;
		this.navigationContainer = null;
		this.target = null;
		this.pagingControl.unload();
		this.pagingControl = null;
	},

	initNavigation:function() {
		this.buildNavigationContainer();
		this.pagingControl = new PagingControl( this.pages, this.navigationContainer );
	},
	
	/** Create navigation DOM structure, unless it already exists. */
	buildNavigationContainer:function() {
		if( !this.hasNavigation() ) {
			var header = this.target.down( ".header" );
			new Insertion.Bottom( header, '<ol class="pageNavigation numbers"></ol>' );	
		}
		this.navigationContainer = this.target.down( ".pageNavigation" );
	},
	
	/** Check if the component already has a .pageNavigation element. */
	hasNavigation:function() {
		return this.target.down( ".pageNavigation" ) != null;
	},
	
	/** Create a local method on the target to access pagingControl */
	createAccessorMethod:function() {
		this.target.getPagingControl = function() {
			return this.pagingControl;
		}.bind( this );
	}
}



/**
 * Component behavior that extracts first link from text content and makes the whole content div clickable
 */
var DNV_LinkedComponentContentBehavior = {};
DNV_LinkedComponentContentBehavior._behaviors = [];
DNV_LinkedComponentContentBehavior.findAndInit = function() {
	var containers = $A(document.getElementsByClassName( "ComponentContainer" ));
	containers.each( function( container ) {
		container.getElementsBySelector(".content.text.linked").each( 
			function( content ) { 
				DNV_LinkedComponentContentBehavior._behaviors.push( new DNV_LinkedContentBehavior( content ) );
			}
		);
	} );
}
DNV_LinkedComponentContentBehavior.unload = function() {
	DNV_LinkedComponentContentBehavior._behaviors.each(function(b){b.unload()});
}

var DNV_LinkedContentBehavior = Class.create();
DNV_LinkedContentBehavior.prototype = {
	
	target:null,
	url:null,
	sourceLinkNode:null,
	disabled:false,
	VALID_LINK_PARENTS:$A(["H1","H2","H3","H4","H5","H6"]),
	
	initialize:function( target ) {
		this.target = target;
		this.initLinking();	
	},
	
	unload:function() {
		this.sourceLinkNode = null;
		this.target = null;
		this.url = "";
	},
	
	initLinking:function() {
		var a = $(this.target.getElementsByTagName("a")[0]);
		if( this.isValidSourceLink( a ) ) {
			this.sourceLinkNode = a;
			this.sourceLinkNode.addClassName("linkedSource");
			this.target.addClassName( "linkedActive" );
			this.url = a.href;
			Event.observe( this.target, "mouseover", this.handleMouseOver.bindAsEventListener( this ) );
			Event.observe( this.target, "mouseout", this.handleMouseOut.bindAsEventListener( this ) );
			Event.observe( this.target, "click", this.handleClick.bindAsEventListener( this ) );
		}
	},
	
	/**
	 * Only links contained in headings are valid.
	 */
	isValidSourceLink:function( linkNode ) {
		if( linkNode == null ) { return false; }
		return this.VALID_LINK_PARENTS.include( linkNode.up().tagName );
	},
	
	handleMouseOver:function(event) {
		this.target.addClassName( "hover" );
	},
	
	handleMouseOut:function(event) {
		window.setTimeout( this.doLateMouseOutCheck.bind(this), 30 );
	},
	
	doLateMouseOutCheck:function() {
		if( this.isMousePositionOutsideTarget() ) {
			this.target.removeClassName( "hover" );
		} else {
			window.setTimeout( this.doLateMouseOutCheck.bind(this), 100 );
		}
	},
	
	isMousePositionOutsideTarget:function() {
		var x = DNV_LinkedContentBehaviorMouseUtil.x;
		var y = DNV_LinkedContentBehaviorMouseUtil.y;
		return !Position.within( this.target, x, y );
	},

	handleClick:function(event) {
		if( this.disabled ) { return; }
		var clicked = Event.element(event);
		if( !this.isOtherLink( clicked ) ) {
			if( this.sourceLinkNode.click ) {
				this.disabled = true;
				this.sourceLinkNode.click();
				var _this = this;
				window.setTimeout( function(){_this.disabled=false;}, 100 );
			} else {
        var a = $(this.target.getElementsByTagName("a")[0]);

        if(a.target == "_blank")
            window.open(this.url);
        else

				window.location.href = this.url;
			}
		}
	},
	
	/**
	 * The linked content may contain other links. This behavior should leave them working normally.
	 * This method checks if the parameter is another link than the one used for this behavior.
	 */
	isOtherLink:function( clickedLink ) {
		if( clickedLink.tagName.toLowerCase() == "a" ) {
			return clickedLink != this.sourceLinkNode;
		} else {
			return false;
		}
	}
	
}


DNV_LinkedContentBehaviorMouseUtil = {
	x:0,y:0,
	handleMouseMove:function(event) {
		DNV_LinkedContentBehaviorMouseUtil.x = Event.pointerX(event);
		DNV_LinkedContentBehaviorMouseUtil.y = Event.pointerY(event);
	}
}
Event.observe( document, "mousemove", DNV_LinkedContentBehaviorMouseUtil.handleMouseMove.bindAsEventListener( this ) );




var DNV_ServiceSelector = Class.create();
DNV_ServiceSelector._selectors = [];
DNV_ServiceSelector.findAndInit = function() {
	var containers = $A(document.getElementsByClassName( "ComponentContainer" ));
	containers.each( function( container ) {
		container.getElementsBySelector(".component.serviceSelector").each( 
			function( component ) { 
				DNV_ServiceSelector._selectors.push( new DNV_ServiceSelector( component ) );
			}
		);
	} );
}
DNV_ServiceSelector.unload = function() {
	DNV_ServiceSelector._selectors.each(function(selector){selector.unload()});
}
DNV_ServiceSelector.prototype = {
	
	target:null,
	descriptions:[],
	data:[],
	currentDescription:"",
	currentUrl:"",
	doubleDropdown:null,
	
	initialize:function( target ) {
		this.target = target;
		this.importDescriptions();
		this.importData();
		this.initDropdowns();
		this.initEvents();
		this.updateDescription();
		if( this.data[0] != null && this.data[0].list[0] != null ) {
			this.currentUrl = this.data[0].list[0].value;
		}
	},
	
	unload:function() {
		this.doubleDropdown.unload();
		this.doubleDropdown = null;
		this.target = null;
		this.data = null;
	},
	
	importDescriptions:function() {
		var descriptions = this.descriptions;
		this.target.getElementsBySelector(".serviceDescriptionData div").each(
			function( div, index ) {
				descriptions[index] = div.innerHTML;
			} );
	},
	
	importData:function() {
		var select = $(this.target.down("select"));
		if( select == null ) { return; }
		var groups = select.getElementsByTagName("optgroup");
		var mainIndex = 0;
		for( var j = 0; j < groups.length; j++ ) {
			var group = $(groups[j]);
			var opt = group.getElementsByTagName("option");
			var list = [];
			for( var i = 0, len = opt.length; i < len; i++ ) {
				var o = opt[i];
				list[i] = { name:o.text, value:o.value, description:this.descriptions[mainIndex] };
				mainIndex++;
			}
			this.data[j] = { name:group.label, list:list };
		}
		select = null;
	},
	
	initDropdowns:function() {
		var industry = this.target.down(".dropdown.industry");
		var itemsPerIndustryColumn = parseInt(industry.down(".itemsPerColumn").innerHTML,10);
		var service = this.target.down(".dropdown.service");
		var itemsPerServiceColumn = parseInt(service.down(".itemsPerColumn").innerHTML,10);
		industry.innerHTML = "";
		service.innerHTML = "";
		this.doubleDropdown = new DoubleDropdownControl( industry, service, this.data, itemsPerIndustryColumn, itemsPerServiceColumn );
		this.doubleDropdown.addChangeListener( this.handleChangeEvent.bind( this ) );
	},
	
	initEvents:function() {
		Event.observe( this.target.down(".goButton"), "click", this.handleGoButtonClick.bindAsEventListener(this) );
	},
	
	handleChangeEvent:function( doubleDropdown ) {
		var d = doubleDropdown.getSelectedData();
		if( d != null ) {
			this.currentDescription = d.description;
			this.currentUrl = d.value;
			this.target.down(".goButton").show();
			this.updateDescription();
		} else {
			this.currentDescription = "";
			this.target.down(".goButton").hide();
			this.updateDescription();
		}
	},
	
	updateDescription:function() {
		this.target.down(".serviceDescription").update( this.currentDescription );
	},
	
	handleGoButtonClick:function( event ) {
		location.href = this.currentUrl;	
	}
	
};



function imageGalleryDownload(imageUrl, photographer){
    var downloadConditions = 'Download conditions\n\nDet Norske Veritas AS (DNV) is the owner of the copyright to the pictures available in image banks on DNV\'s websites and remain the property of DNV, even when reproduced by third parties. \n\nThe images contained in the image bank are approved for royalty-free reproduction in all media by any person or organisation. Use of photos must be credited with the text "© DNV/#photograper#" placed in the immediate vicinity of the image. Images shall be reproduced to a good technical standard. DNV reserves the right to request removal or cease of use of an inappropriate image or application of an image. \n\nUnder no circumstances must the images be used for commercial purposes without DNV\'s prior consent.\n\n© Det Norske Veritas AS. All rights reserved. ';
    downloadConditions = downloadConditions.replace('#photograper#', photographer);
    if (confirm(downloadConditions)){
        window.open(imageUrl);            
    }
    return false;
}/**
 * Control that maintains a list of DOM nodes that can be browsed using a page navigation element.
 */
var PagingControl = Class.create();
PagingControl.prototype = {
	
	/** Array of pages: DOM nodes to be shown/hidden */
	pages:null,
	/** DOM node containing the navigation items */
	navigationContainer:null,
	/** Array of DOM nodes used for page navigation */
	navigationItems:null,
	/** DOM node used as "previous" button */
	previousButton:null,
	/** DOM node used as "next" button */
	nextButton:null,
	/** Index of current page. First page has index 0. */
	currentPageIndex:0,
	/** Element to scroll to on page change. */
	scrollToElement:null,
	
	/**
	 * @constructor
	 *
	 * @param pages                 Array of DOM nodes to be shown/hidden when paging.
	 * @param navigationContainer   <ul> or <ol> node where this control can add <li> elements for navigating.
	 * @param previousButton        DOM node to use for "previous" button
	 * @param nextButton            DOM node to use for "next" button
	 */
	initialize:function( pages, navigationContainer, previousButton, nextButton ) {
		this.pages = $A(pages);
		this.navigationContainer = $(navigationContainer);
		this.previousButton = previousButton;
		this.nextButton = nextButton;
		this.buildNavigation();
		this.updateNavigationContainerWidth();
		this.initEvents();
	},
	
	unload:function() {
		this.navigationContainer = null;
		this.pages = null;
		this.previousButton = null;
		this.nextButton = null;
	},
	
	/**
	 * Build navigation DOM structure if it is not explicitly provided in the HTML.
	 */
	buildNavigation:function() {
		if( !this.hasNavigation() ) {
			for( var i = 0; i < this.pages.length; i++ ) {
				var a = Builder.node( "a", { href:"#" }, i+1 );
				var li = $(Builder.node( "li", [ a ] ));
				if( i == 0 ) { li.addClassName( "current" ) }
				this.navigationContainer.appendChild( li );
			}
		}

		this.navigationContainer.getElementsBySelector("a").each( function(el,i) { el.pageIndex = i } );
		this.navigationItems = $A(this.navigationContainer.getElementsBySelector("li"));
		
	},
	
	initEvents:function() {
		Event.observe( this.navigationContainer, "click", this.handleNavigationClick.bindAsEventListener( this ) );
		Event.observe( this.navigationContainer, "mousedown", function(e) { Event.stop(e) } );
		if( this.previousButton != null ) {
			Event.observe( this.previousButton, "click", this.handleButtonClick.bindAsEventListener(this) );
		}
		if( this.nextButton != null ) {
			Event.observe( this.nextButton, "click", this.handleButtonClick.bindAsEventListener(this) );
		}
	},
	
	hasNavigation:function() {
		return this.navigationContainer.down("li") != null;
	},
	
	/**
	 * Because of floating, the navigation container needs an explicit with. 
	 * This needs to be done at runtime.
	 */
	updateNavigationContainerWidth:function() {
		var width = 20; /* Start with som padding */
		this.navigationItems.each( function( item ) { width += item.getWidth() } );

                                // mamort - cant see why this is necessary. Also seems to break IE 6 with asian templates
		//this.navigationContainer.setStyle({width:width+'px'});
	},
	
	handleNavigationClick:function( event ) {
		Event.stop( event );
		var t = $(Event.element(event));
		if( t.nodeName == "A" ) {
			this.gotoPage( t.pageIndex );
		}
	},
	
	handleButtonClick:function( event ) {
		Event.stop(event);
		var b = Event.element(event);
		if( b == this.previousButton ) {
			this.gotoPage( this.currentPageIndex - 1, false );
		} else if( b == this.nextButton ) {
			this.gotoPage( this.currentPageIndex + 1, false );
		}
	},
	
	gotoPage:function( index, restartIfNeccesary ) {
		index = Number( index );
		if( restartIfNeccesary && index >= this.pages.length ) { index = 0; }
		if( index >= 0 && index < this.pages.length ) {
			this.pages.each( function( page ) { page.removeClassName("current") } );
			this.pages[index].addClassName("current");
			this.currentPageIndex = index;
			this.updateNavigationItems();
			this.updateButtons();
			this.updateScroll();
		}
	},
	
	gotoNextPage:function( restartIfNeccesary ) {
		this.gotoPage( this.currentPageIndex + 1, restartIfNeccesary );
	},
	
	getCurrentPageIndex:function() {
		return this.currentPageIndex;
	},
	
	/** Update which navigation item is marked as current */
	updateNavigationItems:function() {
		this.navigationItems.each( function( li ) { li.removeClassName( "current" ) } );
		this.navigationItems[this.currentPageIndex].addClassName( "current" );
	},
	
	/** Update disabled/enabled state of next and previous buttons */
	updateButtons:function() {
		if( this.previousButton == null || this.nextButton == null ) {
			return;
		}
		if( this.currentPageIndex > 0 ) {
			this.previousButton.removeClassName("disabledPrevious");
		} else {
			this.previousButton.addClassName("disabledPrevious");
		}
		if( this.currentPageIndex < this.pages.length - 1 ) {
			this.nextButton.removeClassName("disabledNext");
		} else {
			this.nextButton.addClassName("disabledNext");
		}
	},
	
	/**
	 * Set which element to scroll to on page change. Set null if scrolling is not wanted.
	 */
	setScrollToElement:function( element ) {
		this.scrollToElement = element;
	},
	
	/** Scroll to top of preset element, if any */
	updateScroll:function() {
		if( this.scrollToElement != null ) {
			$(this.scrollToElement).scrollTo();
		}
	}
}






/**
 * Replace a standard dropdown <select> tag with a multicolumn one that can be styled with script.
 * If there is a <select> inside the provided target, it will be used as data source.
 * If a list is provided as constructor argument, it will be used as a data source instead.
 * The target will be cleared of all contents, and the new DOM structure will be placed in it.
 */
var DropdownControl = Class.create();
DropdownControl.prototype = {

	/** Target element for this widget */
	target:null,
	/** Array of value objects as { name, value } */
	data:[],
	/** Index of the initially selected item */
	selectedIndex:-1,
	
	/** Button element */
	button:null,
	/** List element */
	list:null,
	listInitialized:false,
	/** Flag indicating visibility */
	visible:false,
	
	/** List offset from button */
	listOffsetX : -20,
	listOffsetY :  35,
	
	changeListeners:null,
	toggleListeners:null,
	
	itemsPerColumn:5,
	
	/* If <option>-tag's label starts with the DEFAULT_ITEM ([DefaultItem]), then that option is set as the selected option in the selectlist.
		This option should NOT appear in the optionslist. Used when a text such as "Select industry" or "[Change language] should'nt appear as an option.*/
	DEFAULT_ITEM : "[defaultItem]",
	containsDefaultItem : false,
	
	/** Constructor */
	initialize:function( target, data, itemsPerColumn ) {
		this.target = target;
		this.changeListeners = [];
		this.toggleListeners = [];
		if( data != null ) {
			this.data = data;
			this.selectedIndex = -1;
		} else {
			this.importSelectList();
		}
		
		this.containsDefaultItem = this.listContainsDefaultItem();
				
		if (this.containsDefaultItem)
			this.itemsPerColumn = itemsPerColumn +1; //Adds one space for empty option.
		else
			this.itemsPerColumn = itemsPerColumn;
		
		this.removeOldContents();
		this.buildDropdown();
				
		this.initEvents();
		this.hide();
	},
	
	unload:function() {
		this.data = null;
		this.button = null;
		this.list = null;
		this.changeListeners = null;
		this.toggleListeners = null;
		this.target = null;
	},
	
	
	listContainsDefaultItem:function() {
		
		for (var i = 0; i < this.data.length; i++)
		{
			if(this.data[i].name.startsWith(this.DEFAULT_ITEM))
			{
				return true;
			}
		}
		return false;
	},
	
	getSelectedIndex:function() {
    if(this.containsDefaultItem)
        return this.selectedIndex + 1;
    else
        return this.selectedIndex;
	},
	
	getDefaultItem:function(){
		for (var i = 0; i < this.data.length; i++)
		{
			if(this.data[i].name.startsWith(this.DEFAULT_ITEM))
			{
				return this.data[i];
			}
		}    
		
		return null;  
	},
	
	/** Get list of element from <select> element in target */
	importSelectList:function() {
		var select = this.target.down("select");
		if( select == null ) {
			this.selectedIndex = -1;
		} else {
			this.selectedIndex = select.selectedIndex;
			var opt = select.options;
			for( var i = 0, len = opt.length; i < len; i++ ) {
				var o = opt[i];
				this.data[i] = { name:o.text, value:o.value };
			}
		}
	},
	
	/** Clear the target */
	removeOldContents:function() {
		this.target.childElements().each(function(element){element.hide()});
	},
	
	/** Get the name of the site currently selected */
	getSelectedItemValue:function() {
                    if(this.data == null || this.data.length == 0) return "";
                    var selected = this.getSelectedIndex();

                     if( this.selectedIndex == -1 || this.data[selected] == null) { 
                         var defaultItem = this.getDefaultItem();
                         if(defaultItem != null){
                             return defaultItem.name.substring(this.DEFAULT_ITEM.length);
                         }else{
                             if(selected == -1) selected = 0;
                                 return this.data[selected].name;
                             }
                      }
		
                      return this.data[selected].name;
	},
	
	/** Build the new DOM structure */
	buildDropdown:function() {
		var name = this.getSelectedItemValue();
		new Insertion.Bottom( this.target, '<a href="#" class="button">' + name + '&nbsp;</a><div class="list"></div>' );
		this.button = this.target.down("a");
		this.list = this.target.down("div");
	},
	
	initList:function() {
		if( this.listInitialized ) { return; }
		this.buildLists();
		Position.absolutize(this.list);
		this.list.show();
		this.updateListSize();
		Event.observe( this.list, "click", this.handleListClick.bindAsEventListener( this ) );
		this.listInitialized = true;
		this.list.hide();
	},
	
	buildLists:function() {
		var numColumns = Math.ceil( this.data.size() / this.itemsPerColumn );
		var columns = this.data.eachSlice( this.itemsPerColumn );
		var html = [];
		var _this = this;
		columns.each( function( col, index ) 
		{ 
			html[index] = '<ol>' + _this.createListItems( col ) + '</ol>' 
		} );
		new Insertion.Bottom( this.list, html.join('') );
	},
	
	/** Create an Array of <li><a ... ></li> nodes with labeled links to sites */
	createListItems:function( list ) {
		var _this = this;
		
		return list.collect( 
			function(listItem) {
				if(listItem.name.startsWith(_this.DEFAULT_ITEM)) 
				{
					return "";
				}
				
				return '<li><a href="' + listItem.value + '">' + listItem.name + '</a></li>';
			} ).join("");
	},
	
	/** Add event listeners */
	initEvents:function() {
		Event.observe( this.button, "mousedown", this.handleButtonClick.bindAsEventListener( this ) );
		Event.observe( this.button, "click", function(e) { Event.stop(e); } );
		Event.observe( document, "click", this.hide.bind( this ) );
		new PeriodicalExecuter( this.updateListBounds.bind( this ), 0.1 );
	},
	
	setSelected:function( index ) {
		this.selectedIndex = index;
		this.updateButtonLabel();
		this.triggerChangeListeners();
	},
	
	updateListBounds:function() {
		this.updateListSize();
	},
	
	/** Update list container size to match list columns */
	updateListSize:function() {
		if( this.list.visible() ) {
			var width = 0;
			var height = 0;
			var col = this.list.down( "ol" );
			while( col != null ) {
				height = Math.max( height, col.getHeight() );
				width += col.getWidth();
				col = col.next( "ol" );
			}
			
			if(width < this.button.getWidth())
				width = this.button.getWidth();
			
			this.list.setStyle( {
				width:width+"px",
				height:height+"px" } );
		}
	},
	
	/** Called when the button is clicked */
	handleButtonClick:function( event ) {
		Event.stop( event );
		this.toggle();
	},
	
	/** Called when an item in the list is clicked. */
	handleListClick:function( event ) {
		if( !this.hasChangeListeners() ) { return true; }
		
		var element = $(Event.element( event ));
		if( element.tagName.toUpperCase() == "A" ) {
			Event.stop( event );
			var li = element.up("li");
			this.selectedIndex = this.getListElementIndex(li);
			this.updateButtonLabel();
			this.close();
			this.triggerChangeListeners();
		}
	},
	
	/** Find out at what index a given <li> is at, including all columns. */
	getListElementIndex:function( li ) {
		var countInColumnsBefore = 0;
		var ol = li.up("ol");
		ol = ol.previous("ol");
		while( ol != null ) {
			countInColumnsBefore += ol.getElementsByTagName("li").length;
			ol = ol.previous("ol");
		}
		var indexInThisColumn = 0;
		li = li.previous("li");
		while( li != null ) { 
			indexInThisColumn += 1;
			li = li.previous("li");
		}
		return countInColumnsBefore + indexInThisColumn;
	},
	
	toggle:function() {
		this.initList();
		this.list.toggle();
		this.updateButtonIcon();
		this.triggerToggleListeners();
	},
	
	hide:function() {
		this.list.hide();
		this.updateButtonIcon();
		this.triggerToggleListeners();
	},
	
	close:function() {
		this.hide();
	},
	
	open:function() {
		this.initList();
		this.list.show();
		this.updateButtonIcon();
	},
	
	isOpen:function() {
		return this.list.visible();
	},
	
	updateButtonIcon:function() {
		if( this.list.visible() ) {
			this.button.addClassName("open");
		} else {
			this.button.removeClassName("open");
		}
	},
	
	updateButtonLabel:function() {
		var name = this.getSelectedItemValue();
		if(name.startsWith(this.DEFAULT_ITEM)) name = name.substring(this.DEFAULT_ITEM.length);
		this.button.update( name );
	},
	
	setListOffset:function( x, y ) {
		this.listOffsetX = x;
		this.listOffsetY = y;
	},
	
	addChangeListener:function( listenerFunction ) {
		this.changeListeners[this.changeListeners.length] = listenerFunction;
	},
	
	triggerChangeListeners:function() {
		var dropdown = this;
		this.changeListeners.each( function( listener ) { listener( dropdown ); } )
	},
	
	hasChangeListeners:function() {
		return this.changeListeners.length > 0;
	},
	
	addToggleListener:function( listenerFunction ) {
		this.toggleListeners[this.toggleListeners.length] = listenerFunction;
	},
	
	triggerToggleListeners:function() {
		var dropdown = this;
		this.toggleListeners.each( function( listener ) { listener( dropdown ); } );
	}

}


var DoubleDropdownControl = Class.create();
DoubleDropdownControl.prototype = {
	
	firstDropdown:null,
	secondDropdowns:[],
	secondDropdownTargets:[],
	data:null,
	
	listOffsetX:0,
	listOffsetY:0,
	
	changeListeners:null,
	selectedData:null,
	
	/**
	 * @param target1 DOM element to place the first dropdown in
	 * @param target2 DOM element to place the second dropdown in
	 * @param data A 2-dimensional array of lists to use as data. 
	 *             The items in the data array must be objects like this: { name:<string label for first dropdown>, list:<array of value objects for second dropdown> }.
	 *             See DropdownControl for definition of value objects for second dropdown.
	 */
	initialize:function( target1, target2, data, itemsPerColumn1, itemsPerColumn2 ) {
		this.data = data;
		this.changeListeners = [];
		this.initDropdowns( target1, target2, data, itemsPerColumn1, itemsPerColumn2 );
		this.showSecondDropdown(0);
		this.setListOffset( 0, 20 );
	},
	
	unload:function() {
		this.firstDropdown.unload();
		this.firstDropdown = null;
		this.secondDropdowns.each(function(dd){dd.unload()});
		this.secondDropdowns = null;
	},
	
	initDropdowns:function( target1, target2, data, itemsPerColumn1, itemsPerColumn2 ) {
		var firstData = [];
		for( var i = 0; i < data.length; i++ ) {
			firstData[i] = { name: data[i].name, value: data[i].name };
			this.addSecondDropdown( target2, data[i].list, itemsPerColumn2 );
		}
		this.firstDropdown = new DropdownControl( target1, firstData, itemsPerColumn1 );
		this.firstDropdown.addToggleListener( this.handleToggleEvent.bind( this ) );
		this.firstDropdown.addChangeListener( this.handleChangeEvent.bind( this ) );
		this.firstDropdown.setSelected(-1);
	},
	
	addSecondDropdown:function( target, data, itemsPerColumn ) {
		var index = this.secondDropdowns.length;
		this.secondDropdownTargets[index] = $(target.appendChild( Builder.node( "div" ) ));
		this.secondDropdowns[index] = new DropdownControl( this.secondDropdownTargets[index], data, itemsPerColumn );
		this.secondDropdowns[index].addToggleListener( this.handleToggleEvent.bind( this ) );
		this.secondDropdowns[index].addChangeListener( this.handleChangeEvent.bind( this ) );
		this.secondDropdownTargets[index].hide();
	},
	
	showSecondDropdown:function( index ) {
		var dd = this.secondDropdownTargets;
		var len = dd.length;
		for( var i = 0; i < len; i++ ) {
			if( i == index ) {
				dd[i].show();
				this.secondDropdowns[i].setSelected(0);
			} else {
				dd[i].hide();
			}
		}
		
		if( index < 0 ) { 
        dd[0].show();
				this.secondDropdowns[0].setSelected(-1);
		}
	},
	
	setListOffset:function( x, y ) {
		this.listOffsetX = x;
		this.listOffsetY = y;
		this.firstDropdown.setListOffset( x, y );
		for( var i = 0; i < this.secondDropdowns.length; i++ ) {
			this.secondDropdowns[i].setListOffset(x,y);
		}
	},
	
	handleToggleEvent:function( dropdown ) {
		if( dropdown.isOpen() ) {
			this.firstDropdown.close();
			this.secondDropdowns.each( function( dd ) { dd.close() } );
			dropdown.open();
		}
	},
	
	handleChangeEvent:function( dropdown ) {
		if( dropdown == this.firstDropdown ) {
			var index = dropdown.getSelectedIndex();

			this.showSecondDropdown( index );
		}
		this.updateSelectedData();
		this.triggerChangeListeners();
	},
	
	updateSelectedData:function() {
		var firstIndex = this.firstDropdown.getSelectedIndex();
		var secondIndex = this.secondDropdowns[firstIndex].getSelectedIndex();
		
		if( firstIndex >= 0 && secondIndex >= 0 ) { 
			var list = this.data[firstIndex].list;
			this.selectedData = list[secondIndex];
		}
	},
	
	addChangeListener:function( listenerFunction ) {
		this.changeListeners[this.changeListeners.length] = listenerFunction;
	},
	
	triggerChangeListeners:function() {
		var dropdown = this;
		this.changeListeners.each( function( listener ) { listener( dropdown ) } );
	},
	
	getSelectedData:function() {
		return this.selectedData;
	}
	
	
};/**
 * Utility for creating, reading and deleting cookies.
 *
 * Based on http://www.quirksmode.org/js/cookies.html (July 5, 2007)
 * wich is not copyrighted, according to http://www.quirksmode.org/about/copyright.html (July 5, 2007)
 */
DNV_CookieManager = {
	
	setCookie:function(name,value,days) {
		var expires = "";
		if (days) {
			var date = new Date();
			date.setTime( date.getTime()+(days*24*60*60*1000) );
			expires = "; expires=" + date.toGMTString();
		}
			
		document.cookie = name + "=" + value + expires + "; path=/";
	},
	
	getCookie:function(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	},
	
	eraseCookie:function(name) {
		DNV_CookieManager.createCookie(name,"",-1);
	}	
};/*******************************************/
/**      MENU CONTROLLER                  **/
/*******************************************/


var ComboMenu = Class.create();
ComboMenu.prototype = {
	
	target:null,
	menus:null,
	pendingBreadcrumbItem:null,
	pendingBreadcrumbTime:0,
	hoveredBreadcrumbItem:null,
	currentMenu:null,
	mouse:null,
	MOUSE_SPEED_THRESHOLD: 0.01,
	MOUSE_DELAY_MILLISECONDS: 300,
	MOUSE_OUTSIDE_PADDING_TOP: 20,
	MOUSE_OUTSIDE_PADDING_BOTTOM: 0,
	MOUSE_OUTSIDE_PADDING_LEFT: 20,
	MOUSE_OUTSIDE_PADDING_RIGHT: 50,
	clicked:false,
	
	/** Constructor */
	initialize:function( target ) {		
		this.target = target;
		this.initEvents();
		this.mouse = new ComboMenuMouseUtil( this.handleMouseUpdate.bind( this ) );
		this.initScroll();
	},
	
	unload:function() {
		this.target = null;
		this.menus.each(function(m){
			m.scrollUtil.unload();
			m.scrollUtil = null;
		});
		this.currentMenu = null;
	},
	
	initScroll:function() {
		this.menus = this.target.getElementsBySelector( "li ul" );
		this.currentMenu = this.menus[this.menus.length-1];
		for( var i = 0, len = this.menus.length; i < len; i++ ) {
			var m = this.menus[i];
			var hover = ( i != len - 1 );
			m.scrollUtil = new ComboMenuScrollUtil( this.target, m, hover );
		}
	},
	
	initEvents:function() {
		Event.observe( this.target, "mouseover", this.handleMouseOver.bindAsEventListener( this ) );
		Event.observe( this.target, "mouseout", this.handleMouseOut.bindAsEventListener( this ) );
		Event.observe( this.target, "click", this.handleMouseClick.bindAsEventListener( this ) );
		Event.observe( this.target, "mousedown", this.handleMouseClick.bindAsEventListener( this ) );
		Event.observe( document, "click", this.handleDocumentClick.bind( this ) );
	},
	
	handleMouseOver:function( event ) {
		var t = $(Event.element( event ));
		if( t.tagName.toLowerCase() == "a" || t.hasClassName("title") ) {
			this.setPendingBreadcrumbItem( t.up("li") );
		}
	},
	
	handleMouseOut:function( event ) {
		this.setPendingBreadcrumbItem( null );
	},
	
	handleMouseClick:function( event ) {
		var element = $(Event.element(event));
		if( "A" == element.tagName.toUpperCase() ) {
			if( element.up("li").hasClassName("disabled") ) {
				if( this.currentMenu != null ) {
					event.stop();
				}			
			}
			this.clicked = true;
		}
	},
	
	handleDocumentClick:function( event ) {
		var et = Event.element(event);
		if( "A" == et.tagName.toUpperCase() && et.descendantOf(this.target) ) { return; }
		this.hoverBreadcrumbItem( null );
	},
	
	setPendingBreadcrumbItem:function( li ) {
		if( li == null ) {
			this.pendingBreadcrumbItem = null;
			return;
		}
		if( this.isBreadcrumbItem( li ) ) {
			if( this.pendingBreadcrumbItem != li ) {
				this.pendingBreadcrumbItem = li;
				this.pendingBreadcrumbTime = (new Date()).getTime();
			}
		}
	},
	
	isBreadcrumbItem:function( node ) {
		return node.up(1) == this.target;
	},
	
	handleMouseUpdate:function() {
		this.checkPendingBreadcrumb();
		this.checkMouseOutside();
	},
	
	checkMouseOutside:function() {
		var p = Position.positionedOffset(this.target);
		
		var y1 = p[1];
		var y2 = y1 + this.target.getHeight();
		var ym = this.mouse.pos.y;
		if( ym < ( y1 - this.MOUSE_OUTSIDE_PADDING_TOP ) || ym > ( y2 + this.MOUSE_OUTSIDE_PADDING_BOTTOM ) ) {
			this.hoverBreadcrumbItem( null );
			return;
		}
		
		var x1 = p[0];
		var x2 = x1 + this.target.getWidth();
		var xm = this.mouse.pos.x;
		if( xm < ( x1 - this.MOUSE_OUTSIDE_PADDING_LEFT ) || xm > ( x2 + this.MOUSE_OUTSIDE_PADDING_RIGHT ) ) {
			this.hoverBreadcrumbItem( null );
			return;
		}
	},
	
	checkPendingBreadcrumb:function() {
		var time = (new Date()).getTime();
		var passedTime = time - this.pendingBreadcrumbTime;
		if( passedTime < this.MOUSE_DELAY_MILLISECONDS  ) {
			return;
		}
		if( Math.abs( this.mouse.speed ) <= this.MOUSE_SPEED_THRESHOLD && this.pendingBreadcrumbItem != null ) {
			if( this.isSelectedBreadcrumb( this.pendingBreadcrumbItem ) ) {
				this.hoverBreadcrumbItem( null );
			} else {
				this.hoverBreadcrumbItem( this.pendingBreadcrumbItem );
			}
		}
	},
	
	hoverBreadcrumbItem:function( li ) {
		if( li != null && li.down("ul") == null ) {
			return;
		}
		this.setPendingBreadcrumbItem( null );
		if( li == this.hoveredBreadcrumbItem ) {
			return;
		}
		this.hoveredBreadcrumbItem = li;
		var node = this.target.down( "li" );
		while( node != null ) {
			node.removeClassName("hovered");
			var down = node.down("ul");
			if( down != null ) { down.scrollUtil.disable() };
			node = node.next("li");
		}
		var defaultMenu = this.target.getElementsBySelector("li.selected ul")[0];
		if( li != null ) {
			li.addClassName("hovered");
			li.down("ul").scrollUtil.enable();
			this.currentMenu = li.down("ul");
			defaultMenu.hide();
		} else {
			this.currentMenu = defaultMenu;
			defaultMenu.show();
			defaultMenu.scrollUtil.enable();
		}
	},
	
	isSelectedBreadcrumb:function( li ) {
		return li.hasClassName( "selected" );
	}
	
}


/*******************************************/
/**      SCROLL                           **/
/*******************************************/



var ComboMenuScrollUtil = Class.create();
ComboMenuScrollUtil.prototype = {
	
	CLIP_WIDTH:960,
	CLIP_THRESHOLD_WIDTH:994,
	
	target:null,
	button:null,
	
	leftStartPos:0,
	isRTL:false,
	
	contentWidth:0,
	overflowWidth:0,
	reverse:false,
	enabled:false,
	
	initialize:function( outerTarget, menuTarget, isHoverMenu ) {
		this.target = menuTarget;
		this.isRTL = this.target.down('li').getStyle('float') == 'right';
		this.button = $( outerTarget.appendChild( Builder.node( "a", { href:"#", className:"scrollButton" } ) ) );
		Event.observe( this.button, "mousedown", this.handleButtonClick.bindAsEventListener( this ) );
		Event.observe( this.button, "click", function(e){Event.stop(e)} );
		
		if( isHoverMenu ) {
			this.button.addClassName( "hoverMenu" );
			this.button.hide();
		} else {
			this.enable();
		}
	},
	
	unload:function() {
		this.target = null;
		this.button = null;
	},
	
	handleButtonClick:function( event ) {
		Event.stop( event );
		this.doNextScroll();
	},
	
	doNextScroll:function() {
		if( !this.reverse ) {
			if( this.isRTL ) {
				this.target.style.right = -this.overflowWidth + 'px';
			} else {
				new Effect.MoveBy( this.target, 0, -this.overflowWidth, {duration: 0.4,queue:{position:'end',scope:'menuScroll'}} );
			}
			this.reverse = true;
			this.button.addClassName( "reverse" );
			this.updateItemVisibility( -this.overflowWidth );
		} else {
			if( this.isRTL ) {
				this.target.style.right = 0;
				this.updateItemVisibility(0);
			} else {
				function afterFinish() { this.updateItemVisibility(0); }
				new Effect.MoveBy( this.target, 0,  this.overflowWidth, {duration: 0.4,queue:{position:'end',scope:'menuScroll'},afterFinish:afterFinish.bind(this)} );
			}
			this.reverse = false;
			this.button.removeClassName( "reverse" );
		}
	},
	
	/**
	 * If a menu item crosses the menu edge, it should be marked as disabled.
	 */
	updateItemVisibility:function( scrollOffset ) {
		if( !this.enabled ) { return; }
		var li = this.target.down("li");
		while( li != null ) {
			var isAtEdge = false;
			if( this.isRTL ) {
				var left = scrollOffset + this.target.getWidth() - li.offsetLeft ; // scrollOffset + li.offsetRight;
				var right = left - li.getWidth();
				isAtEdge = left > this.CLIP_WIDTH && right < this.CLIP_WIDTH;
			} else {
				var left = scrollOffset + li.offsetLeft;
				var right = left + li.getWidth();
				isAtEdge = left < this.CLIP_WIDTH && right > this.CLIP_WIDTH;
			}
			if( isAtEdge && !li.hasClassName("disabled") ) {
				li.addClassName("disabled")
			} else if( !isAtEdge && li.hasClassName("disabled") ) {
				li.removeClassName("disabled");
			}
			li = li.next("li");
		}
	},
	
	enable:function() {
		this.updateContentWidth();
		this.resetPosition();
		if( this.contentWidth > this.CLIP_THRESHOLD_WIDTH ) {
			this.button.show();
			this.enabled = true;
		} else {
			this.button.hide();
			this.enabled = false;
		}
	},
	
	disable:function() {
		this.button.hide();
		this.enabled = false;
	},
	
	updateContentWidth:function() {
		var width = 0;
		var li = this.target.down("li");
		while( li != null ) {
			width += li.getWidth();
			li = li.next("li");
		}
		this.contentWidth = width;
		this.overflowWidth = Math.max( 0, width - this.CLIP_WIDTH );
	},
	
	isItemOnOrBeyondEdge:function( li ) {
		if( this.isRTL ) {
			var left = this.target.getWidth() - li.offsetLeft ; // scrollOffset + li.offsetRight;
			var right = left - li.getWidth();
			isAtEdge = left > this.CLIP_WIDTH && right < this.CLIP_WIDTH;
		} else {
			var right = li.offsetLeft + li.getWidth();
			return right > this.CLIP_WIDTH;
		}
	},
	
	resetPosition:function() {
		var selectedIsOutside = false;
		var selectedItem = this.target.getElementsBySelector("li.selected")[0];
		if( selectedItem != null ) {
			selectedIsOutside = this.isItemOnOrBeyondEdge( selectedItem );
		}
		if( this.isRTL ) {
			this.target.style.right = 0;
		} else {
			if( selectedIsOutside ) {
				this.target.style.left = -this.overflowWidth + "px";
				this.updateItemVisibility(-this.overflowWidth);
				this.reverse = true;
				this.button.addClassName( "reverse" );
			} else {
				this.target.style.left = 0;
				this.updateItemVisibility(0);
				this.reverse = false;
				this.button.removeClassName( "reverse" );
			}
			
		}	
	}
	
}


/*******************************************/
/**      MOUSE                            **/
/*******************************************/


var ComboMenuMouseUtil = Class.create();
ComboMenuMouseUtil.prototype = {
	
	pos:{x:0,y:0}, lastPos:{x:0,y:0},
	time:0,
	speed:0,
	updateFunction:function(){},
	
	initialize:function( updateFunction ) {
		this.updateFunction = updateFunction;
		var d = new Date();
		this.time = d.getTime();
		Event.observe( document, "mousemove", this.handleMouseMove.bindAsEventListener( this ) );
		new PeriodicalExecuter( this.handleInterval.bind(this), 0.04 );
	},
	
	handleMouseMove:function( event ) {
		this.pos.x = Event.pointerX( event );
		this.pos.y = Event.pointerY( event );
	},
	
	handleInterval:function() {
		var newTime = ( new Date() ).getTime();
		var deltaTime = newTime - this.time;
		this.time = newTime;
		this.speed = ( ( this.pos.x - this.lastPos.x ) + ( this.pos.y - this.lastPos.y ) ) / deltaTime;
		var debug = $("DebugSpeed");
		if( debug != null ) { debug.innerHTML = this.speed; }
		this.lastPos.x = this.pos.x;
		this.lastPos.y = this.pos.y;
		this.updateFunction();
	}
};if(!Control)var Control={};Control.Slider=Class.create();Control.Slider.prototype={initialize:function(handle,track,options){var slider=this;if(handle instanceof Array){this.handles=handle.collect(function(e){return $(e)});}else{this.handles=[$(handle)];}
this.track=$(track);this.options=options||{};this.axis=this.options.axis||'horizontal';this.increment=this.options.increment||1;this.step=parseInt(this.options.step||'1');this.range=this.options.range||$R(0,1);this.value=0;this.values=this.handles.map(function(){return 0});this.spans=this.options.spans?this.options.spans.map(function(s){return $(s)}):false;this.options.startSpan=$(this.options.startSpan||null);this.options.endSpan=$(this.options.endSpan||null);this.restricted=this.options.restricted||false;this.maximum=this.options.maximum||this.range.end;this.minimum=this.options.minimum||this.range.start;this.alignX=parseInt(this.options.alignX||'0');this.alignY=parseInt(this.options.alignY||'0');this.trackLength=this.maximumOffset()-this.minimumOffset();this.handleLength=this.isVertical()?(this.handles[0].offsetHeight!=0?this.handles[0].offsetHeight:this.handles[0].style.height.replace(/px$/,"")):(this.handles[0].offsetWidth!=0?this.handles[0].offsetWidth:this.handles[0].style.width.replace(/px$/,""));this.active=false;this.dragging=false;this.disabled=false;if(this.options.disabled)this.setDisabled();this.allowedValues=this.options.values?this.options.values.sortBy(Prototype.K):false;if(this.allowedValues){this.minimum=this.allowedValues.min();this.maximum=this.allowedValues.max();}
this.eventMouseDown=this.startDrag.bindAsEventListener(this);this.eventMouseUp=this.endDrag.bindAsEventListener(this);this.eventMouseMove=this.update.bindAsEventListener(this);this.handles.each(function(h,i){i=slider.handles.length-1-i;slider.setValue(parseFloat((slider.options.sliderValue instanceof Array?slider.options.sliderValue[i]:slider.options.sliderValue)||slider.range.start),i);Element.makePositioned(h);Event.observe(h,"mousedown",slider.eventMouseDown);});Event.observe(this.track,"mousedown",this.eventMouseDown);Event.observe(document,"mouseup",this.eventMouseUp);Event.observe(document,"mousemove",this.eventMouseMove);this.initialized=true;},dispose:function(){var slider=this;Event.stopObserving(this.track,"mousedown",this.eventMouseDown);Event.stopObserving(document,"mouseup",this.eventMouseUp);Event.stopObserving(document,"mousemove",this.eventMouseMove);this.handles.each(function(h){Event.stopObserving(h,"mousedown",slider.eventMouseDown);});},setDisabled:function(){this.disabled=true;},setEnabled:function(){this.disabled=false;},getNearestValue:function(value){if(this.allowedValues){if(value>=this.allowedValues.max())return(this.allowedValues.max());if(value<=this.allowedValues.min())return(this.allowedValues.min());var offset=Math.abs(this.allowedValues[0]-value);var newValue=this.allowedValues[0];this.allowedValues.each(function(v){var currentOffset=Math.abs(v-value);if(currentOffset<=offset){newValue=v;offset=currentOffset;}});return newValue;}
if(value>this.range.end)return this.range.end;if(value<this.range.start)return this.range.start;return value;},setValue:function(sliderValue,handleIdx){if(!this.active){this.activeHandleIdx=handleIdx||0;this.activeHandle=this.handles[this.activeHandleIdx];this.updateStyles();}
handleIdx=handleIdx||this.activeHandleIdx||0;if(this.initialized&&this.restricted){if((handleIdx>0)&&(sliderValue<this.values[handleIdx-1]))
sliderValue=this.values[handleIdx-1];if((handleIdx<(this.handles.length-1))&&(sliderValue>this.values[handleIdx+1]))
sliderValue=this.values[handleIdx+1];}
sliderValue=this.getNearestValue(sliderValue);this.values[handleIdx]=sliderValue;this.value=this.values[0];this.handles[handleIdx].style[this.isVertical()?'top':'left']=this.translateToPx(sliderValue);this.drawSpans();if(!this.dragging||!this.event)this.updateFinished();},setValueBy:function(delta,handleIdx){this.setValue(this.values[handleIdx||this.activeHandleIdx||0]+delta,handleIdx||this.activeHandleIdx||0);},translateToPx:function(value){return Math.round(((this.trackLength-this.handleLength)/(this.range.end-this.range.start))*(value-this.range.start))+"px";},translateToValue:function(offset){return((offset/(this.trackLength-this.handleLength)*(this.range.end-this.range.start))+this.range.start);},getRange:function(range){var v=this.values.sortBy(Prototype.K);range=range||0;return $R(v[range],v[range+1]);},minimumOffset:function(){return(this.isVertical()?this.alignY:this.alignX);},maximumOffset:function(){return(this.isVertical()?(this.track.offsetHeight!=0?this.track.offsetHeight:this.track.style.height.replace(/px$/,""))-this.alignY:(this.track.offsetWidth!=0?this.track.offsetWidth:this.track.style.width.replace(/px$/,""))-this.alignY);},isVertical:function(){return(this.axis=='vertical');},drawSpans:function(){var slider=this;if(this.spans)
$R(0,this.spans.length-1).each(function(r){slider.setSpan(slider.spans[r],slider.getRange(r))});if(this.options.startSpan)
this.setSpan(this.options.startSpan,$R(0,this.values.length>1?this.getRange(0).min():this.value));if(this.options.endSpan)
this.setSpan(this.options.endSpan,$R(this.values.length>1?this.getRange(this.spans.length-1).max():this.value,this.maximum));},setSpan:function(span,range){if(this.isVertical()){span.style.top=this.translateToPx(range.start);span.style.height=this.translateToPx(range.end-range.start+this.range.start);}else{span.style.left=this.translateToPx(range.start);span.style.width=this.translateToPx(range.end-range.start+this.range.start);}},updateStyles:function(){this.handles.each(function(h){Element.removeClassName(h,'selected')});Element.addClassName(this.activeHandle,'selected');},startDrag:function(event){if(Event.isLeftClick(event)){if(!this.disabled){this.active=true;var handle=Event.element(event);var pointer=[Event.pointerX(event),Event.pointerY(event)];var track=handle;if(track==this.track){var offsets=Position.cumulativeOffset(this.track);this.event=event;this.setValue(this.translateToValue((this.isVertical()?pointer[1]-offsets[1]:pointer[0]-offsets[0])-(this.handleLength/2)));var offsets=Position.cumulativeOffset(this.activeHandle);this.offsetX=(pointer[0]-offsets[0]);this.offsetY=(pointer[1]-offsets[1]);}else{while((this.handles.indexOf(handle)==-1)&&handle.parentNode)
handle=handle.parentNode;this.activeHandle=handle;this.activeHandleIdx=this.handles.indexOf(this.activeHandle);this.updateStyles();var offsets=Position.cumulativeOffset(this.activeHandle);this.offsetX=(pointer[0]-offsets[0]);this.offsetY=(pointer[1]-offsets[1]);}}
Event.stop(event);}},update:function(event){if(this.active){if(!this.dragging)this.dragging=true;this.draw(event);if(navigator.appVersion.indexOf('AppleWebKit')>0)window.scrollBy(0,0);Event.stop(event);}},draw:function(event){var pointer=[Event.pointerX(event),Event.pointerY(event)];var offsets=Position.cumulativeOffset(this.track);pointer[0]-=this.offsetX+offsets[0];pointer[1]-=this.offsetY+offsets[1];this.event=event;this.setValue(this.translateToValue(this.isVertical()?pointer[1]:pointer[0]));if(this.initialized&&this.options.onSlide)
this.options.onSlide(this.values.length>1?this.values:this.value,this);},endDrag:function(event){if(this.active&&this.dragging){this.finishDrag(event,true);Event.stop(event);}
this.active=false;this.dragging=false;},finishDrag:function(event,success){this.active=false;this.dragging=false;this.updateFinished();},updateFinished:function(){if(this.initialized&&this.options.onChange)
this.options.onChange(this.values.length>1?this.values:this.value,this);this.event=null;}}


var Scroller=Class.create();Scroller.ids=new Object();Scroller.i=0;Scroller.prototype={initialize:function(el){this.outerBox=el;this.decorate();},decorate:function(){$(this.outerBox).makePositioned();Scroller.i=Scroller.i+1;this.myIndex=Scroller.i;this.innerBox=document.createElement("DIV");this.innerBox.className="scroll-innerBox";$(this.innerBox).makePositioned();this.innerBox.style.cssFloat=this.innerBox.style.styleFloat='left';this.innerBox.id="scroll-innerBox-"+Scroller.i;this.innerBox.style.top="0px";while(this.outerBox.hasChildNodes()){this.innerBox.appendChild(this.outerBox.firstChild);}
this.innerBox.style.overflow="hidden";this.outerBox.style.overflow="hidden";this.track=document.createElement('div');this.track.className="scroll-track";$(this.track).makePositioned();this.track.style.cssFloat=this.track.style.styleFloat='left';this.track.id="scroll-track-"+Scroller.i;this.track.appendChild(document.createComment(''));this.tracktop=document.createElement('div');this.tracktop.className="scroll-track-top";$(this.tracktop).makePositioned();this.tracktop.style.cssFloat=this.tracktop.style.styleFloat='left';this.tracktop.id="scroll-track-top-"+Scroller.i;this.tracktop.appendChild(document.createComment(''));this.trackbot=document.createElement('div');this.trackbot.className="scroll-track-bot";$(this.trackbot).makePositioned();this.trackbot.style.cssFloat=this.trackbot.style.styleFloat='left';this.trackbot.id="scroll-track-bot-"+Scroller.i;this.trackbot.appendChild(document.createComment(''));this.handle=document.createElement('div');this.handle.className="scroll-handle-container";this.handle.id="scroll-handle-container"+Scroller.i;this.handle_middle=document.createElement('div');this.handle_middle.className="scroll-handle";$(this.handle_middle).makePositioned();this.handle_middle.id="scroll-handle-"+Scroller.i;this.handle_middle.appendChild(document.createComment(''));this.handletop=document.createElement('div');this.handletop.className="scroll-handle-top";$(this.handletop).makePositioned();this.handletop.id="scroll-handle-top-"+Scroller.i;this.handletop.appendChild(document.createComment(''));this.handlebot=document.createElement('div');this.handlebot.className="scroll-handle-bot";$(this.handlebot).makePositioned();this.handlebot.id="scroll-handle-bot-"+Scroller.i;this.handlebot.appendChild(document.createComment(''));this.track.hide();this.tracktop.hide();this.trackbot.hide();this.outerBox.appendChild(this.innerBox);this.outerBox.appendChild(this.tracktop);this.handle.appendChild(this.handletop);this.handle.appendChild(this.handle_middle);this.handle.appendChild(this.handlebot);this.track.appendChild(this.handle);this.outerBox.appendChild(this.track);this.outerBox.appendChild(this.trackbot);this.slider=new Control.Slider($(this.handle).id,$(this.track).id,{axis:'vertical',minimum:0,maximum:$(this.outerBox).clientHeight});this.slider.options.onSlide=this.slider.options.onChange=this.onChange.bind(this);setTimeout(this.resetScrollbar.bind(this,false),10);this.domMouseCB=this.MouseWheelEvent.bindAsEventListener(this,this.slider);this.mouseWheelCB=this.MouseWheelEvent.bindAsEventListener(this,this.slider);this.trackTopCB=this.tracktopEvent.bindAsEventListener(this,this.slider);this.trackBotCB=this.trackbotEvent.bindAsEventListener(this,this.slider);$(this.outerBox).observe('DOMMouseScroll',this.domMouseCB);$(this.outerBox).observe('mousewheel',this.mouseWheelCB);$(this.tracktop).observe('mousedown',this.trackTopCB);$(this.trackbot).observe('mousedown',this.trackBotCB);},release:function(){$(this.outerBox).stopObserving('DOMMouseScroll',this.domMouseCB);$(this.outerBox).stopObserving('mousewheel',this.mouseWheelCB);$(this.tracktop).stopObserving('mousedown',this.trackTopCB);$(this.trackbot).stopObserving('mousedown',this.trackBotCB);},resetScrollbar:function(repeat){this.track.hide();this.tracktop.hide();this.trackbot.hide();this.enableScroll=false;this.innerHeight=$(this.outerBox).clientHeight;this.innerBox.style.height=this.innerHeight+"px";var newWidth=$(this.outerBox).clientWidth;var tth=Element.getStyle(this.tracktop,"height");if(tth)
tth=tth.replace("px","");else
tth=0;var hth=Element.getStyle(this.handletop,"height");if(hth)
hth=hth.replace("px","");else
hth=0;if(this.innerHeight<this.innerBox.scrollHeight){this.viewportHeight=this.innerHeight-tth*2;this.slider.trackLength=this.viewportHeight;this.track.style.height=this.viewportHeight+"px";this.handleHeight=Math.round(this.viewportHeight*this.innerHeight/this.innerBox.scrollHeight);if(this.handleHeight<(hth*2))
this.handleHeight=(hth*2);if(this.handleHeight<10)
this.handleHeight=10;this.handle.style.height=this.handleHeight+"px";this.handle_middle.style.height=this.handleHeight-hth*2+"px";this.handletop.style.height=hth+"px";this.slider.handleLength=this.handleHeight;this.track.style.display='inline';this.tracktop.style.display='inline';this.trackbot.style.display='inline';this.ieDecreaseBy=1;if(this.outerBox.currentStyle){var borderWidth=this.outerBox.currentStyle["borderWidth"].replace("px","");if(!isNaN(borderWidth)){this.ieDecreaseBy=(borderWidth)*2;}}
newWidth=($(this.outerBox).clientWidth-$(this.track).clientWidth-this.ieDecreaseBy);this.enableScroll=true;}
this.innerBox.style.width=newWidth+"px";if(repeat){setTimeout(this.resetScrollbar.bind(this,false),10);}},MouseWheelEvent:function(event,slider){var delta=0;if(!event)
event=window.event;if(event.wheelDelta){delta=event.wheelDelta/120;}else if(event.detail){delta=-event.detail/3;}
if(delta)
slider.setValueBy(-delta/10);Event.stop(event);},trackbotEvent:function(event,slider){if(Event.isLeftClick(event)){slider.setValueBy(0.2);Event.stop(event);}},tracktopEvent:function(event,slider){if(Event.isLeftClick(event)){slider.setValueBy(-0.2);Event.stop(event);}},onChange:function(val){if(this.enableScroll)
this.innerBox.scrollTop=Math.round(val*(this.innerBox.scrollHeight-this.innerBox.offsetHeight));}}
Scroller.setAll=function(){$$('.makeScroll').each(function(item){Scroller.ids[item.id]=new Scroller(item);});}
Scroller.reset=function(body_id){if($(body_id).className.match(new RegExp("(^|\\s)makeScroll(\\s|$)"))){if(Scroller.ids[body_id])
Scroller.ids[body_id].release();Scroller.ids[body_id]=new Scroller($(body_id));}}
Scroller.updateAll=function(){$H(Scroller.ids).each(function(pair){Scroller.ids[pair.key].resetScrollbar(true);});}
Event.observe(window,"load",Scroller.setAll);Event.observe(window,"resize",Scroller.updateAll);// WebTrends SmartSource Data Collector Tag
// Version: 8.6.0     
// Created: 081216 - support at arena dot no

function WebTrends(){
	var that=this;
	// begin: user modifiable
	this.dcsid="dcs01mzsp10000g4azice0z94_3c2h";
	this.domain="statse.webtrendslive.com";
	this.timezone=1;
	this.fpcdom="";
	this.onsitedoms="";
	this.downloadtypes="xls,doc,pdf,txt,csv,zip,ini,exe,pps,ppt,bat,mov,avi,wma,wmv,mp3,dot,docx,xlsx,ppsx,pptx";
	this.rightclicktypes="xls,doc,pdf,txt,csv,zip,ini,exe,pps,ppt,bat,mov,avi,wma,wmv,mp3,dot,docx,xlsx,ppsx,pptx";
	this.adclickparam="WT.ac";
	this.trackevents=true;
	this.enabled=true;
	this.i18n=false;
	this.fpc="WT_FPC";
	// end: user modifiable
	this.DCS={};
	this.WT={};
	this.DCSext={};
	this.images=[];
	this.index=0;
	this.exre=(function(){return(window.RegExp?new RegExp("dcs(uri)|(ref)|(aut)|(met)|(sta)|(sip)|(pro)|(byt)|(dat)|(p3p)|(cfg)|(redirect)|(cip)","i"):"");})();
	this.re=(function(){return(window.RegExp?(that.i18n?{"%25":/\%/g}:{"%09":/\t/g,"%20":/ /g,"%23":/\#/g,"%26":/\&/g,"%2B":/\+/g,"%3F":/\?/g,"%5C":/\\/g,"%22":/\"/g,"%7F":/\x7F/g,"%A0":/\xA0/g}):"");})();
}
WebTrends.prototype.dcsGetId=function(){
	if (this.enabled&&(document.cookie.indexOf(this.fpc+"=")==-1)&&(document.cookie.indexOf("WTLOPTOUT=")==-1)){
		document.write("<scr"+"ipt type='text/javascript' src='"+"http"+(window.location.protocol.indexOf('https:')==0?'s':'')+"://"+this.domain+"/"+this.dcsid+"/wtid.js"+"'><\/scr"+"ipt>");
	}
}
WebTrends.prototype.dcsGetCookie=function(name){
	var cookies=document.cookie.split("; ");
	var cmatch=[];
	var idx=0;
	var i=0;
	var namelen=name.length;
	var clen=cookies.length;
	for (i=0;i<clen;i++){
		var c=cookies[i];
		if ((c.substring(0,namelen+1))==(name+"=")){
			cmatch[idx++]=c;
		}
	}
	var cmatchCount=cmatch.length;
	if (cmatchCount>0){
		idx=0;
		if ((cmatchCount>1)&&(name==this.fpc)){
			var dLatest=new Date(0);
			for (i=0;i<cmatchCount;i++){
				var lv=parseInt(this.dcsGetCrumb(cmatch[i],"lv"));
				var dLst=new Date(lv);
				if (dLst>dLatest){
					dLatest.setTime(dLst.getTime());
					idx=i;
				}
			}
		}
		return unescape(cmatch[idx].substring(namelen+1));
	}
	else{
		return null;
	}
}
WebTrends.prototype.dcsGetCrumb=function(cval,crumb,sep){
	var aCookie=cval.split(sep||":");
	for (var i=0;i<aCookie.length;i++){
		var aCrumb=aCookie[i].split("=");
		if (crumb==aCrumb[0]){
			return aCrumb[1];
		}
	}
	return null;
}
WebTrends.prototype.dcsGetIdCrumb=function(cval,crumb){
	var id=cval.substring(0,cval.indexOf(":lv="));
	var aCrumb=id.split("=");
	for (var i=0;i<aCrumb.length;i++){
		if (crumb==aCrumb[0]){
			return aCrumb[1];
		}
	}
	return null;
}
WebTrends.prototype.dcsIsFpcSet=function(name,id,lv,ss){
	var c=this.dcsGetCookie(name);
	if (c){
		return ((id==this.dcsGetIdCrumb(c,"id"))&&(lv==this.dcsGetCrumb(c,"lv"))&&(ss==this.dcsGetCrumb(c,"ss")))?0:3;
	}
	return 2;
}
WebTrends.prototype.dcsFPC=function(){
	if (document.cookie.indexOf("WTLOPTOUT=")!=-1){
		return;
	}
	var WT=this.WT;
	var name=this.fpc;
	var dCur=new Date();
	var adj=(dCur.getTimezoneOffset()*60000)+(this.timezone*3600000);
	dCur.setTime(dCur.getTime()+adj);
	var dExp=new Date(dCur.getTime()+315360000000);
	var dSes=new Date(dCur.getTime());
	WT.co_f=WT.vtid=WT.vtvs=WT.vt_f=WT.vt_f_a=WT.vt_f_s=WT.vt_f_d=WT.vt_f_tlh=WT.vt_f_tlv="";
	if (document.cookie.indexOf(name+"=")==-1){
		if ((typeof(gWtId)!="undefined")&&(gWtId!="")){
			WT.co_f=gWtId;
		}
		else if ((typeof(gTempWtId)!="undefined")&&(gTempWtId!="")){
			WT.co_f=gTempWtId;
			WT.vt_f="1";
		}
		else{
			WT.co_f="2";
			var curt=dCur.getTime().toString();
			for (var i=2;i<=(32-curt.length);i++){
				WT.co_f+=Math.floor(Math.random()*16.0).toString(16);
			}
			WT.co_f+=curt;
			WT.vt_f="1";
		}
		if (typeof(gWtAccountRollup)=="undefined"){
			WT.vt_f_a="1";
		}
		WT.vt_f_s=WT.vt_f_d="1";
		WT.vt_f_tlh=WT.vt_f_tlv="0";
	}
	else{
		var c=this.dcsGetCookie(name);
		var id=this.dcsGetIdCrumb(c,"id");
		var lv=parseInt(this.dcsGetCrumb(c,"lv"));
		var ss=parseInt(this.dcsGetCrumb(c,"ss"));
		if ((id==null)||(id=="null")||isNaN(lv)||isNaN(ss)){
			return;
		}
		WT.co_f=id;
		var dLst=new Date(lv);
		WT.vt_f_tlh=Math.floor((dLst.getTime()-adj)/1000);
		dSes.setTime(ss);
		if ((dCur.getTime()>(dLst.getTime()+1800000))||(dCur.getTime()>(dSes.getTime()+28800000))){
			WT.vt_f_tlv=Math.floor((dSes.getTime()-adj)/1000);
			dSes.setTime(dCur.getTime());
			WT.vt_f_s="1";
		}
		if ((dCur.getDay()!=dLst.getDay())||(dCur.getMonth()!=dLst.getMonth())||(dCur.getYear()!=dLst.getYear())){
			WT.vt_f_d="1";
		}
	}
	WT.co_f=escape(WT.co_f);
	WT.vtid=(typeof(this.vtid)=="undefined")?WT.co_f:(this.vtid||"");
	WT.vtvs=(dSes.getTime()-adj).toString();
	var expiry="; expires="+dExp.toGMTString();
	var cur=dCur.getTime().toString();
	var ses=dSes.getTime().toString();
	document.cookie=name+"="+"id="+WT.co_f+":lv="+cur+":ss="+ses+expiry+"; path=/"+(((this.fpcdom!=""))?("; domain="+this.fpcdom):(""));
	var rc=this.dcsIsFpcSet(name,WT.co_f,cur,ses);
	if (rc!=0){
		WT.co_f=WT.vtvs=WT.vt_f_s=WT.vt_f_d=WT.vt_f_tlh=WT.vt_f_tlv="";
		if (typeof(this.vtid)=="undefined"){
			WT.vtid="";
		}
		WT.vt_f=WT.vt_f_a=rc;
    }
}
// Code section for Generate an Ad View query parameter for every Ad Click link.
WebTrends.prototype.dcsAdSearch=function(){
	if (document.links){
		var param=this.adclickparam+"=";
		var paramlen=param.length;
		var paramre=new RegExp(param,"i");
		var len=document.links.length;
		var pos=end=-1;
		var anch=urlp=value="";
		var urlpre;
		var url=document.URL+"";
		var start=url.search(paramre);
		if (start!=-1){
			end=url.indexOf("&",start);
			urlp=url.substring(start,(end!=-1)?end:url.length);
			urlpre=new RegExp(urlp+"(&|#)","i");
		}
		for (var i=0;i<len;i++){
			if (document.links[i].href){
				anch=document.links[i].href+"";
				if (urlp.length>0){
					anch=anch.replace(urlpre,"$1");
				}
				pos=anch.search(paramre);
				if (pos!=-1){
					start=pos+paramlen;
					end=anch.indexOf("&",start);
					value=anch.substring(start,(end!=-1)?end:anch.length);
					this.WT.ad=this.WT.ad?(this.WT.ad+";"+value):value;
				}
			}
		}
	}
}
WebTrends.prototype.dcsIsOnsite=function(host){
	if (host.length>0){
	    host=host.toLowerCase();
	    if (host==window.location.hostname.toLowerCase()){
		    return true;
	    }
	    if (typeof(this.onsitedoms.test)=="function"){
		    return this.onsitedoms.test(host);
	    }
	    else if (this.onsitedoms.length>0){
		    var doms=this.dcsSplit(this.onsitedoms);
		    var len=doms.length;
		    for (var i=0;i<len;i++){
			    if (host==doms[i]){
			        return true;
			    }
		    }
	    }
	}
	return false;
}
WebTrends.prototype.dcsTypeMatch=function(pth, typelist){
	var type=pth.substring(pth.lastIndexOf(".")+1,pth.length);
	var types=this.dcsSplit(typelist);
	var tlen=types.length;	
	for (var i=0;i<tlen;i++){
		if (type==types[i]){
			return true;
		}
	}
	return false;
}
WebTrends.prototype.dcsEvt=function(evt,tag){
	var e=evt.target||evt.srcElement;
	while (e.tagName&&(e.tagName!=tag)){
		e=e.parentElement||e.parentNode;
	}
	return e;
}
WebTrends.prototype.dcsNavigation=function(evt){
	return "";
}
WebTrends.prototype.dcsBind=function(event,func){
	if ((typeof(func)=="function")&&document.body){
		if (document.body.addEventListener){
			document.body.addEventListener(event, func.wtbind(this), true);
		}
		else if(document.body.attachEvent){
			document.body.attachEvent("on"+event, func.wtbind(this));
		}
	}
}
WebTrends.prototype.dcsET=function(){
	var e=(navigator.appVersion.indexOf("MSIE")!=-1)?"click":"mousedown";
	this.dcsBind(e,this.dcsDownload);
	this.dcsBind(e,this.dcsOffsite);
	this.dcsBind(e,this.dcsAnchor);
	this.dcsBind("contextmenu",this.dcsRightClick);
}
WebTrends.prototype.dcsMultiTrack=function(){
	var args=dcsMultiTrack.arguments?dcsMultiTrack.arguments:arguments;
	if (args.length%2==0){
		this.dcsSetProps(args);
		var dCurrent=new Date();
		this.DCS.dcsdat=dCurrent.getTime();
		this.dcsFPC();
		this.dcsTag();
	}
}
WebTrends.prototype.dcsCleanUp=function(){
	this.DCS={};
	this.WT={};
	this.DCSext={};
	if (arguments.length%2==0){
		this.dcsSetProps(arguments);
	}
}
WebTrends.prototype.dcsSetProps=function(args){
	for (var i=0;i<args.length;i+=2){
		if (args[i].indexOf('WT.')==0){
			this.WT[args[i].substring(3)]=args[i+1];
		}
		else if (args[i].indexOf('DCS.')==0){
			this.DCS[args[i].substring(4)]=args[i+1];
		}
		else if (args[i].indexOf('DCSext.')==0){
			this.DCSext[args[i].substring(7)]=args[i+1];
		}
	}
}
WebTrends.prototype.dcsSplit=function(list){
	var items=list.toLowerCase().split(",");
	var len=items.length;
	for (var i=0;i<len;i++){
		items[i]=items[i].replace(/^\s*/,"").replace(/\s*$/,"");
	}
	return items;
}
// Code section for Track clicks to download links.
WebTrends.prototype.dcsDownload=function(evt){
	evt=evt||(window.event||"");
	if (evt&&((typeof(evt.which)!="number")||(evt.which==1))){
		var e=this.dcsEvt(evt,"A");
		if (e.href){
		    var hn=e.hostname?(e.hostname.split(":")[0]):"";
		    if (this.dcsIsOnsite(hn)&&this.dcsTypeMatch(e.pathname,this.downloadtypes)){
		        var qry=e.search?e.search.substring(e.search.indexOf("?")+1,e.search.length):"";
		        var pth=e.pathname?((e.pathname.indexOf("/")!=0)?"/"+e.pathname:e.pathname):"/";
		        var ttl="";
		        var text=document.all?e.innerText:e.text;
		        var img=this.dcsEvt(evt,"IMG");
		        if (img.alt){
			        ttl=img.alt;
		        }
		        else if (text){
			        ttl=text;
		        }
		        else if (e.innerHTML){
			        ttl=e.innerHTML;
		        }
		        this.dcsMultiTrack("DCS.dcssip",hn,"DCS.dcsuri",pth,"DCS.dcsqry",e.search||"","WT.ti","Download:"+ttl,"WT.dl","20","WT.nv",this.dcsNavigation(evt));
		        this.DCS.dcssip=this.DCS.dcsuri=this.DCS.dcsqry=this.WT.ti=this.WT.dl=this.WT.nv="";
		    }
		}
	}
}
// Code section for Track right clicks to download links.
WebTrends.prototype.dcsRightClick=function(evt){
	evt=evt||(window.event||"");
	if (evt){
		var btn=evt.which||evt.button;
		if ((btn!=1)||(navigator.userAgent.indexOf("Safari")!=-1)){
			var e=this.dcsEvt(evt,"A");
			if ((typeof(e.href)!="undefined")&&e.href){
				if ((typeof(e.protocol)!="undefined")&&e.protocol&&(e.protocol.indexOf("http")!=-1)){
					if ((typeof(e.pathname)!="undefined")&&this.dcsTypeMatch(e.pathname,this.rightclicktypes)){
						var pth=e.pathname?((e.pathname.indexOf("/")!=0)?"/"+e.pathname:e.pathname):"/";
						var hn=e.hostname?(e.hostname.split(":")[0]):"";
						this.dcsMultiTrack("DCS.dcssip",hn,"DCS.dcsuri",pth,"DCS.dcsqry","","WT.ti","RightClick:"+pth,"WT.dl","25");
						this.DCS.dcssip=this.DCS.dcsuri=this.WT.ti=this.WT.dl=this.WT.nv="";
					}
				}
			}
		}
	}
}
// Code section for Track clicks to links leading offsite.
WebTrends.prototype.dcsOffsite=function(evt){
	evt=evt||(window.event||"");
	if (evt&&((typeof(evt.which)!="number")||(evt.which==1))){
		var e=this.dcsEvt(evt,"A");
		if (e.href){
		    var hn=e.hostname?(e.hostname.split(":")[0]):"";
		    var pr=e.protocol||"";
		    if ((hn.length>0)&&(pr.indexOf("http")==0)&&!this.dcsIsOnsite(hn)){
			    var qry=e.search?e.search.substring(e.search.indexOf("?")+1,e.search.length):"";
			    var pth=e.pathname?((e.pathname.indexOf("/")!=0)?"/"+e.pathname:e.pathname):"/";
			    var trim=true;
			    this.dcsMultiTrack("DCS.dcssip",hn,"DCS.dcsuri",pth,"DCS.dcsqry",trim?"":qry,"WT.ti","Offsite:"+hn+pth+qry,"WT.dl","24","WT.nv",this.dcsNavigation(evt));
			    this.DCS.dcssip=this.DCS.dcsuri=this.DCS.dcsqry=this.WT.ti=this.WT.dl=this.WT.nv="";
		    }
		}
	}
}

// Code section for Track clicks to links that contain anchors.
WebTrends.prototype.dcsAnchor=function(evt){
	evt=evt||(window.event||"");
	if (evt&&((typeof(evt.which)!="number")||(evt.which==1))){
		var e=this.dcsEvt(evt,"A");
		if (e.href){
		    var hn=e.hostname?(e.hostname.split(":")[0]):"";
		    if (this.dcsIsOnsite(hn)&&e.hash&&(e.hash!="")&&(e.hash!="#")){
		        var qry=e.search?e.search.substring(e.search.indexOf("?")+1,e.search.length):"";
			    var pth=e.pathname?((e.pathname.indexOf("/")!=0)?"/"+e.pathname:e.pathname):"/";
			    this.dcsMultiTrack("DCS.dcssip",hn,"DCS.dcsuri",pth+e.hash,"WT.ti","Anchor:"+e.hash,"WT.dl","21","WT.nv",this.dcsNavigation(evt));
			    this.DCS.dcssip=this.DCS.dcsuri=this.WT.ti=this.WT.dl=this.WT.nv="";
		    }
		}
	}
}
WebTrends.prototype.dcsAdv=function(){
	if (this.trackevents&&(typeof(this.dcsET)=="function")){
		if (window.addEventListener){
			window.addEventListener("load",this.dcsET.wtbind(this),false);
		}
		else if (window.attachEvent){
			window.attachEvent("onload",this.dcsET.wtbind(this));
		}
	}
	this.dcsFPC();
	this.dcsAdSearch();
}
WebTrends.prototype.dcsVar=function(){
	var dCurrent=new Date();
	var WT=this.WT;
	var DCS=this.DCS;
	WT.tz=parseInt(dCurrent.getTimezoneOffset()/60*-1)||"0";
	WT.bh=dCurrent.getHours()||"0";
	WT.ul=navigator.appName=="Netscape"?navigator.language:navigator.userLanguage;
	if (typeof(screen)=="object"){
		WT.cd=navigator.appName=="Netscape"?screen.pixelDepth:screen.colorDepth;
		WT.sr=screen.width+"x"+screen.height;
	}
	if (typeof(navigator.javaEnabled())=="boolean"){
		WT.jo=navigator.javaEnabled()?"Yes":"No";
	}
	if (document.title){
		if (window.RegExp){
			var tire=new RegExp("^"+window.location.protocol+"//"+window.location.hostname+"\\s-\\s");
			WT.ti=document.title.replace(tire,"");
		}
		else{
			WT.ti=document.title;
		}
	}
	WT.js="Yes";
	WT.jv=(function(){
		var agt=navigator.userAgent.toLowerCase();
		var major=parseInt(navigator.appVersion);
		var mac=(agt.indexOf("mac")!=-1);
		var ff=(agt.indexOf("firefox")!=-1);
		var ff0=(agt.indexOf("firefox/0.")!=-1);
		var ff10=(agt.indexOf("firefox/1.0")!=-1);
		var ff15=(agt.indexOf("firefox/1.5")!=-1);
		var ff20=(agt.indexOf("firefox/2.0")!=-1);
		var ff3up=(ff&&!ff0&&!ff10&!ff15&!ff20);
		var nn=(!ff&&(agt.indexOf("mozilla")!=-1)&&(agt.indexOf("compatible")==-1));
		var nn4=(nn&&(major==4));
		var nn6up=(nn&&(major>=5));
		var ie=((agt.indexOf("msie")!=-1)&&(agt.indexOf("opera")==-1));
		var ie4=(ie&&(major==4)&&(agt.indexOf("msie 4")!=-1));
		var ie5up=(ie&&!ie4);
		var op=(agt.indexOf("opera")!=-1);
		var op5=(agt.indexOf("opera 5")!=-1||agt.indexOf("opera/5")!=-1);
		var op6=(agt.indexOf("opera 6")!=-1||agt.indexOf("opera/6")!=-1);
		var op7up=(op&&!op5&&!op6);
		var jv="1.1";
		if (ff3up){
			jv="1.8";
		}
		else if (ff20){
			jv="1.7";
		}
		else if (ff15){
			jv="1.6";
		}
		else if (ff0||ff10||nn6up||op7up){
			jv="1.5";
		}
		else if ((mac&&ie5up)||op6){
			jv="1.4";
		}
		else if (ie5up||nn4||op5){
			jv="1.3";
		}
		else if (ie4){
			jv="1.2";
		}
		return jv;
	})();
	WT.ct="unknown";
	if (document.body&&document.body.addBehavior){
		try{
			document.body.addBehavior("#default#clientCaps");
			WT.ct=document.body.connectionType||"unknown";
			document.body.addBehavior("#default#homePage");
			WT.hp=document.body.isHomePage(location.href)?"1":"0";
		}
		catch(e){
		}
	}
	if (document.all){
		WT.bs=document.body?document.body.offsetWidth+"x"+document.body.offsetHeight:"unknown";
	}
	else{
		WT.bs=window.innerWidth+"x"+window.innerHeight;
	}
	WT.fv=(function(){
		var i,flash;
		if (window.ActiveXObject){
			for(i=10;i>0;i--){
				try{
					flash=new ActiveXObject("ShockwaveFlash.ShockwaveFlash."+i);
					return i+".0";
				}
				catch(e){
				}
			}
		}
		else if (navigator.plugins&&navigator.plugins.length){
			for (i=0;i<navigator.plugins.length;i++){
				if (navigator.plugins[i].name.indexOf('Shockwave Flash')!=-1){
					return navigator.plugins[i].description.split(" ")[2];
				}
			}
		}
		return "Not enabled";
	})();
	WT.slv=(function(){
		var slv="Not enabled";
		try{     
			if (navigator.userAgent.indexOf('MSIE')!=-1){
				var sli = new ActiveXObject('AgControl.AgControl');
				if (sli){
					slv="Unknown";
				}
			}
			else if (navigator.plugins["Silverlight Plug-In"]){
				slv="Unknown";
			}
		}
		catch(e){
		}
		if (slv!="Not enabled"){
			var i,j,v;
			if ((typeof(Silverlight)=="object")&&(typeof(Silverlight.isInstalled)=="function")){
				for (j=9;j>=0;j--){
					for(i=3;i>0;i--){
						v=i+"."+j;
						if (Silverlight.isInstalled(v)){
							slv=v;
							break;
						}
					}
					if (slv==v){
						break;
					}
				}
			}
		}
		return slv;
	})();
	if (this.i18n){
		if (typeof(document.defaultCharset)=="string"){
			WT.le=document.defaultCharset;
		} 
		else if (typeof(document.characterSet)=="string"){
			WT.le=document.characterSet;
		}
		else{
			WT.le="unknown";
		}
	}
	WT.tv="8.6.0";
//	WT.sp="@@SPLITVALUE@@";
	WT.dl="0";
	WT.ssl=(window.location.protocol.indexOf('https:')==0)?"1":"0";
	DCS.dcsdat=dCurrent.getTime();
	DCS.dcssip=window.location.hostname;
	DCS.dcsuri=window.location.pathname;
	WT.es=DCS.dcssip+DCS.dcsuri;
	if (window.location.search){
		DCS.dcsqry=window.location.search;
	}
	if ((window.document.referrer!="")&&(window.document.referrer!="-")){
		if (!(navigator.appName=="Microsoft Internet Explorer"&&parseInt(navigator.appVersion)<4)){
			DCS.dcsref=window.document.referrer;
		}
	}
}
WebTrends.prototype.dcsEscape=function(S, REL){
	if (REL!=""){
		S=S.toString();
		for (var R in REL){
 			if (REL[R] instanceof RegExp){
				S=S.replace(REL[R],R);
 			}
		}
		return S;
	}
	else{
		return escape(S);
	}
}
WebTrends.prototype.dcsA=function(N,V){
	if (this.i18n&&(this.exre!="")&&!this.exre.test(N)){
		if (N=="dcsqry"){
			var newV="";
			var params=V.substring(1).split("&");
			for (var i=0;i<params.length;i++){
				var pair=params[i];
				var pos=pair.indexOf("=");
				if (pos!=-1){
					var key=pair.substring(0,pos);
					var val=pair.substring(pos+1);
					if (i!=0){
						newV+="&";
					}
					newV+=key+"="+this.dcsEncode(val);
				}
			}
			V=V.substring(0,1)+newV;
		}
		else{
			V=this.dcsEncode(V);
		}
	}
	return "&"+N+"="+this.dcsEscape(V, this.re);
}
WebTrends.prototype.dcsEncode=function(S){
	return (typeof(encodeURIComponent)=="function")?encodeURIComponent(S):escape(S);
}
WebTrends.prototype.dcsCreateImage=function(dcsSrc){
	if (document.images){
		this.images[this.index]=new Image();
		this.images[this.index].src=dcsSrc;
		this.index++;
	}
	else{
		document.write('<IMG ALT="" BORDER="0" NAME="DCSIMG" WIDTH="1" HEIGHT="1" SRC="'+dcsSrc+'">');
	}
}
WebTrends.prototype.dcsMeta=function(){
	var elems;
	if (document.all){
		elems=document.all.tags("meta");
	}
	else if (document.documentElement){
		elems=document.getElementsByTagName("meta");
	}
	if (typeof(elems)!="undefined"){
		var length=elems.length;
		for (var i=0;i<length;i++){
			var name=elems.item(i).name;
			var content=elems.item(i).content;
			var equiv=elems.item(i).httpEquiv;
			if (name.length>0){
				if (name.toUpperCase().indexOf("WT.")==0){
					this.WT[name.substring(3)]=content;
				}
				else if (name.toUpperCase().indexOf("DCSEXT.")==0){
					this.DCSext[name.substring(7)]=content;
				}
				else if (name.toUpperCase().indexOf("DCS.")==0){
					this.DCS[name.substring(4)]=content;
				}
			}
		}
	}
}
WebTrends.prototype.dcsTag=function(){
	if (document.cookie.indexOf("WTLOPTOUT=")!=-1){
		return;
	}
	var WT=this.WT;
	var DCS=this.DCS;
	var DCSext=this.DCSext;
	var i18n=this.i18n;
	var P="http"+(window.location.protocol.indexOf('https:')==0?'s':'')+"://"+this.domain+(this.dcsid==""?'':'/'+this.dcsid)+"/dcs.gif?";
	if (i18n){
		WT.dep="";
	}
	for (var N in DCS){
 		if (DCS[N]&&(typeof DCS[N]!="function")){
			P+=this.dcsA(N,DCS[N]);
		}
	}
	var keys=["co_f","vtid","vtvs","vt_f_tlv"];
	for (var i=0;i<keys.length;i++){
		var key=keys[i];
		if (WT[key]){
			P+=this.dcsA("WT."+key,WT[key]);
			delete WT[key];
		}
	}
	for (N in WT){
		if (WT[N]&&(typeof WT[N]!="function")){
			P+=this.dcsA("WT."+N,WT[N]);
		}
	}
	for (N in DCSext){
		if (DCSext[N]&&(typeof DCSext[N]!="function")){
			if (i18n){
				WT.dep=(WT.dep.length==0)?N:(WT.dep+";"+N);
			}
			P+=this.dcsA(N,DCSext[N]);
		}
	}
	if (i18n&&(WT.dep.length>0)){
		P+=this.dcsA("WT.dep",WT.dep);
	}
	if (P.length>2048&&navigator.userAgent.indexOf('MSIE')>=0){
		P=P.substring(0,2040)+"&WT.tu=1";
	}
	this.dcsCreateImage(P);
	this.WT.ad="";
}
WebTrends.prototype.dcsDebug=function(){
	var t=this;
	var i=t.images[0].src;
	var q=i.indexOf("?");
	var r=i.substring(0,q).split("/");
	var m="<b>Protocol</b><br><code>"+r[0]+"<br></code>";
	m+="<b>Domain</b><br><code>"+r[2]+"<br></code>";
	m+="<b>Path</b><br><code>/"+r[3]+"/"+r[4]+"<br></code>";
	m+="<b>Query Params</b><code>"+i.substring(q+1).replace(/\&/g,"<br>")+"</code>";
	m+="<br><b>Cookies</b><br><code>"+document.cookie.replace(/\;/g,"<br>")+"</code>";
	if (t.w&&!t.w.closed){
		t.w.close();
	}
	t.w=window.open("","dcsDebug","width=500,height=650,scrollbars=yes,resizable=yes");
	t.w.document.write(m);
	t.w.focus();
}
WebTrends.prototype.dcsCollect=function(){
    if (this.enabled){
        this.dcsVar();
        this.dcsMeta();
        this.dcsAdv();
        this.dcsTag();
    }
}

function dcsMultiTrack(){
	if (typeof(_tag)!="undefined"){
		return(_tag.dcsMultiTrack());
	}
}

function dcsDebug(){
	if (typeof(_tag)!="undefined"){
		return(_tag.dcsDebug());
	}
}

Function.prototype.wtbind = function(obj){
	var method=this;
	var temp=function(){
		return method.apply(obj,arguments);
	};
	return temp;
}
