ZOOKEEPER-3441: OWASP is flagging jackson-databind-2.9.9.jar for CVE-…

The JIRA is resolved by updating jackson to version 2.9.9.1.

However in so doing I found that it was impossible to run the
dependency check as the CVE lists would always fail to download,
regardless ant or maven. In researching the issue 5.1.0 of the
dependency checker is now available. That fixes this issue (d/l).

However in so doing a couple new problems arise:

The ant dependency check now fails with a circular dependency issue in
one of the transient libraries
(org.sonatype.ossindex#ossindex-service-client;1.2.0). I was unable to
workaround this issue. As such the ant dependency checker is not able
to update to the new version. I believe we should just stop using it
in favor of the maven one as this seems to be Ivy related, as mvn
works just fine with the same change.

Another problem that arises with the dependency checker version
upgrade is that two new issues are identified:

https://www.cvedetails.com/cve/CVE-2008-7220/
which is resolved with the updated prototype.js

https://www.cvedetails.com/cve/CVE-2019-3826/
which seems like a false positive. Please check my work on this.

After these changes the mvn owasp check passes. The code compiles. I
tested the generated documentation and it seems unaffected by the
prototype.js change, although I could have missed this.

Change-Id: I12c9b3111641b066417fc85b155877af5edf9929

Author: Patrick Hunt <phunt@apache.org>

Reviewers: Enrico Olivelli <eolivelli@apache.org>

Closes #1014 from phunt/ZOOKEEPER-3441
diff --git a/pom.xml b/pom.xml
index b6b3a95..e371947 100755
--- a/pom.xml
+++ b/pom.xml
@@ -736,7 +736,7 @@
         <plugin>
           <groupId>org.owasp</groupId>
           <artifactId>dependency-check-maven</artifactId>
-          <version>4.0.2</version>
+          <version>5.1.0</version>
         </plugin>
         <plugin>
           <groupId>org.apache.felix</groupId>
diff --git a/zookeeper-docs/src/main/resources/markdown/skin/prototype.js b/zookeeper-docs/src/main/resources/markdown/skin/prototype.js
index ed7d920..cc89daf 100644
--- a/zookeeper-docs/src/main/resources/markdown/skin/prototype.js
+++ b/zookeeper-docs/src/main/resources/markdown/skin/prototype.js
@@ -1,79 +1,509 @@
-/*  Prototype JavaScript framework, version 1.4.0_pre4
- *  (c) 2005 Sam Stephenson <sam@conio.net>
- *
- *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
- *  against the source tree, available from the Prototype darcs repository. 
+/*  Prototype JavaScript framework, version 1.7.3
+ *  (c) 2005-2010 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/
  *
- *  For details, see the Prototype web site: http://prototype.conio.net/
- *
-/*--------------------------------------------------------------------------*/
+ *--------------------------------------------------------------------------*/
 
 var Prototype = {
-  Version: '1.4.0_pre4',
-  
-  emptyFunction: function() {},
-  K: function(x) {return x}
-}
 
-var Class = {
-  create: function() {
-    return function() { 
+  Version: '1.7.3',
+
+  Browser: (function(){
+    var ua = navigator.userAgent;
+    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
+    return {
+      IE:             !!window.attachEvent && !isOpera,
+      Opera:          isOpera,
+      WebKit:         ua.indexOf('AppleWebKit/') > -1,
+      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
+      MobileSafari:   /Apple.*Mobile/.test(ua)
+    }
+  })(),
+
+  BrowserFeatures: {
+    XPath: !!document.evaluate,
+
+    SelectorsAPI: !!document.querySelector,
+
+    ElementExtensions: (function() {
+      var constructor = window.Element || window.HTMLElement;
+      return !!(constructor && constructor.prototype);
+    })(),
+    SpecificElementExtensions: (function() {
+      if (typeof window.HTMLDivElement !== 'undefined')
+        return true;
+
+      var div = document.createElement('div'),
+          form = document.createElement('form'),
+          isSupported = false;
+
+      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
+        isSupported = true;
+      }
+
+      div = form = null;
+
+      return isSupported;
+    })()
+  },
+
+  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script\\s*>',
+  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 = (function() {
+
+  var IS_DONTENUM_BUGGY = (function(){
+    for (var p in { toString: 1 }) {
+      if (p === 'toString') return false;
+    }
+    return true;
+  })();
+
+  function subclass() {};
+  function create() {
+    var parent = null, properties = $A(arguments);
+    if (Object.isFunction(properties[0]))
+      parent = properties.shift();
+
+    function klass() {
       this.initialize.apply(this, arguments);
     }
-  }
-}
 
-var Abstract = new Object();
+    Object.extend(klass, Class.Methods);
+    klass.superclass = parent;
+    klass.subclasses = [];
 
-Object.extend = function(destination, source) {
-  for (property in source) {
-    destination[property] = source[property];
-  }
-  return destination;
-}
-
-Function.prototype.bind = function(object) {
-  var __method = this;
-  return function() {
-    return __method.apply(object, arguments);
-  }
-}
-
-Function.prototype.bindAsEventListener = function(object) {
-  var __method = this;
-  return function(event) {
-    return __method.call(object, event || window.event);
-  }
-}
-
-Number.prototype.toColorPart = function() {
-  var digits = this.toString(16);
-  if (this < 16) return '0' + digits;
-  return digits;
-}
-
-var Try = {
-  these: function() {
-    var returnValue;
-
-    for (var i = 0; i < arguments.length; i++) {
-      var lambda = arguments[i];
-      try {
-        returnValue = lambda();
-        break;
-      } catch (e) {}
+    if (parent) {
+      subclass.prototype = parent.prototype;
+      klass.prototype = new subclass;
+      parent.subclasses.push(klass);
     }
 
-    return returnValue;
+    for (var i = 0, length = properties.length; i < length; i++)
+      klass.addMethods(properties[i]);
+
+    if (!klass.prototype.initialize)
+      klass.prototype.initialize = Prototype.emptyFunction;
+
+    klass.prototype.constructor = klass;
+    return klass;
   }
-}
 
-/*--------------------------------------------------------------------------*/
+  function addMethods(source) {
+    var ancestor   = this.superclass && this.superclass.prototype,
+        properties = Object.keys(source);
 
-var PeriodicalExecuter = Class.create();
-PeriodicalExecuter.prototype = {
+    if (IS_DONTENUM_BUGGY) {
+      if (source.toString != Object.prototype.toString)
+        properties.push("toString");
+      if (source.valueOf != Object.prototype.valueOf)
+        properties.push("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()[0] == "$super") {
+        var method = value;
+        value = (function(m) {
+          return function() { return ancestor[m].apply(this, arguments); };
+        })(property).wrap(method);
+
+        value.valueOf = (function(method) {
+          return function() { return method.valueOf.call(method); };
+        })(method);
+
+        value.toString = (function(method) {
+          return function() { return method.toString.call(method); };
+        })(method);
+      }
+      this.prototype[property] = value;
+    }
+
+    return this;
+  }
+
+  return {
+    create: create,
+    Methods: {
+      addMethods: addMethods
+    }
+  };
+})();
+(function() {
+
+  var _toString = Object.prototype.toString,
+      _hasOwnProperty = Object.prototype.hasOwnProperty,
+      NULL_TYPE = 'Null',
+      UNDEFINED_TYPE = 'Undefined',
+      BOOLEAN_TYPE = 'Boolean',
+      NUMBER_TYPE = 'Number',
+      STRING_TYPE = 'String',
+      OBJECT_TYPE = 'Object',
+      FUNCTION_CLASS = '[object Function]',
+      BOOLEAN_CLASS = '[object Boolean]',
+      NUMBER_CLASS = '[object Number]',
+      STRING_CLASS = '[object String]',
+      ARRAY_CLASS = '[object Array]',
+      DATE_CLASS = '[object Date]',
+      NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
+        typeof JSON.stringify === 'function' &&
+        JSON.stringify(0) === '0' &&
+        typeof JSON.stringify(Prototype.K) === 'undefined';
+
+
+
+  var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf',
+   'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'];
+
+  var IS_DONTENUM_BUGGY = (function(){
+    for (var p in { toString: 1 }) {
+      if (p === 'toString') return false;
+    }
+    return true;
+  })();
+
+  function Type(o) {
+    switch(o) {
+      case null: return NULL_TYPE;
+      case (void 0): return UNDEFINED_TYPE;
+    }
+    var type = typeof o;
+    switch(type) {
+      case 'boolean': return BOOLEAN_TYPE;
+      case 'number':  return NUMBER_TYPE;
+      case 'string':  return STRING_TYPE;
+    }
+    return OBJECT_TYPE;
+  }
+
+  function extend(destination, source) {
+    for (var property in source)
+      destination[property] = source[property];
+    return destination;
+  }
+
+  function inspect(object) {
+    try {
+      if (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;
+    }
+  }
+
+  function toJSON(value) {
+    return Str('', { '': value }, []);
+  }
+
+  function Str(key, holder, stack) {
+    var value = holder[key];
+    if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
+      value = value.toJSON(key);
+    }
+
+    var _class = _toString.call(value);
+
+    switch (_class) {
+      case NUMBER_CLASS:
+      case BOOLEAN_CLASS:
+      case STRING_CLASS:
+        value = value.valueOf();
+    }
+
+    switch (value) {
+      case null: return 'null';
+      case true: return 'true';
+      case false: return 'false';
+    }
+
+    var type = typeof value;
+    switch (type) {
+      case 'string':
+        return value.inspect(true);
+      case 'number':
+        return isFinite(value) ? String(value) : 'null';
+      case 'object':
+
+        for (var i = 0, length = stack.length; i < length; i++) {
+          if (stack[i] === value) {
+            throw new TypeError("Cyclic reference to '" + value + "' in object");
+          }
+        }
+        stack.push(value);
+
+        var partial = [];
+        if (_class === ARRAY_CLASS) {
+          for (var i = 0, length = value.length; i < length; i++) {
+            var str = Str(i, value, stack);
+            partial.push(typeof str === 'undefined' ? 'null' : str);
+          }
+          partial = '[' + partial.join(',') + ']';
+        } else {
+          var keys = Object.keys(value);
+          for (var i = 0, length = keys.length; i < length; i++) {
+            var key = keys[i], str = Str(key, value, stack);
+            if (typeof str !== "undefined") {
+               partial.push(key.inspect(true)+ ':' + str);
+             }
+          }
+          partial = '{' + partial.join(',') + '}';
+        }
+        stack.pop();
+        return partial;
+    }
+  }
+
+  function stringify(object) {
+    return JSON.stringify(object);
+  }
+
+  function toQueryString(object) {
+    return $H(object).toQueryString();
+  }
+
+  function toHTML(object) {
+    return object && object.toHTML ? object.toHTML() : String.interpret(object);
+  }
+
+  function keys(object) {
+    if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
+    var results = [];
+    for (var property in object) {
+      if (_hasOwnProperty.call(object, property))
+        results.push(property);
+    }
+
+    if (IS_DONTENUM_BUGGY) {
+      for (var i = 0; property = DONT_ENUMS[i]; i++) {
+        if (_hasOwnProperty.call(object, property))
+          results.push(property);
+      }
+    }
+
+    return results;
+  }
+
+  function values(object) {
+    var results = [];
+    for (var property in object)
+      results.push(object[property]);
+    return results;
+  }
+
+  function clone(object) {
+    return extend({ }, object);
+  }
+
+  function isElement(object) {
+    return !!(object && object.nodeType == 1);
+  }
+
+  function isArray(object) {
+    return _toString.call(object) === ARRAY_CLASS;
+  }
+
+  var hasNativeIsArray = (typeof Array.isArray == 'function')
+    && Array.isArray([]) && !Array.isArray({});
+
+  if (hasNativeIsArray) {
+    isArray = Array.isArray;
+  }
+
+  function isHash(object) {
+    return object instanceof Hash;
+  }
+
+  function isFunction(object) {
+    return _toString.call(object) === FUNCTION_CLASS;
+  }
+
+  function isString(object) {
+    return _toString.call(object) === STRING_CLASS;
+  }
+
+  function isNumber(object) {
+    return _toString.call(object) === NUMBER_CLASS;
+  }
+
+  function isDate(object) {
+    return _toString.call(object) === DATE_CLASS;
+  }
+
+  function isUndefined(object) {
+    return typeof object === "undefined";
+  }
+
+  extend(Object, {
+    extend:        extend,
+    inspect:       inspect,
+    toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
+    toQueryString: toQueryString,
+    toHTML:        toHTML,
+    keys:          Object.keys || keys,
+    values:        values,
+    clone:         clone,
+    isElement:     isElement,
+    isArray:       isArray,
+    isHash:        isHash,
+    isFunction:    isFunction,
+    isString:      isString,
+    isNumber:      isNumber,
+    isDate:        isDate,
+    isUndefined:   isUndefined
+  });
+})();
+Object.extend(Function.prototype, (function() {
+  var slice = Array.prototype.slice;
+
+  function update(array, args) {
+    var arrayLength = array.length, length = args.length;
+    while (length--) array[arrayLength + length] = args[length];
+    return array;
+  }
+
+  function merge(array, args) {
+    array = slice.call(array, 0);
+    return update(array, args);
+  }
+
+  function argumentNames() {
+    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
+      .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
+      .replace(/\s+/g, '').split(',');
+    return names.length == 1 && !names[0] ? [] : names;
+  }
+
+
+  function bind(context) {
+    if (arguments.length < 2 && Object.isUndefined(arguments[0]))
+      return this;
+
+    if (!Object.isFunction(this))
+      throw new TypeError("The object is not callable.");
+
+    var nop = function() {};
+    var __method = this, args = slice.call(arguments, 1);
+
+    var bound = function() {
+      var a = merge(args, arguments);
+      var c = this instanceof bound ? this : context;
+      return __method.apply(c, a);
+    };
+
+    nop.prototype   = this.prototype;
+    bound.prototype = new nop();
+
+    return bound;
+  }
+
+  function bindAsEventListener(context) {
+    var __method = this, args = slice.call(arguments, 1);
+    return function(event) {
+      var a = update([event || window.event], args);
+      return __method.apply(context, a);
+    }
+  }
+
+  function curry() {
+    if (!arguments.length) return this;
+    var __method = this, args = slice.call(arguments, 0);
+    return function() {
+      var a = merge(args, arguments);
+      return __method.apply(this, a);
+    }
+  }
+
+  function delay(timeout) {
+    var __method = this, args = slice.call(arguments, 1);
+    timeout = timeout * 1000;
+    return window.setTimeout(function() {
+      return __method.apply(__method, args);
+    }, timeout);
+  }
+
+  function defer() {
+    var args = update([0.01], arguments);
+    return this.delay.apply(this, args);
+  }
+
+  function wrap(wrapper) {
+    var __method = this;
+    return function() {
+      var a = update([__method.bind(this)], arguments);
+      return wrapper.apply(this, a);
+    }
+  }
+
+  function methodize() {
+    if (this._methodized) return this._methodized;
+    var __method = this;
+    return this._methodized = function() {
+      var a = update([this], arguments);
+      return __method.apply(null, a);
+    };
+  }
+
+  var extensions = {
+    argumentNames:       argumentNames,
+    bindAsEventListener: bindAsEventListener,
+    curry:               curry,
+    delay:               delay,
+    defer:               defer,
+    wrap:                wrap,
+    methodize:           methodize
+  };
+
+  if (!Function.prototype.bind)
+    extensions.bind = bind;
+
+  return extensions;
+})());
+
+
+
+(function(proto) {
+
+
+  function toISOString() {
+    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';
+  }
+
+
+  function toJSON() {
+    return this.toISOString();
+  }
+
+  if (!proto.toISOString) proto.toISOString = toISOString;
+  if (!proto.toJSON) proto.toJSON = toJSON;
+
+})(Date.prototype);
+
+
+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;
@@ -83,486 +513,1490 @@
   },
 
   registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+    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 { 
+      try {
         this.currentlyExecuting = true;
-        this.callback(); 
-      } finally { 
+        this.execute();
         this.currentlyExecuting = false;
+      } catch(e) {
+        this.currentlyExecuting = false;
+        throw e;
       }
     }
   }
-}
-
-/*--------------------------------------------------------------------------*/
-
-function $() {
-  var elements = new Array();
-
-  for (var i = 0; i < arguments.length; i++) {
-    var element = arguments[i];
-    if (typeof element == 'string')
-      element = document.getElementById(element);
-
-    if (arguments.length == 1) 
-      return element;
-
-    elements.push(element);
-  }
-
-  return elements;
-}
-
-if (!Array.prototype.push) {
-  Array.prototype.push = function() {
-		var startLength = this.length;
-		for (var i = 0; i < arguments.length; i++)
-      this[startLength + i] = arguments[i];
-	  return this.length;
-  }
-}
-
-if (!Function.prototype.apply) {
-  // Based on code from http://www.youngpup.net/
-  Function.prototype.apply = function(object, parameters) {
-    var parameterStrings = new Array();
-    if (!object)     object = window;
-    if (!parameters) parameters = new Array();
-    
-    for (var i = 0; i < parameters.length; i++)
-      parameterStrings[i] = 'parameters[' + i + ']';
-    
-    object.__apply__ = this;
-    var result = eval('object.__apply__(' + 
-      parameterStrings.join(', ') + ')');
-    object.__apply__ = null;
-    
-    return result;
-  }
-}
-
-Object.extend(String.prototype, {
-  stripTags: function() {
-    return this.replace(/<\/?[^>]+>/gi, '');
+});
+Object.extend(String, {
+  interpret: function(value) {
+    return value == null ? '' : String(value);
   },
-
-  escapeHTML: function() {
-    var div = document.createElement('div');
-    var text = document.createTextNode(this);
-    div.appendChild(text);
-    return div.innerHTML;
-  },
-
-  unescapeHTML: function() {
-    var div = document.createElement('div');
-    div.innerHTML = this.stripTags();
-    return div.childNodes[0].nodeValue;
-  },
-  
-  parseQuery: function() {
-    var str = this;
-    if (str.substring(0,1) == '?') {
-      str = this.substring(1);
-    }
-    var result = {};
-    var pairs = str.split('&');
-    for (var i = 0; i < pairs.length; i++) {
-      var pair = pairs[i].split('=');
-      result[pair[0]] = pair[1];
-    }
-    return result;
+  specialChar: {
+    '\b': '\\b',
+    '\t': '\\t',
+    '\n': '\\n',
+    '\f': '\\f',
+    '\r': '\\r',
+    '\\': '\\\\'
   }
 });
 
+Object.extend(String.prototype, (function() {
+  var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
+    typeof JSON.parse === 'function' &&
+    JSON.parse('{"test": true}').test;
 
-var _break    = new Object();
-var _continue = new Object();
+  function prepareReplacement(replacement) {
+    if (Object.isFunction(replacement)) return replacement;
+    var template = new Template(replacement);
+    return function(match) { return template.evaluate(match) };
+  }
 
-var Enumerable = {
-  each: function(iterator) {
-    var index = 0;
-    try {
-      this._each(function(value) {
-        try {
-          iterator(value, index++);
-        } catch (e) {
-          if (e != _continue) throw e;
-        }
-      });
-    } catch (e) {
-      if (e != _break) throw e;
+  function isNonEmptyRegExp(regexp) {
+    return regexp.source && regexp.source !== '(?:)';
+  }
+
+
+  function gsub(pattern, replacement) {
+    var result = '', source = this, match;
+    replacement = prepareReplacement(replacement);
+
+    if (Object.isString(pattern))
+      pattern = RegExp.escape(pattern);
+
+    if (!(pattern.length || isNonEmptyRegExp(pattern))) {
+      replacement = replacement('');
+      return replacement + source.split('').join(replacement) + replacement;
     }
+
+    while (source.length > 0) {
+      match = source.match(pattern)
+      if (match && match[0].length > 0) {
+        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;
+  }
+
+  function sub(pattern, replacement, count) {
+    replacement = prepareReplacement(replacement);
+    count = Object.isUndefined(count) ? 1 : count;
+
+    return this.gsub(pattern, function(match) {
+      if (--count < 0) return match[0];
+      return replacement(match);
+    });
+  }
+
+  function scan(pattern, iterator) {
+    this.gsub(pattern, iterator);
+    return String(this);
+  }
+
+  function truncate(length, truncation) {
+    length = length || 30;
+    truncation = Object.isUndefined(truncation) ? '...' : truncation;
+    return this.length > length ?
+      this.slice(0, length - truncation.length) + truncation : String(this);
+  }
+
+  function strip() {
+    return this.replace(/^\s+/, '').replace(/\s+$/, '');
+  }
+
+  function stripTags() {
+    return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, '');
+  }
+
+  function stripScripts() {
+    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+  }
+
+  function extractScripts() {
+    var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
+        matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+    return (this.match(matchAll) || []).map(function(scriptTag) {
+      return (scriptTag.match(matchOne) || ['', ''])[1];
+    });
+  }
+
+  function evalScripts() {
+    return this.extractScripts().map(function(script) { return eval(script); });
+  }
+
+  function escapeHTML() {
+    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
+  }
+
+  function unescapeHTML() {
+    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
+  }
+
+
+  function toQueryParams(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()),
+            value = pair.length > 1 ? pair.join('=') : pair[0];
+
+        if (value != undefined) {
+          value = value.gsub('+', ' ');
+          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;
+    });
+  }
+
+  function toArray() {
+    return this.split('');
+  }
+
+  function succ() {
+    return this.slice(0, this.length - 1) +
+      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+  }
+
+  function times(count) {
+    return count < 1 ? '' : new Array(count + 1).join(this);
+  }
+
+  function camelize() {
+    return this.replace(/-+(.)?/g, function(match, chr) {
+      return chr ? chr.toUpperCase() : '';
+    });
+  }
+
+  function capitalize() {
+    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+  }
+
+  function underscore() {
+    return this.replace(/::/g, '/')
+               .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
+               .replace(/([a-z\d])([A-Z])/g, '$1_$2')
+               .replace(/-/g, '_')
+               .toLowerCase();
+  }
+
+  function dasherize() {
+    return this.replace(/_/g, '-');
+  }
+
+  function inspect(useDoubleQuotes) {
+    var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
+      if (character in String.specialChar) {
+        return String.specialChar[character];
+      }
+      return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
+    });
+    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+  }
+
+  function unfilterJSON(filter) {
+    return this.replace(filter || Prototype.JSONFilter, '$1');
+  }
+
+  function isJSON() {
+    var str = this;
+    if (str.blank()) return false;
+    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
+    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
+    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
+    return (/^[\],:{}\s]*$/).test(str);
+  }
+
+  function evalJSON(sanitize) {
+    var json = this.unfilterJSON(),
+        cx = /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\u0000]/g;
+    if (cx.test(json)) {
+      json = json.replace(cx, function (a) {
+        return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+      });
+    }
+    try {
+      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+    } catch (e) { }
+    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+  }
+
+  function parseJSON() {
+    var json = this.unfilterJSON();
+    return JSON.parse(json);
+  }
+
+  function include(pattern) {
+    return this.indexOf(pattern) > -1;
+  }
+
+  function startsWith(pattern, position) {
+    position = Object.isNumber(position) ? position : 0;
+    return this.lastIndexOf(pattern, position) === position;
+  }
+
+  function endsWith(pattern, position) {
+    pattern = String(pattern);
+    position = Object.isNumber(position) ? position : this.length;
+    if (position < 0) position = 0;
+    if (position > this.length) position = this.length;
+    var d = position - pattern.length;
+    return d >= 0 && this.indexOf(pattern, d) === d;
+  }
+
+  function empty() {
+    return this == '';
+  }
+
+  function blank() {
+    return /^\s*$/.test(this);
+  }
+
+  function interpolate(object, pattern) {
+    return new Template(this, pattern).evaluate(object);
+  }
+
+  return {
+    gsub:           gsub,
+    sub:            sub,
+    scan:           scan,
+    truncate:       truncate,
+    strip:          String.prototype.trim || strip,
+    stripTags:      stripTags,
+    stripScripts:   stripScripts,
+    extractScripts: extractScripts,
+    evalScripts:    evalScripts,
+    escapeHTML:     escapeHTML,
+    unescapeHTML:   unescapeHTML,
+    toQueryParams:  toQueryParams,
+    parseQuery:     toQueryParams,
+    toArray:        toArray,
+    succ:           succ,
+    times:          times,
+    camelize:       camelize,
+    capitalize:     capitalize,
+    underscore:     underscore,
+    dasherize:      dasherize,
+    inspect:        inspect,
+    unfilterJSON:   unfilterJSON,
+    isJSON:         isJSON,
+    evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
+    include:        include,
+    startsWith:     String.prototype.startsWith || startsWith,
+    endsWith:       String.prototype.endsWith || endsWith,
+    empty:          empty,
+    blank:          blank,
+    interpolate:    interpolate
+  };
+})());
+
+var Template = Class.create({
+  initialize: function(template, pattern) {
+    this.template = template.toString();
+    this.pattern = pattern || Template.Pattern;
   },
-  
-  all: function(iterator) {
+
+  evaluate: function(object) {
+    if (object && Object.isFunction(object.toTemplateReplacements))
+      object = object.toTemplateReplacements();
+
+    return this.template.gsub(this.pattern, function(match) {
+      if (object == null) return (match[1] + '');
+
+      var before = match[1] || '';
+      if (before == '\\') return match[2];
+
+      var ctx = object, expr = match[3],
+          pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+
+      match = pattern.exec(expr);
+      if (match == null) return before;
+
+      while (match != null) {
+        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : 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 = (function() {
+  function each(iterator, context) {
+    try {
+      this._each(iterator, context);
+    } catch (e) {
+      if (e != $break) throw e;
+    }
+    return this;
+  }
+
+  function eachSlice(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);
+  }
+
+  function all(iterator, context) {
+    iterator = iterator || Prototype.K;
     var result = true;
     this.each(function(value, index) {
-      if (!(result &= (iterator || Prototype.K)(value, index))) 
-        throw _break;
-    });
+      result = result && !!iterator.call(context, value, index, this);
+      if (!result) throw $break;
+    }, this);
     return result;
-  },
-  
-  any: function(iterator) {
-    var result = true;
+  }
+
+  function any(iterator, context) {
+    iterator = iterator || Prototype.K;
+    var result = false;
     this.each(function(value, index) {
-      if (result &= (iterator || Prototype.K)(value, index)) 
-        throw _break;
-    });
+      if (result = !!iterator.call(context, value, index, this))
+        throw $break;
+    }, this);
     return result;
-  },
-  
-  collect: function(iterator) {
+  }
+
+  function collect(iterator, context) {
+    iterator = iterator || Prototype.K;
     var results = [];
     this.each(function(value, index) {
-      results.push(iterator(value, index));
-    });
+      results.push(iterator.call(context, value, index, this));
+    }, this);
     return results;
-  },
-  
-  detect: function (iterator) {
+  }
+
+  function detect(iterator, context) {
     var result;
     this.each(function(value, index) {
-      if (iterator(value, index)) {
+      if (iterator.call(context, value, index, this)) {
         result = value;
-        throw _break;
+        throw $break;
       }
-    });
+    }, this);
     return result;
-  },
-  
-  findAll: function(iterator) {
+  }
+
+  function findAll(iterator, context) {
     var results = [];
     this.each(function(value, index) {
-      if (iterator(value, index))
+      if (iterator.call(context, value, index, this))
         results.push(value);
-    });
+    }, this);
     return results;
-  },
-  
-  grep: function(pattern, iterator) {
+  }
+
+  function grep(filter, iterator, context) {
+    iterator = iterator || Prototype.K;
     var results = [];
+
+    if (Object.isString(filter))
+      filter = new RegExp(RegExp.escape(filter));
+
     this.each(function(value, index) {
-      var stringValue = value.toString();
-      if (stringValue.match(pattern))
-        results.push((iterator || Prototype.K)(value, index));
-    })
+      if (filter.match(value))
+        results.push(iterator.call(context, value, index, this));
+    }, this);
     return results;
-  },
-  
-  include: function(object) {
+  }
+
+  function include(object) {
+    if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1)
+      return true;
+
     var found = false;
     this.each(function(value) {
       if (value == object) {
         found = true;
-        throw _break;
+        throw $break;
       }
     });
     return found;
-  },
-  
-  inject: function(memo, iterator) {
-    this.each(function(value, index) {
-      memo = iterator(memo, value, index);
+  }
+
+  function inGroupsOf(number, fillWith) {
+    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+    return this.eachSlice(number, function(slice) {
+      while(slice.length < number) slice.push(fillWith);
+      return slice;
     });
+  }
+
+  function inject(memo, iterator, context) {
+    this.each(function(value, index) {
+      memo = iterator.call(context, memo, value, index, this);
+    }, this);
     return memo;
-  },
-  
-  invoke: function(method) {
+  }
+
+  function invoke(method) {
     var args = $A(arguments).slice(1);
-    return this.collect(function(value) {
+    return this.map(function(value) {
       return value[method].apply(value, args);
     });
-  },
-  
-  max: function(iterator) {
+  }
+
+  function max(iterator, context) {
+    iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
-      value = (iterator || Prototype.K)(value, index);
-      if (value >= (result || value))
+      value = iterator.call(context, value, index, this);
+      if (result == null || value >= result)
         result = value;
-    });
+    }, this);
     return result;
-  },
-  
-  min: function(iterator) {
+  }
+
+  function min(iterator, context) {
+    iterator = iterator || Prototype.K;
     var result;
     this.each(function(value, index) {
-      value = (iterator || Prototype.K)(value, index);
-      if (value <= (result || value))
+      value = iterator.call(context, value, index, this);
+      if (result == null || value < result)
         result = value;
-    });
+    }, this);
     return result;
-  },
-  
-  partition: function(iterator) {
+  }
+
+  function partition(iterator, context) {
+    iterator = iterator || Prototype.K;
     var trues = [], falses = [];
     this.each(function(value, index) {
-      ((iterator || Prototype.K)(value, index) ? 
+      (iterator.call(context, value, index, this) ?
         trues : falses).push(value);
-    });
+    }, this);
     return [trues, falses];
-  },
-  
-  pluck: function(property) {
+  }
+
+  function pluck(property) {
     var results = [];
-    this.each(function(value, index) {
+    this.each(function(value) {
       results.push(value[property]);
     });
     return results;
-  },
-  
-  reject: function(iterator) {
+  }
+
+  function reject(iterator, context) {
     var results = [];
     this.each(function(value, index) {
-      if (!iterator(value, index))
+      if (!iterator.call(context, value, index, this))
         results.push(value);
-    });
+    }, this);
     return results;
-  },
-  
-  sortBy: function(iterator) {
-    return this.collect(function(value, index) {
-      return {value: value, criteria: iterator(value, index)};
-    }).sort(function(left, right) {
+  }
+
+  function sortBy(iterator, context) {
+    return this.map(function(value, index) {
+      return {
+        value: value,
+        criteria: iterator.call(context, value, index, this)
+      };
+    }, this).sort(function(left, right) {
       var a = left.criteria, b = right.criteria;
       return a < b ? -1 : a > b ? 1 : 0;
     }).pluck('value');
-  },
-  
-  toArray: function() {
-    return this.collect(Prototype.K);
-  },
-  
-  zip: function() {
+  }
+
+  function toArray() {
+    return this.map();
+  }
+
+  function zip() {
     var iterator = Prototype.K, args = $A(arguments);
-    if (typeof args.last() == 'function')
+    if (Object.isFunction(args.last()))
       iterator = args.pop();
 
     var collections = [this].concat(args).map($A);
     return this.map(function(value, index) {
-      iterator(value = collections.pluck(index));
-      return value;
+      return iterator(collections.pluck(index));
     });
   }
-}
 
-Object.extend(Enumerable, {
-  map:     Enumerable.collect,
-  find:    Enumerable.detect,
-  select:  Enumerable.findAll,
-  member:  Enumerable.include,
-  entries: Enumerable.toArray
-});
+  function size() {
+    return this.toArray().length;
+  }
 
-$A = Array.from = function(iterable) {
-  var results = [];
-  for (var i = 0; i < iterable.length; i++)
-    results.push(iterable[i]);
+  function inspect() {
+    return '#<Enumerable:' + this.toArray().inspect() + '>';
+  }
+
+
+
+
+
+
+
+
+
+  return {
+    each:       each,
+    eachSlice:  eachSlice,
+    all:        all,
+    every:      all,
+    any:        any,
+    some:       any,
+    collect:    collect,
+    map:        collect,
+    detect:     detect,
+    findAll:    findAll,
+    select:     findAll,
+    filter:     findAll,
+    grep:       grep,
+    include:    include,
+    member:     include,
+    inGroupsOf: inGroupsOf,
+    inject:     inject,
+    invoke:     invoke,
+    max:        max,
+    min:        min,
+    partition:  partition,
+    pluck:      pluck,
+    reject:     reject,
+    sortBy:     sortBy,
+    toArray:    toArray,
+    entries:    toArray,
+    zip:        zip,
+    size:       size,
+    inspect:    inspect,
+    find:       detect
+  };
+})();
+
+function $A(iterable) {
+  if (!iterable) return [];
+  if ('toArray' in Object(iterable)) return iterable.toArray();
+  var length = iterable.length || 0, results = new Array(length);
+  while (length--) results[length] = iterable[length];
   return results;
 }
 
-Object.extend(Array.prototype, {
-  _each: function(iterator) {
-    for (var i = 0; i < this.length; i++)
-      iterator(this[i]);
-  },
-  
-  first: function() {
+
+function $w(string) {
+  if (!Object.isString(string)) return [];
+  string = string.strip();
+  return string ? string.split(/\s+/) : [];
+}
+
+Array.from = $A;
+
+
+(function() {
+  var arrayProto = Array.prototype,
+      slice = arrayProto.slice,
+      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
+
+  function each(iterator, context) {
+    for (var i = 0, length = this.length >>> 0; i < length; i++) {
+      if (i in this) iterator.call(context, this[i], i, this);
+    }
+  }
+  if (!_each) _each = each;
+
+  function clear() {
+    this.length = 0;
+    return this;
+  }
+
+  function first() {
     return this[0];
-  },
-  
-  last: function() {
+  }
+
+  function last() {
     return this[this.length - 1];
   }
-});
 
-Object.extend(Array.prototype, Enumerable);
+  function compact() {
+    return this.select(function(value) {
+      return value != null;
+    });
+  }
 
+  function flatten() {
+    return this.inject([], function(array, value) {
+      if (Object.isArray(value))
+        return array.concat(value.flatten());
+      array.push(value);
+      return array;
+    });
+  }
+
+  function without() {
+    var values = slice.call(arguments, 0);
+    return this.select(function(value) {
+      return !values.include(value);
+    });
+  }
+
+  function reverse(inline) {
+    return (inline === false ? this.toArray() : this)._reverse();
+  }
+
+  function uniq(sorted) {
+    return this.inject([], function(array, value, index) {
+      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+        array.push(value);
+      return array;
+    });
+  }
+
+  function intersect(array) {
+    return this.uniq().findAll(function(item) {
+      return array.indexOf(item) !== -1;
+    });
+  }
+
+
+  function clone() {
+    return slice.call(this, 0);
+  }
+
+  function size() {
+    return this.length;
+  }
+
+  function inspect() {
+    return '[' + this.map(Object.inspect).join(', ') + ']';
+  }
+
+  function indexOf(item, i) {
+    if (this == null) throw new TypeError();
+
+    var array = Object(this), length = array.length >>> 0;
+    if (length === 0) return -1;
+
+    i = Number(i);
+    if (isNaN(i)) {
+      i = 0;
+    } else if (i !== 0 && isFinite(i)) {
+      i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
+    }
+
+    if (i > length) return -1;
+
+    var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0);
+    for (; k < length; k++)
+      if (k in array && array[k] === item) return k;
+    return -1;
+  }
+
+
+  function lastIndexOf(item, i) {
+    if (this == null) throw new TypeError();
+
+    var array = Object(this), length = array.length >>> 0;
+    if (length === 0) return -1;
+
+    if (!Object.isUndefined(i)) {
+      i = Number(i);
+      if (isNaN(i)) {
+        i = 0;
+      } else if (i !== 0 && isFinite(i)) {
+        i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
+      }
+    } else {
+      i = length;
+    }
+
+    var k = i >= 0 ? Math.min(i, length - 1) :
+     length - Math.abs(i);
+
+    for (; k >= 0; k--)
+      if (k in array && array[k] === item) return k;
+    return -1;
+  }
+
+  function concat(_) {
+    var array = [], items = slice.call(arguments, 0), item, n = 0;
+    items.unshift(this);
+    for (var i = 0, length = items.length; i < length; i++) {
+      item = items[i];
+      if (Object.isArray(item) && !('callee' in item)) {
+        for (var j = 0, arrayLength = item.length; j < arrayLength; j++) {
+          if (j in item) array[n] = item[j];
+          n++;
+        }
+      } else {
+        array[n++] = item;
+      }
+    }
+    array.length = n;
+    return array;
+  }
+
+
+  function wrapNative(method) {
+    return function() {
+      if (arguments.length === 0) {
+        return method.call(this, Prototype.K);
+      } else if (arguments[0] === undefined) {
+        var args = slice.call(arguments, 1);
+        args.unshift(Prototype.K);
+        return method.apply(this, args);
+      } else {
+        return method.apply(this, arguments);
+      }
+    };
+  }
+
+
+  function map(iterator) {
+    if (this == null) throw new TypeError();
+    iterator = iterator || Prototype.K;
+
+    var object = Object(this);
+    var results = [], context = arguments[1], n = 0;
+
+    for (var i = 0, length = object.length >>> 0; i < length; i++) {
+      if (i in object) {
+        results[n] = iterator.call(context, object[i], i, object);
+      }
+      n++;
+    }
+    results.length = n;
+    return results;
+  }
+
+  if (arrayProto.map) {
+    map = wrapNative(Array.prototype.map);
+  }
+
+  function filter(iterator) {
+    if (this == null || !Object.isFunction(iterator))
+      throw new TypeError();
+
+    var object = Object(this);
+    var results = [], context = arguments[1], value;
+
+    for (var i = 0, length = object.length >>> 0; i < length; i++) {
+      if (i in object) {
+        value = object[i];
+        if (iterator.call(context, value, i, object)) {
+          results.push(value);
+        }
+      }
+    }
+    return results;
+  }
+
+  if (arrayProto.filter) {
+    filter = Array.prototype.filter;
+  }
+
+  function some(iterator) {
+    if (this == null) throw new TypeError();
+    iterator = iterator || Prototype.K;
+    var context = arguments[1];
+
+    var object = Object(this);
+    for (var i = 0, length = object.length >>> 0; i < length; i++) {
+      if (i in object && iterator.call(context, object[i], i, object)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  if (arrayProto.some) {
+    some = wrapNative(Array.prototype.some);
+  }
+
+  function every(iterator) {
+    if (this == null) throw new TypeError();
+    iterator = iterator || Prototype.K;
+    var context = arguments[1];
+
+    var object = Object(this);
+    for (var i = 0, length = object.length >>> 0; i < length; i++) {
+      if (i in object && !iterator.call(context, object[i], i, object)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  if (arrayProto.every) {
+    every = wrapNative(Array.prototype.every);
+  }
+
+
+  Object.extend(arrayProto, Enumerable);
+
+  if (arrayProto.entries === Enumerable.entries) {
+    delete arrayProto.entries;
+  }
+
+  if (!arrayProto._reverse)
+    arrayProto._reverse = arrayProto.reverse;
+
+  Object.extend(arrayProto, {
+    _each:     _each,
+
+    map:       map,
+    collect:   map,
+    select:    filter,
+    filter:    filter,
+    findAll:   filter,
+    some:      some,
+    any:       some,
+    every:     every,
+    all:       every,
+
+    clear:     clear,
+    first:     first,
+    last:      last,
+    compact:   compact,
+    flatten:   flatten,
+    without:   without,
+    reverse:   reverse,
+    uniq:      uniq,
+    intersect: intersect,
+    clone:     clone,
+    toArray:   clone,
+    size:      size,
+    inspect:   inspect
+  });
+
+  var CONCAT_ARGUMENTS_BUGGY = (function() {
+    return [].concat(arguments)[0][0] !== 1;
+  })(1,2);
+
+  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
+
+  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
+  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
+})();
+function $H(object) {
+  return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+  function initialize(object) {
+    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+  }
+
+
+  function _each(iterator, context) {
+    var i = 0;
+    for (var key in this._object) {
+      var value = this._object[key], pair = [key, value];
+      pair.key = key;
+      pair.value = value;
+      iterator.call(context, pair, i);
+      i++;
+    }
+  }
+
+  function set(key, value) {
+    return this._object[key] = value;
+  }
+
+  function get(key) {
+    if (this._object[key] !== Object.prototype[key])
+      return this._object[key];
+  }
+
+  function unset(key) {
+    var value = this._object[key];
+    delete this._object[key];
+    return value;
+  }
+
+  function toObject() {
+    return Object.clone(this._object);
+  }
+
+
+
+  function keys() {
+    return this.pluck('key');
+  }
+
+  function values() {
+    return this.pluck('value');
+  }
+
+  function index(value) {
+    var match = this.detect(function(pair) {
+      return pair.value === value;
+    });
+    return match && match.key;
+  }
+
+  function merge(object) {
+    return this.clone().update(object);
+  }
+
+  function update(object) {
+    return new Hash(object).inject(this, function(result, pair) {
+      result.set(pair.key, pair.value);
+      return result;
+    });
+  }
+
+  function toQueryPair(key, value) {
+    if (Object.isUndefined(value)) return key;
+
+    value = String.interpret(value);
+
+    value = value.gsub(/(\r)?\n/, '\r\n');
+    value = encodeURIComponent(value);
+    value = value.gsub(/%20/, '+');
+    return key + '=' + value;
+  }
+
+  function toQueryString() {
+    return this.inject([], function(results, pair) {
+      var key = encodeURIComponent(pair.key), values = pair.value;
+
+      if (values && typeof values == 'object') {
+        if (Object.isArray(values)) {
+          var queryValues = [];
+          for (var i = 0, len = values.length, value; i < len; i++) {
+            value = values[i];
+            queryValues.push(toQueryPair(key, value));
+          }
+          return results.concat(queryValues);
+        }
+      } else results.push(toQueryPair(key, values));
+      return results;
+    }).join('&');
+  }
+
+  function inspect() {
+    return '#<Hash:{' + this.map(function(pair) {
+      return pair.map(Object.inspect).join(': ');
+    }).join(', ') + '}>';
+  }
+
+  function clone() {
+    return new Hash(this);
+  }
+
+  return {
+    initialize:             initialize,
+    _each:                  _each,
+    set:                    set,
+    get:                    get,
+    unset:                  unset,
+    toObject:               toObject,
+    toTemplateReplacements: toObject,
+    keys:                   keys,
+    values:                 values,
+    index:                  index,
+    merge:                  merge,
+    update:                 update,
+    toQueryString:          toQueryString,
+    inspect:                inspect,
+    toJSON:                 toObject,
+    clone:                  clone
+  };
+})());
+
+Hash.from = $H;
+Object.extend(Number.prototype, (function() {
+  function toColorPart() {
+    return this.toPaddedString(2, 16);
+  }
+
+  function succ() {
+    return this + 1;
+  }
+
+  function times(iterator, context) {
+    $R(0, this, true).each(iterator, context);
+    return this;
+  }
+
+  function toPaddedString(length, radix) {
+    var string = this.toString(radix || 10);
+    return '0'.times(length - string.length) + string;
+  }
+
+  function abs() {
+    return Math.abs(this);
+  }
+
+  function round() {
+    return Math.round(this);
+  }
+
+  function ceil() {
+    return Math.ceil(this);
+  }
+
+  function floor() {
+    return Math.floor(this);
+  }
+
+  return {
+    toColorPart:    toColorPart,
+    succ:           succ,
+    times:          times,
+    toPaddedString: toPaddedString,
+    abs:            abs,
+    round:          round,
+    ceil:           ceil,
+    floor:          floor
+  };
+})());
+
+function $R(start, end, exclusive) {
+  return new ObjectRange(start, end, exclusive);
+}
+
+var ObjectRange = Class.create(Enumerable, (function() {
+  function initialize(start, end, exclusive) {
+    this.start = start;
+    this.end = end;
+    this.exclusive = exclusive;
+  }
+
+  function _each(iterator, context) {
+    var value = this.start, i;
+    for (i = 0; this.include(value); i++) {
+      iterator.call(context, value, i);
+      value = value.succ();
+    }
+  }
+
+  function include(value) {
+    if (value < this.start)
+      return false;
+    if (this.exclusive)
+      return value < this.end;
+    return value <= this.end;
+  }
+
+  return {
+    initialize: initialize,
+    _each:      _each,
+    include:    include
+  };
+})());
+
+
+
+var Abstract = { };
+
+
+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;
+  }
+};
 
 var Ajax = {
   getTransport: function() {
     return Try.these(
+      function() {return new XMLHttpRequest()},
       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
-      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
-      function() {return new XMLHttpRequest()}
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
     ) || false;
-  }
-}
+  },
 
-Ajax.Base = function() {};
-Ajax.Base.prototype = {
-  setOptions: function(options) {
+  activeRequestCount: 0
+};
+
+Ajax.Responders = {
+  responders: [],
+
+  _each: function(iterator, context) {
+    this.responders._each(iterator, context);
+  },
+
+  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,
-      parameters:   ''
-    }
-    Object.extend(this.options, options || {});
-  },
+      contentType:  'application/x-www-form-urlencoded',
+      encoding:     'UTF-8',
+      parameters:   '',
+      evalJSON:     true,
+      evalJS:       true
+    };
+    Object.extend(this.options, options || { });
 
-  responseIsSuccess: function() {
-    return this.transport.status == undefined
-        || this.transport.status == 0 
-        || (this.transport.status >= 200 && this.transport.status < 300);
-  },
+    this.options.method = this.options.method.toLowerCase();
 
-  responseIsFailure: function() {
-    return !this.responseIsSuccess();
+    if (Object.isHash(this.options.parameters))
+      this.options.parameters = this.options.parameters.toObject();
   }
-}
+});
+Ajax.Request = Class.create(Ajax.Base, {
+  _complete: false,
 
-Ajax.Request = Class.create();
-Ajax.Request.Events = 
-  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
-
-Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
-  initialize: function(url, options) {
+  initialize: function($super, url, options) {
+    $super(options);
     this.transport = Ajax.getTransport();
-    this.setOptions(options);
     this.request(url);
   },
 
   request: function(url) {
-    var parameters = this.options.parameters || '';
-    if (parameters.length > 0) parameters += '&_=';
+    this.url = url;
+    this.method = this.options.method;
+    var params = Object.isString(this.options.parameters) ?
+          this.options.parameters :
+          Object.toQueryString(this.options.parameters);
+
+    if (!['get', 'post'].include(this.method)) {
+      params += (params ? '&' : '') + "_method=" + this.method;
+      this.method = 'post';
+    }
+
+    if (params && this.method === 'get') {
+      this.url += (this.url.include('?') ? '&' : '?') + params;
+    }
+
+    this.parameters = params.toQueryParams();
 
     try {
-      if (this.options.method == 'get')
-        url += '?' + parameters;
+      var response = new Ajax.Response(this);
+      if (this.options.onCreate) this.options.onCreate(response);
+      Ajax.Responders.dispatch('onCreate', this, response);
 
-      this.transport.open(this.options.method, url,
+      this.transport.open(this.method.toUpperCase(), this.url,
         this.options.asynchronous);
 
-      if (this.options.asynchronous) {
-        this.transport.onreadystatechange = this.onStateChange.bind(this);
-        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
-      }
+      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
 
+      this.transport.onreadystatechange = this.onStateChange.bind(this);
       this.setRequestHeaders();
 
-      var body = this.options.postBody ? this.options.postBody : parameters;
-      this.transport.send(this.options.method == 'post' ? body : null);
+      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+      this.transport.send(this.body);
 
-    } catch (e) {
+      /* Force Firefox to handle ready state 4 for synchronous requests */
+      if (!this.options.asynchronous && this.transport.overrideMimeType)
+        this.onStateChange();
+
     }
-  },
-
-  setRequestHeaders: function() {
-    var requestHeaders = 
-      ['X-Requested-With', 'XMLHttpRequest',
-       'X-Prototype-Version', Prototype.Version];
-
-    if (this.options.method == 'post') {
-      requestHeaders.push('Content-type', 
-        'application/x-www-form-urlencoded');
-
-      /* Force "Connection: close" for Mozilla browsers to work around
-       * a bug where XMLHttpReqeuest sends an incorrect Content-length
-       * header. See Mozilla Bugzilla #246651. 
-       */
-      if (this.transport.overrideMimeType)
-        requestHeaders.push('Connection', 'close');
+    catch (e) {
+      this.dispatchException(e);
     }
-
-    if (this.options.requestHeaders)
-      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
-
-    for (var i = 0; i < requestHeaders.length; i += 2)
-      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
   },
 
   onStateChange: function() {
     var readyState = this.transport.readyState;
-    if (readyState != 1)
+    if (readyState > 1 && !((readyState == 4) && this._complete))
       this.respondToReadyState(this.transport.readyState);
   },
 
-  respondToReadyState: function(readyState) {
-    var event = Ajax.Request.Events[readyState];
+  setRequestHeaders: function() {
+    var headers = {
+      'X-Requested-With': 'XMLHttpRequest',
+      'X-Prototype-Version': Prototype.Version,
+      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+    };
 
-    if (event == 'Complete')
-      (this.options['on' + this.transport.status]
-       || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
-       || Prototype.emptyFunction)(this.transport);
+    if (this.method == 'post') {
+      headers['Content-type'] = this.options.contentType +
+        (this.options.encoding ? '; charset=' + this.options.encoding : '');
 
-    (this.options['on' + event] || Prototype.emptyFunction)(this.transport);
-
-    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
-    if (event == 'Complete')
-      this.transport.onreadystatechange = Prototype.emptyFunction;
-  }
-});
-
-Ajax.Updater = Class.create();
-Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
-
-Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
-  initialize: function(container, url, options) {
-    this.containers = {
-      success: container.success ? $(container.success) : $(container),
-      failure: container.failure ? $(container.failure) :
-        (container.success ? null : $(container))
+      /* 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';
     }
 
-    this.transport = Ajax.getTransport();
-    this.setOptions(options);
+    if (typeof this.options.requestHeaders == 'object') {
+      var extras = this.options.requestHeaders;
 
-    var onComplete = this.options.onComplete || Prototype.emptyFunction;
-    this.options.onComplete = (function() {
-      this.updateContent();
-      onComplete(this.transport);
-    }).bind(this);
+      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 });
+    }
 
-    this.request(url);
+    for (var name in headers)
+      if (headers[name] != null)
+        this.transport.setRequestHeader(name, headers[name]);
   },
 
-  updateContent: function() {
-    var receiver = this.responseIsSuccess() ?
-      this.containers.success : this.containers.failure;
+  success: function() {
+    var status = this.getStatus();
+    return !status || (status >= 200 && status < 300) || status == 304;
+  },
 
-    var match    = new RegExp(Ajax.Updater.ScriptFragment, 'img');
-    var response = this.transport.responseText.replace(match, '');
-    var scripts  = this.transport.responseText.match(match);
+  getStatus: function() {
+    try {
+      if (this.transport.status === 1223) return 204;
+      return this.transport.status || 0;
+    } catch (e) { return 0 }
+  },
 
-    if (receiver) {
-      if (this.options.insertion) {
-        new this.options.insertion(receiver, response);
-      } else {
-        receiver.innerHTML = response;
+  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();
     }
 
-    if (this.responseIsSuccess()) {
-      if (this.onComplete)
-        setTimeout((function() {this.onComplete(
-          this.transport)}).bind(this), 10);
+    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 (this.options.evalScripts && scripts) {
-      match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
-      setTimeout((function() {
-        for (var i = 0; i < scripts.length; i++)
-          eval(scripts[i].match(match)[1]);
-      }).bind(this), 10);
+    if (state == 'Complete') {
+      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;
+
+    try {
+      json = decodeURIComponent(escape(json));
+    } catch(e) {
+    }
+
+    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.PeriodicalUpdater = Class.create();
-Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
-  initialize: function(container, url, options) {
-    this.setOptions(options);
+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 = 1;
+    this.decay = (this.options.decay || 1);
 
-    this.updater = {};
+    this.updater = { };
     this.container = container;
     this.url = url;
 
@@ -575,20 +2009,19 @@
   },
 
   stop: function() {
-    this.updater.onComplete = undefined;
+    this.updater.options.onComplete = undefined;
     clearTimeout(this.timer);
-    (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
+    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
   },
 
-  updateComplete: function(request) {
+  updateComplete: function(response) {
     if (this.options.decay) {
-      this.decay = (request.responseText == this.lastText ? 
+      this.decay = (response.responseText == this.lastText ?
         this.decay * this.options.decay : 1);
 
-      this.lastText = request.responseText;
+      this.lastText = response.responseText;
     }
-    this.timer = setTimeout(this.onTimerEvent.bind(this), 
-      this.decay * this.frequency * 1000);
+    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
   },
 
   onTimerEvent: function() {
@@ -596,391 +2029,4601 @@
   }
 });
 
-document.getElementsByClassName = function(className) {
-  var children = document.getElementsByTagName('*') || document.all;
-  var elements = new Array();
-  
-  for (var i = 0; i < children.length; i++) {
-    var child = children[i];
-    var classNames = child.className.split(' ');
-    for (var j = 0; j < classNames.length; j++) {
-      if (classNames[j] == className) {
-        elements.push(child);
-        break;
-      }
+(function(GLOBAL) {
+
+  var UNDEFINED;
+  var SLICE = Array.prototype.slice;
+
+  var DIV = document.createElement('div');
+
+
+  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);
   }
-  
-  return elements;
-}
 
-/*--------------------------------------------------------------------------*/
+  GLOBAL.$ = $;
 
-if (!window.Element) {
-  var Element = new Object();
-}
 
-Object.extend(Element, {
-  toggle: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 
-        (element.style.display == 'none' ? '' : 'none');
+  if (!GLOBAL.Node) GLOBAL.Node = {};
+
+  if (!GLOBAL.Node.ELEMENT_NODE) {
+    Object.extend(GLOBAL.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
+    });
+  }
+
+  var ELEMENT_CACHE = {};
+
+  function shouldUseCreationCache(tagName, attributes) {
+    if (tagName === 'select') return false;
+    if ('type' in attributes) return false;
+    return true;
+  }
+
+  var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
+    try {
+      var el = document.createElement('<input name="x">');
+      return el.tagName.toLowerCase() === 'input' && el.name === 'x';
     }
-  },
-
-  hide: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = 'none';
+    catch(err) {
+      return false;
     }
-  },
+  })();
 
-  show: function() {
-    for (var i = 0; i < arguments.length; i++) {
-      var element = $(arguments[i]);
-      element.style.display = '';
+
+  var oldElement = GLOBAL.Element;
+  function Element(tagName, attributes) {
+    attributes = attributes || {};
+    tagName = tagName.toLowerCase();
+
+    if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
+      tagName = '<' + tagName + ' name="' + attributes.name + '">';
+      delete attributes.name;
+      return Element.writeAttribute(document.createElement(tagName), attributes);
     }
-  },
 
-  remove: function(element) {
+    if (!ELEMENT_CACHE[tagName])
+      ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName));
+
+    var node = shouldUseCreationCache(tagName, attributes) ?
+     ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName);
+
+    return Element.writeAttribute(node, attributes);
+  }
+
+  GLOBAL.Element = Element;
+
+  Object.extend(GLOBAL.Element, oldElement || {});
+  if (oldElement) GLOBAL.Element.prototype = oldElement.prototype;
+
+  Element.Methods = { ByTag: {}, Simulated: {} };
+
+  var methods = {};
+
+  var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' };
+  function inspect(element) {
+    element = $(element);
+    var result = '<' + element.tagName.toLowerCase();
+
+    var attribute, value;
+    for (var property in INSPECT_ATTRIBUTES) {
+      attribute = INSPECT_ATTRIBUTES[property];
+      value = (element[property] || '').toString();
+      if (value) result += ' ' + attribute + '=' + value.inspect(true);
+    }
+
+    return result + '>';
+  }
+
+  methods.inspect = inspect;
+
+
+  function visible(element) {
+    return $(element).getStyle('display') !== 'none';
+  }
+
+  function toggle(element, bool) {
+    element = $(element);
+    if (typeof bool !== 'boolean')
+      bool = !Element.visible(element);
+    Element[bool ? 'show' : 'hide'](element);
+
+    return element;
+  }
+
+  function hide(element) {
+    element = $(element);
+    element.style.display = 'none';
+    return element;
+  }
+
+  function show(element) {
+    element = $(element);
+    element.style.display = '';
+    return element;
+  }
+
+
+  Object.extend(methods, {
+    visible: visible,
+    toggle:  toggle,
+    hide:    hide,
+    show:    show
+  });
+
+
+  function remove(element) {
     element = $(element);
     element.parentNode.removeChild(element);
-  },
-   
-  getHeight: function(element) {
-    element = $(element);
-    return element.offsetHeight; 
-  },
+    return element;
+  }
 
-  hasClassName: function(element, className) {
-    element = $(element);
-    if (!element)
-      return;
-    var a = element.className.split(' ');
-    for (var i = 0; i < a.length; i++) {
-      if (a[i] == className)
-        return true;
+  var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
+    var el = document.createElement("select"),
+        isBuggy = true;
+    el.innerHTML = "<option value=\"test\">test</option>";
+    if (el.options && el.options[0]) {
+      isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
     }
+    el = null;
+    return isBuggy;
+  })();
+
+  var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
+    try {
+      var el = document.createElement("table");
+      if (el && el.tBodies) {
+        el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
+        var isBuggy = typeof el.tBodies[0] == "undefined";
+        el = null;
+        return isBuggy;
+      }
+    } catch (e) {
+      return true;
+    }
+  })();
+
+  var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
+    try {
+      var el = document.createElement('div');
+      el.innerHTML = "<link />";
+      var isBuggy = (el.childNodes.length === 0);
+      el = null;
+      return isBuggy;
+    } catch(e) {
+      return true;
+    }
+  })();
+
+  var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
+   TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
+
+  var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
+    var s = document.createElement("script"),
+        isBuggy = false;
+    try {
+      s.appendChild(document.createTextNode(""));
+      isBuggy = !s.firstChild ||
+        s.firstChild && s.firstChild.nodeType !== 3;
+    } catch (e) {
+      isBuggy = true;
+    }
+    s = null;
+    return isBuggy;
+  })();
+
+  function update(element, content) {
+    element = $(element);
+
+    var descendants = element.getElementsByTagName('*'),
+     i = descendants.length;
+    while (i--) purgeElement(descendants[i]);
+
+    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 === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
+      element.text = content;
+      return element;
+    }
+
+    if (ANY_INNERHTML_BUGGY) {
+      if (tagName in INSERTION_TRANSLATIONS.tags) {
+        while (element.firstChild)
+          element.removeChild(element.firstChild);
+
+        var nodes = getContentFromAnonymousElement(tagName, content.stripScripts());
+        for (var i = 0, node; node = nodes[i]; i++)
+          element.appendChild(node);
+
+      } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
+        while (element.firstChild)
+          element.removeChild(element.firstChild);
+
+        var nodes = getContentFromAnonymousElement(tagName,
+         content.stripScripts(), true);
+
+        for (var i = 0, node; node = nodes[i]; i++)
+          element.appendChild(node);
+      } else {
+        element.innerHTML = content.stripScripts();
+      }
+    } else {
+      element.innerHTML = content.stripScripts();
+    }
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  }
+
+  function replace(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;
+  }
+
+  var INSERTION_TRANSLATIONS = {
+    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]
+    }
+  };
+
+  var tags = INSERTION_TRANSLATIONS.tags;
+
+  Object.extend(tags, {
+    THEAD: tags.TBODY,
+    TFOOT: tags.TBODY,
+    TH:    tags.TD
+  });
+
+  function replace_IE(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 (tagName in INSERTION_TRANSLATIONS.tags) {
+      var nextSibling = Element.next(element);
+      var fragments = getContentFromAnonymousElement(
+       tagName, content.stripScripts());
+
+      parent.removeChild(element);
+
+      var iterator;
+      if (nextSibling)
+        iterator = function(node) { parent.insertBefore(node, nextSibling) };
+      else
+        iterator = function(node) { parent.appendChild(node); }
+
+      fragments.each(iterator);
+    } else {
+      element.outerHTML = content.stripScripts();
+    }
+
+    content.evalScripts.bind(content).defer();
+    return element;
+  }
+
+  if ('outerHTML' in document.documentElement)
+    replace = replace_IE;
+
+  function isContent(content) {
+    if (Object.isUndefined(content) || content === null) return false;
+
+    if (Object.isString(content) || Object.isNumber(content)) return true;
+    if (Object.isElement(content)) return true;
+    if (content.toElement || content.toHTML) return true;
+
     return false;
-  },
+  }
 
-  addClassName: function(element, className) {
-    element = $(element);
-    Element.removeClassName(element, className);
-    element.className += ' ' + className;
-  },
+  function insertContentAt(element, content, position) {
+    position   = position.toLowerCase();
+    var method = INSERTION_TRANSLATIONS[position];
 
-  removeClassName: function(element, className) {
+    if (content && content.toElement) content = content.toElement();
+    if (Object.isElement(content)) {
+      method(element, content);
+      return element;
+    }
+
+    content = Object.toHTML(content);
+    var tagName = ((position === 'before' || position === 'after') ?
+     element.parentNode : element).tagName.toUpperCase();
+
+    var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts());
+
+    if (position === 'top' || position === 'after') childNodes.reverse();
+
+    for (var i = 0, node; node = childNodes[i]; i++)
+      method(element, node);
+
+    content.evalScripts.bind(content).defer();
+  }
+
+  function insert(element, insertions) {
     element = $(element);
-    if (!element)
-      return;
-    var newClassName = '';
-    var a = element.className.split(' ');
-    for (var i = 0; i < a.length; i++) {
-      if (a[i] != className) {
-        if (i > 0)
-          newClassName += ' ';
-        newClassName += a[i];
+
+    if (isContent(insertions))
+      insertions = { bottom: insertions };
+
+    for (var position in insertions)
+      insertContentAt(element, insertions[position], position);
+
+    return element;
+  }
+
+  function wrap(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;
+  }
+
+  function cleanWhitespace(element) {
+    element = $(element);
+    var node = element.firstChild;
+
+    while (node) {
+      var nextNode = node.nextSibling;
+      if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue))
+        element.removeChild(node);
+      node = nextNode;
+    }
+    return element;
+  }
+
+  function empty(element) {
+    return $(element).innerHTML.blank();
+  }
+
+  function getContentFromAnonymousElement(tagName, html, force) {
+    var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV;
+
+    var workaround = !!t;
+    if (!workaround && force) {
+      workaround = true;
+      t = ['', '', 0];
+    }
+
+    if (workaround) {
+      div.innerHTML = '&#160;' + t[0] + html + t[1];
+      div.removeChild(div.firstChild);
+      for (var i = t[2]; i--; )
+        div = div.firstChild;
+    } else {
+      div.innerHTML = html;
+    }
+
+    return $A(div.childNodes);
+  }
+
+  function clone(element, deep) {
+    if (!(element = $(element))) return;
+    var clone = element.cloneNode(deep);
+    if (!HAS_UNIQUE_ID_PROPERTY) {
+      clone._prototypeUID = UNDEFINED;
+      if (deep) {
+        var descendants = Element.select(clone, '*'),
+         i = descendants.length;
+        while (i--)
+          descendants[i]._prototypeUID = UNDEFINED;
       }
     }
-    element.className = newClassName;
-  },
-  
-  // removes whitespace-only text node children
-  cleanWhitespace: function(element) {
-    var element = $(element);
-    for (var i = 0; i < element.childNodes.length; i++) {
-      var node = element.childNodes[i];
-      if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) 
-        Element.remove(node);
+    return Element.extend(clone);
+  }
+
+  function purgeElement(element) {
+    var uid = getUniqueElementID(element);
+    if (uid) {
+      Element.stopObserving(element);
+      if (!HAS_UNIQUE_ID_PROPERTY)
+        element._prototypeUID = UNDEFINED;
+      delete Element.Storage[uid];
     }
   }
-});
 
-var Toggle = new Object();
-Toggle.display = Element.toggle;
+  function purgeCollection(elements) {
+    var i = elements.length;
+    while (i--)
+      purgeElement(elements[i]);
+  }
 
-/*--------------------------------------------------------------------------*/
+  function purgeCollection_IE(elements) {
+    var i = elements.length, element, uid;
+    while (i--) {
+      element = elements[i];
+      uid = getUniqueElementID(element);
+      delete Element.Storage[uid];
+      delete Event.cache[uid];
+    }
+  }
 
-Abstract.Insertion = function(adjacency) {
-  this.adjacency = adjacency;
-}
+  if (HAS_UNIQUE_ID_PROPERTY) {
+    purgeCollection = purgeCollection_IE;
+  }
 
-Abstract.Insertion.prototype = {
-  initialize: function(element, content) {
-    this.element = $(element);
-    this.content = content;
-    
-    if (this.adjacency && this.element.insertAdjacentHTML) {
-      this.element.insertAdjacentHTML(this.adjacency, this.content);
+
+  function purge(element) {
+    if (!(element = $(element))) return;
+    purgeElement(element);
+
+    var descendants = element.getElementsByTagName('*'),
+     i = descendants.length;
+
+    while (i--) purgeElement(descendants[i]);
+
+    return null;
+  }
+
+  Object.extend(methods, {
+    remove:  remove,
+    update:  update,
+    replace: replace,
+    insert:  insert,
+    wrap:    wrap,
+    cleanWhitespace: cleanWhitespace,
+    empty:   empty,
+    clone:   clone,
+    purge:   purge
+  });
+
+
+
+  function recursivelyCollect(element, property, maximumLength) {
+    element = $(element);
+    maximumLength = maximumLength || -1;
+    var elements = [];
+
+    while (element = element[property]) {
+      if (element.nodeType === Node.ELEMENT_NODE)
+        elements.push(Element.extend(element));
+
+      if (elements.length === maximumLength) break;
+    }
+
+    return elements;
+  }
+
+
+  function ancestors(element) {
+    return recursivelyCollect(element, 'parentNode');
+  }
+
+  function descendants(element) {
+    return Element.select(element, '*');
+  }
+
+  function firstDescendant(element) {
+    element = $(element).firstChild;
+    while (element && element.nodeType !== Node.ELEMENT_NODE)
+      element = element.nextSibling;
+
+    return $(element);
+  }
+
+  function immediateDescendants(element) {
+    var results = [], child = $(element).firstChild;
+
+    while (child) {
+      if (child.nodeType === Node.ELEMENT_NODE)
+        results.push(Element.extend(child));
+
+      child = child.nextSibling;
+    }
+
+    return results;
+  }
+
+  function previousSiblings(element) {
+    return recursivelyCollect(element, 'previousSibling');
+  }
+
+  function nextSiblings(element) {
+    return recursivelyCollect(element, 'nextSibling');
+  }
+
+  function siblings(element) {
+    element = $(element);
+    var previous = previousSiblings(element),
+     next = nextSiblings(element);
+    return previous.reverse().concat(next);
+  }
+
+  function match(element, selector) {
+    element = $(element);
+
+    if (Object.isString(selector))
+      return Prototype.Selector.match(element, selector);
+
+    return selector.match(element);
+  }
+
+
+  function _recursivelyFind(element, property, expression, index) {
+    element = $(element), expression = expression || 0, index = index || 0;
+    if (Object.isNumber(expression)) {
+      index = expression, expression = null;
+    }
+
+    while (element = element[property]) {
+      if (element.nodeType !== 1) continue;
+      if (expression && !Prototype.Selector.match(element, expression))
+        continue;
+      if (--index >= 0) continue;
+
+      return Element.extend(element);
+    }
+  }
+
+
+  function up(element, expression, index) {
+    element = $(element);
+
+    if (arguments.length === 1) return $(element.parentNode);
+    return _recursivelyFind(element, 'parentNode', expression, index);
+  }
+
+  function down(element, expression, index) {
+    if (arguments.length === 1) return firstDescendant(element);
+    element = $(element), expression = expression || 0, index = index || 0;
+
+    if (Object.isNumber(expression))
+      index = expression, expression = '*';
+
+    var node = Prototype.Selector.select(expression, element)[index];
+    return Element.extend(node);
+  }
+
+  function previous(element, expression, index) {
+    return _recursivelyFind(element, 'previousSibling', expression, index);
+  }
+
+  function next(element, expression, index) {
+    return _recursivelyFind(element, 'nextSibling', expression, index);
+  }
+
+  function select(element) {
+    element = $(element);
+    var expressions = SLICE.call(arguments, 1).join(', ');
+    return Prototype.Selector.select(expressions, element);
+  }
+
+  function adjacent(element) {
+    element = $(element);
+    var expressions = SLICE.call(arguments, 1).join(', ');
+    var siblings = Element.siblings(element), results = [];
+    for (var i = 0, sibling; sibling = siblings[i]; i++) {
+      if (Prototype.Selector.match(sibling, expressions))
+        results.push(sibling);
+    }
+
+    return results;
+  }
+
+  function descendantOf_DOM(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    if (!element || !ancestor) return false;
+    while (element = element.parentNode)
+      if (element === ancestor) return true;
+    return false;
+  }
+
+  function descendantOf_contains(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    if (!element || !ancestor) return false;
+    if (!ancestor.contains) return descendantOf_DOM(element, ancestor);
+    return ancestor.contains(element) && ancestor !== element;
+  }
+
+  function descendantOf_compareDocumentPosition(element, ancestor) {
+    element = $(element), ancestor = $(ancestor);
+    if (!element || !ancestor) return false;
+    return (element.compareDocumentPosition(ancestor) & 8) === 8;
+  }
+
+  var descendantOf;
+  if (DIV.compareDocumentPosition) {
+    descendantOf = descendantOf_compareDocumentPosition;
+  } else if (DIV.contains) {
+    descendantOf = descendantOf_contains;
+  } else {
+    descendantOf = descendantOf_DOM;
+  }
+
+
+  Object.extend(methods, {
+    recursivelyCollect:   recursivelyCollect,
+    ancestors:            ancestors,
+    descendants:          descendants,
+    firstDescendant:      firstDescendant,
+    immediateDescendants: immediateDescendants,
+    previousSiblings:     previousSiblings,
+    nextSiblings:         nextSiblings,
+    siblings:             siblings,
+    match:                match,
+    up:                   up,
+    down:                 down,
+    previous:             previous,
+    next:                 next,
+    select:               select,
+    adjacent:             adjacent,
+    descendantOf:         descendantOf,
+
+    getElementsBySelector: select,
+
+    childElements:         immediateDescendants
+  });
+
+
+  var idCounter = 1;
+  function identify(element) {
+    element = $(element);
+    var id = Element.readAttribute(element, 'id');
+    if (id) return id;
+
+    do { id = 'anonymous_element_' + idCounter++ } while ($(id));
+
+    Element.writeAttribute(element, 'id', id);
+    return id;
+  }
+
+
+  function readAttribute(element, name) {
+    return $(element).getAttribute(name);
+  }
+
+  function readAttribute_IE(element, name) {
+    element = $(element);
+
+    var table = ATTRIBUTE_TRANSLATIONS.read;
+    if (table.values[name])
+      return table.values[name](element, name);
+
+    if (table.names[name]) name = table.names[name];
+
+    if (name.include(':')) {
+      if (!element.attributes || !element.attributes[name]) return null;
+      return element.attributes[name].value;
+    }
+
+    return element.getAttribute(name);
+  }
+
+  function readAttribute_Opera(element, name) {
+    if (name === 'title') return element.title;
+    return element.getAttribute(name);
+  }
+
+  var PROBLEMATIC_ATTRIBUTE_READING = (function() {
+    DIV.setAttribute('onclick', []);
+    var value = DIV.getAttribute('onclick');
+    var isFunction = Object.isArray(value);
+    DIV.removeAttribute('onclick');
+    return isFunction;
+  })();
+
+  if (PROBLEMATIC_ATTRIBUTE_READING) {
+    readAttribute = readAttribute_IE;
+  } else if (Prototype.Browser.Opera) {
+    readAttribute = readAttribute_Opera;
+  }
+
+
+  function writeAttribute(element, name, value) {
+    element = $(element);
+    var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write;
+
+    if (typeof name === 'object') {
+      attributes = name;
     } else {
-      this.range = this.element.ownerDocument.createRange();
-      if (this.initializeRange) this.initializeRange();
-      this.fragment = this.range.createContextualFragment(this.content);
-      this.insertContent();
+      attributes[name] = Object.isUndefined(value) ? true : value;
+    }
+
+    for (var attr in attributes) {
+      name = table.names[attr] || attr;
+      value = attributes[attr];
+      if (table.values[attr]) {
+        value = table.values[attr](element, value);
+        if (Object.isUndefined(value)) continue;
+      }
+      if (value === false || value === null)
+        element.removeAttribute(name);
+      else if (value === true)
+        element.setAttribute(name, name);
+      else element.setAttribute(name, value);
+    }
+
+    return element;
+  }
+
+  var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () {
+    if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) {
+      return false;
+    }
+    var checkbox = document.createElement('<input type="checkbox">');
+    checkbox.checked = true;
+    var node = checkbox.getAttributeNode('checked');
+    return !node || !node.specified;
+  })();
+
+  function hasAttribute(element, attribute) {
+    attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute;
+    var node = $(element).getAttributeNode(attribute);
+    return !!(node && node.specified);
+  }
+
+  function hasAttribute_IE(element, attribute) {
+    if (attribute === 'checked') {
+      return element.checked;
+    }
+    return hasAttribute(element, attribute);
+  }
+
+  GLOBAL.Element.Methods.Simulated.hasAttribute =
+   PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ?
+   hasAttribute_IE : hasAttribute;
+
+  function classNames(element) {
+    return new Element.ClassNames(element);
+  }
+
+  var regExpCache = {};
+  function getRegExpForClassName(className) {
+    if (regExpCache[className]) return regExpCache[className];
+
+    var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)");
+    regExpCache[className] = re;
+    return re;
+  }
+
+  function hasClassName(element, className) {
+    if (!(element = $(element))) return;
+
+    var elementClassName = element.className;
+
+    if (elementClassName.length === 0) return false;
+    if (elementClassName === className) return true;
+
+    return getRegExpForClassName(className).test(elementClassName);
+  }
+
+  function addClassName(element, className) {
+    if (!(element = $(element))) return;
+
+    if (!hasClassName(element, className))
+      element.className += (element.className ? ' ' : '') + className;
+
+    return element;
+  }
+
+  function removeClassName(element, className) {
+    if (!(element = $(element))) return;
+
+    element.className = element.className.replace(
+     getRegExpForClassName(className), ' ').strip();
+
+    return element;
+  }
+
+  function toggleClassName(element, className, bool) {
+    if (!(element = $(element))) return;
+
+    if (Object.isUndefined(bool))
+      bool = !hasClassName(element, className);
+
+    var method = Element[bool ? 'addClassName' : 'removeClassName'];
+    return method(element, className);
+  }
+
+  var ATTRIBUTE_TRANSLATIONS = {};
+
+  var classProp = 'className', forProp = 'for';
+
+  DIV.setAttribute(classProp, 'x');
+  if (DIV.className !== 'x') {
+    DIV.setAttribute('class', 'x');
+    if (DIV.className === 'x')
+      classProp = 'class';
+  }
+
+  var LABEL = document.createElement('label');
+  LABEL.setAttribute(forProp, 'x');
+  if (LABEL.htmlFor !== 'x') {
+    LABEL.setAttribute('htmlFor', 'x');
+    if (LABEL.htmlFor === 'x')
+      forProp = 'htmlFor';
+  }
+  LABEL = null;
+
+  function _getAttr(element, attribute) {
+    return element.getAttribute(attribute);
+  }
+
+  function _getAttr2(element, attribute) {
+    return element.getAttribute(attribute, 2);
+  }
+
+  function _getAttrNode(element, attribute) {
+    var node = element.getAttributeNode(attribute);
+    return node ? node.value : '';
+  }
+
+  function _getFlag(element, attribute) {
+    return $(element).hasAttribute(attribute) ? attribute : null;
+  }
+
+  DIV.onclick = Prototype.emptyFunction;
+  var onclickValue = DIV.getAttribute('onclick');
+
+  var _getEv;
+
+  if (String(onclickValue).indexOf('{') > -1) {
+    _getEv = function(element, attribute) {
+      var value = element.getAttribute(attribute);
+      if (!value) return null;
+      value = value.toString();
+      value = value.split('{')[1];
+      value = value.split('}')[0];
+      return value.strip();
+    };
+  }
+  else if (onclickValue === '') {
+    _getEv = function(element, attribute) {
+      var value = element.getAttribute(attribute);
+      if (!value) return null;
+      return value.strip();
+    };
+  }
+
+  ATTRIBUTE_TRANSLATIONS.read = {
+    names: {
+      'class':     classProp,
+      'className': classProp,
+      'for':       forProp,
+      'htmlFor':   forProp
+    },
+
+    values: {
+      style: function(element) {
+        return element.style.cssText.toLowerCase();
+      },
+      title: function(element) {
+        return element.title;
+      }
+    }
+  };
+
+  ATTRIBUTE_TRANSLATIONS.write = {
+    names: {
+      className:   'class',
+      htmlFor:     'for',
+      cellpadding: 'cellPadding',
+      cellspacing: 'cellSpacing'
+    },
+
+    values: {
+      checked: function(element, value) {
+        value = !!value;
+        element.checked = value;
+        return value ? 'checked' : null;
+      },
+
+      style: function(element, value) {
+        element.style.cssText = value ? value : '';
+      }
+    }
+  };
+
+  ATTRIBUTE_TRANSLATIONS.has = { names: {} };
+
+  Object.extend(ATTRIBUTE_TRANSLATIONS.write.names,
+   ATTRIBUTE_TRANSLATIONS.read.names);
+
+  var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' +
+   'accessKey tabIndex encType maxLength readOnly longDesc frameBorder');
+
+  for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) {
+    ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr;
+    ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()]   = attr;
+  }
+
+  Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, {
+    href:        _getAttr2,
+    src:         _getAttr2,
+    type:        _getAttr,
+    action:      _getAttrNode,
+    disabled:    _getFlag,
+    checked:     _getFlag,
+    readonly:    _getFlag,
+    multiple:    _getFlag,
+    onload:      _getEv,
+    onunload:    _getEv,
+    onclick:     _getEv,
+    ondblclick:  _getEv,
+    onmousedown: _getEv,
+    onmouseup:   _getEv,
+    onmouseover: _getEv,
+    onmousemove: _getEv,
+    onmouseout:  _getEv,
+    onfocus:     _getEv,
+    onblur:      _getEv,
+    onkeypress:  _getEv,
+    onkeydown:   _getEv,
+    onkeyup:     _getEv,
+    onsubmit:    _getEv,
+    onreset:     _getEv,
+    onselect:    _getEv,
+    onchange:    _getEv
+  });
+
+
+  Object.extend(methods, {
+    identify:        identify,
+    readAttribute:   readAttribute,
+    writeAttribute:  writeAttribute,
+    classNames:      classNames,
+    hasClassName:    hasClassName,
+    addClassName:    addClassName,
+    removeClassName: removeClassName,
+    toggleClassName: toggleClassName
+  });
+
+
+  function normalizeStyleName(style) {
+    if (style === 'float' || style === 'styleFloat')
+      return 'cssFloat';
+    return style.camelize();
+  }
+
+  function normalizeStyleName_IE(style) {
+    if (style === 'float' || style === 'cssFloat')
+      return 'styleFloat';
+    return style.camelize();
+  }
+
+  function setStyle(element, styles) {
+    element = $(element);
+    var elementStyle = element.style, match;
+
+    if (Object.isString(styles)) {
+      elementStyle.cssText += ';' + styles;
+      if (styles.include('opacity')) {
+        var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1];
+        Element.setOpacity(element, opacity);
+      }
+      return element;
+    }
+
+    for (var property in styles) {
+      if (property === 'opacity') {
+        Element.setOpacity(element, styles[property]);
+      } else {
+        var value = styles[property];
+        if (property === 'float' || property === 'cssFloat') {
+          property = Object.isUndefined(elementStyle.styleFloat) ?
+           'cssFloat' : 'styleFloat';
+        }
+        elementStyle[property] = value;
+      }
+    }
+
+    return element;
+  }
+
+
+  function getStyle(element, style) {
+    element = $(element);
+    style = normalizeStyleName(style);
+
+    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;
+  }
+
+  function getStyle_Opera(element, style) {
+    switch (style) {
+      case 'height': case 'width':
+        if (!Element.visible(element)) return null;
+
+        var dim = parseInt(getStyle(element, style), 10);
+
+        if (dim !== element['offset' + style.capitalize()])
+          return dim + 'px';
+
+        return Element.measure(element, style);
+
+      default: return getStyle(element, style);
     }
   }
-}
 
-var Insertion = new Object();
+  function getStyle_IE(element, style) {
+    element = $(element);
+    style = normalizeStyleName_IE(style);
 
-Insertion.Before = Class.create();
-Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
-  initializeRange: function() {
-    this.range.setStartBefore(this.element);
-  },
-  
-  insertContent: function() {
-    this.element.parentNode.insertBefore(this.fragment, this.element);
+    var value = element.style[style];
+    if (!value && element.currentStyle) {
+      value = element.currentStyle[style];
+    }
+
+    if (style === 'opacity') {
+      if (!STANDARD_CSS_OPACITY_SUPPORTED)
+        return getOpacity_IE(element);
+      else return value ? parseFloat(value) : 1.0;
+    }
+
+    if (value === 'auto') {
+      if ((style === 'width' || style === 'height') && Element.visible(element))
+        return Element.measure(element, style) + 'px';
+      return null;
+    }
+
+    return value;
   }
-});
 
-Insertion.Top = Class.create();
-Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
-  initializeRange: function() {
-    this.range.selectNodeContents(this.element);
-    this.range.collapse(true);
-  },
-  
-  insertContent: function() {  
-    this.element.insertBefore(this.fragment, this.element.firstChild);
+  function stripAlphaFromFilter_IE(filter) {
+    return (filter || '').replace(/alpha\([^\)]*\)/gi, '');
   }
-});
 
-Insertion.Bottom = Class.create();
-Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
-  initializeRange: function() {
-    this.range.selectNodeContents(this.element);
-    this.range.collapse(this.element);
-  },
-  
-  insertContent: function() {
-    this.element.appendChild(this.fragment);
+  function hasLayout_IE(element) {
+    if (!element.currentStyle || !element.currentStyle.hasLayout)
+      element.style.zoom = 1;
+    return element;
   }
-});
 
-Insertion.After = Class.create();
-Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
-  initializeRange: function() {
-    this.range.setStartAfter(this.element);
-  },
-  
-  insertContent: function() {
-    this.element.parentNode.insertBefore(this.fragment, 
-      this.element.nextSibling);
+  var STANDARD_CSS_OPACITY_SUPPORTED = (function() {
+    DIV.style.cssText = "opacity:.55";
+    return /^0.55/.test(DIV.style.opacity);
+  })();
+
+  function setOpacity(element, value) {
+    element = $(element);
+    if (value == 1 || value === '') value = '';
+    else if (value < 0.00001) value = 0;
+    element.style.opacity = value;
+    return element;
   }
-});
 
-var Field = {
-  clear: function() {
-    for (var i = 0; i < arguments.length; i++)
-      $(arguments[i]).value = '';
-  },
+  function setOpacity_IE(element, value) {
+    if (STANDARD_CSS_OPACITY_SUPPORTED)
+      return setOpacity(element, value);
 
-  focus: function(element) {
-    $(element).focus();
-  },
-  
-  present: function() {
-    for (var i = 0; i < arguments.length; i++)
-      if ($(arguments[i]).value == '') return false;
+    element = hasLayout_IE($(element));
+    var filter = Element.getStyle(element, 'filter'),
+     style = element.style;
+
+    if (value == 1 || value === '') {
+      filter = stripAlphaFromFilter_IE(filter);
+      if (filter) style.filter = filter;
+      else style.removeAttribute('filter');
+      return element;
+    }
+
+    if (value < 0.00001) value = 0;
+
+    style.filter = stripAlphaFromFilter_IE(filter) +
+     ' alpha(opacity=' + (value * 100) + ')';
+
+    return element;
+  }
+
+
+  function getOpacity(element) {
+    return Element.getStyle(element, 'opacity');
+  }
+
+  function getOpacity_IE(element) {
+    if (STANDARD_CSS_OPACITY_SUPPORTED)
+      return getOpacity(element);
+
+    var filter = Element.getStyle(element, 'filter');
+    if (filter.length === 0) return 1.0;
+    var match = (filter || '').match(/alpha\(opacity=(.*)\)/i);
+    if (match && match[1]) return parseFloat(match[1]) / 100;
+    return 1.0;
+  }
+
+
+  Object.extend(methods, {
+    setStyle:   setStyle,
+    getStyle:   getStyle,
+    setOpacity: setOpacity,
+    getOpacity: getOpacity
+  });
+
+  if ('styleFloat' in DIV.style) {
+    methods.getStyle = getStyle_IE;
+    methods.setOpacity = setOpacity_IE;
+    methods.getOpacity = getOpacity_IE;
+  }
+
+  var UID = 0;
+
+  GLOBAL.Element.Storage = { UID: 1 };
+
+  function getUniqueElementID(element) {
+    if (element === window) return 0;
+
+    if (typeof element._prototypeUID === 'undefined')
+      element._prototypeUID = Element.Storage.UID++;
+    return element._prototypeUID;
+  }
+
+  function getUniqueElementID_IE(element) {
+    if (element === window) return 0;
+    if (element == document) return 1;
+    return element.uniqueID;
+  }
+
+  var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV);
+  if (HAS_UNIQUE_ID_PROPERTY)
+    getUniqueElementID = getUniqueElementID_IE;
+
+  function getStorage(element) {
+    if (!(element = $(element))) return;
+
+    var uid = getUniqueElementID(element);
+
+    if (!Element.Storage[uid])
+      Element.Storage[uid] = $H();
+
+    return Element.Storage[uid];
+  }
+
+  function store(element, key, value) {
+    if (!(element = $(element))) return;
+    var storage = getStorage(element);
+    if (arguments.length === 2) {
+      storage.update(key);
+    } else {
+      storage.set(key, value);
+    }
+    return element;
+  }
+
+  function retrieve(element, key, defaultValue) {
+    if (!(element = $(element))) return;
+    var storage = getStorage(element), value = storage.get(key);
+
+    if (Object.isUndefined(value)) {
+      storage.set(key, defaultValue);
+      value = defaultValue;
+    }
+
+    return value;
+  }
+
+
+  Object.extend(methods, {
+    getStorage: getStorage,
+    store:      store,
+    retrieve:   retrieve
+  });
+
+
+  var Methods = {}, ByTag = Element.Methods.ByTag,
+   F = Prototype.BrowserFeatures;
+
+  if (!F.ElementExtensions && ('__proto__' in DIV)) {
+    GLOBAL.HTMLElement = {};
+    GLOBAL.HTMLElement.prototype = DIV['__proto__'];
+    F.ElementExtensions = true;
+  }
+
+  function checkElementPrototypeDeficiency(tagName) {
+    if (typeof window.Element === 'undefined') return false;
+    if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false;
+    var proto = window.Element.prototype;
+    if (proto) {
+      var id = '_' + (Math.random() + '').slice(2),
+       el = document.createElement(tagName);
+      proto[id] = 'x';
+      var isBuggy = (el[id] !== 'x');
+      delete proto[id];
+      el = null;
+      return isBuggy;
+    }
+
+    return false;
+  }
+
+  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY =
+   checkElementPrototypeDeficiency('object');
+
+  function extendElementWith(element, methods) {
+    for (var property in methods) {
+      var value = methods[property];
+      if (Object.isFunction(value) && !(property in element))
+        element[property] = value.methodize();
+    }
+  }
+
+  var EXTENDED = {};
+  function elementIsExtended(element) {
+    var uid = getUniqueElementID(element);
+    return (uid in EXTENDED);
+  }
+
+  function extend(element) {
+    if (!element || elementIsExtended(element)) return element;
+    if (element.nodeType !== Node.ELEMENT_NODE || element == window)
+      return element;
+
+    var methods = Object.clone(Methods),
+     tagName = element.tagName.toUpperCase();
+
+    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+    extendElementWith(element, methods);
+    EXTENDED[getUniqueElementID(element)] = true;
+    return element;
+  }
+
+  function extend_IE8(element) {
+    if (!element || elementIsExtended(element)) return element;
+
+    var t = element.tagName;
+    if (t && (/^(?:object|applet|embed)$/i.test(t))) {
+      extendElementWith(element, Element.Methods);
+      extendElementWith(element, Element.Methods.Simulated);
+      extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
+    }
+
+    return element;
+  }
+
+  if (F.SpecificElementExtensions) {
+    extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K;
+  }
+
+  function addMethodsToTagName(tagName, methods) {
+    tagName = tagName.toUpperCase();
+    if (!ByTag[tagName]) ByTag[tagName] = {};
+    Object.extend(ByTag[tagName], methods);
+  }
+
+  function mergeMethods(destination, methods, onlyIfAbsent) {
+    if (Object.isUndefined(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];
+
+    var element = document.createElement(tagName),
+     proto = element['__proto__'] || element.constructor.prototype;
+
+    element = null;
+    return proto;
+  }
+
+  function addMethods(methods) {
+    if (arguments.length === 0) addFormMethods();
+
+    if (arguments.length === 2) {
+      var tagName = methods;
+      methods = arguments[1];
+    }
+
+    if (!tagName) {
+      Object.extend(Element.Methods, methods || {});
+    } else {
+      if (Object.isArray(tagName)) {
+        for (var i = 0, tag; tag = tagName[i]; i++)
+          addMethodsToTagName(tag, methods);
+      } else {
+        addMethodsToTagName(tagName, methods);
+      }
+    }
+
+    var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype :
+     Element.prototype;
+
+    if (F.ElementExtensions) {
+      mergeMethods(ELEMENT_PROTOTYPE, Element.Methods);
+      mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true);
+    }
+
+    if (F.SpecificElementExtensions) {
+      for (var tag in Element.Methods.ByTag) {
+        var klass = findDOMClass(tag);
+        if (Object.isUndefined(klass)) continue;
+        mergeMethods(klass.prototype, ByTag[tag]);
+      }
+    }
+
+    Object.extend(Element, Element.Methods);
+    Object.extend(Element, Element.Methods.Simulated);
+    delete Element.ByTag;
+    delete Element.Simulated;
+
+    Element.extend.refresh();
+
+    ELEMENT_CACHE = {};
+  }
+
+  Object.extend(GLOBAL.Element, {
+    extend:     extend,
+    addMethods: addMethods
+  });
+
+  if (extend === Prototype.K) {
+    GLOBAL.Element.extend.refresh = Prototype.emptyFunction;
+  } else {
+    GLOBAL.Element.extend.refresh = function() {
+      if (Prototype.BrowserFeatures.ElementExtensions) return;
+      Object.extend(Methods, Element.Methods);
+      Object.extend(Methods, Element.Methods.Simulated);
+
+      EXTENDED = {};
+    };
+  }
+
+  function addFormMethods() {
+    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),
+      "BUTTON":   Object.clone(Form.Element.Methods)
+    });
+  }
+
+  Element.addMethods(methods);
+
+  function destroyCache_IE() {
+    DIV = null;
+    ELEMENT_CACHE = null;
+  }
+
+  if (window.attachEvent)
+    window.attachEvent('onunload', destroyCache_IE);
+
+})(this);
+(function() {
+
+  function toDecimal(pctString) {
+    var match = pctString.match(/^(\d+)%?$/i);
+    if (!match) return null;
+    return (Number(match[1]) / 100);
+  }
+
+  function getRawStyle(element, style) {
+    element = $(element);
+
+    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;
+  }
+
+  function getRawStyle_IE(element, style) {
+    var value = element.style[style];
+    if (!value && element.currentStyle) {
+      value = element.currentStyle[style];
+    }
+    return value;
+  }
+
+  function getContentWidth(element, context) {
+    var boxWidth = element.offsetWidth;
+
+    var bl = getPixelValue(element, 'borderLeftWidth',  context) || 0;
+    var br = getPixelValue(element, 'borderRightWidth', context) || 0;
+    var pl = getPixelValue(element, 'paddingLeft',      context) || 0;
+    var pr = getPixelValue(element, 'paddingRight',     context) || 0;
+
+    return boxWidth - bl - br - pl - pr;
+  }
+
+  if (!Object.isUndefined(document.documentElement.currentStyle) && !Prototype.Browser.Opera) {
+    getRawStyle = getRawStyle_IE;
+  }
+
+
+  function getPixelValue(value, property, context) {
+    var element = null;
+    if (Object.isElement(value)) {
+      element = value;
+      value = getRawStyle(element, property);
+    }
+
+    if (value === null || Object.isUndefined(value)) {
+      return null;
+    }
+
+    if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
+      return window.parseFloat(value);
+    }
+
+    var isPercentage = value.include('%'), isViewport = (context === document.viewport);
+
+    if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
+      var style = element.style.left, rStyle = element.runtimeStyle.left;
+      element.runtimeStyle.left = element.currentStyle.left;
+      element.style.left = value || 0;
+      value = element.style.pixelLeft;
+      element.style.left = style;
+      element.runtimeStyle.left = rStyle;
+
+      return value;
+    }
+
+    if (element && isPercentage) {
+      context = context || element.parentNode;
+      var decimal = toDecimal(value), whole = null;
+
+      var isHorizontal = property.include('left') || property.include('right') ||
+       property.include('width');
+
+      var isVertical   = property.include('top') || property.include('bottom') ||
+        property.include('height');
+
+      if (context === document.viewport) {
+        if (isHorizontal) {
+          whole = document.viewport.getWidth();
+        } else if (isVertical) {
+          whole = document.viewport.getHeight();
+        }
+      } else {
+        if (isHorizontal) {
+          whole = $(context).measure('width');
+        } else if (isVertical) {
+          whole = $(context).measure('height');
+        }
+      }
+
+      return (whole === null) ? 0 : whole * decimal;
+    }
+
+    return 0;
+  }
+
+  function toCSSPixels(number) {
+    if (Object.isString(number) && number.endsWith('px'))
+      return number;
+    return number + 'px';
+  }
+
+  function isDisplayed(element) {
+    while (element && element.parentNode) {
+      var display = element.getStyle('display');
+      if (display === 'none') {
+        return false;
+      }
+      element = $(element.parentNode);
+    }
     return true;
-  },
-  
-  select: function(element) {
-    $(element).select();
-  },
-   
-  activate: function(element) {
-    $(element).focus();
-    $(element).select();
   }
-}
 
-/*--------------------------------------------------------------------------*/
+  var hasLayout = Prototype.K;
+  if ('currentStyle' in document.documentElement) {
+    hasLayout = function(element) {
+      if (!element.currentStyle.hasLayout) {
+        element.style.zoom = 1;
+      }
+      return element;
+    };
+  }
 
-var Form = {
-  serialize: function(form) {
-    var elements = Form.getElements($(form));
-    var queryComponents = new Array();
-    
-    for (var i = 0; i < elements.length; i++) {
-      var queryComponent = Form.Element.serialize(elements[i]);
-      if (queryComponent)
-        queryComponents.push(queryComponent);
+  function cssNameFor(key) {
+    if (key.include('border')) key = key + '-width';
+    return key.camelize();
+  }
+
+  Element.Layout = Class.create(Hash, {
+    initialize: function($super, element, preCompute) {
+      $super();
+      this.element = $(element);
+
+      Element.Layout.PROPERTIES.each( function(property) {
+        this._set(property, null);
+      }, this);
+
+      if (preCompute) {
+        this._preComputing = true;
+        this._begin();
+        Element.Layout.PROPERTIES.each( this._compute, this );
+        this._end();
+        this._preComputing = false;
+      }
+    },
+
+    _set: function(property, value) {
+      return Hash.prototype.set.call(this, property, value);
+    },
+
+    set: function(property, value) {
+      throw "Properties of Element.Layout are read-only.";
+    },
+
+    get: function($super, property) {
+      var value = $super(property);
+      return value === null ? this._compute(property) : value;
+    },
+
+    _begin: function() {
+      if (this._isPrepared()) return;
+
+      var element = this.element;
+      if (isDisplayed(element)) {
+        this._setPrepared(true);
+        return;
+      }
+
+
+      var originalStyles = {
+        position:   element.style.position   || '',
+        width:      element.style.width      || '',
+        visibility: element.style.visibility || '',
+        display:    element.style.display    || ''
+      };
+
+      element.store('prototype_original_styles', originalStyles);
+
+      var position = getRawStyle(element, 'position'), width = element.offsetWidth;
+
+      if (width === 0 || width === null) {
+        element.style.display = 'block';
+        width = element.offsetWidth;
+      }
+
+      var context = (position === 'fixed') ? document.viewport :
+       element.parentNode;
+
+      var tempStyles = {
+        visibility: 'hidden',
+        display:    'block'
+      };
+
+      if (position !== 'fixed') tempStyles.position = 'absolute';
+
+      element.setStyle(tempStyles);
+
+      var positionedWidth = element.offsetWidth, newWidth;
+      if (width && (positionedWidth === width)) {
+        newWidth = getContentWidth(element, context);
+      } else if (position === 'absolute' || position === 'fixed') {
+        newWidth = getContentWidth(element, context);
+      } else {
+        var parent = element.parentNode, pLayout = $(parent).getLayout();
+
+        newWidth = pLayout.get('width') -
+         this.get('margin-left') -
+         this.get('border-left') -
+         this.get('padding-left') -
+         this.get('padding-right') -
+         this.get('border-right') -
+         this.get('margin-right');
+      }
+
+      element.setStyle({ width: newWidth + 'px' });
+
+      this._setPrepared(true);
+    },
+
+    _end: function() {
+      var element = this.element;
+      var originalStyles = element.retrieve('prototype_original_styles');
+      element.store('prototype_original_styles', null);
+      element.setStyle(originalStyles);
+      this._setPrepared(false);
+    },
+
+    _compute: function(property) {
+      var COMPUTATIONS = Element.Layout.COMPUTATIONS;
+      if (!(property in COMPUTATIONS)) {
+        throw "Property not found.";
+      }
+
+      return this._set(property, COMPUTATIONS[property].call(this, this.element));
+    },
+
+    _isPrepared: function() {
+      return this.element.retrieve('prototype_element_layout_prepared', false);
+    },
+
+    _setPrepared: function(bool) {
+      return this.element.store('prototype_element_layout_prepared', bool);
+    },
+
+    toObject: function() {
+      var args = $A(arguments);
+      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
+       args.join(' ').split(' ');
+      var obj = {};
+      keys.each( function(key) {
+        if (!Element.Layout.PROPERTIES.include(key)) return;
+        var value = this.get(key);
+        if (value != null) obj[key] = value;
+      }, this);
+      return obj;
+    },
+
+    toHash: function() {
+      var obj = this.toObject.apply(this, arguments);
+      return new Hash(obj);
+    },
+
+    toCSS: function() {
+      var args = $A(arguments);
+      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
+       args.join(' ').split(' ');
+      var css = {};
+
+      keys.each( function(key) {
+        if (!Element.Layout.PROPERTIES.include(key)) return;
+        if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
+
+        var value = this.get(key);
+        if (value != null) css[cssNameFor(key)] = value + 'px';
+      }, this);
+      return css;
+    },
+
+    inspect: function() {
+      return "#<Element.Layout>";
     }
-    
-    return queryComponents.join('&');
-  },
-  
-  getElements: function(form) {
-    var form = $(form);
-    var elements = new Array();
+  });
 
-    for (tagName in Form.Element.Serializers) {
-      var tagElements = form.getElementsByTagName(tagName);
-      for (var j = 0; j < tagElements.length; j++)
-        elements.push(tagElements[j]);
+  Object.extend(Element.Layout, {
+    PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
+
+    COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
+
+    COMPUTATIONS: {
+      'height': function(element) {
+        if (!this._preComputing) this._begin();
+
+        var bHeight = this.get('border-box-height');
+        if (bHeight <= 0) {
+          if (!this._preComputing) this._end();
+          return 0;
+        }
+
+        var bTop = this.get('border-top'),
+         bBottom = this.get('border-bottom');
+
+        var pTop = this.get('padding-top'),
+         pBottom = this.get('padding-bottom');
+
+        if (!this._preComputing) this._end();
+
+        return bHeight - bTop - bBottom - pTop - pBottom;
+      },
+
+      'width': function(element) {
+        if (!this._preComputing) this._begin();
+
+        var bWidth = this.get('border-box-width');
+        if (bWidth <= 0) {
+          if (!this._preComputing) this._end();
+          return 0;
+        }
+
+        var bLeft = this.get('border-left'),
+         bRight = this.get('border-right');
+
+        var pLeft = this.get('padding-left'),
+         pRight = this.get('padding-right');
+
+        if (!this._preComputing) this._end();
+        return bWidth - bLeft - bRight - pLeft - pRight;
+      },
+
+      'padding-box-height': function(element) {
+        var height = this.get('height'),
+         pTop = this.get('padding-top'),
+         pBottom = this.get('padding-bottom');
+
+        return height + pTop + pBottom;
+      },
+
+      'padding-box-width': function(element) {
+        var width = this.get('width'),
+         pLeft = this.get('padding-left'),
+         pRight = this.get('padding-right');
+
+        return width + pLeft + pRight;
+      },
+
+      'border-box-height': function(element) {
+        if (!this._preComputing) this._begin();
+        var height = element.offsetHeight;
+        if (!this._preComputing) this._end();
+        return height;
+      },
+
+      'border-box-width': function(element) {
+        if (!this._preComputing) this._begin();
+        var width = element.offsetWidth;
+        if (!this._preComputing) this._end();
+        return width;
+      },
+
+      'margin-box-height': function(element) {
+        var bHeight = this.get('border-box-height'),
+         mTop = this.get('margin-top'),
+         mBottom = this.get('margin-bottom');
+
+        if (bHeight <= 0) return 0;
+
+        return bHeight + mTop + mBottom;
+      },
+
+      'margin-box-width': function(element) {
+        var bWidth = this.get('border-box-width'),
+         mLeft = this.get('margin-left'),
+         mRight = this.get('margin-right');
+
+        if (bWidth <= 0) return 0;
+
+        return bWidth + mLeft + mRight;
+      },
+
+      'top': function(element) {
+        var offset = element.positionedOffset();
+        return offset.top;
+      },
+
+      'bottom': function(element) {
+        var offset = element.positionedOffset(),
+         parent = element.getOffsetParent(),
+         pHeight = parent.measure('height');
+
+        var mHeight = this.get('border-box-height');
+
+        return pHeight - mHeight - offset.top;
+      },
+
+      'left': function(element) {
+        var offset = element.positionedOffset();
+        return offset.left;
+      },
+
+      'right': function(element) {
+        var offset = element.positionedOffset(),
+         parent = element.getOffsetParent(),
+         pWidth = parent.measure('width');
+
+        var mWidth = this.get('border-box-width');
+
+        return pWidth - mWidth - offset.left;
+      },
+
+      'padding-top': function(element) {
+        return getPixelValue(element, 'paddingTop');
+      },
+
+      'padding-bottom': function(element) {
+        return getPixelValue(element, 'paddingBottom');
+      },
+
+      'padding-left': function(element) {
+        return getPixelValue(element, 'paddingLeft');
+      },
+
+      'padding-right': function(element) {
+        return getPixelValue(element, 'paddingRight');
+      },
+
+      'border-top': function(element) {
+        return getPixelValue(element, 'borderTopWidth');
+      },
+
+      'border-bottom': function(element) {
+        return getPixelValue(element, 'borderBottomWidth');
+      },
+
+      'border-left': function(element) {
+        return getPixelValue(element, 'borderLeftWidth');
+      },
+
+      'border-right': function(element) {
+        return getPixelValue(element, 'borderRightWidth');
+      },
+
+      'margin-top': function(element) {
+        return getPixelValue(element, 'marginTop');
+      },
+
+      'margin-bottom': function(element) {
+        return getPixelValue(element, 'marginBottom');
+      },
+
+      'margin-left': function(element) {
+        return getPixelValue(element, 'marginLeft');
+      },
+
+      'margin-right': function(element) {
+        return getPixelValue(element, 'marginRight');
+      }
+    }
+  });
+
+  if ('getBoundingClientRect' in document.documentElement) {
+    Object.extend(Element.Layout.COMPUTATIONS, {
+      'right': function(element) {
+        var parent = hasLayout(element.getOffsetParent());
+        var rect = element.getBoundingClientRect(),
+         pRect = parent.getBoundingClientRect();
+
+        return (pRect.right - rect.right).round();
+      },
+
+      'bottom': function(element) {
+        var parent = hasLayout(element.getOffsetParent());
+        var rect = element.getBoundingClientRect(),
+         pRect = parent.getBoundingClientRect();
+
+        return (pRect.bottom - rect.bottom).round();
+      }
+    });
+  }
+
+  Element.Offset = Class.create({
+    initialize: function(left, top) {
+      this.left = left.round();
+      this.top  = top.round();
+
+      this[0] = this.left;
+      this[1] = this.top;
+    },
+
+    relativeTo: function(offset) {
+      return new Element.Offset(
+        this.left - offset.left,
+        this.top  - offset.top
+      );
+    },
+
+    inspect: function() {
+      return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
+    },
+
+    toString: function() {
+      return "[#{left}, #{top}]".interpolate(this);
+    },
+
+    toArray: function() {
+      return [this.left, this.top];
+    }
+  });
+
+  function getLayout(element, preCompute) {
+    return new Element.Layout(element, preCompute);
+  }
+
+  function measure(element, property) {
+    return $(element).getLayout().get(property);
+  }
+
+  function getHeight(element) {
+    return Element.getDimensions(element).height;
+  }
+
+  function getWidth(element) {
+    return Element.getDimensions(element).width;
+  }
+
+  function getDimensions(element) {
+    element = $(element);
+    var display = Element.getStyle(element, 'display');
+
+    if (display && display !== 'none') {
+      return { width: element.offsetWidth, height: element.offsetHeight };
+    }
+
+    var style = element.style;
+    var originalStyles = {
+      visibility: style.visibility,
+      position:   style.position,
+      display:    style.display
+    };
+
+    var newStyles = {
+      visibility: 'hidden',
+      display:    'block'
+    };
+
+    if (originalStyles.position !== 'fixed')
+      newStyles.position = 'absolute';
+
+    Element.setStyle(element, newStyles);
+
+    var dimensions = {
+      width:  element.offsetWidth,
+      height: element.offsetHeight
+    };
+
+    Element.setStyle(element, originalStyles);
+
+    return dimensions;
+  }
+
+  function getOffsetParent(element) {
+    element = $(element);
+
+    function selfOrBody(element) {
+      return isHtml(element) ? $(document.body) : $(element);
+    }
+
+    if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
+      return $(document.body);
+
+    var isInline = (Element.getStyle(element, 'display') === 'inline');
+    if (!isInline && element.offsetParent) return selfOrBody(element.offsetParent);
+
+    while ((element = element.parentNode) && element !== document.body) {
+      if (Element.getStyle(element, 'position') !== 'static') {
+        return selfOrBody(element);
+      }
+    }
+
+    return $(document.body);
+  }
+
+
+  function cumulativeOffset(element) {
+    element = $(element);
+    var valueT = 0, valueL = 0;
+    if (element.parentNode) {
+      do {
+        valueT += element.offsetTop  || 0;
+        valueL += element.offsetLeft || 0;
+        element = element.offsetParent;
+      } while (element);
+    }
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function positionedOffset(element) {
+    element = $(element);
+
+    var layout = element.getLayout();
+
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+      if (element) {
+        if (isBody(element)) break;
+        var p = Element.getStyle(element, 'position');
+        if (p !== 'static') break;
+      }
+    } while (element);
+
+    valueL -= layout.get('margin-left');
+    valueT -= layout.get('margin-top');
+
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function cumulativeScrollOffset(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      if (element === document.body) {
+        var bodyScrollNode = document.documentElement || document.body.parentNode || document.body;
+        valueT += !Object.isUndefined(window.pageYOffset) ? window.pageYOffset : bodyScrollNode.scrollTop || 0;
+        valueL += !Object.isUndefined(window.pageXOffset) ? window.pageXOffset : bodyScrollNode.scrollLeft || 0;
+        break;
+      } else {
+        valueT += element.scrollTop  || 0;
+        valueL += element.scrollLeft || 0;
+        element = element.parentNode;
+      }
+    } while (element);
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function viewportOffset(forElement) {
+    var valueT = 0, valueL = 0, docBody = document.body;
+
+    forElement = $(forElement);
+    var element = forElement;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      if (element.offsetParent == docBody &&
+        Element.getStyle(element, 'position') == 'absolute') break;
+    } while (element = element.offsetParent);
+
+    element = forElement;
+    do {
+      if (element != docBody) {
+        valueT -= element.scrollTop  || 0;
+        valueL -= element.scrollLeft || 0;
+      }
+    } while (element = element.parentNode);
+    return new Element.Offset(valueL, valueT);
+  }
+
+  function absolutize(element) {
+    element = $(element);
+
+    if (Element.getStyle(element, 'position') === 'absolute') {
+      return element;
+    }
+
+    var offsetParent = getOffsetParent(element);
+    var eOffset = element.viewportOffset(),
+     pOffset = offsetParent.viewportOffset();
+
+    var offset = eOffset.relativeTo(pOffset);
+    var layout = element.getLayout();
+
+    element.store('prototype_absolutize_original_styles', {
+      position: element.getStyle('position'),
+      left:     element.getStyle('left'),
+      top:      element.getStyle('top'),
+      width:    element.getStyle('width'),
+      height:   element.getStyle('height')
+    });
+
+    element.setStyle({
+      position: 'absolute',
+      top:    offset.top + 'px',
+      left:   offset.left + 'px',
+      width:  layout.get('width') + 'px',
+      height: layout.get('height') + 'px'
+    });
+
+    return element;
+  }
+
+  function relativize(element) {
+    element = $(element);
+    if (Element.getStyle(element, 'position') === 'relative') {
+      return element;
+    }
+
+    var originalStyles =
+     element.retrieve('prototype_absolutize_original_styles');
+
+    if (originalStyles) element.setStyle(originalStyles);
+    return element;
+  }
+
+
+  function scrollTo(element) {
+    element = $(element);
+    var pos = Element.cumulativeOffset(element);
+    window.scrollTo(pos.left, pos.top);
+    return element;
+  }
+
+
+  function makePositioned(element) {
+    element = $(element);
+    var position = Element.getStyle(element, 'position'), styles = {};
+    if (position === 'static' || !position) {
+      styles.position = 'relative';
+      if (Prototype.Browser.Opera) {
+        styles.top  = 0;
+        styles.left = 0;
+      }
+      Element.setStyle(element, styles);
+      Element.store(element, 'prototype_made_positioned', true);
+    }
+    return element;
+  }
+
+  function undoPositioned(element) {
+    element = $(element);
+    var storage = Element.getStorage(element),
+     madePositioned = storage.get('prototype_made_positioned');
+
+    if (madePositioned) {
+      storage.unset('prototype_made_positioned');
+      Element.setStyle(element, {
+        position: '',
+        top:      '',
+        bottom:   '',
+        left:     '',
+        right:    ''
+      });
+    }
+    return element;
+  }
+
+  function makeClipping(element) {
+    element = $(element);
+
+    var storage = Element.getStorage(element),
+     madeClipping = storage.get('prototype_made_clipping');
+
+    if (Object.isUndefined(madeClipping)) {
+      var overflow = Element.getStyle(element, 'overflow');
+      storage.set('prototype_made_clipping', overflow);
+      if (overflow !== 'hidden')
+        element.style.overflow = 'hidden';
+    }
+
+    return element;
+  }
+
+  function undoClipping(element) {
+    element = $(element);
+    var storage = Element.getStorage(element),
+     overflow = storage.get('prototype_made_clipping');
+
+    if (!Object.isUndefined(overflow)) {
+      storage.unset('prototype_made_clipping');
+      element.style.overflow = overflow || '';
+    }
+
+    return element;
+  }
+
+  function clonePosition(element, source, options) {
+    options = Object.extend({
+      setLeft:    true,
+      setTop:     true,
+      setWidth:   true,
+      setHeight:  true,
+      offsetTop:  0,
+      offsetLeft: 0
+    }, options || {});
+
+    var docEl = document.documentElement;
+
+    source  = $(source);
+    element = $(element);
+    var p, delta, layout, styles = {};
+
+    if (options.setLeft || options.setTop) {
+      p = Element.viewportOffset(source);
+      delta = [0, 0];
+      if (Element.getStyle(element, 'position') === 'absolute') {
+        var parent = Element.getOffsetParent(element);
+        if (parent !== document.body) delta = Element.viewportOffset(parent);
+      }
+    }
+
+    function pageScrollXY() {
+      var x = 0, y = 0;
+      if (Object.isNumber(window.pageXOffset)) {
+        x = window.pageXOffset;
+        y = window.pageYOffset;
+      } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
+        x = document.body.scrollLeft;
+        y = document.body.scrollTop;
+      } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) {
+        x = docEl.scrollLeft;
+        y = docEl.scrollTop;
+      }
+      return { x: x, y: y };
+    }
+
+    var pageXY = pageScrollXY();
+
+
+    if (options.setWidth || options.setHeight) {
+      layout = Element.getLayout(source);
+    }
+
+    if (options.setLeft)
+      styles.left = (p[0] + pageXY.x - delta[0] + options.offsetLeft) + 'px';
+    if (options.setTop)
+      styles.top  = (p[1] + pageXY.y - delta[1] + options.offsetTop)  + 'px';
+
+    var currentLayout = element.getLayout();
+
+    if (options.setWidth) {
+      styles.width = layout.get('width')  + 'px';
+    }
+    if (options.setHeight) {
+      styles.height = layout.get('height') + 'px';
+    }
+
+    return Element.setStyle(element, styles);
+  }
+
+
+  if (Prototype.Browser.IE) {
+    getOffsetParent = getOffsetParent.wrap(
+      function(proceed, element) {
+        element = $(element);
+
+        if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
+          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;
+      }
+    );
+
+    positionedOffset = positionedOffset.wrap(function(proceed, element) {
+      element = $(element);
+      if (!element.parentNode) return new Element.Offset(0, 0);
+      var position = element.getStyle('position');
+      if (position !== 'static') return proceed(element);
+
+      var offsetParent = element.getOffsetParent();
+      if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+        hasLayout(offsetParent);
+
+      element.setStyle({ position: 'relative' });
+      var value = proceed(element);
+      element.setStyle({ position: position });
+      return value;
+    });
+  } else if (Prototype.Browser.Webkit) {
+    cumulativeOffset = function(element) {
+      element = $(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 new Element.Offset(valueL, valueT);
+    };
+  }
+
+
+  Element.addMethods({
+    getLayout:              getLayout,
+    measure:                measure,
+    getWidth:               getWidth,
+    getHeight:              getHeight,
+    getDimensions:          getDimensions,
+    getOffsetParent:        getOffsetParent,
+    cumulativeOffset:       cumulativeOffset,
+    positionedOffset:       positionedOffset,
+    cumulativeScrollOffset: cumulativeScrollOffset,
+    viewportOffset:         viewportOffset,
+    absolutize:             absolutize,
+    relativize:             relativize,
+    scrollTo:               scrollTo,
+    makePositioned:         makePositioned,
+    undoPositioned:         undoPositioned,
+    makeClipping:           makeClipping,
+    undoClipping:           undoClipping,
+    clonePosition:          clonePosition
+  });
+
+  function isBody(element) {
+    return element.nodeName.toUpperCase() === 'BODY';
+  }
+
+  function isHtml(element) {
+    return element.nodeName.toUpperCase() === 'HTML';
+  }
+
+  function isDocument(element) {
+    return element.nodeType === Node.DOCUMENT_NODE;
+  }
+
+  function isDetached(element) {
+    return element !== document.body &&
+     !Element.descendantOf(element, document.body);
+  }
+
+  if ('getBoundingClientRect' in document.documentElement) {
+    Element.addMethods({
+      viewportOffset: function(element) {
+        element = $(element);
+        if (isDetached(element)) return new Element.Offset(0, 0);
+
+        var rect = element.getBoundingClientRect(),
+         docEl = document.documentElement;
+        return new Element.Offset(rect.left - docEl.clientLeft,
+         rect.top - docEl.clientTop);
+      }
+    });
+  }
+
+
+})();
+
+(function() {
+
+  var IS_OLD_OPERA = Prototype.Browser.Opera &&
+   (window.parseFloat(window.opera.version()) < 9.5);
+  var ROOT = null;
+  function getRootElement() {
+    if (ROOT) return ROOT;
+    ROOT = IS_OLD_OPERA ? document.body : document.documentElement;
+    return ROOT;
+  }
+
+  function getDimensions() {
+    return { width: this.getWidth(), height: this.getHeight() };
+  }
+
+  function getWidth() {
+    return getRootElement().clientWidth;
+  }
+
+  function getHeight() {
+    return getRootElement().clientHeight;
+  }
+
+  function getScrollOffsets() {
+    var x = window.pageXOffset || document.documentElement.scrollLeft ||
+     document.body.scrollLeft;
+    var y = window.pageYOffset || document.documentElement.scrollTop ||
+     document.body.scrollTop;
+
+    return new Element.Offset(x, y);
+  }
+
+  document.viewport = {
+    getDimensions:    getDimensions,
+    getWidth:         getWidth,
+    getHeight:        getHeight,
+    getScrollOffsets: getScrollOffsets
+  };
+
+})();
+window.$$ = function() {
+  var expression = $A(arguments).join(', ');
+  return Prototype.Selector.select(expression, document);
+};
+
+Prototype.Selector = (function() {
+
+  function select() {
+    throw new Error('Method "Prototype.Selector.select" must be defined.');
+  }
+
+  function match() {
+    throw new Error('Method "Prototype.Selector.match" must be defined.');
+  }
+
+  function find(elements, expression, index) {
+    index = index || 0;
+    var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
+
+    for (i = 0; i < length; i++) {
+      if (match(elements[i], expression) && index == matchIndex++) {
+        return Element.extend(elements[i]);
+      }
+    }
+  }
+
+  function extendElements(elements) {
+    for (var i = 0, length = elements.length; i < length; i++) {
+      Element.extend(elements[i]);
     }
     return elements;
+  }
+
+
+  var K = Prototype.K;
+
+  return {
+    select: select,
+    match: match,
+    find: find,
+    extendElements: (Element.extend === K) ? K : extendElements,
+    extendElement: Element.extend
+  };
+})();
+Prototype._original_property = window.Sizzle;
+
+;(function () {
+  function fakeDefine(fn) {
+    Prototype._actual_sizzle = fn();
+  }
+  fakeDefine.amd = true;
+
+  if (typeof define !== 'undefined' && define.amd) {
+    Prototype._original_define = define;
+    Prototype._actual_sizzle = null;
+    window.define = fakeDefine;
+  }
+})();
+
+/*!
+ * Sizzle CSS Selector Engine v1.10.18
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-02-05
+ */
+(function( window ) {
+
+var i,
+	support,
+	Expr,
+	getText,
+	isXML,
+	compile,
+	select,
+	outermostContext,
+	sortInput,
+	hasDuplicate,
+
+	setDocument,
+	document,
+	docElem,
+	documentIsHTML,
+	rbuggyQSA,
+	rbuggyMatches,
+	matches,
+	contains,
+
+	expando = "sizzle" + -(new Date()),
+	preferredDoc = window.document,
+	dirruns = 0,
+	done = 0,
+	classCache = createCache(),
+	tokenCache = createCache(),
+	compilerCache = createCache(),
+	sortOrder = function( a, b ) {
+		if ( a === b ) {
+			hasDuplicate = true;
+		}
+		return 0;
+	},
+
+	strundefined = typeof undefined,
+	MAX_NEGATIVE = 1 << 31,
+
+	hasOwn = ({}).hasOwnProperty,
+	arr = [],
+	pop = arr.pop,
+	push_native = arr.push,
+	push = arr.push,
+	slice = arr.slice,
+	indexOf = arr.indexOf || function( elem ) {
+		var i = 0,
+			len = this.length;
+		for ( ; i < len; i++ ) {
+			if ( this[i] === elem ) {
+				return i;
+			}
+		}
+		return -1;
+	},
+
+	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+
+	whitespace = "[\\x20\\t\\r\\n\\f]",
+	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+	identifier = characterEncoding.replace( "w", "w#" ),
+
+	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
+		"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
+
+	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
+
+	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
+
+	rpseudo = new RegExp( pseudos ),
+	ridentifier = new RegExp( "^" + identifier + "$" ),
+
+	matchExpr = {
+		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
+		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+		"ATTR": new RegExp( "^" + attributes ),
+		"PSEUDO": new RegExp( "^" + pseudos ),
+		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+	},
+
+	rinputs = /^(?:input|select|textarea|button)$/i,
+	rheader = /^h\d$/i,
+
+	rnative = /^[^{]+\{\s*\[native \w/,
+
+	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+	rsibling = /[+~]/,
+	rescape = /'|\\/g,
+
+	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+	funescape = function( _, escaped, escapedWhitespace ) {
+		var high = "0x" + escaped - 0x10000;
+		return high !== high || escapedWhitespace ?
+			escaped :
+			high < 0 ?
+				String.fromCharCode( high + 0x10000 ) :
+				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+	};
+
+try {
+	push.apply(
+		(arr = slice.call( preferredDoc.childNodes )),
+		preferredDoc.childNodes
+	);
+	arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+	push = { apply: arr.length ?
+
+		function( target, els ) {
+			push_native.apply( target, slice.call(els) );
+		} :
+
+		function( target, els ) {
+			var j = target.length,
+				i = 0;
+			while ( (target[j++] = els[i++]) ) {}
+			target.length = j - 1;
+		}
+	};
+}
+
+function Sizzle( selector, context, results, seed ) {
+	var match, elem, m, nodeType,
+		i, groups, old, nid, newContext, newSelector;
+
+	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+		setDocument( context );
+	}
+
+	context = context || document;
+	results = results || [];
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( documentIsHTML && !seed ) {
+
+		if ( (match = rquickExpr.exec( selector )) ) {
+			if ( (m = match[1]) ) {
+				if ( nodeType === 9 ) {
+					elem = context.getElementById( m );
+					if ( elem && elem.parentNode ) {
+						if ( elem.id === m ) {
+							results.push( elem );
+							return results;
+						}
+					} else {
+						return results;
+					}
+				} else {
+					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+						contains( context, elem ) && elem.id === m ) {
+						results.push( elem );
+						return results;
+					}
+				}
+
+			} else if ( match[2] ) {
+				push.apply( results, context.getElementsByTagName( selector ) );
+				return results;
+
+			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+				push.apply( results, context.getElementsByClassName( m ) );
+				return results;
+			}
+		}
+
+		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+			nid = old = expando;
+			newContext = context;
+			newSelector = nodeType === 9 && selector;
+
+			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+				groups = tokenize( selector );
+
+				if ( (old = context.getAttribute("id")) ) {
+					nid = old.replace( rescape, "\\$&" );
+				} else {
+					context.setAttribute( "id", nid );
+				}
+				nid = "[id='" + nid + "'] ";
+
+				i = groups.length;
+				while ( i-- ) {
+					groups[i] = nid + toSelector( groups[i] );
+				}
+				newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
+				newSelector = groups.join(",");
+			}
+
+			if ( newSelector ) {
+				try {
+					push.apply( results,
+						newContext.querySelectorAll( newSelector )
+					);
+					return results;
+				} catch(qsaError) {
+				} finally {
+					if ( !old ) {
+						context.removeAttribute("id");
+					}
+				}
+			}
+		}
+	}
+
+	return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ *	deleting the oldest entry
+ */
+function createCache() {
+	var keys = [];
+
+	function cache( key, value ) {
+		if ( keys.push( key + " " ) > Expr.cacheLength ) {
+			delete cache[ keys.shift() ];
+		}
+		return (cache[ key + " " ] = value);
+	}
+	return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+	fn[ expando ] = true;
+	return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+	var div = document.createElement("div");
+
+	try {
+		return !!fn( div );
+	} catch (e) {
+		return false;
+	} finally {
+		if ( div.parentNode ) {
+			div.parentNode.removeChild( div );
+		}
+		div = null;
+	}
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+	var arr = attrs.split("|"),
+		i = attrs.length;
+
+	while ( i-- ) {
+		Expr.attrHandle[ arr[i] ] = handler;
+	}
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+	var cur = b && a,
+		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+			( ~b.sourceIndex || MAX_NEGATIVE ) -
+			( ~a.sourceIndex || MAX_NEGATIVE );
+
+	if ( diff ) {
+		return diff;
+	}
+
+	if ( cur ) {
+		while ( (cur = cur.nextSibling) ) {
+			if ( cur === b ) {
+				return -1;
+			}
+		}
+	}
+
+	return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return name === "input" && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return (name === "input" || name === "button") && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+	return markFunction(function( argument ) {
+		argument = +argument;
+		return markFunction(function( seed, matches ) {
+			var j,
+				matchIndexes = fn( [], seed.length, argument ),
+				i = matchIndexes.length;
+
+			while ( i-- ) {
+				if ( seed[ (j = matchIndexes[i]) ] ) {
+					seed[j] = !(matches[j] = seed[j]);
+				}
+			}
+		});
+	});
+}
+
+/**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+function testContext( context ) {
+	return context && typeof context.getElementsByTagName !== strundefined && context;
+}
+
+support = Sizzle.support = {};
+
+/**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+isXML = Sizzle.isXML = function( elem ) {
+	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+	return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+	var hasCompare,
+		doc = node ? node.ownerDocument || node : preferredDoc,
+		parent = doc.defaultView;
+
+	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+		return document;
+	}
+
+	document = doc;
+	docElem = doc.documentElement;
+
+	documentIsHTML = !isXML( doc );
+
+	if ( parent && parent !== parent.top ) {
+		if ( parent.addEventListener ) {
+			parent.addEventListener( "unload", function() {
+				setDocument();
+			}, false );
+		} else if ( parent.attachEvent ) {
+			parent.attachEvent( "onunload", function() {
+				setDocument();
+			});
+		}
+	}
+
+	/* Attributes
+	---------------------------------------------------------------------- */
+
+	support.attributes = assert(function( div ) {
+		div.className = "i";
+		return !div.getAttribute("className");
+	});
+
+	/* getElement(s)By*
+	---------------------------------------------------------------------- */
+
+	support.getElementsByTagName = assert(function( div ) {
+		div.appendChild( doc.createComment("") );
+		return !div.getElementsByTagName("*").length;
+	});
+
+	support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
+		div.innerHTML = "<div class='a'></div><div class='a i'></div>";
+
+		div.firstChild.className = "i";
+		return div.getElementsByClassName("i").length === 2;
+	});
+
+	support.getById = assert(function( div ) {
+		docElem.appendChild( div ).id = expando;
+		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+	});
+
+	if ( support.getById ) {
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+				var m = context.getElementById( id );
+				return m && m.parentNode ? [m] : [];
+			}
+		};
+		Expr.filter["ID"] = function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				return elem.getAttribute("id") === attrId;
+			};
+		};
+	} else {
+		delete Expr.find["ID"];
+
+		Expr.filter["ID"] =  function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+				return node && node.value === attrId;
+			};
+		};
+	}
+
+	Expr.find["TAG"] = support.getElementsByTagName ?
+		function( tag, context ) {
+			if ( typeof context.getElementsByTagName !== strundefined ) {
+				return context.getElementsByTagName( tag );
+			}
+		} :
+		function( tag, context ) {
+			var elem,
+				tmp = [],
+				i = 0,
+				results = context.getElementsByTagName( tag );
+
+			if ( tag === "*" ) {
+				while ( (elem = results[i++]) ) {
+					if ( elem.nodeType === 1 ) {
+						tmp.push( elem );
+					}
+				}
+
+				return tmp;
+			}
+			return results;
+		};
+
+	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+			return context.getElementsByClassName( className );
+		}
+	};
+
+	/* QSA/matchesSelector
+	---------------------------------------------------------------------- */
+
+
+	rbuggyMatches = [];
+
+	rbuggyQSA = [];
+
+	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+		assert(function( div ) {
+			div.innerHTML = "<select t=''><option selected=''></option></select>";
+
+			if ( div.querySelectorAll("[t^='']").length ) {
+				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+			}
+
+			if ( !div.querySelectorAll("[selected]").length ) {
+				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+			}
+
+			if ( !div.querySelectorAll(":checked").length ) {
+				rbuggyQSA.push(":checked");
+			}
+		});
+
+		assert(function( div ) {
+			var input = doc.createElement("input");
+			input.setAttribute( "type", "hidden" );
+			div.appendChild( input ).setAttribute( "name", "D" );
+
+			if ( div.querySelectorAll("[name=d]").length ) {
+				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+			}
+
+			if ( !div.querySelectorAll(":enabled").length ) {
+				rbuggyQSA.push( ":enabled", ":disabled" );
+			}
+
+			div.querySelectorAll("*,:x");
+			rbuggyQSA.push(",.*:");
+		});
+	}
+
+	if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
+		docElem.mozMatchesSelector ||
+		docElem.oMatchesSelector ||
+		docElem.msMatchesSelector) )) ) {
+
+		assert(function( div ) {
+			support.disconnectedMatch = matches.call( div, "div" );
+
+			matches.call( div, "[s!='']:x" );
+			rbuggyMatches.push( "!=", pseudos );
+		});
+	}
+
+	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+	/* Contains
+	---------------------------------------------------------------------- */
+	hasCompare = rnative.test( docElem.compareDocumentPosition );
+
+	contains = hasCompare || rnative.test( docElem.contains ) ?
+		function( a, b ) {
+			var adown = a.nodeType === 9 ? a.documentElement : a,
+				bup = b && b.parentNode;
+			return a === bup || !!( bup && bup.nodeType === 1 && (
+				adown.contains ?
+					adown.contains( bup ) :
+					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+			));
+		} :
+		function( a, b ) {
+			if ( b ) {
+				while ( (b = b.parentNode) ) {
+					if ( b === a ) {
+						return true;
+					}
+				}
+			}
+			return false;
+		};
+
+	/* Sorting
+	---------------------------------------------------------------------- */
+
+	sortOrder = hasCompare ?
+	function( a, b ) {
+
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+		if ( compare ) {
+			return compare;
+		}
+
+		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
+			a.compareDocumentPosition( b ) :
+
+			1;
+
+		if ( compare & 1 ||
+			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+			if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+				return -1;
+			}
+			if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+				return 1;
+			}
+
+			return sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+		}
+
+		return compare & 4 ? -1 : 1;
+	} :
+	function( a, b ) {
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		var cur,
+			i = 0,
+			aup = a.parentNode,
+			bup = b.parentNode,
+			ap = [ a ],
+			bp = [ b ];
+
+		if ( !aup || !bup ) {
+			return a === doc ? -1 :
+				b === doc ? 1 :
+				aup ? -1 :
+				bup ? 1 :
+				sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+
+		} else if ( aup === bup ) {
+			return siblingCheck( a, b );
+		}
+
+		cur = a;
+		while ( (cur = cur.parentNode) ) {
+			ap.unshift( cur );
+		}
+		cur = b;
+		while ( (cur = cur.parentNode) ) {
+			bp.unshift( cur );
+		}
+
+		while ( ap[i] === bp[i] ) {
+			i++;
+		}
+
+		return i ?
+			siblingCheck( ap[i], bp[i] ) :
+
+			ap[i] === preferredDoc ? -1 :
+			bp[i] === preferredDoc ? 1 :
+			0;
+	};
+
+	return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+	return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	expr = expr.replace( rattributeQuotes, "='$1']" );
+
+	if ( support.matchesSelector && documentIsHTML &&
+		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
+
+		try {
+			var ret = matches.call( elem, expr );
+
+			if ( ret || support.disconnectedMatch ||
+					elem.document && elem.document.nodeType !== 11 ) {
+				return ret;
+			}
+		} catch(e) {}
+	}
+
+	return Sizzle( expr, document, null, [elem] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+	if ( ( context.ownerDocument || context ) !== document ) {
+		setDocument( context );
+	}
+	return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	var fn = Expr.attrHandle[ name.toLowerCase() ],
+		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+			fn( elem, name, !documentIsHTML ) :
+			undefined;
+
+	return val !== undefined ?
+		val :
+		support.attributes || !documentIsHTML ?
+			elem.getAttribute( name ) :
+			(val = elem.getAttributeNode(name)) && val.specified ?
+				val.value :
+				null;
+};
+
+Sizzle.error = function( msg ) {
+	throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+	var elem,
+		duplicates = [],
+		j = 0,
+		i = 0;
+
+	hasDuplicate = !support.detectDuplicates;
+	sortInput = !support.sortStable && results.slice( 0 );
+	results.sort( sortOrder );
+
+	if ( hasDuplicate ) {
+		while ( (elem = results[i++]) ) {
+			if ( elem === results[ i ] ) {
+				j = duplicates.push( i );
+			}
+		}
+		while ( j-- ) {
+			results.splice( duplicates[ j ], 1 );
+		}
+	}
+
+	sortInput = null;
+
+	return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+	var node,
+		ret = "",
+		i = 0,
+		nodeType = elem.nodeType;
+
+	if ( !nodeType ) {
+		while ( (node = elem[i++]) ) {
+			ret += getText( node );
+		}
+	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+		if ( typeof elem.textContent === "string" ) {
+			return elem.textContent;
+		} else {
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				ret += getText( elem );
+			}
+		}
+	} else if ( nodeType === 3 || nodeType === 4 ) {
+		return elem.nodeValue;
+	}
+
+	return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+	cacheLength: 50,
+
+	createPseudo: markFunction,
+
+	match: matchExpr,
+
+	attrHandle: {},
+
+	find: {},
+
+	relative: {
+		">": { dir: "parentNode", first: true },
+		" ": { dir: "parentNode" },
+		"+": { dir: "previousSibling", first: true },
+		"~": { dir: "previousSibling" }
+	},
+
+	preFilter: {
+		"ATTR": function( match ) {
+			match[1] = match[1].replace( runescape, funescape );
+
+			match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
+
+			if ( match[2] === "~=" ) {
+				match[3] = " " + match[3] + " ";
+			}
+
+			return match.slice( 0, 4 );
+		},
+
+		"CHILD": function( match ) {
+			/* matches from matchExpr["CHILD"]
+				1 type (only|nth|...)
+				2 what (child|of-type)
+				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+				4 xn-component of xn+y argument ([+-]?\d*n|)
+				5 sign of xn-component
+				6 x of xn-component
+				7 sign of y-component
+				8 y of y-component
+			*/
+			match[1] = match[1].toLowerCase();
+
+			if ( match[1].slice( 0, 3 ) === "nth" ) {
+				if ( !match[3] ) {
+					Sizzle.error( match[0] );
+				}
+
+				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+			} else if ( match[3] ) {
+				Sizzle.error( match[0] );
+			}
+
+			return match;
+		},
+
+		"PSEUDO": function( match ) {
+			var excess,
+				unquoted = !match[5] && match[2];
+
+			if ( matchExpr["CHILD"].test( match[0] ) ) {
+				return null;
+			}
+
+			if ( match[3] && match[4] !== undefined ) {
+				match[2] = match[4];
+
+			} else if ( unquoted && rpseudo.test( unquoted ) &&
+				(excess = tokenize( unquoted, true )) &&
+				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+				match[0] = match[0].slice( 0, excess );
+				match[2] = unquoted.slice( 0, excess );
+			}
+
+			return match.slice( 0, 3 );
+		}
+	},
+
+	filter: {
+
+		"TAG": function( nodeNameSelector ) {
+			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+			return nodeNameSelector === "*" ?
+				function() { return true; } :
+				function( elem ) {
+					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+				};
+		},
+
+		"CLASS": function( className ) {
+			var pattern = classCache[ className + " " ];
+
+			return pattern ||
+				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+				classCache( className, function( elem ) {
+					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+				});
+		},
+
+		"ATTR": function( name, operator, check ) {
+			return function( elem ) {
+				var result = Sizzle.attr( elem, name );
+
+				if ( result == null ) {
+					return operator === "!=";
+				}
+				if ( !operator ) {
+					return true;
+				}
+
+				result += "";
+
+				return operator === "=" ? result === check :
+					operator === "!=" ? result !== check :
+					operator === "^=" ? check && result.indexOf( check ) === 0 :
+					operator === "*=" ? check && result.indexOf( check ) > -1 :
+					operator === "$=" ? check && result.slice( -check.length ) === check :
+					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+					false;
+			};
+		},
+
+		"CHILD": function( type, what, argument, first, last ) {
+			var simple = type.slice( 0, 3 ) !== "nth",
+				forward = type.slice( -4 ) !== "last",
+				ofType = what === "of-type";
+
+			return first === 1 && last === 0 ?
+
+				function( elem ) {
+					return !!elem.parentNode;
+				} :
+
+				function( elem, context, xml ) {
+					var cache, outerCache, node, diff, nodeIndex, start,
+						dir = simple !== forward ? "nextSibling" : "previousSibling",
+						parent = elem.parentNode,
+						name = ofType && elem.nodeName.toLowerCase(),
+						useCache = !xml && !ofType;
+
+					if ( parent ) {
+
+						if ( simple ) {
+							while ( dir ) {
+								node = elem;
+								while ( (node = node[ dir ]) ) {
+									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+										return false;
+									}
+								}
+								start = dir = type === "only" && !start && "nextSibling";
+							}
+							return true;
+						}
+
+						start = [ forward ? parent.firstChild : parent.lastChild ];
+
+						if ( forward && useCache ) {
+							outerCache = parent[ expando ] || (parent[ expando ] = {});
+							cache = outerCache[ type ] || [];
+							nodeIndex = cache[0] === dirruns && cache[1];
+							diff = cache[0] === dirruns && cache[2];
+							node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								if ( node.nodeType === 1 && ++diff && node === elem ) {
+									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+									break;
+								}
+							}
+
+						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+							diff = cache[1];
+
+						} else {
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+									if ( useCache ) {
+										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+									}
+
+									if ( node === elem ) {
+										break;
+									}
+								}
+							}
+						}
+
+						diff -= last;
+						return diff === first || ( diff % first === 0 && diff / first >= 0 );
+					}
+				};
+		},
+
+		"PSEUDO": function( pseudo, argument ) {
+			var args,
+				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+					Sizzle.error( "unsupported pseudo: " + pseudo );
+
+			if ( fn[ expando ] ) {
+				return fn( argument );
+			}
+
+			if ( fn.length > 1 ) {
+				args = [ pseudo, pseudo, "", argument ];
+				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+					markFunction(function( seed, matches ) {
+						var idx,
+							matched = fn( seed, argument ),
+							i = matched.length;
+						while ( i-- ) {
+							idx = indexOf.call( seed, matched[i] );
+							seed[ idx ] = !( matches[ idx ] = matched[i] );
+						}
+					}) :
+					function( elem ) {
+						return fn( elem, 0, args );
+					};
+			}
+
+			return fn;
+		}
+	},
+
+	pseudos: {
+		"not": markFunction(function( selector ) {
+			var input = [],
+				results = [],
+				matcher = compile( selector.replace( rtrim, "$1" ) );
+
+			return matcher[ expando ] ?
+				markFunction(function( seed, matches, context, xml ) {
+					var elem,
+						unmatched = matcher( seed, null, xml, [] ),
+						i = seed.length;
+
+					while ( i-- ) {
+						if ( (elem = unmatched[i]) ) {
+							seed[i] = !(matches[i] = elem);
+						}
+					}
+				}) :
+				function( elem, context, xml ) {
+					input[0] = elem;
+					matcher( input, null, xml, results );
+					return !results.pop();
+				};
+		}),
+
+		"has": markFunction(function( selector ) {
+			return function( elem ) {
+				return Sizzle( selector, elem ).length > 0;
+			};
+		}),
+
+		"contains": markFunction(function( text ) {
+			return function( elem ) {
+				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+			};
+		}),
+
+		"lang": markFunction( function( lang ) {
+			if ( !ridentifier.test(lang || "") ) {
+				Sizzle.error( "unsupported lang: " + lang );
+			}
+			lang = lang.replace( runescape, funescape ).toLowerCase();
+			return function( elem ) {
+				var elemLang;
+				do {
+					if ( (elemLang = documentIsHTML ?
+						elem.lang :
+						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+						elemLang = elemLang.toLowerCase();
+						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+					}
+				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+				return false;
+			};
+		}),
+
+		"target": function( elem ) {
+			var hash = window.location && window.location.hash;
+			return hash && hash.slice( 1 ) === elem.id;
+		},
+
+		"root": function( elem ) {
+			return elem === docElem;
+		},
+
+		"focus": function( elem ) {
+			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+		},
+
+		"enabled": function( elem ) {
+			return elem.disabled === false;
+		},
+
+		"disabled": function( elem ) {
+			return elem.disabled === true;
+		},
+
+		"checked": function( elem ) {
+			var nodeName = elem.nodeName.toLowerCase();
+			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+		},
+
+		"selected": function( elem ) {
+			if ( elem.parentNode ) {
+				elem.parentNode.selectedIndex;
+			}
+
+			return elem.selected === true;
+		},
+
+		"empty": function( elem ) {
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				if ( elem.nodeType < 6 ) {
+					return false;
+				}
+			}
+			return true;
+		},
+
+		"parent": function( elem ) {
+			return !Expr.pseudos["empty"]( elem );
+		},
+
+		"header": function( elem ) {
+			return rheader.test( elem.nodeName );
+		},
+
+		"input": function( elem ) {
+			return rinputs.test( elem.nodeName );
+		},
+
+		"button": function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return name === "input" && elem.type === "button" || name === "button";
+		},
+
+		"text": function( elem ) {
+			var attr;
+			return elem.nodeName.toLowerCase() === "input" &&
+				elem.type === "text" &&
+
+				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+		},
+
+		"first": createPositionalPseudo(function() {
+			return [ 0 ];
+		}),
+
+		"last": createPositionalPseudo(function( matchIndexes, length ) {
+			return [ length - 1 ];
+		}),
+
+		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			return [ argument < 0 ? argument + length : argument ];
+		}),
+
+		"even": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 0;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"odd": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 1;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; --i >= 0; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; ++i < length; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		})
+	}
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+	Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+	Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+function tokenize( selector, parseOnly ) {
+	var matched, match, tokens, type,
+		soFar, groups, preFilters,
+		cached = tokenCache[ selector + " " ];
+
+	if ( cached ) {
+		return parseOnly ? 0 : cached.slice( 0 );
+	}
+
+	soFar = selector;
+	groups = [];
+	preFilters = Expr.preFilter;
+
+	while ( soFar ) {
+
+		if ( !matched || (match = rcomma.exec( soFar )) ) {
+			if ( match ) {
+				soFar = soFar.slice( match[0].length ) || soFar;
+			}
+			groups.push( (tokens = []) );
+		}
+
+		matched = false;
+
+		if ( (match = rcombinators.exec( soFar )) ) {
+			matched = match.shift();
+			tokens.push({
+				value: matched,
+				type: match[0].replace( rtrim, " " )
+			});
+			soFar = soFar.slice( matched.length );
+		}
+
+		for ( type in Expr.filter ) {
+			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+				(match = preFilters[ type ]( match ))) ) {
+				matched = match.shift();
+				tokens.push({
+					value: matched,
+					type: type,
+					matches: match
+				});
+				soFar = soFar.slice( matched.length );
+			}
+		}
+
+		if ( !matched ) {
+			break;
+		}
+	}
+
+	return parseOnly ?
+		soFar.length :
+		soFar ?
+			Sizzle.error( selector ) :
+			tokenCache( selector, groups ).slice( 0 );
+}
+
+function toSelector( tokens ) {
+	var i = 0,
+		len = tokens.length,
+		selector = "";
+	for ( ; i < len; i++ ) {
+		selector += tokens[i].value;
+	}
+	return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+	var dir = combinator.dir,
+		checkNonElements = base && dir === "parentNode",
+		doneName = done++;
+
+	return combinator.first ?
+		function( elem, context, xml ) {
+			while ( (elem = elem[ dir ]) ) {
+				if ( elem.nodeType === 1 || checkNonElements ) {
+					return matcher( elem, context, xml );
+				}
+			}
+		} :
+
+		function( elem, context, xml ) {
+			var oldCache, outerCache,
+				newCache = [ dirruns, doneName ];
+
+			if ( xml ) {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						if ( matcher( elem, context, xml ) ) {
+							return true;
+						}
+					}
+				}
+			} else {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						outerCache = elem[ expando ] || (elem[ expando ] = {});
+						if ( (oldCache = outerCache[ dir ]) &&
+							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+
+							return (newCache[ 2 ] = oldCache[ 2 ]);
+						} else {
+							outerCache[ dir ] = newCache;
+
+							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+								return true;
+							}
+						}
+					}
+				}
+			}
+		};
+}
+
+function elementMatcher( matchers ) {
+	return matchers.length > 1 ?
+		function( elem, context, xml ) {
+			var i = matchers.length;
+			while ( i-- ) {
+				if ( !matchers[i]( elem, context, xml ) ) {
+					return false;
+				}
+			}
+			return true;
+		} :
+		matchers[0];
+}
+
+function multipleContexts( selector, contexts, results ) {
+	var i = 0,
+		len = contexts.length;
+	for ( ; i < len; i++ ) {
+		Sizzle( selector, contexts[i], results );
+	}
+	return results;
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+	var elem,
+		newUnmatched = [],
+		i = 0,
+		len = unmatched.length,
+		mapped = map != null;
+
+	for ( ; i < len; i++ ) {
+		if ( (elem = unmatched[i]) ) {
+			if ( !filter || filter( elem, context, xml ) ) {
+				newUnmatched.push( elem );
+				if ( mapped ) {
+					map.push( i );
+				}
+			}
+		}
+	}
+
+	return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+	if ( postFilter && !postFilter[ expando ] ) {
+		postFilter = setMatcher( postFilter );
+	}
+	if ( postFinder && !postFinder[ expando ] ) {
+		postFinder = setMatcher( postFinder, postSelector );
+	}
+	return markFunction(function( seed, results, context, xml ) {
+		var temp, i, elem,
+			preMap = [],
+			postMap = [],
+			preexisting = results.length,
+
+			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+			matcherIn = preFilter && ( seed || !selector ) ?
+				condense( elems, preMap, preFilter, context, xml ) :
+				elems,
+
+			matcherOut = matcher ?
+				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+					[] :
+
+					results :
+				matcherIn;
+
+		if ( matcher ) {
+			matcher( matcherIn, matcherOut, context, xml );
+		}
+
+		if ( postFilter ) {
+			temp = condense( matcherOut, postMap );
+			postFilter( temp, [], context, xml );
+
+			i = temp.length;
+			while ( i-- ) {
+				if ( (elem = temp[i]) ) {
+					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+				}
+			}
+		}
+
+		if ( seed ) {
+			if ( postFinder || preFilter ) {
+				if ( postFinder ) {
+					temp = [];
+					i = matcherOut.length;
+					while ( i-- ) {
+						if ( (elem = matcherOut[i]) ) {
+							temp.push( (matcherIn[i] = elem) );
+						}
+					}
+					postFinder( null, (matcherOut = []), temp, xml );
+				}
+
+				i = matcherOut.length;
+				while ( i-- ) {
+					if ( (elem = matcherOut[i]) &&
+						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+						seed[temp] = !(results[temp] = elem);
+					}
+				}
+			}
+
+		} else {
+			matcherOut = condense(
+				matcherOut === results ?
+					matcherOut.splice( preexisting, matcherOut.length ) :
+					matcherOut
+			);
+			if ( postFinder ) {
+				postFinder( null, results, matcherOut, xml );
+			} else {
+				push.apply( results, matcherOut );
+			}
+		}
+	});
+}
+
+function matcherFromTokens( tokens ) {
+	var checkContext, matcher, j,
+		len = tokens.length,
+		leadingRelative = Expr.relative[ tokens[0].type ],
+		implicitRelative = leadingRelative || Expr.relative[" "],
+		i = leadingRelative ? 1 : 0,
+
+		matchContext = addCombinator( function( elem ) {
+			return elem === checkContext;
+		}, implicitRelative, true ),
+		matchAnyContext = addCombinator( function( elem ) {
+			return indexOf.call( checkContext, elem ) > -1;
+		}, implicitRelative, true ),
+		matchers = [ function( elem, context, xml ) {
+			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+				(checkContext = context).nodeType ?
+					matchContext( elem, context, xml ) :
+					matchAnyContext( elem, context, xml ) );
+		} ];
+
+	for ( ; i < len; i++ ) {
+		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+		} else {
+			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+			if ( matcher[ expando ] ) {
+				j = ++i;
+				for ( ; j < len; j++ ) {
+					if ( Expr.relative[ tokens[j].type ] ) {
+						break;
+					}
+				}
+				return setMatcher(
+					i > 1 && elementMatcher( matchers ),
+					i > 1 && toSelector(
+						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+					).replace( rtrim, "$1" ),
+					matcher,
+					i < j && matcherFromTokens( tokens.slice( i, j ) ),
+					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+					j < len && toSelector( tokens )
+				);
+			}
+			matchers.push( matcher );
+		}
+	}
+
+	return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+	var bySet = setMatchers.length > 0,
+		byElement = elementMatchers.length > 0,
+		superMatcher = function( seed, context, xml, results, outermost ) {
+			var elem, j, matcher,
+				matchedCount = 0,
+				i = "0",
+				unmatched = seed && [],
+				setMatched = [],
+				contextBackup = outermostContext,
+				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
+				len = elems.length;
+
+			if ( outermost ) {
+				outermostContext = context !== document && context;
+			}
+
+			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+				if ( byElement && elem ) {
+					j = 0;
+					while ( (matcher = elementMatchers[j++]) ) {
+						if ( matcher( elem, context, xml ) ) {
+							results.push( elem );
+							break;
+						}
+					}
+					if ( outermost ) {
+						dirruns = dirrunsUnique;
+					}
+				}
+
+				if ( bySet ) {
+					if ( (elem = !matcher && elem) ) {
+						matchedCount--;
+					}
+
+					if ( seed ) {
+						unmatched.push( elem );
+					}
+				}
+			}
+
+			matchedCount += i;
+			if ( bySet && i !== matchedCount ) {
+				j = 0;
+				while ( (matcher = setMatchers[j++]) ) {
+					matcher( unmatched, setMatched, context, xml );
+				}
+
+				if ( seed ) {
+					if ( matchedCount > 0 ) {
+						while ( i-- ) {
+							if ( !(unmatched[i] || setMatched[i]) ) {
+								setMatched[i] = pop.call( results );
+							}
+						}
+					}
+
+					setMatched = condense( setMatched );
+				}
+
+				push.apply( results, setMatched );
+
+				if ( outermost && !seed && setMatched.length > 0 &&
+					( matchedCount + setMatchers.length ) > 1 ) {
+
+					Sizzle.uniqueSort( results );
+				}
+			}
+
+			if ( outermost ) {
+				dirruns = dirrunsUnique;
+				outermostContext = contextBackup;
+			}
+
+			return unmatched;
+		};
+
+	return bySet ?
+		markFunction( superMatcher ) :
+		superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+	var i,
+		setMatchers = [],
+		elementMatchers = [],
+		cached = compilerCache[ selector + " " ];
+
+	if ( !cached ) {
+		if ( !match ) {
+			match = tokenize( selector );
+		}
+		i = match.length;
+		while ( i-- ) {
+			cached = matcherFromTokens( match[i] );
+			if ( cached[ expando ] ) {
+				setMatchers.push( cached );
+			} else {
+				elementMatchers.push( cached );
+			}
+		}
+
+		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+
+		cached.selector = selector;
+	}
+	return cached;
+};
+
+/**
+ * A low-level selection function that works with Sizzle's compiled
+ *  selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ *  selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+select = Sizzle.select = function( selector, context, results, seed ) {
+	var i, tokens, token, type, find,
+		compiled = typeof selector === "function" && selector,
+		match = !seed && tokenize( (selector = compiled.selector || selector) );
+
+	results = results || [];
+
+	if ( match.length === 1 ) {
+
+		tokens = match[0] = match[0].slice( 0 );
+		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+				support.getById && context.nodeType === 9 && documentIsHTML &&
+				Expr.relative[ tokens[1].type ] ) {
+
+			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+			if ( !context ) {
+				return results;
+
+			} else if ( compiled ) {
+				context = context.parentNode;
+			}
+
+			selector = selector.slice( tokens.shift().value.length );
+		}
+
+		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+		while ( i-- ) {
+			token = tokens[i];
+
+			if ( Expr.relative[ (type = token.type) ] ) {
+				break;
+			}
+			if ( (find = Expr.find[ type ]) ) {
+				if ( (seed = find(
+					token.matches[0].replace( runescape, funescape ),
+					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+				)) ) {
+
+					tokens.splice( i, 1 );
+					selector = seed.length && toSelector( tokens );
+					if ( !selector ) {
+						push.apply( results, seed );
+						return results;
+					}
+
+					break;
+				}
+			}
+		}
+	}
+
+	( compiled || compile( selector, match ) )(
+		seed,
+		context,
+		!documentIsHTML,
+		results,
+		rsibling.test( selector ) && testContext( context.parentNode ) || context
+	);
+	return results;
+};
+
+
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+support.detectDuplicates = !!hasDuplicate;
+
+setDocument();
+
+support.sortDetached = assert(function( div1 ) {
+	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+if ( !assert(function( div ) {
+	div.innerHTML = "<a href='#'></a>";
+	return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+	addHandle( "type|href|height|width", function( elem, name, isXML ) {
+		if ( !isXML ) {
+			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+		}
+	});
+}
+
+if ( !support.attributes || !assert(function( div ) {
+	div.innerHTML = "<input/>";
+	div.firstChild.setAttribute( "value", "" );
+	return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+	addHandle( "value", function( elem, name, isXML ) {
+		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+			return elem.defaultValue;
+		}
+	});
+}
+
+if ( !assert(function( div ) {
+	return div.getAttribute("disabled") == null;
+}) ) {
+	addHandle( booleans, function( elem, name, isXML ) {
+		var val;
+		if ( !isXML ) {
+			return elem[ name ] === true ? name.toLowerCase() :
+					(val = elem.getAttributeNode( name )) && val.specified ?
+					val.value :
+				null;
+		}
+	});
+}
+
+if ( typeof define === "function" && define.amd ) {
+	define(function() { return Sizzle; });
+} else if ( typeof module !== "undefined" && module.exports ) {
+	module.exports = Sizzle;
+} else {
+	window.Sizzle = Sizzle;
+}
+
+})( window );
+
+;(function() {
+  if (typeof Sizzle !== 'undefined') {
+    return;
+  }
+
+  if (typeof define !== 'undefined' && define.amd) {
+    window.Sizzle = Prototype._actual_sizzle;
+    window.define = Prototype._original_define;
+    delete Prototype._actual_sizzle;
+    delete Prototype._original_define;
+  } else if (typeof module !== 'undefined' && module.exports) {
+    window.Sizzle = module.exports;
+    module.exports = {};
+  }
+})();
+
+;(function(engine) {
+  var extendElements = Prototype.Selector.extendElements;
+
+  function select(selector, scope) {
+    return extendElements(engine(selector, scope || document));
+  }
+
+  function match(element, selector) {
+    return engine.matches(selector, [element]).length == 1;
+  }
+
+  Prototype.Selector.engine = engine;
+  Prototype.Selector.select = select;
+  Prototype.Selector.match = match;
+})(Sizzle);
+
+window.Sizzle = Prototype._original_property;
+delete Prototype._original_property;
+
+var Form = {
+  reset: function(form) {
+    form = $(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, accumulator, initial;
+
+    if (options.hash) {
+      initial = {};
+      accumulator = function(result, key, value) {
+        if (key in result) {
+          if (!Object.isArray(result[key])) result[key] = [result[key]];
+          result[key] = result[key].concat(value);
+        } else result[key] = value;
+        return result;
+      };
+    } else {
+      initial = '';
+      accumulator = function(result, key, values) {
+        if (!Object.isArray(values)) {values = [values];}
+        if (!values.length) {return result;}
+        var encodedKey = encodeURIComponent(key).gsub(/%20/, '+');
+        return result + (result ? "&" : "") + values.map(function (value) {
+          value = value.gsub(/(\r)?\n/, '\r\n');
+          value = encodeURIComponent(value);
+          value = value.gsub(/%20/, '+');
+          return encodedKey + "=" + value;
+        }).join("&");
+      };
+    }
+
+    return elements.inject(initial, 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)))) {
+          result = accumulator(result, key, value);
+        }
+      }
+      return result;
+    });
+  }
+};
+
+Form.Methods = {
+  serialize: function(form, options) {
+    return Form.serializeElements(Form.getElements(form), options);
+  },
+
+
+  getElements: function(form) {
+    var elements = $(form).getElementsByTagName('*');
+    var element, results = [], serializers = Form.Element.Serializers;
+
+    for (var i = 0; element = elements[i]; i++) {
+      if (serializers[element.tagName.toLowerCase()])
+        results.push(Element.extend(element));
+    }
+    return results;
+  },
+
   getInputs: function(form, typeName, name) {
-    var form = $(form);
+    form = $(form);
     var inputs = form.getElementsByTagName('input');
-    
-    if (!typeName && !name)
-      return inputs;
-      
-    var matchingInputs = new Array();
-    for (var i = 0; i < inputs.length; i++) {
+
+    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)) 
+      if ((typeName && input.type != typeName) || (name && input.name != name))
         continue;
-      matchingInputs.push(input);
+      matchingInputs.push(Element.extend(input));
     }
 
     return matchingInputs;
   },
 
   disable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.blur();
-      element.disabled = 'true';
-    }
+    form = $(form);
+    Form.getElements(form).invoke('disable');
+    return form;
   },
 
   enable: function(form) {
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      element.disabled = '';
-    }
+    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)$/i.test(element.tagName);
+    });
   },
 
   focusFirstElement: function(form) {
-    var form = $(form);
-    var elements = Form.getElements(form);
-    for (var i = 0; i < elements.length; i++) {
-      var element = elements[i];
-      if (element.type != 'hidden' && !element.disabled) {
-        Field.activate(element);
-        break;
-      }
-    }
+    form = $(form);
+    var element = form.findFirstElement();
+    if (element) element.activate();
+    return form;
   },
 
-  reset: function(form) {
-    $(form).reset();
+  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 = {
-  serialize: function(element) {
-    var element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
-    
-    if (parameter)
-      return encodeURIComponent(parameter[0]) + '=' + 
-        encodeURIComponent(parameter[1]);                   
-  },
-  
-  getValue: function(element) {
-    var element = $(element);
-    var method = element.tagName.toLowerCase();
-    var parameter = Form.Element.Serializers[method](element);
-    
-    if (parameter) 
-      return parameter[1];
-  }
-}
-
-Form.Element.Serializers = {
-  input: function(element) {
-    switch (element.type.toLowerCase()) {
-      case 'submit':
-      case 'hidden':
-      case 'password':
-      case 'text':
-        return Form.Element.Serializers.textarea(element);
-      case 'checkbox':  
-      case 'radio':
-        return Form.Element.Serializers.inputSelector(element);
-    }
-    return false;
-  },
-
-  inputSelector: function(element) {
-    if (element.checked)
-      return [element.name, element.value];
-  },
-
-  textarea: function(element) {
-    return [element.name, element.value];
+  focus: function(element) {
+    $(element).focus();
+    return element;
   },
 
   select: function(element) {
-    var value = '';
-    if (element.type == 'select-one') {
-      var index = element.selectedIndex;
-      if (index >= 0)
-        value = element.options[index].value || element.options[index].text;
-    } else {
-      value = new Array();
-      for (var i = 0; i < element.length; i++) {
-        var opt = element.options[i];
-        if (opt.selected)
-          value.push(opt.value || opt.text);
+    $(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 [element.name, value];
+    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)$/i.test(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 $F = Form.Element.getValue;
+var Field = Form.Element;
+
+var $F = Form.Element.Methods.getValue;
 
 /*--------------------------------------------------------------------------*/
 
-Abstract.TimedObserver = function() {}
-Abstract.TimedObserver.prototype = {
-  initialize: function(element, frequency, callback) {
-    this.frequency = frequency;
+Form.Element.Serializers = (function() {
+  function input(element, value) {
+    switch (element.type.toLowerCase()) {
+      case 'checkbox':
+      case 'radio':
+        return inputSelector(element, value);
+      default:
+        return valueSelector(element, value);
+    }
+  }
+
+  function inputSelector(element, value) {
+    if (Object.isUndefined(value))
+      return element.checked ? element.value : null;
+    else element.checked = !!value;
+  }
+
+  function valueSelector(element, value) {
+    if (Object.isUndefined(value)) return element.value;
+    else element.value = value;
+  }
+
+  function select(element, value) {
+    if (Object.isUndefined(value))
+      return (element.type === 'select-one' ? selectOne : selectMany)(element);
+
+    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);
+    }
+  }
+
+  function selectOne(element) {
+    var index = element.selectedIndex;
+    return index >= 0 ? optionValue(element.options[index]) : null;
+  }
+
+  function selectMany(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(optionValue(opt));
+    }
+    return values;
+  }
+
+  function optionValue(opt) {
+    return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
+  }
+
+  return {
+    input:         input,
+    inputSelector: inputSelector,
+    textarea:      valueSelector,
+    select:        select,
+    selectOne:     selectOne,
+    selectMany:    selectMany,
+    optionValue:   optionValue,
+    button:        valueSelector
+  };
+})();
+
+/*--------------------------------------------------------------------------*/
+
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+  initialize: function($super, element, frequency, callback) {
+    $super(callback, frequency);
     this.element   = $(element);
-    this.callback  = callback;
-    
     this.lastValue = this.getValue();
-    this.registerCallback();
   },
-  
-  registerCallback: function() {
-    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
-  },
-  
-  onTimerEvent: function() {
+
+  execute: function() {
     var value = this.getValue();
-    if (this.lastValue != value) {
+    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();
-Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
   getValue: function() {
     return Form.Element.getValue(this.element);
   }
 });
 
-Form.Observer = Class.create();
-Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
+Form.Observer = Class.create(Abstract.TimedObserver, {
   getValue: function() {
     return Form.serialize(this.element);
   }
@@ -988,19 +6631,18 @@
 
 /*--------------------------------------------------------------------------*/
 
-Abstract.EventObserver = function() {}
-Abstract.EventObserver.prototype = {
+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) {
@@ -1008,250 +6650,939 @@
       this.lastValue = value;
     }
   },
-  
+
   registerFormCallbacks: function() {
-    var elements = Form.getElements(this.element);
-    for (var i = 0; i < elements.length; i++)
-      this.registerCallback(elements[i]);
+    Form.getElements(this.element).each(this.registerCallback, this);
   },
-  
+
   registerCallback: function(element) {
     if (element.type) {
       switch (element.type.toLowerCase()) {
-        case 'checkbox':  
+        case 'checkbox':
         case 'radio':
-          element.target = this;
-          element.prev_onclick = element.onclick || Prototype.emptyFunction;
-          element.onclick = function() {
-            this.prev_onclick(); 
-            this.target.onElementEvent();
-          }
+          Event.observe(element, 'click', this.onElementEvent.bind(this));
           break;
-        case 'password':
-        case 'text':
-        case 'textarea':
-        case 'select-one':
-        case 'select-multiple':
-          element.target = this;
-          element.prev_onchange = element.onchange || Prototype.emptyFunction;
-          element.onchange = function() {
-            this.prev_onchange(); 
-            this.target.onElementEvent();
-          }
+        default:
+          Event.observe(element, 'change', this.onElementEvent.bind(this));
           break;
       }
-    }    
+    }
   }
-}
+});
 
-Form.Element.EventObserver = Class.create();
-Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
   getValue: function() {
     return Form.Element.getValue(this.element);
   }
 });
 
-Form.EventObserver = Class.create();
-Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
+Form.EventObserver = Class.create(Abstract.EventObserver, {
   getValue: function() {
     return Form.serialize(this.element);
   }
 });
+(function(GLOBAL) {
+  var DIV = document.createElement('div');
+  var docEl = document.documentElement;
+  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
+   && 'onmouseleave' in docEl;
+
+  var 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
+  };
 
 
-if (!window.Event) {
-  var Event = new Object();
-}
+  var isIELegacyEvent = function(event) { return false; };
 
-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,
-
-  element: function(event) {
-    return event.target || event.srcElement;
-  },
-
-  isLeftClick: function(event) {
-    return (((event.which) && (event.which == 1)) ||
-            ((event.button) && (event.button == 1)));
-  },
-
-  pointerX: function(event) {
-    return event.pageX || (event.clientX + 
-      (document.documentElement.scrollLeft || document.body.scrollLeft));
-  },
-
-  pointerY: function(event) {
-    return event.pageY || (event.clientY + 
-      (document.documentElement.scrollTop || document.body.scrollTop));
-  },
-
-  stop: function(event) {
-    if (event.preventDefault) { 
-      event.preventDefault(); 
-      event.stopPropagation(); 
+  if (window.attachEvent) {
+    if (window.addEventListener) {
+      isIELegacyEvent = function(event) {
+        return !(event instanceof window.Event);
+      };
     } else {
-      event.returnValue = false;
-    }
-  },
-
-  // find the first node with the given tagName, starting from the
-  // node the event was triggered on; traverses the DOM upwards
-  findElement: function(event, tagName) {
-    var element = Event.element(event);
-    while (element.parentNode && (!element.tagName ||
-        (element.tagName.toUpperCase() != tagName.toUpperCase())))
-      element = element.parentNode;
-    return element;
-  },
-
-  observers: false,
-  
-  _observeAndCache: function(element, name, observer, useCapture) {
-    if (!this.observers) this.observers = [];
-    if (element.addEventListener) {
-      this.observers.push([element, name, observer, useCapture]);
-      element.addEventListener(name, observer, useCapture);
-    } else if (element.attachEvent) {
-      this.observers.push([element, name, observer, useCapture]);
-      element.attachEvent('on' + name, observer);
-    }
-  },
-  
-  unloadCache: function() {
-    if (!Event.observers) return;
-    for (var i = 0; i < Event.observers.length; i++) {
-      Event.stopObserving.apply(this, Event.observers[i]);
-      Event.observers[i][0] = null;
-    }
-    Event.observers = false;
-  },
-
-  observe: function(element, name, observer, useCapture) {
-    var element = $(element);
-    useCapture = useCapture || false;
-    
-    if (name == 'keypress' &&
-        ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) 
-        || element.attachEvent))
-      name = 'keydown';
-    
-    this._observeAndCache(element, name, observer, useCapture);
-  },
-
-  stopObserving: function(element, name, observer, useCapture) {
-    var element = $(element);
-    useCapture = useCapture || false;
-    
-    if (name == 'keypress' &&
-        ((/Konqueror|Safari|KHTML/.test(navigator.userAgent)) 
-        || element.detachEvent))
-      name = 'keydown';
-    
-    if (element.removeEventListener) {
-      element.removeEventListener(name, observer, useCapture);
-    } else if (element.detachEvent) {
-      element.detachEvent('on' + name, observer);
+      isIELegacyEvent = function(event) { return true; };
     }
   }
+
+  var _isButton;
+
+  function _isButtonForDOMEvents(event, code) {
+    return event.which ? (event.which === code + 1) : (event.button === code);
+  }
+
+  var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
+  function _isButtonForLegacyEvents(event, code) {
+    return event.button === legacyButtonMap[code];
+  }
+
+  function _isButtonForWebKit(event, code) {
+    switch (code) {
+      case 0: return event.which == 1 && !event.metaKey;
+      case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
+      case 2: return event.which == 3;
+      default: return false;
+    }
+  }
+
+  if (window.attachEvent) {
+    if (!window.addEventListener) {
+      _isButton = _isButtonForLegacyEvents;
+    } else {
+      _isButton = function(event, code) {
+        return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
+         _isButtonForDOMEvents(event, code);
+      }
+    }
+  } else if (Prototype.Browser.WebKit) {
+    _isButton = _isButtonForWebKit;
+  } else {
+    _isButton = _isButtonForDOMEvents;
+  }
+
+  function isLeftClick(event)   { return _isButton(event, 0) }
+
+  function isMiddleClick(event) { return _isButton(event, 1) }
+
+  function isRightClick(event)  { return _isButton(event, 2) }
+
+  function element(event) {
+    return Element.extend(_element(event));
+  }
+
+  function _element(event) {
+    event = Event.extend(event);
+
+    var node = event.target, type = event.type,
+     currentTarget = event.currentTarget;
+
+    if (currentTarget && currentTarget.tagName) {
+      if (type === 'load' || type === 'error' ||
+        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
+          && currentTarget.type === 'radio'))
+            node = currentTarget;
+    }
+
+    return node.nodeType == Node.TEXT_NODE ? node.parentNode : node;
+  }
+
+  function findElement(event, expression) {
+    var element = _element(event), selector = Prototype.Selector;
+    if (!expression) return Element.extend(element);
+    while (element) {
+      if (Object.isElement(element) && selector.match(element, expression))
+        return Element.extend(element);
+      element = element.parentNode;
+    }
+  }
+
+  function pointer(event) {
+    return { x: pointerX(event), y: pointerY(event) };
+  }
+
+  function pointerX(event) {
+    var docElement = document.documentElement,
+     body = document.body || { scrollLeft: 0 };
+
+    return event.pageX || (event.clientX +
+      (docElement.scrollLeft || body.scrollLeft) -
+      (docElement.clientLeft || 0));
+  }
+
+  function pointerY(event) {
+    var docElement = document.documentElement,
+     body = document.body || { scrollTop: 0 };
+
+    return  event.pageY || (event.clientY +
+       (docElement.scrollTop || body.scrollTop) -
+       (docElement.clientTop || 0));
+  }
+
+
+  function stop(event) {
+    Event.extend(event);
+    event.preventDefault();
+    event.stopPropagation();
+
+    event.stopped = true;
+  }
+
+
+  Event.Methods = {
+    isLeftClick:   isLeftClick,
+    isMiddleClick: isMiddleClick,
+    isRightClick:  isRightClick,
+
+    element:     element,
+    findElement: findElement,
+
+    pointer:  pointer,
+    pointerX: pointerX,
+    pointerY: pointerY,
+
+    stop: stop
+  };
+
+  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+    m[name] = Event.Methods[name].methodize();
+    return m;
+  });
+
+  if (window.attachEvent) {
+    function _relatedTarget(event) {
+      var element;
+      switch (event.type) {
+        case 'mouseover':
+        case 'mouseenter':
+          element = event.fromElement;
+          break;
+        case 'mouseout':
+        case 'mouseleave':
+          element = event.toElement;
+          break;
+        default:
+          return null;
+      }
+      return Element.extend(element);
+    }
+
+    var additionalMethods = {
+      stopPropagation: function() { this.cancelBubble = true },
+      preventDefault:  function() { this.returnValue = false },
+      inspect: function() { return '[object Event]' }
+    };
+
+    Event.extend = function(event, element) {
+      if (!event) return false;
+
+      if (!isIELegacyEvent(event)) return event;
+
+      if (event._extendedByPrototype) return event;
+      event._extendedByPrototype = Prototype.emptyFunction;
+
+      var pointer = Event.pointer(event);
+
+      Object.extend(event, {
+        target: event.srcElement || element,
+        relatedTarget: _relatedTarget(event),
+        pageX:  pointer.x,
+        pageY:  pointer.y
+      });
+
+      Object.extend(event, methods);
+      Object.extend(event, additionalMethods);
+
+      return event;
+    };
+  } else {
+    Event.extend = Prototype.K;
+  }
+
+  if (window.addEventListener) {
+    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
+    Object.extend(Event.prototype, methods);
+  }
+
+  var EVENT_TRANSLATIONS = {
+    mouseenter: 'mouseover',
+    mouseleave: 'mouseout'
+  };
+
+  function getDOMEventName(eventName) {
+    return EVENT_TRANSLATIONS[eventName] || eventName;
+  }
+
+  if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED)
+    getDOMEventName = Prototype.K;
+
+  function getUniqueElementID(element) {
+    if (element === window) return 0;
+
+    if (typeof element._prototypeUID === 'undefined')
+      element._prototypeUID = Element.Storage.UID++;
+    return element._prototypeUID;
+  }
+
+  function getUniqueElementID_IE(element) {
+    if (element === window) return 0;
+    if (element == document) return 1;
+    return element.uniqueID;
+  }
+
+  if ('uniqueID' in DIV)
+    getUniqueElementID = getUniqueElementID_IE;
+
+  function isCustomEvent(eventName) {
+    return eventName.include(':');
+  }
+
+  Event._isCustomEvent = isCustomEvent;
+
+  function getOrCreateRegistryFor(element, uid) {
+    var CACHE = GLOBAL.Event.cache;
+    if (Object.isUndefined(uid))
+      uid = getUniqueElementID(element);
+    if (!CACHE[uid]) CACHE[uid] = { element: element };
+    return CACHE[uid];
+  }
+
+  function destroyRegistryForElement(element, uid) {
+    if (Object.isUndefined(uid))
+      uid = getUniqueElementID(element);
+    delete GLOBAL.Event.cache[uid];
+  }
+
+
+  function register(element, eventName, handler) {
+    var registry = getOrCreateRegistryFor(element);
+    if (!registry[eventName]) registry[eventName] = [];
+    var entries = registry[eventName];
+
+    var i = entries.length;
+    while (i--)
+      if (entries[i].handler === handler) return null;
+
+    var uid = getUniqueElementID(element);
+    var responder = GLOBAL.Event._createResponder(uid, eventName, handler);
+    var entry = {
+      responder: responder,
+      handler:   handler
+    };
+
+    entries.push(entry);
+    return entry;
+  }
+
+  function unregister(element, eventName, handler) {
+    var registry = getOrCreateRegistryFor(element);
+    var entries = registry[eventName] || [];
+
+    var i = entries.length, entry;
+    while (i--) {
+      if (entries[i].handler === handler) {
+        entry = entries[i];
+        break;
+      }
+    }
+
+    if (entry) {
+      var index = entries.indexOf(entry);
+      entries.splice(index, 1);
+    }
+
+    if (entries.length === 0) {
+      delete registry[eventName];
+      if (Object.keys(registry).length === 1 && ('element' in registry))
+        destroyRegistryForElement(element);
+    }
+
+    return entry;
+  }
+
+
+  function observe(element, eventName, handler) {
+    element = $(element);
+    var entry = register(element, eventName, handler);
+
+    if (entry === null) return element;
+
+    var responder = entry.responder;
+    if (isCustomEvent(eventName))
+      observeCustomEvent(element, eventName, responder);
+    else
+      observeStandardEvent(element, eventName, responder);
+
+    return element;
+  }
+
+  function observeStandardEvent(element, eventName, responder) {
+    var actualEventName = getDOMEventName(eventName);
+    if (element.addEventListener) {
+      element.addEventListener(actualEventName, responder, false);
+    } else {
+      element.attachEvent('on' + actualEventName, responder);
+    }
+  }
+
+  function observeCustomEvent(element, eventName, responder) {
+    if (element.addEventListener) {
+      element.addEventListener('dataavailable', responder, false);
+    } else {
+      element.attachEvent('ondataavailable', responder);
+      element.attachEvent('onlosecapture',   responder);
+    }
+  }
+
+  function stopObserving(element, eventName, handler) {
+    element = $(element);
+    var handlerGiven = !Object.isUndefined(handler),
+     eventNameGiven = !Object.isUndefined(eventName);
+
+    if (!eventNameGiven && !handlerGiven) {
+      stopObservingElement(element);
+      return element;
+    }
+
+    if (!handlerGiven) {
+      stopObservingEventName(element, eventName);
+      return element;
+    }
+
+    var entry = unregister(element, eventName, handler);
+
+    if (!entry) return element;
+    removeEvent(element, eventName, entry.responder);
+    return element;
+  }
+
+  function stopObservingStandardEvent(element, eventName, responder) {
+    var actualEventName = getDOMEventName(eventName);
+    if (element.removeEventListener) {
+      element.removeEventListener(actualEventName, responder, false);
+    } else {
+      element.detachEvent('on' + actualEventName, responder);
+    }
+  }
+
+  function stopObservingCustomEvent(element, eventName, responder) {
+    if (element.removeEventListener) {
+      element.removeEventListener('dataavailable', responder, false);
+    } else {
+      element.detachEvent('ondataavailable', responder);
+      element.detachEvent('onlosecapture',   responder);
+    }
+  }
+
+
+
+  function stopObservingElement(element) {
+    var uid = getUniqueElementID(element), registry = GLOBAL.Event.cache[uid];
+    if (!registry) return;
+
+    destroyRegistryForElement(element, uid);
+
+    var entries, i;
+    for (var eventName in registry) {
+      if (eventName === 'element') continue;
+
+      entries = registry[eventName];
+      i = entries.length;
+      while (i--)
+        removeEvent(element, eventName, entries[i].responder);
+    }
+  }
+
+  function stopObservingEventName(element, eventName) {
+    var registry = getOrCreateRegistryFor(element);
+    var entries = registry[eventName];
+    if (entries) {
+      delete registry[eventName];
+    }
+
+    entries = entries || [];
+
+    var i = entries.length;
+    while (i--)
+      removeEvent(element, eventName, entries[i].responder);
+
+    for (var name in registry) {
+      if (name === 'element') continue;
+      return; // There is another registered event
+    }
+
+    destroyRegistryForElement(element);
+  }
+
+
+  function removeEvent(element, eventName, handler) {
+    if (isCustomEvent(eventName))
+      stopObservingCustomEvent(element, eventName, handler);
+    else
+      stopObservingStandardEvent(element, eventName, handler);
+  }
+
+
+
+  function getFireTarget(element) {
+    if (element !== document) return element;
+    if (document.createEvent && !element.dispatchEvent)
+      return document.documentElement;
+    return element;
+  }
+
+  function fire(element, eventName, memo, bubble) {
+    element = getFireTarget($(element));
+    if (Object.isUndefined(bubble)) bubble = true;
+    memo = memo || {};
+
+    var event = fireEvent(element, eventName, memo, bubble);
+    return Event.extend(event);
+  }
+
+  function fireEvent_DOM(element, eventName, memo, bubble) {
+    var event = document.createEvent('HTMLEvents');
+    event.initEvent('dataavailable', bubble, true);
+
+    event.eventName = eventName;
+    event.memo = memo;
+
+    element.dispatchEvent(event);
+    return event;
+  }
+
+  function fireEvent_IE(element, eventName, memo, bubble) {
+    var event = document.createEventObject();
+    event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
+
+    event.eventName = eventName;
+    event.memo = memo;
+
+    element.fireEvent(event.eventType, event);
+    return event;
+  }
+
+  var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE;
+
+
+
+  Event.Handler = Class.create({
+    initialize: function(element, eventName, selector, callback) {
+      this.element   = $(element);
+      this.eventName = eventName;
+      this.selector  = selector;
+      this.callback  = callback;
+      this.handler   = this.handleEvent.bind(this);
+    },
+
+
+    start: function() {
+      Event.observe(this.element, this.eventName, this.handler);
+      return this;
+    },
+
+    stop: function() {
+      Event.stopObserving(this.element, this.eventName, this.handler);
+      return this;
+    },
+
+    handleEvent: function(event) {
+      var element = Event.findElement(event, this.selector);
+      if (element) this.callback.call(this.element, event, element);
+    }
+  });
+
+  function on(element, eventName, selector, callback) {
+    element = $(element);
+    if (Object.isFunction(selector) && Object.isUndefined(callback)) {
+      callback = selector, selector = null;
+    }
+
+    return new Event.Handler(element, eventName, selector, callback).start();
+  }
+
+  Object.extend(Event, Event.Methods);
+
+  Object.extend(Event, {
+    fire:          fire,
+    observe:       observe,
+    stopObserving: stopObserving,
+    on:            on
+  });
+
+  Element.addMethods({
+    fire:          fire,
+
+    observe:       observe,
+
+    stopObserving: stopObserving,
+
+    on:            on
+  });
+
+  Object.extend(document, {
+    fire:          fire.methodize(),
+
+    observe:       observe.methodize(),
+
+    stopObserving: stopObserving.methodize(),
+
+    on:            on.methodize(),
+
+    loaded:        false
+  });
+
+  if (GLOBAL.Event) Object.extend(window.Event, Event);
+  else GLOBAL.Event = Event;
+
+  GLOBAL.Event.cache = {};
+
+  function destroyCache_IE() {
+    GLOBAL.Event.cache = null;
+  }
+
+  if (window.attachEvent)
+    window.attachEvent('onunload', destroyCache_IE);
+
+  DIV = null;
+  docEl = null;
+})(this);
+
+(function(GLOBAL) {
+  /* Code for creating leak-free event responders is based on work by
+   John-David Dalton. */
+
+  var docEl = document.documentElement;
+  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
+    && 'onmouseleave' in docEl;
+
+  function isSimulatedMouseEnterLeaveEvent(eventName) {
+    return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
+     (eventName === 'mouseenter' || eventName === 'mouseleave');
+  }
+
+  function createResponder(uid, eventName, handler) {
+    if (Event._isCustomEvent(eventName))
+      return createResponderForCustomEvent(uid, eventName, handler);
+    if (isSimulatedMouseEnterLeaveEvent(eventName))
+      return createMouseEnterLeaveResponder(uid, eventName, handler);
+
+    return function(event) {
+      if (!Event.cache) return;
+
+      var element = Event.cache[uid].element;
+      Event.extend(event, element);
+      handler.call(element, event);
+    };
+  }
+
+  function createResponderForCustomEvent(uid, eventName, handler) {
+    return function(event) {
+      var cache = Event.cache[uid];
+      var element =  cache && cache.element;
+
+      if (Object.isUndefined(event.eventName))
+        return false;
+
+      if (event.eventName !== eventName)
+        return false;
+
+      Event.extend(event, element);
+      handler.call(element, event);
+    };
+  }
+
+  function createMouseEnterLeaveResponder(uid, eventName, handler) {
+    return function(event) {
+      var element = Event.cache[uid].element;
+
+      Event.extend(event, element);
+      var parent = event.relatedTarget;
+
+      while (parent && parent !== element) {
+        try { parent = parent.parentNode; }
+        catch(e) { parent = element; }
+      }
+
+      if (parent === element) return;
+      handler.call(element, event);
+    }
+  }
+
+  GLOBAL.Event._createResponder = createResponder;
+  docEl = null;
+})(this);
+
+(function(GLOBAL) {
+  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
+
+  var TIMER;
+
+  function fireContentLoadedEvent() {
+    if (document.loaded) return;
+    if (TIMER) window.clearTimeout(TIMER);
+    document.loaded = true;
+    document.fire('dom:loaded');
+  }
+
+  function checkReadyState() {
+    if (document.readyState === 'complete') {
+      document.detachEvent('onreadystatechange', checkReadyState);
+      fireContentLoadedEvent();
+    }
+  }
+
+  function pollDoScroll() {
+    try {
+      document.documentElement.doScroll('left');
+    } catch (e) {
+      TIMER = pollDoScroll.defer();
+      return;
+    }
+
+    fireContentLoadedEvent();
+  }
+
+
+  if (document.readyState === 'complete') {
+    fireContentLoadedEvent();
+    return;
+  }
+
+  if (document.addEventListener) {
+    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
+  } else {
+    document.attachEvent('onreadystatechange', checkReadyState);
+    if (window == top) TIMER = pollDoScroll.defer();
+  }
+
+  Event.observe(window, 'load', fireContentLoadedEvent);
+})(this);
+
+
+Element.addMethods();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.addMethods({
+  childOf: Element.Methods.descendantOf
 });
 
-/* prevent memory leaks in IE */
-Event.observe(window, 'unload', Event.unloadCache, false);
+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');
 
 var Position = {
+  includeScrollOffsets: false,
 
-  // 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 
+    this.deltaX =  window.pageXOffset
+                || document.documentElement.scrollLeft
+                || document.body.scrollLeft
                 || 0;
-    this.deltaY =  window.pageYOffset 
-                || document.documentElement.scrollTop 
-                || document.body.scrollTop 
+    this.deltaY =  window.pageYOffset
+                || document.documentElement.scrollTop
+                || document.body.scrollTop
                 || 0;
   },
 
-  realOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.scrollTop  || 0;
-      valueL += element.scrollLeft || 0; 
-      element = element.parentNode;
-    } while (element);
-    return [valueL, valueT];
-  },
-
-  cumulativeOffset: function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      element = element.offsetParent;
-    } while (element);
-    return [valueL, valueT];
-  },
-
-  // caches x/y coordinate pair to use with overlap
   within: function(element, x, y) {
     if (this.includeScrollOffsets)
       return this.withinIncludingScrolloffsets(element, x, y);
     this.xcomp = x;
     this.ycomp = y;
-    this.offset = this.cumulativeOffset(element);
+    this.offset = Element.cumulativeOffset(element);
 
     return (y >= this.offset[1] &&
             y <  this.offset[1] + element.offsetHeight &&
-            x >= this.offset[0] && 
+            x >= this.offset[0] &&
             x <  this.offset[0] + element.offsetWidth);
   },
 
   withinIncludingScrolloffsets: function(element, x, y) {
-    var offsetcache = this.realOffset(element);
+    var offsetcache = Element.cumulativeScrollOffset(element);
 
     this.xcomp = x + offsetcache[0] - this.deltaX;
     this.ycomp = y + offsetcache[1] - this.deltaY;
-    this.offset = this.cumulativeOffset(element);
+    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] &&
             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) / 
+  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) / 
+      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
         element.offsetWidth;
   },
 
-  clone: function(source, target) {
-    source = $(source);
-    target = $(target);
-    target.style.position = 'absolute';
-    var offsets = this.cumulativeOffset(source);
-    target.style.top    = offsets[1] + 'px';
-    target.style.left   = offsets[0] + 'px';
-    target.style.width  = source.offsetWidth + 'px';
-    target.style.height = source.offsetHeight + 'px';
+
+  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, context) {
+    this.element.className.split(/\s+/).select(function(name) {
+      return name.length > 0;
+    })._each(iterator, context);
+  },
+
+  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);
+
+/*--------------------------------------------------------------------------*/
+
+(function() {
+  window.Selector = Class.create({
+    initialize: function(expression) {
+      this.expression = expression.strip();
+    },
+
+    findElements: function(rootElement) {
+      return Prototype.Selector.select(this.expression, rootElement);
+    },
+
+    match: function(element) {
+      return Prototype.Selector.match(element, this.expression);
+    },
+
+    toString: function() {
+      return this.expression;
+    },
+
+    inspect: function() {
+      return "#<Selector: " + this.expression + ">";
+    }
+  });
+
+  Object.extend(Selector, {
+    matchElements: function(elements, expression) {
+      var match = Prototype.Selector.match,
+          results = [];
+
+      for (var i = 0, length = elements.length; i < length; i++) {
+        var element = elements[i];
+        if (match(element, expression)) {
+          results.push(Element.extend(element));
+        }
+      }
+      return results;
+    },
+
+    findElement: function(elements, expression, index) {
+      index = index || 0;
+      var matchIndex = 0, element;
+      for (var i = 0, length = elements.length; i < length; i++) {
+        element = elements[i];
+        if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
+          return Element.extend(element);
+        }
+      }
+    },
+
+    findChildElements: function(element, expressions) {
+      var selector = expressions.toArray().join(', ');
+      return Prototype.Selector.select(selector, element || document);
+    }
+  });
+})();