Subversion Repositories HomeAutomation

Rev

Blame | Last modification | View Log | SVN | RSS feed

  1. /*  Prototype JavaScript framework, version 1.6.1_rc3
  2.  *  (c) 2005-2009 Sam Stephenson
  3.  *
  4.  *  Prototype is freely distributable under the terms of an MIT-style license.
  5.  *  For details, see the Prototype web site: http://www.prototypejs.org/
  6.  *
  7.  *--------------------------------------------------------------------------*/
  8.  
  9. var Prototype = {
  10.   Version: '1.6.1_rc3',
  11.  
  12.   Browser: (function(){
  13.     var ua = navigator.userAgent;
  14.     var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
  15.     return {
  16.       IE:             !!window.attachEvent && !isOpera,
  17.       Opera:          isOpera,
  18.       WebKit:         ua.indexOf('AppleWebKit/') > -1,
  19.       Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
  20.       MobileSafari:   /Apple.*Mobile.*Safari/.test(ua)
  21.     }
  22.   })(),
  23.  
  24.   BrowserFeatures: {
  25.     XPath: !!document.evaluate,
  26.     SelectorsAPI: !!document.querySelector,
  27.     ElementExtensions: (function() {
  28.       var constructor = window.Element || window.HTMLElement;
  29.       return !!(constructor && constructor.prototype);
  30.     })(),
  31.     SpecificElementExtensions: (function() {
  32.       if (typeof window.HTMLDivElement !== 'undefined')
  33.         return true;
  34.  
  35.       var div = document.createElement('div');
  36.       var form = document.createElement('form');
  37.       var isSupported = false;
  38.  
  39.       if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
  40.         isSupported = true;
  41.       }
  42.  
  43.       div = form = null;
  44.  
  45.       return isSupported;
  46.     })()
  47.   },
  48.  
  49.   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  50.   JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
  51.  
  52.   emptyFunction: function() { },
  53.   K: function(x) { return x }
  54. };
  55.  
  56. if (Prototype.Browser.MobileSafari)
  57.   Prototype.BrowserFeatures.SpecificElementExtensions = false;
  58.  
  59.  
  60. var Abstract = { };
  61.  
  62.  
  63. var Try = {
  64.   these: function() {
  65.     var returnValue;
  66.  
  67.     for (var i = 0, length = arguments.length; i < length; i++) {
  68.       var lambda = arguments[i];
  69.       try {
  70.         returnValue = lambda();
  71.         break;
  72.       } catch (e) { }
  73.     }
  74.  
  75.     return returnValue;
  76.   }
  77. };
  78.  
  79. /* Based on Alex Arnell's inheritance implementation. */
  80.  
  81. var Class = (function() {
  82.   function subclass() {};
  83.   function create() {
  84.     var parent = null, properties = $A(arguments);
  85.     if (Object.isFunction(properties[0]))
  86.       parent = properties.shift();
  87.  
  88.     function klass() {
  89.       this.initialize.apply(this, arguments);
  90.     }
  91.  
  92.     Object.extend(klass, Class.Methods);
  93.     klass.superclass = parent;
  94.     klass.subclasses = [];
  95.  
  96.     if (parent) {
  97.       subclass.prototype = parent.prototype;
  98.       klass.prototype = new subclass;
  99.       parent.subclasses.push(klass);
  100.     }
  101.  
  102.     for (var i = 0; i < properties.length; i++)
  103.       klass.addMethods(properties[i]);
  104.  
  105.     if (!klass.prototype.initialize)
  106.       klass.prototype.initialize = Prototype.emptyFunction;
  107.  
  108.     klass.prototype.constructor = klass;
  109.     return klass;
  110.   }
  111.  
  112.   function addMethods(source) {
  113.     var ancestor   = this.superclass && this.superclass.prototype;
  114.     var properties = Object.keys(source);
  115.  
  116.     if (!Object.keys({ toString: true }).length) {
  117.       if (source.toString != Object.prototype.toString)
  118.         properties.push("toString");
  119.       if (source.valueOf != Object.prototype.valueOf)
  120.         properties.push("valueOf");
  121.     }
  122.  
  123.     for (var i = 0, length = properties.length; i < length; i++) {
  124.       var property = properties[i], value = source[property];
  125.       if (ancestor && Object.isFunction(value) &&
  126.           value.argumentNames().first() == "$super") {
  127.         var method = value;
  128.         value = (function(m) {
  129.           return function() { return ancestor[m].apply(this, arguments); };
  130.         })(property).wrap(method);
  131.  
  132.         value.valueOf = method.valueOf.bind(method);
  133.         value.toString = method.toString.bind(method);
  134.       }
  135.       this.prototype[property] = value;
  136.     }
  137.  
  138.     return this;
  139.   }
  140.  
  141.   return {
  142.     create: create,
  143.     Methods: {
  144.       addMethods: addMethods
  145.     }
  146.   };
  147. })();
  148. (function() {
  149.  
  150.   function getClass(object) {
  151.     return Object.prototype.toString.call(object)
  152.      .match(/^\[object\s(.*)\]$/)[1];
  153.   }
  154.  
  155.   function extend(destination, source) {
  156.     for (var property in source)
  157.       destination[property] = source[property];
  158.     return destination;
  159.   }
  160.  
  161.   function inspect(object) {
  162.     try {
  163.       if (isUndefined(object)) return 'undefined';
  164.       if (object === null) return 'null';
  165.       return object.inspect ? object.inspect() : String(object);
  166.     } catch (e) {
  167.       if (e instanceof RangeError) return '...';
  168.       throw e;
  169.     }
  170.   }
  171.  
  172.   function toJSON(object) {
  173.     var type = typeof object;
  174.     switch (type) {
  175.       case 'undefined':
  176.       case 'function':
  177.       case 'unknown': return;
  178.       case 'boolean': return object.toString();
  179.     }
  180.  
  181.     if (object === null) return 'null';
  182.     if (object.toJSON) return object.toJSON();
  183.     if (isElement(object)) return;
  184.  
  185.     var results = [];
  186.     for (var property in object) {
  187.       var value = toJSON(object[property]);
  188.       if (!isUndefined(value))
  189.         results.push(property.toJSON() + ': ' + value);
  190.     }
  191.  
  192.     return '{' + results.join(', ') + '}';
  193.   }
  194.  
  195.   function toQueryString(object) {
  196.     return $H(object).toQueryString();
  197.   }
  198.  
  199.   function toHTML(object) {
  200.     return object && object.toHTML ? object.toHTML() : String.interpret(object);
  201.   }
  202.  
  203.   function keys(object) {
  204.     var results = [];
  205.     for (var property in object)
  206.       results.push(property);
  207.     return results;
  208.   }
  209.  
  210.   function values(object) {
  211.     var results = [];
  212.     for (var property in object)
  213.       results.push(object[property]);
  214.     return results;
  215.   }
  216.  
  217.   function clone(object) {
  218.     return extend({ }, object);
  219.   }
  220.  
  221.   function isElement(object) {
  222.     return !!(object && object.nodeType == 1);
  223.   }
  224.  
  225.   function isArray(object) {
  226.     return getClass(object) === "Array";
  227.   }
  228.  
  229.  
  230.   function isHash(object) {
  231.     return object instanceof Hash;
  232.   }
  233.  
  234.   function isFunction(object) {
  235.     return typeof object === "function";
  236.   }
  237.  
  238.   function isString(object) {
  239.     return getClass(object) === "String";
  240.   }
  241.  
  242.   function isNumber(object) {
  243.     return getClass(object) === "Number";
  244.   }
  245.  
  246.   function isUndefined(object) {
  247.     return typeof object === "undefined";
  248.   }
  249.  
  250.   extend(Object, {
  251.     extend:        extend,
  252.     inspect:       inspect,
  253.     toJSON:        toJSON,
  254.     toQueryString: toQueryString,
  255.     toHTML:        toHTML,
  256.     keys:          keys,
  257.     values:        values,
  258.     clone:         clone,
  259.     isElement:     isElement,
  260.     isArray:       isArray,
  261.     isHash:        isHash,
  262.     isFunction:    isFunction,
  263.     isString:      isString,
  264.     isNumber:      isNumber,
  265.     isUndefined:   isUndefined
  266.   });
  267. })();
  268. Object.extend(Function.prototype, (function() {
  269.   var slice = Array.prototype.slice;
  270.  
  271.   function update(array, args) {
  272.     var arrayLength = array.length, length = args.length;
  273.     while (length--) array[arrayLength + length] = args[length];
  274.     return array;
  275.   }
  276.  
  277.   function merge(array, args) {
  278.     array = slice.call(array, 0);
  279.     return update(array, args);
  280.   }
  281.  
  282.   function argumentNames() {
  283.     var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
  284.       .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
  285.       .replace(/\s+/g, '').split(',');
  286.     return names.length == 1 && !names[0] ? [] : names;
  287.   }
  288.  
  289.   function bind(context) {
  290.     if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
  291.     var __method = this, args = slice.call(arguments, 1);
  292.     return function() {
  293.       var a = merge(args, arguments);
  294.       return __method.apply(context, a);
  295.     }
  296.   }
  297.  
  298.   function bindAsEventListener(context) {
  299.     var __method = this, args = slice.call(arguments, 1);
  300.     return function(event) {
  301.       var a = update([event || window.event], args);
  302.       return __method.apply(context, a);
  303.     }
  304.   }
  305.  
  306.   function curry() {
  307.     if (!arguments.length) return this;
  308.     var __method = this, args = slice.call(arguments, 0);
  309.     return function() {
  310.       var a = merge(args, arguments);
  311.       return __method.apply(this, a);
  312.     }
  313.   }
  314.  
  315.   function delay(timeout) {
  316.     var __method = this, args = slice.call(arguments, 1);
  317.     timeout = timeout * 1000
  318.     return window.setTimeout(function() {
  319.       return __method.apply(__method, args);
  320.     }, timeout);
  321.   }
  322.  
  323.   function defer() {
  324.     var args = update([0.01], arguments);
  325.     return this.delay.apply(this, args);
  326.   }
  327.  
  328.   function wrap(wrapper) {
  329.     var __method = this;
  330.     return function() {
  331.       var a = update([__method.bind(this)], arguments);
  332.       return wrapper.apply(this, a);
  333.     }
  334.   }
  335.  
  336.   function methodize() {
  337.     if (this._methodized) return this._methodized;
  338.     var __method = this;
  339.     return this._methodized = function() {
  340.       var a = update([this], arguments);
  341.       return __method.apply(null, a);
  342.     };
  343.   }
  344.  
  345.   return {
  346.     argumentNames:       argumentNames,
  347.     bind:                bind,
  348.     bindAsEventListener: bindAsEventListener,
  349.     curry:               curry,
  350.     delay:               delay,
  351.     defer:               defer,
  352.     wrap:                wrap,
  353.     methodize:           methodize
  354.   }
  355. })());
  356.  
  357.  
  358. Date.prototype.toJSON = function() {
  359.   return '"' + this.getUTCFullYear() + '-' +
  360.     (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
  361.     this.getUTCDate().toPaddedString(2) + 'T' +
  362.     this.getUTCHours().toPaddedString(2) + ':' +
  363.     this.getUTCMinutes().toPaddedString(2) + ':' +
  364.     this.getUTCSeconds().toPaddedString(2) + 'Z"';
  365. };
  366.  
  367.  
  368. RegExp.prototype.match = RegExp.prototype.test;
  369.  
  370. RegExp.escape = function(str) {
  371.   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
  372. };
  373. var PeriodicalExecuter = Class.create({
  374.   initialize: function(callback, frequency) {
  375.     this.callback = callback;
  376.     this.frequency = frequency;
  377.     this.currentlyExecuting = false;
  378.  
  379.     this.registerCallback();
  380.   },
  381.  
  382.   registerCallback: function() {
  383.     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  384.   },
  385.  
  386.   execute: function() {
  387.     this.callback(this);
  388.   },
  389.  
  390.   stop: function() {
  391.     if (!this.timer) return;
  392.     clearInterval(this.timer);
  393.     this.timer = null;
  394.   },
  395.  
  396.   onTimerEvent: function() {
  397.     if (!this.currentlyExecuting) {
  398.       try {
  399.         this.currentlyExecuting = true;
  400.         this.execute();
  401.       } catch(e) {
  402.         /* empty catch for clients that don't support try/finally */
  403.       }
  404.       finally {
  405.         this.currentlyExecuting = false;
  406.       }
  407.     }
  408.   }
  409. });
  410. Object.extend(String, {
  411.   interpret: function(value) {
  412.     return value == null ? '' : String(value);
  413.   },
  414.   specialChar: {
  415.     '\b': '\\b',
  416.     '\t': '\\t',
  417.     '\n': '\\n',
  418.     '\f': '\\f',
  419.     '\r': '\\r',
  420.     '\\': '\\\\'
  421.   }
  422. });
  423.  
  424. Object.extend(String.prototype, (function() {
  425.  
  426.   function prepareReplacement(replacement) {
  427.     if (Object.isFunction(replacement)) return replacement;
  428.     var template = new Template(replacement);
  429.     return function(match) { return template.evaluate(match) };
  430.   }
  431.  
  432.   function gsub(pattern, replacement) {
  433.     var result = '', source = this, match;
  434.     replacement = prepareReplacement(replacement);
  435.  
  436.     if (Object.isString(pattern))
  437.       pattern = RegExp.escape(pattern);
  438.  
  439.     if (!(pattern.length || pattern.source)) {
  440.       replacement = replacement('');
  441.       return replacement + source.split('').join(replacement) + replacement;
  442.     }
  443.  
  444.     while (source.length > 0) {
  445.       if (match = source.match(pattern)) {
  446.         result += source.slice(0, match.index);
  447.         result += String.interpret(replacement(match));
  448.         source  = source.slice(match.index + match[0].length);
  449.       } else {
  450.         result += source, source = '';
  451.       }
  452.     }
  453.     return result;
  454.   }
  455.  
  456.   function sub(pattern, replacement, count) {
  457.     replacement = prepareReplacement(replacement);
  458.     count = Object.isUndefined(count) ? 1 : count;
  459.  
  460.     return this.gsub(pattern, function(match) {
  461.       if (--count < 0) return match[0];
  462.       return replacement(match);
  463.     });
  464.   }
  465.  
  466.   function scan(pattern, iterator) {
  467.     this.gsub(pattern, iterator);
  468.     return String(this);
  469.   }
  470.  
  471.   function truncate(length, truncation) {
  472.     length = length || 30;
  473.     truncation = Object.isUndefined(truncation) ? '...' : truncation;
  474.     return this.length > length ?
  475.       this.slice(0, length - truncation.length) + truncation : String(this);
  476.   }
  477.  
  478.   function strip() {
  479.     return this.replace(/^\s+/, '').replace(/\s+$/, '');
  480.   }
  481.  
  482.   function stripTags() {
  483.     return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
  484.   }
  485.  
  486.   function stripScripts() {
  487.     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  488.   }
  489.  
  490.   function extractScripts() {
  491.     var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
  492.     var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
  493.     return (this.match(matchAll) || []).map(function(scriptTag) {
  494.       return (scriptTag.match(matchOne) || ['', ''])[1];
  495.     });
  496.   }
  497.  
  498.   function evalScripts() {
  499.     return this.extractScripts().map(function(script) { return eval(script) });
  500.   }
  501.  
  502.   function escapeHTML() {
  503.     escapeHTML.text.data = this;
  504.     return escapeHTML.div.innerHTML;
  505.   }
  506.  
  507.   function unescapeHTML() {
  508.     var div = document.createElement('div');
  509.     div.innerHTML = this.stripTags();
  510.     return div.childNodes[0] ? (div.childNodes.length > 1 ?
  511.       $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
  512.       div.childNodes[0].nodeValue) : '';
  513.   }
  514.  
  515.  
  516.   function toQueryParams(separator) {
  517.     var match = this.strip().match(/([^?#]*)(#.*)?$/);
  518.     if (!match) return { };
  519.  
  520.     return match[1].split(separator || '&').inject({ }, function(hash, pair) {
  521.       if ((pair = pair.split('='))[0]) {
  522.         var key = decodeURIComponent(pair.shift());
  523.         var value = pair.length > 1 ? pair.join('=') : pair[0];
  524.         if (value != undefined) value = decodeURIComponent(value);
  525.  
  526.         if (key in hash) {
  527.           if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
  528.           hash[key].push(value);
  529.         }
  530.         else hash[key] = value;
  531.       }
  532.       return hash;
  533.     });
  534.   }
  535.  
  536.   function toArray() {
  537.     return this.split('');
  538.   }
  539.  
  540.   function succ() {
  541.     return this.slice(0, this.length - 1) +
  542.       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  543.   }
  544.  
  545.   function times(count) {
  546.     return count < 1 ? '' : new Array(count + 1).join(this);
  547.   }
  548.  
  549.   function camelize() {
  550.     var parts = this.split('-'), len = parts.length;
  551.     if (len == 1) return parts[0];
  552.  
  553.     var camelized = this.charAt(0) == '-'
  554.       ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
  555.       : parts[0];
  556.  
  557.     for (var i = 1; i < len; i++)
  558.       camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
  559.  
  560.     return camelized;
  561.   }
  562.  
  563.   function capitalize() {
  564.     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  565.   }
  566.  
  567.   function underscore() {
  568.     return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  569.   }
  570.  
  571.   function dasherize() {
  572.     return this.gsub(/_/,'-');
  573.   }
  574.  
  575.   function inspect(useDoubleQuotes) {
  576.     var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
  577.       var character = String.specialChar[match[0]];
  578.       return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
  579.     });
  580.     if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
  581.     return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  582.   }
  583.  
  584.   function toJSON() {
  585.     return this.inspect(true);
  586.   }
  587.  
  588.   function unfilterJSON(filter) {
  589.     return this.sub(filter || Prototype.JSONFilter, '#{1}');
  590.   }
  591.  
  592.   function isJSON() {
  593.     var str = this;
  594.     if (str.blank()) return false;
  595.     str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
  596.     return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  597.   }
  598.  
  599.   function evalJSON(sanitize) {
  600.     var json = this.unfilterJSON();
  601.     try {
  602.       if (!sanitize || json.isJSON()) return eval('(' + json + ')');
  603.     } catch (e) { }
  604.     throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  605.   }
  606.  
  607.   function include(pattern) {
  608.     return this.indexOf(pattern) > -1;
  609.   }
  610.  
  611.   function startsWith(pattern) {
  612.     return this.indexOf(pattern) === 0;
  613.   }
  614.  
  615.   function endsWith(pattern) {
  616.     var d = this.length - pattern.length;
  617.     return d >= 0 && this.lastIndexOf(pattern) === d;
  618.   }
  619.  
  620.   function empty() {
  621.     return this == '';
  622.   }
  623.  
  624.   function blank() {
  625.     return /^\s*$/.test(this);
  626.   }
  627.  
  628.   function interpolate(object, pattern) {
  629.     return new Template(this, pattern).evaluate(object);
  630.   }
  631.  
  632.   return {
  633.     gsub:           gsub,
  634.     sub:            sub,
  635.     scan:           scan,
  636.     truncate:       truncate,
  637.     strip:          String.prototype.trim ? String.prototype.trim : strip,
  638.     stripTags:      stripTags,
  639.     stripScripts:   stripScripts,
  640.     extractScripts: extractScripts,
  641.     evalScripts:    evalScripts,
  642.     escapeHTML:     escapeHTML,
  643.     unescapeHTML:   unescapeHTML,
  644.     toQueryParams:  toQueryParams,
  645.     parseQuery:     toQueryParams,
  646.     toArray:        toArray,
  647.     succ:           succ,
  648.     times:          times,
  649.     camelize:       camelize,
  650.     capitalize:     capitalize,
  651.     underscore:     underscore,
  652.     dasherize:      dasherize,
  653.     inspect:        inspect,
  654.     toJSON:         toJSON,
  655.     unfilterJSON:   unfilterJSON,
  656.     isJSON:         isJSON,
  657.     evalJSON:       evalJSON,
  658.     include:        include,
  659.     startsWith:     startsWith,
  660.     endsWith:       endsWith,
  661.     empty:          empty,
  662.     blank:          blank,
  663.     interpolate:    interpolate
  664.   };
  665. })());
  666.  
  667. Object.extend(String.prototype.escapeHTML, {
  668.   div:  document.createElement('div'),
  669.   text: document.createTextNode('')
  670. });
  671.  
  672. String.prototype.escapeHTML.div.appendChild(String.prototype.escapeHTML.text);
  673.  
  674. if ('<\n>'.escapeHTML() !== '&lt;\n&gt;') {
  675.   String.prototype.escapeHTML = function() {
  676.     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  677.   };
  678. }
  679.  
  680. if ('&lt;\n&gt;'.unescapeHTML() !== '<\n>') {
  681.   String.prototype.unescapeHTML = function() {
  682.     return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
  683.   };
  684. }
  685. var Template = Class.create({
  686.   initialize: function(template, pattern) {
  687.     this.template = template.toString();
  688.     this.pattern = pattern || Template.Pattern;
  689.   },
  690.  
  691.   evaluate: function(object) {
  692.     if (object && Object.isFunction(object.toTemplateReplacements))
  693.       object = object.toTemplateReplacements();
  694.  
  695.     return this.template.gsub(this.pattern, function(match) {
  696.       if (object == null) return (match[1] + '');
  697.  
  698.       var before = match[1] || '';
  699.       if (before == '\\') return match[2];
  700.  
  701.       var ctx = object, expr = match[3];
  702.       var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
  703.       match = pattern.exec(expr);
  704.       if (match == null) return before;
  705.  
  706.       while (match != null) {
  707.         var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
  708.         ctx = ctx[comp];
  709.         if (null == ctx || '' == match[3]) break;
  710.         expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
  711.         match = pattern.exec(expr);
  712.       }
  713.  
  714.       return before + String.interpret(ctx);
  715.     });
  716.   }
  717. });
  718. Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
  719.  
  720. var $break = { };
  721.  
  722. var Enumerable = (function() {
  723.   function each(iterator, context) {
  724.     var index = 0;
  725.     try {
  726.       this._each(function(value) {
  727.         iterator.call(context, value, index++);
  728.       });
  729.     } catch (e) {
  730.       if (e != $break) throw e;
  731.     }
  732.     return this;
  733.   }
  734.  
  735.   function eachSlice(number, iterator, context) {
  736.     var index = -number, slices = [], array = this.toArray();
  737.     if (number < 1) return array;
  738.     while ((index += number) < array.length)
  739.       slices.push(array.slice(index, index+number));
  740.     return slices.collect(iterator, context);
  741.   }
  742.  
  743.   function all(iterator, context) {
  744.     iterator = iterator || Prototype.K;
  745.     var result = true;
  746.     this.each(function(value, index) {
  747.       result = result && !!iterator.call(context, value, index);
  748.       if (!result) throw $break;
  749.     });
  750.     return result;
  751.   }
  752.  
  753.   function any(iterator, context) {
  754.     iterator = iterator || Prototype.K;
  755.     var result = false;
  756.     this.each(function(value, index) {
  757.       if (result = !!iterator.call(context, value, index))
  758.         throw $break;
  759.     });
  760.     return result;
  761.   }
  762.  
  763.   function collect(iterator, context) {
  764.     iterator = iterator || Prototype.K;
  765.     var results = [];
  766.     this.each(function(value, index) {
  767.       results.push(iterator.call(context, value, index));
  768.     });
  769.     return results;
  770.   }
  771.  
  772.   function detect(iterator, context) {
  773.     var result;
  774.     this.each(function(value, index) {
  775.       if (iterator.call(context, value, index)) {
  776.         result = value;
  777.         throw $break;
  778.       }
  779.     });
  780.     return result;
  781.   }
  782.  
  783.   function findAll(iterator, context) {
  784.     var results = [];
  785.     this.each(function(value, index) {
  786.       if (iterator.call(context, value, index))
  787.         results.push(value);
  788.     });
  789.     return results;
  790.   }
  791.  
  792.   function grep(filter, iterator, context) {
  793.     iterator = iterator || Prototype.K;
  794.     var results = [];
  795.  
  796.     if (Object.isString(filter))
  797.       filter = new RegExp(RegExp.escape(filter));
  798.  
  799.     this.each(function(value, index) {
  800.       if (filter.match(value))
  801.         results.push(iterator.call(context, value, index));
  802.     });
  803.     return results;
  804.   }
  805.  
  806.   function include(object) {
  807.     if (Object.isFunction(this.indexOf))
  808.       if (this.indexOf(object) != -1) return true;
  809.  
  810.     var found = false;
  811.     this.each(function(value) {
  812.       if (value == object) {
  813.         found = true;
  814.         throw $break;
  815.       }
  816.     });
  817.     return found;
  818.   }
  819.  
  820.   function inGroupsOf(number, fillWith) {
  821.     fillWith = Object.isUndefined(fillWith) ? null : fillWith;
  822.     return this.eachSlice(number, function(slice) {
  823.       while(slice.length < number) slice.push(fillWith);
  824.       return slice;
  825.     });
  826.   }
  827.  
  828.   function inject(memo, iterator, context) {
  829.     this.each(function(value, index) {
  830.       memo = iterator.call(context, memo, value, index);
  831.     });
  832.     return memo;
  833.   }
  834.  
  835.   function invoke(method) {
  836.     var args = $A(arguments).slice(1);
  837.     return this.map(function(value) {
  838.       return value[method].apply(value, args);
  839.     });
  840.   }
  841.  
  842.   function max(iterator, context) {
  843.     iterator = iterator || Prototype.K;
  844.     var result;
  845.     this.each(function(value, index) {
  846.       value = iterator.call(context, value, index);
  847.       if (result == null || value >= result)
  848.         result = value;
  849.     });
  850.     return result;
  851.   }
  852.  
  853.   function min(iterator, context) {
  854.     iterator = iterator || Prototype.K;
  855.     var result;
  856.     this.each(function(value, index) {
  857.       value = iterator.call(context, value, index);
  858.       if (result == null || value < result)
  859.         result = value;
  860.     });
  861.     return result;
  862.   }
  863.  
  864.   function partition(iterator, context) {
  865.     iterator = iterator || Prototype.K;
  866.     var trues = [], falses = [];
  867.     this.each(function(value, index) {
  868.       (iterator.call(context, value, index) ?
  869.         trues : falses).push(value);
  870.     });
  871.     return [trues, falses];
  872.   }
  873.  
  874.   function pluck(property) {
  875.     var results = [];
  876.     this.each(function(value) {
  877.       results.push(value[property]);
  878.     });
  879.     return results;
  880.   }
  881.  
  882.   function reject(iterator, context) {
  883.     var results = [];
  884.     this.each(function(value, index) {
  885.       if (!iterator.call(context, value, index))
  886.         results.push(value);
  887.     });
  888.     return results;
  889.   }
  890.  
  891.   function sortBy(iterator, context) {
  892.     return this.map(function(value, index) {
  893.       return {
  894.         value: value,
  895.         criteria: iterator.call(context, value, index)
  896.       };
  897.     }).sort(function(left, right) {
  898.       var a = left.criteria, b = right.criteria;
  899.       return a < b ? -1 : a > b ? 1 : 0;
  900.     }).pluck('value');
  901.   }
  902.  
  903.   function toArray() {
  904.     return this.map();
  905.   }
  906.  
  907.   function zip() {
  908.     var iterator = Prototype.K, args = $A(arguments);
  909.     if (Object.isFunction(args.last()))
  910.       iterator = args.pop();
  911.  
  912.     var collections = [this].concat(args).map($A);
  913.     return this.map(function(value, index) {
  914.       return iterator(collections.pluck(index));
  915.     });
  916.   }
  917.  
  918.   function size() {
  919.     return this.toArray().length;
  920.   }
  921.  
  922.   function inspect() {
  923.     return '#<Enumerable:' + this.toArray().inspect() + '>';
  924.   }
  925.  
  926.  
  927.  
  928.  
  929.  
  930.  
  931.  
  932.  
  933.  
  934.   return {
  935.     each:       each,
  936.     eachSlice:  eachSlice,
  937.     all:        all,
  938.     every:      all,
  939.     any:        any,
  940.     some:       any,
  941.     collect:    collect,
  942.     map:        collect,
  943.     detect:     detect,
  944.     findAll:    findAll,
  945.     select:     findAll,
  946.     filter:     findAll,
  947.     grep:       grep,
  948.     include:    include,
  949.     member:     include,
  950.     inGroupsOf: inGroupsOf,
  951.     inject:     inject,
  952.     invoke:     invoke,
  953.     max:        max,
  954.     min:        min,
  955.     partition:  partition,
  956.     pluck:      pluck,
  957.     reject:     reject,
  958.     sortBy:     sortBy,
  959.     toArray:    toArray,
  960.     entries:    toArray,
  961.     zip:        zip,
  962.     size:       size,
  963.     inspect:    inspect,
  964.     find:       detect
  965.   };
  966. })();
  967. function $A(iterable) {
  968.   if (!iterable) return [];
  969.   if ('toArray' in Object(iterable)) return iterable.toArray();
  970.   var length = iterable.length || 0, results = new Array(length);
  971.   while (length--) results[length] = iterable[length];
  972.   return results;
  973. }
  974.  
  975. function $w(string) {
  976.   if (!Object.isString(string)) return [];
  977.   string = string.strip();
  978.   return string ? string.split(/\s+/) : [];
  979. }
  980.  
  981. Array.from = $A;
  982.  
  983.  
  984. (function() {
  985.   var arrayProto = Array.prototype,
  986.       slice = arrayProto.slice,
  987.       _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
  988.  
  989.   function each(iterator) {
  990.     for (var i = 0, length = this.length; i < length; i++)
  991.       iterator(this[i]);
  992.   }
  993.   if (!_each) _each = each;
  994.  
  995.   function clear() {
  996.     this.length = 0;
  997.     return this;
  998.   }
  999.  
  1000.   function first() {
  1001.     return this[0];
  1002.   }
  1003.  
  1004.   function last() {
  1005.     return this[this.length - 1];
  1006.   }
  1007.  
  1008.   function compact() {
  1009.     return this.select(function(value) {
  1010.       return value != null;
  1011.     });
  1012.   }
  1013.  
  1014.   function flatten() {
  1015.     return this.inject([], function(array, value) {
  1016.       if (Object.isArray(value))
  1017.         return array.concat(value.flatten());
  1018.       array.push(value);
  1019.       return array;
  1020.     });
  1021.   }
  1022.  
  1023.   function without() {
  1024.     var values = slice.call(arguments, 0);
  1025.     return this.select(function(value) {
  1026.       return !values.include(value);
  1027.     });
  1028.   }
  1029.  
  1030.   function reverse(inline) {
  1031.     return (inline !== false ? this : this.toArray())._reverse();
  1032.   }
  1033.  
  1034.   function uniq(sorted) {
  1035.     return this.inject([], function(array, value, index) {
  1036.       if (0 == index || (sorted ? array.last() != value : !array.include(value)))
  1037.         array.push(value);
  1038.       return array;
  1039.     });
  1040.   }
  1041.  
  1042.   function intersect(array) {
  1043.     return this.uniq().findAll(function(item) {
  1044.       return array.detect(function(value) { return item === value });
  1045.     });
  1046.   }
  1047.  
  1048.  
  1049.   function clone() {
  1050.     return slice.call(this, 0);
  1051.   }
  1052.  
  1053.   function size() {
  1054.     return this.length;
  1055.   }
  1056.  
  1057.   function inspect() {
  1058.     return '[' + this.map(Object.inspect).join(', ') + ']';
  1059.   }
  1060.  
  1061.   function toJSON() {
  1062.     var results = [];
  1063.     this.each(function(object) {
  1064.       var value = Object.toJSON(object);
  1065.       if (!Object.isUndefined(value)) results.push(value);
  1066.     });
  1067.     return '[' + results.join(', ') + ']';
  1068.   }
  1069.  
  1070.   function indexOf(item, i) {
  1071.     i || (i = 0);
  1072.     var length = this.length;
  1073.     if (i < 0) i = length + i;
  1074.     for (; i < length; i++)
  1075.       if (this[i] === item) return i;
  1076.     return -1;
  1077.   }
  1078.  
  1079.   function lastIndexOf(item, i) {
  1080.     i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
  1081.     var n = this.slice(0, i).reverse().indexOf(item);
  1082.     return (n < 0) ? n : i - n - 1;
  1083.   }
  1084.  
  1085.   function concat() {
  1086.     var array = slice.call(this, 0), item;
  1087.     for (var i = 0, length = arguments.length; i < length; i++) {
  1088.       item = arguments[i];
  1089.       if (Object.isArray(item) && !('callee' in item)) {
  1090.         for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
  1091.           array.push(item[j]);
  1092.       } else {
  1093.         array.push(item);
  1094.       }
  1095.     }
  1096.     return array;
  1097.   }
  1098.  
  1099.   Object.extend(arrayProto, Enumerable);
  1100.  
  1101.   if (!arrayProto._reverse)
  1102.     arrayProto._reverse = arrayProto.reverse;
  1103.  
  1104.   Object.extend(arrayProto, {
  1105.     _each:     _each,
  1106.     clear:     clear,
  1107.     first:     first,
  1108.     last:      last,
  1109.     compact:   compact,
  1110.     flatten:   flatten,
  1111.     without:   without,
  1112.     reverse:   reverse,
  1113.     uniq:      uniq,
  1114.     intersect: intersect,
  1115.     clone:     clone,
  1116.     toArray:   clone,
  1117.     size:      size,
  1118.     inspect:   inspect,
  1119.     toJSON:    toJSON
  1120.   });
  1121.  
  1122.   var CONCAT_ARGUMENTS_BUGGY = (function() {
  1123.     return [].concat(arguments)[0][0] !== 1;
  1124.   })(1,2)
  1125.  
  1126.   if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
  1127.  
  1128.   if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
  1129.   if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
  1130. })();
  1131. function $H(object) {
  1132.   return new Hash(object);
  1133. };
  1134.  
  1135. var Hash = Class.create(Enumerable, (function() {
  1136.   function initialize(object) {
  1137.     this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
  1138.   }
  1139.  
  1140.   function _each(iterator) {
  1141.     for (var key in this._object) {
  1142.       var value = this._object[key], pair = [key, value];
  1143.       pair.key = key;
  1144.       pair.value = value;
  1145.       iterator(pair);
  1146.     }
  1147.   }
  1148.  
  1149.   function set(key, value) {
  1150.     return this._object[key] = value;
  1151.   }
  1152.  
  1153.   function get(key) {
  1154.     if (this._object[key] !== Object.prototype[key])
  1155.       return this._object[key];
  1156.   }
  1157.  
  1158.   function unset(key) {
  1159.     var value = this._object[key];
  1160.     delete this._object[key];
  1161.     return value;
  1162.   }
  1163.  
  1164.   function toObject() {
  1165.     return Object.clone(this._object);
  1166.   }
  1167.  
  1168.   function keys() {
  1169.     return this.pluck('key');
  1170.   }
  1171.  
  1172.   function values() {
  1173.     return this.pluck('value');
  1174.   }
  1175.  
  1176.   function index(value) {
  1177.     var match = this.detect(function(pair) {
  1178.       return pair.value === value;
  1179.     });
  1180.     return match && match.key;
  1181.   }
  1182.  
  1183.   function merge(object) {
  1184.     return this.clone().update(object);
  1185.   }
  1186.  
  1187.   function update(object) {
  1188.     return new Hash(object).inject(this, function(result, pair) {
  1189.       result.set(pair.key, pair.value);
  1190.       return result;
  1191.     });
  1192.   }
  1193.  
  1194.   function toQueryPair(key, value) {
  1195.     if (Object.isUndefined(value)) return key;
  1196.     return key + '=' + encodeURIComponent(String.interpret(value));
  1197.   }
  1198.  
  1199.   function toQueryString() {
  1200.     return this.inject([], function(results, pair) {
  1201.       var key = encodeURIComponent(pair.key), values = pair.value;
  1202.  
  1203.       if (values && typeof values == 'object') {
  1204.         if (Object.isArray(values))
  1205.           return results.concat(values.map(toQueryPair.curry(key)));
  1206.       } else results.push(toQueryPair(key, values));
  1207.       return results;
  1208.     }).join('&');
  1209.   }
  1210.  
  1211.   function inspect() {
  1212.     return '#<Hash:{' + this.map(function(pair) {
  1213.       return pair.map(Object.inspect).join(': ');
  1214.     }).join(', ') + '}>';
  1215.   }
  1216.  
  1217.   function toJSON() {
  1218.     return Object.toJSON(this.toObject());
  1219.   }
  1220.  
  1221.   function clone() {
  1222.     return new Hash(this);
  1223.   }
  1224.  
  1225.   return {
  1226.     initialize:             initialize,
  1227.     _each:                  _each,
  1228.     set:                    set,
  1229.     get:                    get,
  1230.     unset:                  unset,
  1231.     toObject:               toObject,
  1232.     toTemplateReplacements: toObject,
  1233.     keys:                   keys,
  1234.     values:                 values,
  1235.     index:                  index,
  1236.     merge:                  merge,
  1237.     update:                 update,
  1238.     toQueryString:          toQueryString,
  1239.     inspect:                inspect,
  1240.     toJSON:                 toJSON,
  1241.     clone:                  clone
  1242.   };
  1243. })());
  1244.  
  1245. Hash.from = $H;
  1246. Object.extend(Number.prototype, (function() {
  1247.   function toColorPart() {
  1248.     return this.toPaddedString(2, 16);
  1249.   }
  1250.  
  1251.   function succ() {
  1252.     return this + 1;
  1253.   }
  1254.  
  1255.   function times(iterator, context) {
  1256.     $R(0, this, true).each(iterator, context);
  1257.     return this;
  1258.   }
  1259.  
  1260.   function toPaddedString(length, radix) {
  1261.     var string = this.toString(radix || 10);
  1262.     return '0'.times(length - string.length) + string;
  1263.   }
  1264.  
  1265.   function toJSON() {
  1266.     return isFinite(this) ? this.toString() : 'null';
  1267.   }
  1268.  
  1269.   function abs() {
  1270.     return Math.abs(this);
  1271.   }
  1272.  
  1273.   function round() {
  1274.     return Math.round(this);
  1275.   }
  1276.  
  1277.   function ceil() {
  1278.     return Math.ceil(this);
  1279.   }
  1280.  
  1281.   function floor() {
  1282.     return Math.floor(this);
  1283.   }
  1284.  
  1285.   return {
  1286.     toColorPart:    toColorPart,
  1287.     succ:           succ,
  1288.     times:          times,
  1289.     toPaddedString: toPaddedString,
  1290.     toJSON:         toJSON,
  1291.     abs:            abs,
  1292.     round:          round,
  1293.     ceil:           ceil,
  1294.     floor:          floor
  1295.   };
  1296. })());
  1297.  
  1298. function $R(start, end, exclusive) {
  1299.   return new ObjectRange(start, end, exclusive);
  1300. }
  1301.  
  1302. var ObjectRange = Class.create(Enumerable, (function() {
  1303.   function initialize(start, end, exclusive) {
  1304.     this.start = start;
  1305.     this.end = end;
  1306.     this.exclusive = exclusive;
  1307.   }
  1308.  
  1309.   function _each(iterator) {
  1310.     var value = this.start;
  1311.     while (this.include(value)) {
  1312.       iterator(value);
  1313.       value = value.succ();
  1314.     }
  1315.   }
  1316.  
  1317.   function include(value) {
  1318.     if (value < this.start)
  1319.       return false;
  1320.     if (this.exclusive)
  1321.       return value < this.end;
  1322.     return value <= this.end;
  1323.   }
  1324.  
  1325.   return {
  1326.     initialize: initialize,
  1327.     _each:      _each,
  1328.     include:    include
  1329.   };
  1330. })());
  1331.  
  1332.  
  1333.  
  1334. var Ajax = {
  1335.   getTransport: function() {
  1336.     return Try.these(
  1337.       function() {return new XMLHttpRequest()},
  1338.       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
  1339.       function() {return new ActiveXObject('Microsoft.XMLHTTP')}
  1340.     ) || false;
  1341.   },
  1342.  
  1343.   activeRequestCount: 0
  1344. };
  1345.  
  1346. Ajax.Responders = {
  1347.   responders: [],
  1348.  
  1349.   _each: function(iterator) {
  1350.     this.responders._each(iterator);
  1351.   },
  1352.  
  1353.   register: function(responder) {
  1354.     if (!this.include(responder))
  1355.       this.responders.push(responder);
  1356.   },
  1357.  
  1358.   unregister: function(responder) {
  1359.     this.responders = this.responders.without(responder);
  1360.   },
  1361.  
  1362.   dispatch: function(callback, request, transport, json) {
  1363.     this.each(function(responder) {
  1364.       if (Object.isFunction(responder[callback])) {
  1365.         try {
  1366.           responder[callback].apply(responder, [request, transport, json]);
  1367.         } catch (e) { }
  1368.       }
  1369.     });
  1370.   }
  1371. };
  1372.  
  1373. Object.extend(Ajax.Responders, Enumerable);
  1374.  
  1375. Ajax.Responders.register({
  1376.   onCreate:   function() { Ajax.activeRequestCount++ },
  1377.   onComplete: function() { Ajax.activeRequestCount-- }
  1378. });
  1379. Ajax.Base = Class.create({
  1380.   initialize: function(options) {
  1381.     this.options = {
  1382.       method:       'post',
  1383.       asynchronous: true,
  1384.       contentType:  'application/x-www-form-urlencoded',
  1385.       encoding:     'UTF-8',
  1386.       parameters:   '',
  1387.       evalJSON:     true,
  1388.       evalJS:       true
  1389.     };
  1390.     Object.extend(this.options, options || { });
  1391.  
  1392.     this.options.method = this.options.method.toLowerCase();
  1393.  
  1394.     if (Object.isString(this.options.parameters))
  1395.       this.options.parameters = this.options.parameters.toQueryParams();
  1396.     else if (Object.isHash(this.options.parameters))
  1397.       this.options.parameters = this.options.parameters.toObject();
  1398.   }
  1399. });
  1400. Ajax.Request = Class.create(Ajax.Base, {
  1401.   _complete: false,
  1402.  
  1403.   initialize: function($super, url, options) {
  1404.     $super(options);
  1405.     this.transport = Ajax.getTransport();
  1406.     this.request(url);
  1407.   },
  1408.  
  1409.   request: function(url) {
  1410.     this.url = url;
  1411.     this.method = this.options.method;
  1412.     var params = Object.clone(this.options.parameters);
  1413.  
  1414.     if (!['get', 'post'].include(this.method)) {
  1415.       params['_method'] = this.method;
  1416.       this.method = 'post';
  1417.     }
  1418.  
  1419.     this.parameters = params;
  1420.  
  1421.     if (params = Object.toQueryString(params)) {
  1422.       if (this.method == 'get')
  1423.         this.url += (this.url.include('?') ? '&' : '?') + params;
  1424.       else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
  1425.         params += '&_=';
  1426.     }
  1427.  
  1428.     try {
  1429.       var response = new Ajax.Response(this);
  1430.       if (this.options.onCreate) this.options.onCreate(response);
  1431.       Ajax.Responders.dispatch('onCreate', this, response);
  1432.  
  1433.       this.transport.open(this.method.toUpperCase(), this.url,
  1434.         this.options.asynchronous);
  1435.  
  1436.       if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
  1437.  
  1438.       this.transport.onreadystatechange = this.onStateChange.bind(this);
  1439.       this.setRequestHeaders();
  1440.  
  1441.       this.body = this.method == 'post' ? (this.options.postBody || params) : null;
  1442.       this.transport.send(this.body);
  1443.  
  1444.       /* Force Firefox to handle ready state 4 for synchronous requests */
  1445.       if (!this.options.asynchronous && this.transport.overrideMimeType)
  1446.         this.onStateChange();
  1447.  
  1448.     }
  1449.     catch (e) {
  1450.       this.dispatchException(e);
  1451.     }
  1452.   },
  1453.  
  1454.   onStateChange: function() {
  1455.     var readyState = this.transport.readyState;
  1456.     if (readyState > 1 && !((readyState == 4) && this._complete))
  1457.       this.respondToReadyState(this.transport.readyState);
  1458.   },
  1459.  
  1460.   setRequestHeaders: function() {
  1461.     var headers = {
  1462.       'X-Requested-With': 'XMLHttpRequest',
  1463.       'X-Prototype-Version': Prototype.Version,
  1464.       'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
  1465.     };
  1466.  
  1467.     if (this.method == 'post') {
  1468.       headers['Content-type'] = this.options.contentType +
  1469.         (this.options.encoding ? '; charset=' + this.options.encoding : '');
  1470.  
  1471.       /* Force "Connection: close" for older Mozilla browsers to work
  1472.        * around a bug where XMLHttpRequest sends an incorrect
  1473.        * Content-length header. See Mozilla Bugzilla #246651.
  1474.        */
  1475.       if (this.transport.overrideMimeType &&
  1476.           (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
  1477.             headers['Connection'] = 'close';
  1478.     }
  1479.  
  1480.     if (typeof this.options.requestHeaders == 'object') {
  1481.       var extras = this.options.requestHeaders;
  1482.  
  1483.       if (Object.isFunction(extras.push))
  1484.         for (var i = 0, length = extras.length; i < length; i += 2)
  1485.           headers[extras[i]] = extras[i+1];
  1486.       else
  1487.         $H(extras).each(function(pair) { headers[pair.key] = pair.value });
  1488.     }
  1489.  
  1490.     for (var name in headers)
  1491.       this.transport.setRequestHeader(name, headers[name]);
  1492.   },
  1493.  
  1494.   success: function() {
  1495.     var status = this.getStatus();
  1496.     return !status || (status >= 200 && status < 300);
  1497.   },
  1498.  
  1499.   getStatus: function() {
  1500.     try {
  1501.       return this.transport.status || 0;
  1502.     } catch (e) { return 0 }
  1503.   },
  1504.  
  1505.   respondToReadyState: function(readyState) {
  1506.     var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
  1507.  
  1508.     if (state == 'Complete') {
  1509.       try {
  1510.         this._complete = true;
  1511.         (this.options['on' + response.status]
  1512.          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
  1513.          || Prototype.emptyFunction)(response, response.headerJSON);
  1514.       } catch (e) {
  1515.         this.dispatchException(e);
  1516.       }
  1517.  
  1518.       var contentType = response.getHeader('Content-type');
  1519.       if (this.options.evalJS == 'force'
  1520.           || (this.options.evalJS && this.isSameOrigin() && contentType
  1521.           && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
  1522.         this.evalResponse();
  1523.     }
  1524.  
  1525.     try {
  1526.       (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
  1527.       Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
  1528.     } catch (e) {
  1529.       this.dispatchException(e);
  1530.     }
  1531.  
  1532.     if (state == 'Complete') {
  1533.       this.transport.onreadystatechange = Prototype.emptyFunction;
  1534.     }
  1535.   },
  1536.  
  1537.   isSameOrigin: function() {
  1538.     var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
  1539.     return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
  1540.       protocol: location.protocol,
  1541.       domain: document.domain,
  1542.       port: location.port ? ':' + location.port : ''
  1543.     }));
  1544.   },
  1545.  
  1546.   getHeader: function(name) {
  1547.     try {
  1548.       return this.transport.getResponseHeader(name) || null;
  1549.     } catch (e) { return null; }
  1550.   },
  1551.  
  1552.   evalResponse: function() {
  1553.     try {
  1554.       return eval((this.transport.responseText || '').unfilterJSON());
  1555.     } catch (e) {
  1556.       this.dispatchException(e);
  1557.     }
  1558.   },
  1559.  
  1560.   dispatchException: function(exception) {
  1561.     (this.options.onException || Prototype.emptyFunction)(this, exception);
  1562.     Ajax.Responders.dispatch('onException', this, exception);
  1563.   }
  1564. });
  1565.  
  1566. Ajax.Request.Events =
  1567.   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
  1568.  
  1569.  
  1570.  
  1571.  
  1572.  
  1573.  
  1574.  
  1575.  
  1576. Ajax.Response = Class.create({
  1577.   initialize: function(request){
  1578.     this.request = request;
  1579.     var transport  = this.transport  = request.transport,
  1580.         readyState = this.readyState = transport.readyState;
  1581.  
  1582.     if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
  1583.       this.status       = this.getStatus();
  1584.       this.statusText   = this.getStatusText();
  1585.       this.responseText = String.interpret(transport.responseText);
  1586.       this.headerJSON   = this._getHeaderJSON();
  1587.     }
  1588.  
  1589.     if(readyState == 4) {
  1590.       var xml = transport.responseXML;
  1591.       this.responseXML  = Object.isUndefined(xml) ? null : xml;
  1592.       this.responseJSON = this._getResponseJSON();
  1593.     }
  1594.   },
  1595.  
  1596.   status:      0,
  1597.  
  1598.   statusText: '',
  1599.  
  1600.   getStatus: Ajax.Request.prototype.getStatus,
  1601.  
  1602.   getStatusText: function() {
  1603.     try {
  1604.       return this.transport.statusText || '';
  1605.     } catch (e) { return '' }
  1606.   },
  1607.  
  1608.   getHeader: Ajax.Request.prototype.getHeader,
  1609.  
  1610.   getAllHeaders: function() {
  1611.     try {
  1612.       return this.getAllResponseHeaders();
  1613.     } catch (e) { return null }
  1614.   },
  1615.  
  1616.   getResponseHeader: function(name) {
  1617.     return this.transport.getResponseHeader(name);
  1618.   },
  1619.  
  1620.   getAllResponseHeaders: function() {
  1621.     return this.transport.getAllResponseHeaders();
  1622.   },
  1623.  
  1624.   _getHeaderJSON: function() {
  1625.     var json = this.getHeader('X-JSON');
  1626.     if (!json) return null;
  1627.     json = decodeURIComponent(escape(json));
  1628.     try {
  1629.       return json.evalJSON(this.request.options.sanitizeJSON ||
  1630.         !this.request.isSameOrigin());
  1631.     } catch (e) {
  1632.       this.request.dispatchException(e);
  1633.     }
  1634.   },
  1635.  
  1636.   _getResponseJSON: function() {
  1637.     var options = this.request.options;
  1638.     if (!options.evalJSON || (options.evalJSON != 'force' &&
  1639.       !(this.getHeader('Content-type') || '').include('application/json')) ||
  1640.         this.responseText.blank())
  1641.           return null;
  1642.     try {
  1643.       return this.responseText.evalJSON(options.sanitizeJSON ||
  1644.         !this.request.isSameOrigin());
  1645.     } catch (e) {
  1646.       this.request.dispatchException(e);
  1647.     }
  1648.   }
  1649. });
  1650.  
  1651. Ajax.Updater = Class.create(Ajax.Request, {
  1652.   initialize: function($super, container, url, options) {
  1653.     this.container = {
  1654.       success: (container.success || container),
  1655.       failure: (container.failure || (container.success ? null : container))
  1656.     };
  1657.  
  1658.     options = Object.clone(options);
  1659.     var onComplete = options.onComplete;
  1660.     options.onComplete = (function(response, json) {
  1661.       this.updateContent(response.responseText);
  1662.       if (Object.isFunction(onComplete)) onComplete(response, json);
  1663.     }).bind(this);
  1664.  
  1665.     $super(url, options);
  1666.   },
  1667.  
  1668.   updateContent: function(responseText) {
  1669.     var receiver = this.container[this.success() ? 'success' : 'failure'],
  1670.         options = this.options;
  1671.  
  1672.     if (!options.evalScripts) responseText = responseText.stripScripts();
  1673.  
  1674.     if (receiver = $(receiver)) {
  1675.       if (options.insertion) {
  1676.         if (Object.isString(options.insertion)) {
  1677.           var insertion = { }; insertion[options.insertion] = responseText;
  1678.           receiver.insert(insertion);
  1679.         }
  1680.         else options.insertion(receiver, responseText);
  1681.       }
  1682.       else receiver.update(responseText);
  1683.     }
  1684.   }
  1685. });
  1686.  
  1687. Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  1688.   initialize: function($super, container, url, options) {
  1689.     $super(options);
  1690.     this.onComplete = this.options.onComplete;
  1691.  
  1692.     this.frequency = (this.options.frequency || 2);
  1693.     this.decay = (this.options.decay || 1);
  1694.  
  1695.     this.updater = { };
  1696.     this.container = container;
  1697.     this.url = url;
  1698.  
  1699.     this.start();
  1700.   },
  1701.  
  1702.   start: function() {
  1703.     this.options.onComplete = this.updateComplete.bind(this);
  1704.     this.onTimerEvent();
  1705.   },
  1706.  
  1707.   stop: function() {
  1708.     this.updater.options.onComplete = undefined;
  1709.     clearTimeout(this.timer);
  1710.     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  1711.   },
  1712.  
  1713.   updateComplete: function(response) {
  1714.     if (this.options.decay) {
  1715.       this.decay = (response.responseText == this.lastText ?
  1716.         this.decay * this.options.decay : 1);
  1717.  
  1718.       this.lastText = response.responseText;
  1719.     }
  1720.     this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  1721.   },
  1722.  
  1723.   onTimerEvent: function() {
  1724.     this.updater = new Ajax.Updater(this.container, this.url, this.options);
  1725.   }
  1726. });
  1727.  
  1728.  
  1729.  
  1730. function $(element) {
  1731.   if (arguments.length > 1) {
  1732.     for (var i = 0, elements = [], length = arguments.length; i < length; i++)
  1733.       elements.push($(arguments[i]));
  1734.     return elements;
  1735.   }
  1736.   if (Object.isString(element))
  1737.     element = document.getElementById(element);
  1738.   return Element.extend(element);
  1739. }
  1740.  
  1741. if (Prototype.BrowserFeatures.XPath) {
  1742.   document._getElementsByXPath = function(expression, parentElement) {
  1743.     var results = [];
  1744.     var query = document.evaluate(expression, $(parentElement) || document,
  1745.       null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  1746.     for (var i = 0, length = query.snapshotLength; i < length; i++)
  1747.       results.push(Element.extend(query.snapshotItem(i)));
  1748.     return results;
  1749.   };
  1750. }
  1751.  
  1752. /*--------------------------------------------------------------------------*/
  1753.  
  1754. if (!window.Node) var Node = { };
  1755.  
  1756. if (!Node.ELEMENT_NODE) {
  1757.   Object.extend(Node, {
  1758.     ELEMENT_NODE: 1,
  1759.     ATTRIBUTE_NODE: 2,
  1760.     TEXT_NODE: 3,
  1761.     CDATA_SECTION_NODE: 4,
  1762.     ENTITY_REFERENCE_NODE: 5,
  1763.     ENTITY_NODE: 6,
  1764.     PROCESSING_INSTRUCTION_NODE: 7,
  1765.     COMMENT_NODE: 8,
  1766.     DOCUMENT_NODE: 9,
  1767.     DOCUMENT_TYPE_NODE: 10,
  1768.     DOCUMENT_FRAGMENT_NODE: 11,
  1769.     NOTATION_NODE: 12
  1770.   });
  1771. }
  1772.  
  1773.  
  1774. (function(global) {
  1775.  
  1776.   var SETATTRIBUTE_IGNORES_NAME = (function(){
  1777.     var elForm = document.createElement("form");
  1778.     var elInput = document.createElement("input");
  1779.     var root = document.documentElement;
  1780.     elInput.setAttribute("name", "test");
  1781.     elForm.appendChild(elInput);
  1782.     root.appendChild(elForm);
  1783.     var isBuggy = elForm.elements
  1784.       ? (typeof elForm.elements.test == "undefined")
  1785.       : null;
  1786.     root.removeChild(elForm);
  1787.     elForm = elInput = null;
  1788.     return isBuggy;
  1789.   })();
  1790.  
  1791.   var element = global.Element;
  1792.   global.Element = function(tagName, attributes) {
  1793.     attributes = attributes || { };
  1794.     tagName = tagName.toLowerCase();
  1795.     var cache = Element.cache;
  1796.     if (SETATTRIBUTE_IGNORES_NAME && attributes.name) {
  1797.       tagName = '<' + tagName + ' name="' + attributes.name + '">';
  1798.       delete attributes.name;
  1799.       return Element.writeAttribute(document.createElement(tagName), attributes);
  1800.     }
  1801.     if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
  1802.     return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  1803.   };
  1804.   Object.extend(global.Element, element || { });
  1805.   if (element) global.Element.prototype = element.prototype;
  1806. })(this);
  1807.  
  1808. Element.cache = { };
  1809. Element.idCounter = 1;
  1810.  
  1811. Element.Methods = {
  1812.   visible: function(element) {
  1813.     return $(element).style.display != 'none';
  1814.   },
  1815.  
  1816.   toggle: function(element) {
  1817.     element = $(element);
  1818.     Element[Element.visible(element) ? 'hide' : 'show'](element);
  1819.     return element;
  1820.   },
  1821.  
  1822.  
  1823.   hide: function(element) {
  1824.     element = $(element);
  1825.     element.style.display = 'none';
  1826.     return element;
  1827.   },
  1828.  
  1829.   show: function(element) {
  1830.     element = $(element);
  1831.     element.style.display = '';
  1832.     return element;
  1833.   },
  1834.  
  1835.   remove: function(element) {
  1836.     element = $(element);
  1837.     element.parentNode.removeChild(element);
  1838.     return element;
  1839.   },
  1840.  
  1841.   update: (function(){
  1842.  
  1843.     var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
  1844.       var el = document.createElement("select"),
  1845.           isBuggy = true;
  1846.       el.innerHTML = "<option value=\"test\">test</option>";
  1847.       if (el.options && el.options[0]) {
  1848.         isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
  1849.       }
  1850.       el = null;
  1851.       return isBuggy;
  1852.     })();
  1853.  
  1854.     var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
  1855.       try {
  1856.         var el = document.createElement("table");
  1857.         if (el && el.tBodies) {
  1858.           el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
  1859.           var isBuggy = typeof el.tBodies[0] == "undefined";
  1860.           el = null;
  1861.           return isBuggy;
  1862.         }
  1863.       } catch (e) {
  1864.         return true;
  1865.       }
  1866.     })();
  1867.  
  1868.     var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
  1869.       var s = document.createElement("script"),
  1870.           isBuggy = false;
  1871.       try {
  1872.         s.appendChild(document.createTextNode(""));
  1873.         isBuggy = !s.firstChild ||
  1874.           s.firstChild && s.firstChild.nodeType !== 3;
  1875.       } catch (e) {
  1876.         isBuggy = true;
  1877.       }
  1878.       s = null;
  1879.       return isBuggy;
  1880.     })();
  1881.  
  1882.     function update(element, content) {
  1883.       element = $(element);
  1884.  
  1885.       if (content && content.toElement)
  1886.         content = content.toElement();
  1887.  
  1888.       if (Object.isElement(content))
  1889.         return element.update().insert(content);
  1890.  
  1891.       content = Object.toHTML(content);
  1892.  
  1893.       var tagName = element.tagName.toUpperCase();
  1894.  
  1895.       if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
  1896.         element.text = content;
  1897.         return element;
  1898.       }
  1899.  
  1900.       if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
  1901.         if (tagName in Element._insertionTranslations.tags) {
  1902.           while (element.firstChild) {
  1903.             element.removeChild(element.firstChild);
  1904.           }
  1905.           Element._getContentFromAnonymousElement(tagName, content.stripScripts())
  1906.             .each(function(node) {
  1907.               element.appendChild(node)
  1908.             });
  1909.         }
  1910.         else {
  1911.           element.innerHTML = content.stripScripts();
  1912.         }
  1913.       }
  1914.       else {
  1915.         element.innerHTML = content.stripScripts();
  1916.       }
  1917.  
  1918.       content.evalScripts.bind(content).defer();
  1919.       return element;
  1920.     }
  1921.  
  1922.     return update;
  1923.   })(),
  1924.  
  1925.   replace: function(element, content) {
  1926.     element = $(element);
  1927.     if (content && content.toElement) content = content.toElement();
  1928.     else if (!Object.isElement(content)) {
  1929.       content = Object.toHTML(content);
  1930.       var range = element.ownerDocument.createRange();
  1931.       range.selectNode(element);
  1932.       content.evalScripts.bind(content).defer();
  1933.       content = range.createContextualFragment(content.stripScripts());
  1934.     }
  1935.     element.parentNode.replaceChild(content, element);
  1936.     return element;
  1937.   },
  1938.  
  1939.   insert: function(element, insertions) {
  1940.     element = $(element);
  1941.  
  1942.     if (Object.isString(insertions) || Object.isNumber(insertions) ||
  1943.         Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
  1944.           insertions = {bottom:insertions};
  1945.  
  1946.     var content, insert, tagName, childNodes;
  1947.  
  1948.     for (var position in insertions) {
  1949.       content  = insertions[position];
  1950.       position = position.toLowerCase();
  1951.       insert = Element._insertionTranslations[position];
  1952.  
  1953.       if (content && content.toElement) content = content.toElement();
  1954.       if (Object.isElement(content)) {
  1955.         insert(element, content);
  1956.         continue;
  1957.       }
  1958.  
  1959.       content = Object.toHTML(content);
  1960.  
  1961.       tagName = ((position == 'before' || position == 'after')
  1962.         ? element.parentNode : element).tagName.toUpperCase();
  1963.  
  1964.       childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
  1965.  
  1966.       if (position == 'top' || position == 'after') childNodes.reverse();
  1967.       childNodes.each(insert.curry(element));
  1968.  
  1969.       content.evalScripts.bind(content).defer();
  1970.     }
  1971.  
  1972.     return element;
  1973.   },
  1974.  
  1975.   wrap: function(element, wrapper, attributes) {
  1976.     element = $(element);
  1977.     if (Object.isElement(wrapper))
  1978.       $(wrapper).writeAttribute(attributes || { });
  1979.     else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
  1980.     else wrapper = new Element('div', wrapper);
  1981.     if (element.parentNode)
  1982.       element.parentNode.replaceChild(wrapper, element);
  1983.     wrapper.appendChild(element);
  1984.     return wrapper;
  1985.   },
  1986.  
  1987.   inspect: function(element) {
  1988.     element = $(element);
  1989.     var result = '<' + element.tagName.toLowerCase();
  1990.     $H({'id': 'id', 'className': 'class'}).each(function(pair) {
  1991.       var property = pair.first(), attribute = pair.last();
  1992.       var value = (element[property] || '').toString();
  1993.       if (value) result += ' ' + attribute + '=' + value.inspect(true);
  1994.     });
  1995.     return result + '>';
  1996.   },
  1997.  
  1998.   recursivelyCollect: function(element, property) {
  1999.     element = $(element);
  2000.     var elements = [];
  2001.     while (element = element[property])
  2002.       if (element.nodeType == 1)
  2003.         elements.push(Element.extend(element));
  2004.     return elements;
  2005.   },
  2006.  
  2007.   ancestors: function(element) {
  2008.     return Element.recursivelyCollect(element, 'parentNode');
  2009.   },
  2010.  
  2011.   descendants: function(element) {
  2012.     return Element.select(element, "*");
  2013.   },
  2014.  
  2015.   firstDescendant: function(element) {
  2016.     element = $(element).firstChild;
  2017.     while (element && element.nodeType != 1) element = element.nextSibling;
  2018.     return $(element);
  2019.   },
  2020.  
  2021.   immediateDescendants: function(element) {
  2022.     if (!(element = $(element).firstChild)) return [];
  2023.     while (element && element.nodeType != 1) element = element.nextSibling;
  2024.     if (element) return [element].concat($(element).nextSiblings());
  2025.     return [];
  2026.   },
  2027.  
  2028.   previousSiblings: function(element) {
  2029.     return Element.recursivelyCollect(element, 'previousSibling');
  2030.   },
  2031.  
  2032.   nextSiblings: function(element) {
  2033.     return Element.recursivelyCollect(element, 'nextSibling');
  2034.   },
  2035.  
  2036.   siblings: function(element) {
  2037.     element = $(element);
  2038.     return Element.previousSiblings(element).reverse()
  2039.       .concat(Element.nextSiblings(element));
  2040.   },
  2041.  
  2042.   match: function(element, selector) {
  2043.     if (Object.isString(selector))
  2044.       selector = new Selector(selector);
  2045.     return selector.match($(element));
  2046.   },
  2047.  
  2048.   up: function(element, expression, index) {
  2049.     element = $(element);
  2050.     if (arguments.length == 1) return $(element.parentNode);
  2051.     var ancestors = Element.ancestors(element);
  2052.     return Object.isNumber(expression) ? ancestors[expression] :
  2053.       Selector.findElement(ancestors, expression, index);
  2054.   },
  2055.  
  2056.   down: function(element, expression, index) {
  2057.     element = $(element);
  2058.     if (arguments.length == 1) return Element.firstDescendant(element);
  2059.     return Object.isNumber(expression) ? Element.descendants(element)[expression] :
  2060.       Element.select(element, expression)[index || 0];
  2061.   },
  2062.  
  2063.   previous: function(element, expression, index) {
  2064.     element = $(element);
  2065.     if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
  2066.     var previousSiblings = Element.previousSiblings(element);
  2067.     return Object.isNumber(expression) ? previousSiblings[expression] :
  2068.       Selector.findElement(previousSiblings, expression, index);
  2069.   },
  2070.  
  2071.   next: function(element, expression, index) {
  2072.     element = $(element);
  2073.     if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
  2074.     var nextSiblings = Element.nextSiblings(element);
  2075.     return Object.isNumber(expression) ? nextSiblings[expression] :
  2076.       Selector.findElement(nextSiblings, expression, index);
  2077.   },
  2078.  
  2079.  
  2080.   select: function(element) {
  2081.     var args = Array.prototype.slice.call(arguments, 1);
  2082.     return Selector.findChildElements(element, args);
  2083.   },
  2084.  
  2085.   adjacent: function(element) {
  2086.     var args = Array.prototype.slice.call(arguments, 1);
  2087.     return Selector.findChildElements(element.parentNode, args).without(element);
  2088.   },
  2089.  
  2090.   identify: function(element) {
  2091.     element = $(element);
  2092.     var id = Element.readAttribute(element, 'id');
  2093.     if (id) return id;
  2094.     do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
  2095.     Element.writeAttribute(element, 'id', id);
  2096.     return id;
  2097.   },
  2098.  
  2099.   readAttribute: (function(){
  2100.  
  2101.     var iframeGetAttributeThrowsError = (function(){
  2102.       var el = document.createElement('iframe'),
  2103.           isBuggy = false;
  2104.  
  2105.       document.documentElement.appendChild(el);
  2106.       try {
  2107.         el.getAttribute('type', 2);
  2108.       } catch(e) {
  2109.         isBuggy = true;
  2110.       }
  2111.       document.documentElement.removeChild(el);
  2112.       el = null;
  2113.       return isBuggy;
  2114.     })();
  2115.  
  2116.     return function(element, name) {
  2117.       element = $(element);
  2118.       if (iframeGetAttributeThrowsError &&
  2119.           name === 'type' &&
  2120.           element.tagName.toUpperCase() == 'IFRAME') {
  2121.         return element.getAttribute('type');
  2122.       }
  2123.       if (Prototype.Browser.IE) {
  2124.         var t = Element._attributeTranslations.read;
  2125.         if (t.values[name]) return t.values[name](element, name);
  2126.         if (t.names[name]) name = t.names[name];
  2127.         if (name.include(':')) {
  2128.           return (!element.attributes || !element.attributes[name]) ? null :
  2129.            element.attributes[name].value;
  2130.         }
  2131.       }
  2132.       return element.getAttribute(name);
  2133.     }
  2134.   })(),
  2135.  
  2136.   writeAttribute: function(element, name, value) {
  2137.     element = $(element);
  2138.     var attributes = { }, t = Element._attributeTranslations.write;
  2139.  
  2140.     if (typeof name == 'object') attributes = name;
  2141.     else attributes[name] = Object.isUndefined(value) ? true : value;
  2142.  
  2143.     for (var attr in attributes) {
  2144.       name = t.names[attr] || attr;
  2145.       value = attributes[attr];
  2146.       if (t.values[attr]) name = t.values[attr](element, value);
  2147.       if (value === false || value === null)
  2148.         element.removeAttribute(name);
  2149.       else if (value === true)
  2150.         element.setAttribute(name, name);
  2151.       else element.setAttribute(name, value);
  2152.     }
  2153.     return element;
  2154.   },
  2155.  
  2156.   getHeight: function(element) {
  2157.     return Element.getDimensions(element).height;
  2158.   },
  2159.  
  2160.   getWidth: function(element) {
  2161.     return Element.getDimensions(element).width;
  2162.   },
  2163.  
  2164.   classNames: function(element) {
  2165.     return new Element.ClassNames(element);
  2166.   },
  2167.  
  2168.   hasClassName: function(element, className) {
  2169.     if (!(element = $(element))) return;
  2170.     var elementClassName = element.className;
  2171.     return (elementClassName.length > 0 && (elementClassName == className ||
  2172.       new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  2173.   },
  2174.  
  2175.   addClassName: function(element, className) {
  2176.     if (!(element = $(element))) return;
  2177.     if (!Element.hasClassName(element, className))
  2178.       element.className += (element.className ? ' ' : '') + className;
  2179.     return element;
  2180.   },
  2181.  
  2182.   removeClassName: function(element, className) {
  2183.     if (!(element = $(element))) return;
  2184.     element.className = element.className.replace(
  2185.       new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
  2186.     return element;
  2187.   },
  2188.  
  2189.   toggleClassName: function(element, className) {
  2190.     if (!(element = $(element))) return;
  2191.     return Element[Element.hasClassName(element, className) ?
  2192.       'removeClassName' : 'addClassName'](element, className);
  2193.   },
  2194.  
  2195.   cleanWhitespace: function(element) {
  2196.     element = $(element);
  2197.     var node = element.firstChild;
  2198.     while (node) {
  2199.       var nextNode = node.nextSibling;
  2200.       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
  2201.         element.removeChild(node);
  2202.       node = nextNode;
  2203.     }
  2204.     return element;
  2205.   },
  2206.  
  2207.   empty: function(element) {
  2208.     return $(element).innerHTML.blank();
  2209.   },
  2210.  
  2211.   descendantOf: function(element, ancestor) {
  2212.     element = $(element), ancestor = $(ancestor);
  2213.  
  2214.     if (element.compareDocumentPosition)
  2215.       return (element.compareDocumentPosition(ancestor) & 8) === 8;
  2216.  
  2217.     if (ancestor.contains)
  2218.       return ancestor.contains(element) && ancestor !== element;
  2219.  
  2220.     while (element = element.parentNode)
  2221.       if (element == ancestor) return true;
  2222.  
  2223.     return false;
  2224.   },
  2225.  
  2226.   scrollTo: function(element) {
  2227.     element = $(element);
  2228.     var pos = Element.cumulativeOffset(element);
  2229.     window.scrollTo(pos[0], pos[1]);
  2230.     return element;
  2231.   },
  2232.  
  2233.   getStyle: function(element, style) {
  2234.     element = $(element);
  2235.     style = style == 'float' ? 'cssFloat' : style.camelize();
  2236.     var value = element.style[style];
  2237.     if (!value || value == 'auto') {
  2238.       var css = document.defaultView.getComputedStyle(element, null);
  2239.       value = css ? css[style] : null;
  2240.     }
  2241.     if (style == 'opacity') return value ? parseFloat(value) : 1.0;
  2242.     return value == 'auto' ? null : value;
  2243.   },
  2244.  
  2245.   getOpacity: function(element) {
  2246.     return $(element).getStyle('opacity');
  2247.   },
  2248.  
  2249.   setStyle: function(element, styles) {
  2250.     element = $(element);
  2251.     var elementStyle = element.style, match;
  2252.     if (Object.isString(styles)) {
  2253.       element.style.cssText += ';' + styles;
  2254.       return styles.include('opacity') ?
  2255.         element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
  2256.     }
  2257.     for (var property in styles)
  2258.       if (property == 'opacity') element.setOpacity(styles[property]);
  2259.       else
  2260.         elementStyle[(property == 'float' || property == 'cssFloat') ?
  2261.           (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
  2262.             property] = styles[property];
  2263.  
  2264.     return element;
  2265.   },
  2266.  
  2267.   setOpacity: function(element, value) {
  2268.     element = $(element);
  2269.     element.style.opacity = (value == 1 || value === '') ? '' :
  2270.       (value < 0.00001) ? 0 : value;
  2271.     return element;
  2272.   },
  2273.  
  2274.   getDimensions: function(element) {
  2275.     element = $(element);
  2276.     var display = Element.getStyle(element, 'display');
  2277.     if (display != 'none' && display != null) // Safari bug
  2278.       return {width: element.offsetWidth, height: element.offsetHeight};
  2279.  
  2280.     var els = element.style;
  2281.     var originalVisibility = els.visibility;
  2282.     var originalPosition = els.position;
  2283.     var originalDisplay = els.display;
  2284.     els.visibility = 'hidden';
  2285.     if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
  2286.       els.position = 'absolute';
  2287.     els.display = 'block';
  2288.     var originalWidth = element.clientWidth;
  2289.     var originalHeight = element.clientHeight;
  2290.     els.display = originalDisplay;
  2291.     els.position = originalPosition;
  2292.     els.visibility = originalVisibility;
  2293.     return {width: originalWidth, height: originalHeight};
  2294.   },
  2295.  
  2296.   makePositioned: function(element) {
  2297.     element = $(element);
  2298.     var pos = Element.getStyle(element, 'position');
  2299.     if (pos == 'static' || !pos) {
  2300.       element._madePositioned = true;
  2301.       element.style.position = 'relative';
  2302.       if (Prototype.Browser.Opera) {
  2303.         element.style.top = 0;
  2304.         element.style.left = 0;
  2305.       }
  2306.     }
  2307.     return element;
  2308.   },
  2309.  
  2310.   undoPositioned: function(element) {
  2311.     element = $(element);
  2312.     if (element._madePositioned) {
  2313.       element._madePositioned = undefined;
  2314.       element.style.position =
  2315.         element.style.top =
  2316.         element.style.left =
  2317.         element.style.bottom =
  2318.         element.style.right = '';
  2319.     }
  2320.     return element;
  2321.   },
  2322.  
  2323.   makeClipping: function(element) {
  2324.     element = $(element);
  2325.     if (element._overflow) return element;
  2326.     element._overflow = Element.getStyle(element, 'overflow') || 'auto';
  2327.     if (element._overflow !== 'hidden')
  2328.       element.style.overflow = 'hidden';
  2329.     return element;
  2330.   },
  2331.  
  2332.   undoClipping: function(element) {
  2333.     element = $(element);
  2334.     if (!element._overflow) return element;
  2335.     element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
  2336.     element._overflow = null;
  2337.     return element;
  2338.   },
  2339.  
  2340.   cumulativeOffset: function(element) {
  2341.     var valueT = 0, valueL = 0;
  2342.     do {
  2343.       valueT += element.offsetTop  || 0;
  2344.       valueL += element.offsetLeft || 0;
  2345.       element = element.offsetParent;
  2346.     } while (element);
  2347.     return Element._returnOffset(valueL, valueT);
  2348.   },
  2349.  
  2350.   positionedOffset: function(element) {
  2351.     var valueT = 0, valueL = 0;
  2352.     do {
  2353.       valueT += element.offsetTop  || 0;
  2354.       valueL += element.offsetLeft || 0;
  2355.       element = element.offsetParent;
  2356.       if (element) {
  2357.         if (element.tagName.toUpperCase() == 'BODY') break;
  2358.         var p = Element.getStyle(element, 'position');
  2359.         if (p !== 'static') break;
  2360.       }
  2361.     } while (element);
  2362.     return Element._returnOffset(valueL, valueT);
  2363.   },
  2364.  
  2365.   absolutize: function(element) {
  2366.     element = $(element);
  2367.     if (Element.getStyle(element, 'position') == 'absolute') return element;
  2368.  
  2369.     var offsets = Element.positionedOffset(element);
  2370.     var top     = offsets[1];
  2371.     var left    = offsets[0];
  2372.     var width   = element.clientWidth;
  2373.     var height  = element.clientHeight;
  2374.  
  2375.     element._originalLeft   = left - parseFloat(element.style.left  || 0);
  2376.     element._originalTop    = top  - parseFloat(element.style.top || 0);
  2377.     element._originalWidth  = element.style.width;
  2378.     element._originalHeight = element.style.height;
  2379.  
  2380.     element.style.position = 'absolute';
  2381.     element.style.top    = top + 'px';
  2382.     element.style.left   = left + 'px';
  2383.     element.style.width  = width + 'px';
  2384.     element.style.height = height + 'px';
  2385.     return element;
  2386.   },
  2387.  
  2388.   relativize: function(element) {
  2389.     element = $(element);
  2390.     if (Element.getStyle(element, 'position') == 'relative') return element;
  2391.  
  2392.     element.style.position = 'relative';
  2393.     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
  2394.     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
  2395.  
  2396.     element.style.top    = top + 'px';
  2397.     element.style.left   = left + 'px';
  2398.     element.style.height = element._originalHeight;
  2399.     element.style.width  = element._originalWidth;
  2400.     return element;
  2401.   },
  2402.  
  2403.   cumulativeScrollOffset: function(element) {
  2404.     var valueT = 0, valueL = 0;
  2405.     do {
  2406.       valueT += element.scrollTop  || 0;
  2407.       valueL += element.scrollLeft || 0;
  2408.       element = element.parentNode;
  2409.     } while (element);
  2410.     return Element._returnOffset(valueL, valueT);
  2411.   },
  2412.  
  2413.   getOffsetParent: function(element) {
  2414.     if (element.offsetParent) return $(element.offsetParent);
  2415.     if (element == document.body) return $(element);
  2416.  
  2417.     while ((element = element.parentNode) && element != document.body)
  2418.       if (Element.getStyle(element, 'position') != 'static')
  2419.         return $(element);
  2420.  
  2421.     return $(document.body);
  2422.   },
  2423.  
  2424.   viewportOffset: function(forElement) {
  2425.     var valueT = 0, valueL = 0;
  2426.  
  2427.     var element = forElement;
  2428.     do {
  2429.       valueT += element.offsetTop  || 0;
  2430.       valueL += element.offsetLeft || 0;
  2431.  
  2432.       if (element.offsetParent == document.body &&
  2433.         Element.getStyle(element, 'position') == 'absolute') break;
  2434.  
  2435.     } while (element = element.offsetParent);
  2436.  
  2437.     element = forElement;
  2438.     do {
  2439.       if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
  2440.         valueT -= element.scrollTop  || 0;
  2441.         valueL -= element.scrollLeft || 0;
  2442.       }
  2443.     } while (element = element.parentNode);
  2444.  
  2445.     return Element._returnOffset(valueL, valueT);
  2446.   },
  2447.  
  2448.   clonePosition: function(element, source) {
  2449.     var options = Object.extend({
  2450.       setLeft:    true,
  2451.       setTop:     true,
  2452.       setWidth:   true,
  2453.       setHeight:  true,
  2454.       offsetTop:  0,
  2455.       offsetLeft: 0
  2456.     }, arguments[2] || { });
  2457.  
  2458.     source = $(source);
  2459.     var p = Element.viewportOffset(source);
  2460.  
  2461.     element = $(element);
  2462.     var delta = [0, 0];
  2463.     var parent = null;
  2464.     if (Element.getStyle(element, 'position') == 'absolute') {
  2465.       parent = Element.getOffsetParent(element);
  2466.       delta = Element.viewportOffset(parent);
  2467.     }
  2468.  
  2469.     if (parent == document.body) {
  2470.       delta[0] -= document.body.offsetLeft;
  2471.       delta[1] -= document.body.offsetTop;
  2472.     }
  2473.  
  2474.     if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
  2475.     if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
  2476.     if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
  2477.     if (options.setHeight) element.style.height = source.offsetHeight + 'px';
  2478.     return element;
  2479.   }
  2480. };
  2481.  
  2482. Object.extend(Element.Methods, {
  2483.   getElementsBySelector: Element.Methods.select,
  2484.  
  2485.   childElements: Element.Methods.immediateDescendants
  2486. });
  2487.  
  2488. Element._attributeTranslations = {
  2489.   write: {
  2490.     names: {
  2491.       className: 'class',
  2492.       htmlFor:   'for'
  2493.     },
  2494.     values: { }
  2495.   }
  2496. };
  2497.  
  2498. if (Prototype.Browser.Opera) {
  2499.   Element.Methods.getStyle = Element.Methods.getStyle.wrap(
  2500.     function(proceed, element, style) {
  2501.       switch (style) {
  2502.         case 'left': case 'top': case 'right': case 'bottom':
  2503.           if (proceed(element, 'position') === 'static') return null;
  2504.         case 'height': case 'width':
  2505.           if (!Element.visible(element)) return null;
  2506.  
  2507.           var dim = parseInt(proceed(element, style), 10);
  2508.  
  2509.           if (dim !== element['offset' + style.capitalize()])
  2510.             return dim + 'px';
  2511.  
  2512.           var properties;
  2513.           if (style === 'height') {
  2514.             properties = ['border-top-width', 'padding-top',
  2515.              'padding-bottom', 'border-bottom-width'];
  2516.           }
  2517.           else {
  2518.             properties = ['border-left-width', 'padding-left',
  2519.              'padding-right', 'border-right-width'];
  2520.           }
  2521.           return properties.inject(dim, function(memo, property) {
  2522.             var val = proceed(element, property);
  2523.             return val === null ? memo : memo - parseInt(val, 10);
  2524.           }) + 'px';
  2525.         default: return proceed(element, style);
  2526.       }
  2527.     }
  2528.   );
  2529.  
  2530.   Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
  2531.     function(proceed, element, attribute) {
  2532.       if (attribute === 'title') return element.title;
  2533.       return proceed(element, attribute);
  2534.     }
  2535.   );
  2536. }
  2537.  
  2538. else if (Prototype.Browser.IE) {
  2539.   Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
  2540.     function(proceed, element) {
  2541.       element = $(element);
  2542.       try { element.offsetParent }
  2543.       catch(e) { return $(document.body) }
  2544.       var position = element.getStyle('position');
  2545.       if (position !== 'static') return proceed(element);
  2546.       element.setStyle({ position: 'relative' });
  2547.       var value = proceed(element);
  2548.       element.setStyle({ position: position });
  2549.       return value;
  2550.     }
  2551.   );
  2552.  
  2553.   $w('positionedOffset viewportOffset').each(function(method) {
  2554.     Element.Methods[method] = Element.Methods[method].wrap(
  2555.       function(proceed, element) {
  2556.         element = $(element);
  2557.         try { element.offsetParent }
  2558.         catch(e) { return Element._returnOffset(0,0) }
  2559.         var position = element.getStyle('position');
  2560.         if (position !== 'static') return proceed(element);
  2561.         var offsetParent = element.getOffsetParent();
  2562.         if (offsetParent && offsetParent.getStyle('position') === 'fixed')
  2563.           offsetParent.setStyle({ zoom: 1 });
  2564.         element.setStyle({ position: 'relative' });
  2565.         var value = proceed(element);
  2566.         element.setStyle({ position: position });
  2567.         return value;
  2568.       }
  2569.     );
  2570.   });
  2571.  
  2572.   Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
  2573.     function(proceed, element) {
  2574.       try { element.offsetParent }
  2575.       catch(e) { return Element._returnOffset(0,0) }
  2576.       return proceed(element);
  2577.     }
  2578.   );
  2579.  
  2580.   Element.Methods.getStyle = function(element, style) {
  2581.     element = $(element);
  2582.     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
  2583.     var value = element.style[style];
  2584.     if (!value && element.currentStyle) value = element.currentStyle[style];
  2585.  
  2586.     if (style == 'opacity') {
  2587.       if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
  2588.         if (value[1]) return parseFloat(value[1]) / 100;
  2589.       return 1.0;
  2590.     }
  2591.  
  2592.     if (value == 'auto') {
  2593.       if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
  2594.         return element['offset' + style.capitalize()] + 'px';
  2595.       return null;
  2596.     }
  2597.     return value;
  2598.   };
  2599.  
  2600.   Element.Methods.setOpacity = function(element, value) {
  2601.     function stripAlpha(filter){
  2602.       return filter.replace(/alpha\([^\)]*\)/gi,'');
  2603.     }
  2604.     element = $(element);
  2605.     var currentStyle = element.currentStyle;
  2606.     if ((currentStyle && !currentStyle.hasLayout) ||
  2607.       (!currentStyle && element.style.zoom == 'normal'))
  2608.         element.style.zoom = 1;
  2609.  
  2610.     var filter = element.getStyle('filter'), style = element.style;
  2611.     if (value == 1 || value === '') {
  2612.       (filter = stripAlpha(filter)) ?
  2613.         style.filter = filter : style.removeAttribute('filter');
  2614.       return element;
  2615.     } else if (value < 0.00001) value = 0;
  2616.     style.filter = stripAlpha(filter) +
  2617.       'alpha(opacity=' + (value * 100) + ')';
  2618.     return element;
  2619.   };
  2620.  
  2621.   Element._attributeTranslations = (function(){
  2622.  
  2623.     var classProp = 'className';
  2624.     var forProp = 'for';
  2625.  
  2626.     var el = document.createElement('div');
  2627.  
  2628.     el.setAttribute(classProp, 'x');
  2629.  
  2630.     if (el.className !== 'x') {
  2631.       el.setAttribute('class', 'x');
  2632.       if (el.className === 'x') {
  2633.         classProp = 'class';
  2634.       }
  2635.     }
  2636.     el = null;
  2637.  
  2638.     el = document.createElement('label');
  2639.     el.setAttribute(forProp, 'x');
  2640.     if (el.htmlFor !== 'x') {
  2641.       el.setAttribute('htmlFor', 'x');
  2642.       if (el.htmlFor === 'x') {
  2643.         forProp = 'htmlFor';
  2644.       }
  2645.     }
  2646.     el = null;
  2647.  
  2648.     return {
  2649.       read: {
  2650.         names: {
  2651.           'class':      classProp,
  2652.           'className':  classProp,
  2653.           'for':        forProp,
  2654.           'htmlFor':    forProp
  2655.         },
  2656.         values: {
  2657.           _getAttr: function(element, attribute) {
  2658.             return element.getAttribute(attribute, 2);
  2659.           },
  2660.           _getAttrNode: function(element, attribute) {
  2661.             var node = element.getAttributeNode(attribute);
  2662.             return node ? node.value : "";
  2663.           },
  2664.           _getEv: (function(){
  2665.  
  2666.             var el = document.createElement('div');
  2667.             el.onclick = Prototype.emptyFunction;
  2668.             var value = el.getAttribute('onclick');
  2669.             var f;
  2670.  
  2671.             if (String(value).indexOf('{') > -1) {
  2672.               f = function(element, attribute) {
  2673.                 attribute = element.getAttribute(attribute);
  2674.                 if (!attribute) return null;
  2675.                 attribute = attribute.toString();
  2676.                 attribute = attribute.split('{')[1];
  2677.                 attribute = attribute.split('}')[0];
  2678.                 return attribute.strip();
  2679.               }
  2680.             }
  2681.             else if (value === '') {
  2682.               f = function(element, attribute) {
  2683.                 attribute = element.getAttribute(attribute);
  2684.                 if (!attribute) return null;
  2685.                 return attribute.strip();
  2686.               }
  2687.             }
  2688.             el = null;
  2689.             return f;
  2690.           })(),
  2691.           _flag: function(element, attribute) {
  2692.             return $(element).hasAttribute(attribute) ? attribute : null;
  2693.           },
  2694.           style: function(element) {
  2695.             return element.style.cssText.toLowerCase();
  2696.           },
  2697.           title: function(element) {
  2698.             return element.title;
  2699.           }
  2700.         }
  2701.       }
  2702.     }
  2703.   })();
  2704.  
  2705.   Element._attributeTranslations.write = {
  2706.     names: Object.extend({
  2707.       cellpadding: 'cellPadding',
  2708.       cellspacing: 'cellSpacing'
  2709.     }, Element._attributeTranslations.read.names),
  2710.     values: {
  2711.       checked: function(element, value) {
  2712.         element.checked = !!value;
  2713.       },
  2714.  
  2715.       style: function(element, value) {
  2716.         element.style.cssText = value ? value : '';
  2717.       }
  2718.     }
  2719.   };
  2720.  
  2721.   Element._attributeTranslations.has = {};
  2722.  
  2723.   $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
  2724.       'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
  2725.     Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
  2726.     Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  2727.   });
  2728.  
  2729.   (function(v) {
  2730.     Object.extend(v, {
  2731.       href:        v._getAttr,
  2732.       src:         v._getAttr,
  2733.       type:        v._getAttr,
  2734.       action:      v._getAttrNode,
  2735.       disabled:    v._flag,
  2736.       checked:     v._flag,
  2737.       readonly:    v._flag,
  2738.       multiple:    v._flag,
  2739.       onload:      v._getEv,
  2740.       onunload:    v._getEv,
  2741.       onclick:     v._getEv,
  2742.       ondblclick:  v._getEv,
  2743.       onmousedown: v._getEv,
  2744.       onmouseup:   v._getEv,
  2745.       onmouseover: v._getEv,
  2746.       onmousemove: v._getEv,
  2747.       onmouseout:  v._getEv,
  2748.       onfocus:     v._getEv,
  2749.       onblur:      v._getEv,
  2750.       onkeypress:  v._getEv,
  2751.       onkeydown:   v._getEv,
  2752.       onkeyup:     v._getEv,
  2753.       onsubmit:    v._getEv,
  2754.       onreset:     v._getEv,
  2755.       onselect:    v._getEv,
  2756.       onchange:    v._getEv
  2757.     });
  2758.   })(Element._attributeTranslations.read.values);
  2759.  
  2760.   if (Prototype.BrowserFeatures.ElementExtensions) {
  2761.     (function() {
  2762.       function _descendants(element) {
  2763.         var nodes = element.getElementsByTagName('*'), results = [];
  2764.         for (var i = 0, node; node = nodes[i]; i++)
  2765.           if (node.tagName !== "!") // Filter out comment nodes.
  2766.             results.push(node);
  2767.         return results;
  2768.       }
  2769.  
  2770.       Element.Methods.down = function(element, expression, index) {
  2771.         element = $(element);
  2772.         if (arguments.length == 1) return element.firstDescendant();
  2773.         return Object.isNumber(expression) ? _descendants(element)[expression] :
  2774.           Element.select(element, expression)[index || 0];
  2775.       }
  2776.     })();
  2777.   }
  2778.  
  2779. }
  2780.  
  2781. else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  2782.   Element.Methods.setOpacity = function(element, value) {
  2783.     element = $(element);
  2784.     element.style.opacity = (value == 1) ? 0.999999 :
  2785.       (value === '') ? '' : (value < 0.00001) ? 0 : value;
  2786.     return element;
  2787.   };
  2788. }
  2789.  
  2790. else if (Prototype.Browser.WebKit) {
  2791.   Element.Methods.setOpacity = function(element, value) {
  2792.     element = $(element);
  2793.     element.style.opacity = (value == 1 || value === '') ? '' :
  2794.       (value < 0.00001) ? 0 : value;
  2795.  
  2796.     if (value == 1)
  2797.       if(element.tagName.toUpperCase() == 'IMG' && element.width) {
  2798.         element.width++; element.width--;
  2799.       } else try {
  2800.         var n = document.createTextNode(' ');
  2801.         element.appendChild(n);
  2802.         element.removeChild(n);
  2803.       } catch (e) { }
  2804.  
  2805.     return element;
  2806.   };
  2807.  
  2808.   Element.Methods.cumulativeOffset = function(element) {
  2809.     var valueT = 0, valueL = 0;
  2810.     do {
  2811.       valueT += element.offsetTop  || 0;
  2812.       valueL += element.offsetLeft || 0;
  2813.       if (element.offsetParent == document.body)
  2814.         if (Element.getStyle(element, 'position') == 'absolute') break;
  2815.  
  2816.       element = element.offsetParent;
  2817.     } while (element);
  2818.  
  2819.     return Element._returnOffset(valueL, valueT);
  2820.   };
  2821. }
  2822.  
  2823. if ('outerHTML' in document.documentElement) {
  2824.   Element.Methods.replace = function(element, content) {
  2825.     element = $(element);
  2826.  
  2827.     if (content && content.toElement) content = content.toElement();
  2828.     if (Object.isElement(content)) {
  2829.       element.parentNode.replaceChild(content, element);
  2830.       return element;
  2831.     }
  2832.  
  2833.     content = Object.toHTML(content);
  2834.     var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
  2835.  
  2836.     if (Element._insertionTranslations.tags[tagName]) {
  2837.       var nextSibling = element.next();
  2838.       var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
  2839.       parent.removeChild(element);
  2840.       if (nextSibling)
  2841.         fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
  2842.       else
  2843.         fragments.each(function(node) { parent.appendChild(node) });
  2844.     }
  2845.     else element.outerHTML = content.stripScripts();
  2846.  
  2847.     content.evalScripts.bind(content).defer();
  2848.     return element;
  2849.   };
  2850. }
  2851.  
  2852. Element._returnOffset = function(l, t) {
  2853.   var result = [l, t];
  2854.   result.left = l;
  2855.   result.top = t;
  2856.   return result;
  2857. };
  2858.  
  2859. Element._getContentFromAnonymousElement = function(tagName, html) {
  2860.   var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
  2861.   if (t) {
  2862.     div.innerHTML = t[0] + html + t[1];
  2863.     t[2].times(function() { div = div.firstChild });
  2864.   } else div.innerHTML = html;
  2865.   return $A(div.childNodes);
  2866. };
  2867.  
  2868. Element._insertionTranslations = {
  2869.   before: function(element, node) {
  2870.     element.parentNode.insertBefore(node, element);
  2871.   },
  2872.   top: function(element, node) {
  2873.     element.insertBefore(node, element.firstChild);
  2874.   },
  2875.   bottom: function(element, node) {
  2876.     element.appendChild(node);
  2877.   },
  2878.   after: function(element, node) {
  2879.     element.parentNode.insertBefore(node, element.nextSibling);
  2880.   },
  2881.   tags: {
  2882.     TABLE:  ['<table>',                '</table>',                   1],
  2883.     TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
  2884.     TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
  2885.     TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
  2886.     SELECT: ['<select>',               '</select>',                  1]
  2887.   }
  2888. };
  2889.  
  2890. (function() {
  2891.   var tags = Element._insertionTranslations.tags;
  2892.   Object.extend(tags, {
  2893.     THEAD: tags.TBODY,
  2894.     TFOOT: tags.TBODY,
  2895.     TH:    tags.TD
  2896.   });
  2897. })();
  2898.  
  2899. Element.Methods.Simulated = {
  2900.   hasAttribute: function(element, attribute) {
  2901.     attribute = Element._attributeTranslations.has[attribute] || attribute;
  2902.     var node = $(element).getAttributeNode(attribute);
  2903.     return !!(node && node.specified);
  2904.   }
  2905. };
  2906.  
  2907. Element.Methods.ByTag = { };
  2908.  
  2909. Object.extend(Element, Element.Methods);
  2910.  
  2911. (function(div) {
  2912.  
  2913.   if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
  2914.     window.HTMLElement = { };
  2915.     window.HTMLElement.prototype = div['__proto__'];
  2916.     Prototype.BrowserFeatures.ElementExtensions = true;
  2917.   }
  2918.  
  2919.   div = null;
  2920.  
  2921. })(document.createElement('div'))
  2922.  
  2923. Element.extend = (function() {
  2924.  
  2925.   function checkDeficiency(tagName) {
  2926.     if (typeof window.Element != 'undefined') {
  2927.       var proto = window.Element.prototype;
  2928.       if (proto) {
  2929.         var id = '_' + (Math.random()+'').slice(2);
  2930.         var el = document.createElement(tagName);
  2931.         proto[id] = 'x';
  2932.         var isBuggy = (el[id] !== 'x');
  2933.         delete proto[id];
  2934.         el = null;
  2935.         return isBuggy;
  2936.       }
  2937.     }
  2938.     return false;
  2939.   }
  2940.  
  2941.   function extendElementWith(element, methods) {
  2942.     for (var property in methods) {
  2943.       var value = methods[property];
  2944.       if (Object.isFunction(value) && !(property in element))
  2945.         element[property] = value.methodize();
  2946.     }
  2947.   }
  2948.  
  2949.   var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
  2950.   var HTMLAPPLETELEMENT_PROTOTYPE_BUGGY = checkDeficiency('applet');
  2951.  
  2952.   if (Prototype.BrowserFeatures.SpecificElementExtensions) {
  2953.     if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY &&
  2954.         HTMLAPPLETELEMENT_PROTOTYPE_BUGGY) {
  2955.       return function(element) {
  2956.         if (element && typeof element._extendedByPrototype == 'undefined') {
  2957.           var t = element.tagName;
  2958.           if (t && (/^(?:object|applet|embed)$/i.test(t))) {
  2959.             extendElementWith(element, Element.Methods);
  2960.             extendElementWith(element, Element.Methods.Simulated);
  2961.             extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
  2962.           }
  2963.         }
  2964.         return element;
  2965.       }
  2966.     }
  2967.     return Prototype.K;
  2968.   }
  2969.  
  2970.   var Methods = { }, ByTag = Element.Methods.ByTag;
  2971.  
  2972.   var extend = Object.extend(function(element) {
  2973.     if (!element || typeof element._extendedByPrototype != 'undefined' ||
  2974.         element.nodeType != 1 || element == window) return element;
  2975.  
  2976.     var methods = Object.clone(Methods),
  2977.         tagName = element.tagName.toUpperCase();
  2978.  
  2979.     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
  2980.  
  2981.     extendElementWith(element, methods);
  2982.  
  2983.     element._extendedByPrototype = Prototype.emptyFunction;
  2984.     return element;
  2985.  
  2986.   }, {
  2987.     refresh: function() {
  2988.       if (!Prototype.BrowserFeatures.ElementExtensions) {
  2989.         Object.extend(Methods, Element.Methods);
  2990.         Object.extend(Methods, Element.Methods.Simulated);
  2991.       }
  2992.     }
  2993.   });
  2994.  
  2995.   extend.refresh();
  2996.   return extend;
  2997. })();
  2998.  
  2999. Element.hasAttribute = function(element, attribute) {
  3000.   if (element.hasAttribute) return element.hasAttribute(attribute);
  3001.   return Element.Methods.Simulated.hasAttribute(element, attribute);
  3002. };
  3003.  
  3004. Element.addMethods = function(methods) {
  3005.   var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
  3006.  
  3007.   if (!methods) {
  3008.     Object.extend(Form, Form.Methods);
  3009.     Object.extend(Form.Element, Form.Element.Methods);
  3010.     Object.extend(Element.Methods.ByTag, {
  3011.       "FORM":     Object.clone(Form.Methods),
  3012.       "INPUT":    Object.clone(Form.Element.Methods),
  3013.       "SELECT":   Object.clone(Form.Element.Methods),
  3014.       "TEXTAREA": Object.clone(Form.Element.Methods)
  3015.     });
  3016.   }
  3017.  
  3018.   if (arguments.length == 2) {
  3019.     var tagName = methods;
  3020.     methods = arguments[1];
  3021.   }
  3022.  
  3023.   if (!tagName) Object.extend(Element.Methods, methods || { });
  3024.   else {
  3025.     if (Object.isArray(tagName)) tagName.each(extend);
  3026.     else extend(tagName);
  3027.   }
  3028.  
  3029.   function extend(tagName) {
  3030.     tagName = tagName.toUpperCase();
  3031.     if (!Element.Methods.ByTag[tagName])
  3032.       Element.Methods.ByTag[tagName] = { };
  3033.     Object.extend(Element.Methods.ByTag[tagName], methods);
  3034.   }
  3035.  
  3036.   function copy(methods, destination, onlyIfAbsent) {
  3037.     onlyIfAbsent = onlyIfAbsent || false;
  3038.     for (var property in methods) {
  3039.       var value = methods[property];
  3040.       if (!Object.isFunction(value)) continue;
  3041.       if (!onlyIfAbsent || !(property in destination))
  3042.         destination[property] = value.methodize();
  3043.     }
  3044.   }
  3045.  
  3046.   function findDOMClass(tagName) {
  3047.     var klass;
  3048.     var trans = {
  3049.       "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
  3050.       "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
  3051.       "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
  3052.       "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
  3053.       "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
  3054.       "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
  3055.       "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
  3056.       "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
  3057.       "FrameSet", "IFRAME": "IFrame"
  3058.     };
  3059.     if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
  3060.     if (window[klass]) return window[klass];
  3061.     klass = 'HTML' + tagName + 'Element';
  3062.     if (window[klass]) return window[klass];
  3063.     klass = 'HTML' + tagName.capitalize() + 'Element';
  3064.     if (window[klass]) return window[klass];
  3065.  
  3066.     var element = document.createElement(tagName);
  3067.     var proto = element['__proto__'] || element.constructor.prototype;
  3068.     element = null;
  3069.     return proto;
  3070.   }
  3071.  
  3072.   var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
  3073.    Element.prototype;
  3074.  
  3075.   if (F.ElementExtensions) {
  3076.     copy(Element.Methods, elementPrototype);
  3077.     copy(Element.Methods.Simulated, elementPrototype, true);
  3078.   }
  3079.  
  3080.   if (F.SpecificElementExtensions) {
  3081.     for (var tag in Element.Methods.ByTag) {
  3082.       var klass = findDOMClass(tag);
  3083.       if (Object.isUndefined(klass)) continue;
  3084.       copy(T[tag], klass.prototype);
  3085.     }
  3086.   }
  3087.  
  3088.   Object.extend(Element, Element.Methods);
  3089.   delete Element.ByTag;
  3090.  
  3091.   if (Element.extend.refresh) Element.extend.refresh();
  3092.   Element.cache = { };
  3093. };
  3094.  
  3095.  
  3096. document.viewport = {
  3097.  
  3098.   getDimensions: function() {
  3099.     return { width: this.getWidth(), height: this.getHeight() };
  3100.   },
  3101.  
  3102.   getScrollOffsets: function() {
  3103.     return Element._returnOffset(
  3104.       window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
  3105.       window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
  3106.   }
  3107. };
  3108.  
  3109. (function(viewport) {
  3110.   var B = Prototype.Browser, doc = document, element, property = {};
  3111.  
  3112.   function getRootElement() {
  3113.     if (B.WebKit && !doc.evaluate)
  3114.       return document;
  3115.  
  3116.     if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
  3117.       return document.body;
  3118.  
  3119.     return document.documentElement;
  3120.   }
  3121.  
  3122.   function define(D) {
  3123.     if (!element) element = getRootElement();
  3124.  
  3125.     property[D] = 'client' + D;
  3126.  
  3127.     viewport['get' + D] = function() { return element[property[D]] };
  3128.     return viewport['get' + D]();
  3129.   }
  3130.  
  3131.   viewport.getWidth  = define.curry('Width');
  3132.  
  3133.   viewport.getHeight = define.curry('Height');
  3134. })(document.viewport);
  3135.  
  3136.  
  3137. Element.Storage = {
  3138.   UID: 1
  3139. };
  3140.  
  3141. Element.addMethods({
  3142.   getStorage: function(element) {
  3143.     if (!(element = $(element))) return;
  3144.  
  3145.     var uid;
  3146.     if (element === window) {
  3147.       uid = 0;
  3148.     } else {
  3149.       if (typeof element._prototypeUID === "undefined")
  3150.         element._prototypeUID = [Element.Storage.UID++];
  3151.       uid = element._prototypeUID[0];
  3152.     }
  3153.  
  3154.     if (!Element.Storage[uid])
  3155.       Element.Storage[uid] = $H();
  3156.  
  3157.     return Element.Storage[uid];
  3158.   },
  3159.  
  3160.   store: function(element, key, value) {
  3161.     if (!(element = $(element))) return;
  3162.  
  3163.     if (arguments.length === 2) {
  3164.       Element.getStorage(element).update(key);
  3165.     } else {
  3166.       Element.getStorage(element).set(key, value);
  3167.     }
  3168.  
  3169.     return element;
  3170.   },
  3171.  
  3172.   retrieve: function(element, key, defaultValue) {
  3173.     if (!(element = $(element))) return;
  3174.     var hash = Element.getStorage(element), value = hash.get(key);
  3175.  
  3176.     if (Object.isUndefined(value)) {
  3177.       hash.set(key, defaultValue);
  3178.       value = defaultValue;
  3179.     }
  3180.  
  3181.     return value;
  3182.   },
  3183.  
  3184.   clone: function(element, deep) {
  3185.     if (!(element = $(element))) return;
  3186.     var clone = element.cloneNode(deep);
  3187.     clone._prototypeUID = void 0;
  3188.     if (deep) {
  3189.       var descendants = Element.select(clone, '*'),
  3190.           i = descendants.length;
  3191.       while (i--) {
  3192.         descendants[i]._prototypeUID = void 0;
  3193.       }
  3194.     }
  3195.     return Element.extend(clone);
  3196.   }
  3197. });
  3198. /* Portions of the Selector class are derived from Jack Slocum's DomQuery,
  3199.  * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
  3200.  * license.  Please see http://www.yui-ext.com/ for more information. */
  3201.  
  3202. var Selector = Class.create({
  3203.   initialize: function(expression) {
  3204.     this.expression = expression.strip();
  3205.  
  3206.     if (this.shouldUseSelectorsAPI()) {
  3207.       this.mode = 'selectorsAPI';
  3208.     } else if (this.shouldUseXPath()) {
  3209.       this.mode = 'xpath';
  3210.       this.compileXPathMatcher();
  3211.     } else {
  3212.       this.mode = "normal";
  3213.       this.compileMatcher();
  3214.     }
  3215.  
  3216.   },
  3217.  
  3218.   shouldUseXPath: (function() {
  3219.  
  3220.     var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
  3221.       var isBuggy = false;
  3222.       if (document.evaluate && window.XPathResult) {
  3223.         var el = document.createElement('div');
  3224.         el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';
  3225.  
  3226.         var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
  3227.           "//*[local-name()='li' or local-name()='LI']";
  3228.  
  3229.         var result = document.evaluate(xpath, el, null,
  3230.           XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  3231.  
  3232.         isBuggy = (result.snapshotLength !== 2);
  3233.         el = null;
  3234.       }
  3235.       return isBuggy;
  3236.     })();
  3237.  
  3238.     return function() {
  3239.       if (!Prototype.BrowserFeatures.XPath) return false;
  3240.  
  3241.       var e = this.expression;
  3242.  
  3243.       if (Prototype.Browser.WebKit &&
  3244.        (e.include("-of-type") || e.include(":empty")))
  3245.         return false;
  3246.  
  3247.       if ((/(\[[\w-]*?:|:checked)/).test(e))
  3248.         return false;
  3249.  
  3250.       if (IS_DESCENDANT_SELECTOR_BUGGY) return false;
  3251.  
  3252.       return true;
  3253.     }
  3254.  
  3255.   })(),
  3256.  
  3257.   shouldUseSelectorsAPI: function() {
  3258.     if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
  3259.  
  3260.     if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;
  3261.  
  3262.     if (!Selector._div) Selector._div = new Element('div');
  3263.  
  3264.     try {
  3265.       Selector._div.querySelector(this.expression);
  3266.     } catch(e) {
  3267.       return false;
  3268.     }
  3269.  
  3270.     return true;
  3271.   },
  3272.  
  3273.   compileMatcher: function() {
  3274.     var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
  3275.         c = Selector.criteria, le, p, m, len = ps.length, name;
  3276.  
  3277.     if (Selector._cache[e]) {
  3278.       this.matcher = Selector._cache[e];
  3279.       return;
  3280.     }
  3281.  
  3282.     this.matcher = ["this.matcher = function(root) {",
  3283.                     "var r = root, h = Selector.handlers, c = false, n;"];
  3284.  
  3285.     while (e && le != e && (/\S/).test(e)) {
  3286.       le = e;
  3287.       for (var i = 0; i<len; i++) {
  3288.         p = ps[i].re;
  3289.         name = ps[i].name;
  3290.         if (m = e.match(p)) {
  3291.           this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :
  3292.             new Template(c[name]).evaluate(m));
  3293.           e = e.replace(m[0], '');
  3294.           break;
  3295.         }
  3296.       }
  3297.     }
  3298.  
  3299.     this.matcher.push("return h.unique(n);\n}");
  3300.     eval(this.matcher.join('\n'));
  3301.     Selector._cache[this.expression] = this.matcher;
  3302.   },
  3303.  
  3304.   compileXPathMatcher: function() {
  3305.     var e = this.expression, ps = Selector.patterns,
  3306.         x = Selector.xpath, le, m, len = ps.length, name;
  3307.  
  3308.     if (Selector._cache[e]) {
  3309.       this.xpath = Selector._cache[e]; return;
  3310.     }
  3311.  
  3312.     this.matcher = ['.//*'];
  3313.     while (e && le != e && (/\S/).test(e)) {
  3314.       le = e;
  3315.       for (var i = 0; i<len; i++) {
  3316.         name = ps[i].name;
  3317.         if (m = e.match(ps[i].re)) {
  3318.           this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
  3319.             new Template(x[name]).evaluate(m));
  3320.           e = e.replace(m[0], '');
  3321.           break;
  3322.         }
  3323.       }
  3324.     }
  3325.  
  3326.     this.xpath = this.matcher.join('');
  3327.     Selector._cache[this.expression] = this.xpath;
  3328.   },
  3329.  
  3330.   findElements: function(root) {
  3331.     root = root || document;
  3332.     var e = this.expression, results;
  3333.  
  3334.     switch (this.mode) {
  3335.       case 'selectorsAPI':
  3336.         if (root !== document) {
  3337.           var oldId = root.id, id = $(root).identify();
  3338.           id = id.replace(/[\.:]/g, "\\$0");
  3339.           e = "#" + id + " " + e;
  3340.         }
  3341.  
  3342.         results = $A(root.querySelectorAll(e)).map(Element.extend);
  3343.         root.id = oldId;
  3344.  
  3345.         return results;
  3346.       case 'xpath':
  3347.         return document._getElementsByXPath(this.xpath, root);
  3348.       default:
  3349.        return this.matcher(root);
  3350.     }
  3351.   },
  3352.  
  3353.   match: function(element) {
  3354.     this.tokens = [];
  3355.  
  3356.     var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
  3357.     var le, p, m, len = ps.length, name;
  3358.  
  3359.     while (e && le !== e && (/\S/).test(e)) {
  3360.       le = e;
  3361.       for (var i = 0; i<len; i++) {
  3362.         p = ps[i].re;
  3363.         name = ps[i].name;
  3364.         if (m = e.match(p)) {
  3365.           if (as[name]) {
  3366.             this.tokens.push([name, Object.clone(m)]);
  3367.             e = e.replace(m[0], '');
  3368.           } else {
  3369.             return this.findElements(document).include(element);
  3370.           }
  3371.         }
  3372.       }
  3373.     }
  3374.  
  3375.     var match = true, name, matches;
  3376.     for (var i = 0, token; token = this.tokens[i]; i++) {
  3377.       name = token[0], matches = token[1];
  3378.       if (!Selector.assertions[name](element, matches)) {
  3379.         match = false; break;
  3380.       }
  3381.     }
  3382.  
  3383.     return match;
  3384.   },
  3385.  
  3386.   toString: function() {
  3387.     return this.expression;
  3388.   },
  3389.  
  3390.   inspect: function() {
  3391.     return "#<Selector:" + this.expression.inspect() + ">";
  3392.   }
  3393. });
  3394.  
  3395. if (Prototype.BrowserFeatures.SelectorsAPI &&
  3396.  document.compatMode === 'BackCompat') {
  3397.   Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
  3398.     var div = document.createElement('div'),
  3399.      span = document.createElement('span');
  3400.  
  3401.     div.id = "prototype_test_id";
  3402.     span.className = 'Test';
  3403.     div.appendChild(span);
  3404.     var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
  3405.     div = span = null;
  3406.     return isIgnored;
  3407.   })();
  3408. }
  3409.  
  3410. Object.extend(Selector, {
  3411.   _cache: { },
  3412.  
  3413.   xpath: {
  3414.     descendant:   "//*",
  3415.     child:        "/*",
  3416.     adjacent:     "/following-sibling::*[1]",
  3417.     laterSibling: '/following-sibling::*',
  3418.     tagName:      function(m) {
  3419.       if (m[1] == '*') return '';
  3420.       return "[local-name()='" + m[1].toLowerCase() +
  3421.              "' or local-name()='" + m[1].toUpperCase() + "']";
  3422.     },
  3423.     className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
  3424.     id:           "[@id='#{1}']",
  3425.     attrPresence: function(m) {
  3426.       m[1] = m[1].toLowerCase();
  3427.       return new Template("[@#{1}]").evaluate(m);
  3428.     },
  3429.     attr: function(m) {
  3430.       m[1] = m[1].toLowerCase();
  3431.       m[3] = m[5] || m[6];
  3432.       return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
  3433.     },
  3434.     pseudo: function(m) {
  3435.       var h = Selector.xpath.pseudos[m[1]];
  3436.       if (!h) return '';
  3437.       if (Object.isFunction(h)) return h(m);
  3438.       return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
  3439.     },
  3440.     operators: {
  3441.       '=':  "[@#{1}='#{3}']",
  3442.       '!=': "[@#{1}!='#{3}']",
  3443.       '^=': "[starts-with(@#{1}, '#{3}')]",
  3444.       '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
  3445.       '*=': "[contains(@#{1}, '#{3}')]",
  3446.       '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
  3447.       '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
  3448.     },
  3449.     pseudos: {
  3450.       'first-child': '[not(preceding-sibling::*)]',
  3451.       'last-child':  '[not(following-sibling::*)]',
  3452.       'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
  3453.       'empty':       "[count(*) = 0 and (count(text()) = 0)]",
  3454.       'checked':     "[@checked]",
  3455.       'disabled':    "[(@disabled) and (@type!='hidden')]",
  3456.       'enabled':     "[not(@disabled) and (@type!='hidden')]",
  3457.       'not': function(m) {
  3458.         var e = m[6], p = Selector.patterns,
  3459.             x = Selector.xpath, le, v, len = p.length, name;
  3460.  
  3461.         var exclusion = [];
  3462.         while (e && le != e && (/\S/).test(e)) {
  3463.           le = e;
  3464.           for (var i = 0; i<len; i++) {
  3465.             name = p[i].name
  3466.             if (m = e.match(p[i].re)) {
  3467.               v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m);
  3468.               exclusion.push("(" + v.substring(1, v.length - 1) + ")");
  3469.               e = e.replace(m[0], '');
  3470.               break;
  3471.             }
  3472.           }
  3473.         }
  3474.         return "[not(" + exclusion.join(" and ") + ")]";
  3475.       },
  3476.       'nth-child':      function(m) {
  3477.         return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
  3478.       },
  3479.       'nth-last-child': function(m) {
  3480.         return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
  3481.       },
  3482.       'nth-of-type':    function(m) {
  3483.         return Selector.xpath.pseudos.nth("position() ", m);
  3484.       },
  3485.       'nth-last-of-type': function(m) {
  3486.         return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
  3487.       },
  3488.       'first-of-type':  function(m) {
  3489.         m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
  3490.       },
  3491.       'last-of-type':   function(m) {
  3492.         m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
  3493.       },
  3494.       'only-of-type':   function(m) {
  3495.         var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
  3496.       },
  3497.       nth: function(fragment, m) {
  3498.         var mm, formula = m[6], predicate;
  3499.         if (formula == 'even') formula = '2n+0';
  3500.         if (formula == 'odd')  formula = '2n+1';
  3501.         if (mm = formula.match(/^(\d+)$/)) // digit only
  3502.           return '[' + fragment + "= " + mm[1] + ']';
  3503.         if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
  3504.           if (mm[1] == "-") mm[1] = -1;
  3505.           var a = mm[1] ? Number(mm[1]) : 1;
  3506.           var b = mm[2] ? Number(mm[2]) : 0;
  3507.           predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
  3508.           "((#{fragment} - #{b}) div #{a} >= 0)]";
  3509.           return new Template(predicate).evaluate({
  3510.             fragment: fragment, a: a, b: b });
  3511.         }
  3512.       }
  3513.     }
  3514.   },
  3515.  
  3516.   criteria: {
  3517.     tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
  3518.     className:    'n = h.className(n, r, "#{1}", c);    c = false;',
  3519.     id:           'n = h.id(n, r, "#{1}", c);           c = false;',
  3520.     attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
  3521.     attr: function(m) {
  3522.       m[3] = (m[5] || m[6]);
  3523.       return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
  3524.     },
  3525.     pseudo: function(m) {
  3526.       if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
  3527.       return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
  3528.     },
  3529.     descendant:   'c = "descendant";',
  3530.     child:        'c = "child";',
  3531.     adjacent:     'c = "adjacent";',
  3532.     laterSibling: 'c = "laterSibling";'
  3533.   },
  3534.  
  3535.   patterns: [
  3536.     { name: 'laterSibling', re: /^\s*~\s*/ },
  3537.     { name: 'child',        re: /^\s*>\s*/ },
  3538.     { name: 'adjacent',     re: /^\s*\+\s*/ },
  3539.     { name: 'descendant',   re: /^\s/ },
  3540.  
  3541.     { name: 'tagName',      re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
  3542.     { name: 'id',           re: /^#([\w\-\*]+)(\b|$)/ },
  3543.     { name: 'className',    re: /^\.([\w\-\*]+)(\b|$)/ },
  3544.     { name: 'pseudo',       re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
  3545.     { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
  3546.     { name: 'attr',         re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
  3547.   ],
  3548.  
  3549.   assertions: {
  3550.     tagName: function(element, matches) {
  3551.       return matches[1].toUpperCase() == element.tagName.toUpperCase();
  3552.     },
  3553.  
  3554.     className: function(element, matches) {
  3555.       return Element.hasClassName(element, matches[1]);
  3556.     },
  3557.  
  3558.     id: function(element, matches) {
  3559.       return element.id === matches[1];
  3560.     },
  3561.  
  3562.     attrPresence: function(element, matches) {
  3563.       return Element.hasAttribute(element, matches[1]);
  3564.     },
  3565.  
  3566.     attr: function(element, matches) {
  3567.       var nodeValue = Element.readAttribute(element, matches[1]);
  3568.       return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
  3569.     }
  3570.   },
  3571.  
  3572.   handlers: {
  3573.     concat: function(a, b) {
  3574.       for (var i = 0, node; node = b[i]; i++)
  3575.         a.push(node);
  3576.       return a;
  3577.     },
  3578.  
  3579.     mark: function(nodes) {
  3580.       var _true = Prototype.emptyFunction;
  3581.       for (var i = 0, node; node = nodes[i]; i++)
  3582.         node._countedByPrototype = _true;
  3583.       return nodes;
  3584.     },
  3585.  
  3586.     unmark: (function(){
  3587.  
  3588.       var PROPERTIES_ATTRIBUTES_MAP = (function(){
  3589.         var el = document.createElement('div'),
  3590.             isBuggy = false,
  3591.             propName = '_countedByPrototype',
  3592.             value = 'x'
  3593.         el[propName] = value;
  3594.         isBuggy = (el.getAttribute(propName) === value);
  3595.         el = null;
  3596.         return isBuggy;
  3597.       })();
  3598.  
  3599.       return PROPERTIES_ATTRIBUTES_MAP ?
  3600.         function(nodes) {
  3601.           for (var i = 0, node; node = nodes[i]; i++)
  3602.             node.removeAttribute('_countedByPrototype');
  3603.           return nodes;
  3604.         } :
  3605.         function(nodes) {
  3606.           for (var i = 0, node; node = nodes[i]; i++)
  3607.             node._countedByPrototype = void 0;
  3608.           return nodes;
  3609.         }
  3610.     })(),
  3611.  
  3612.     index: function(parentNode, reverse, ofType) {
  3613.       parentNode._countedByPrototype = Prototype.emptyFunction;
  3614.       if (reverse) {
  3615.         for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
  3616.           var node = nodes[i];
  3617.           if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
  3618.         }
  3619.       } else {
  3620.         for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
  3621.           if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
  3622.       }
  3623.     },
  3624.  
  3625.     unique: function(nodes) {
  3626.       if (nodes.length == 0) return nodes;
  3627.       var results = [], n;
  3628.       for (var i = 0, l = nodes.length; i < l; i++)
  3629.         if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
  3630.           n._countedByPrototype = Prototype.emptyFunction;
  3631.           results.push(Element.extend(n));
  3632.         }
  3633.       return Selector.handlers.unmark(results);
  3634.     },
  3635.  
  3636.     descendant: function(nodes) {
  3637.       var h = Selector.handlers;
  3638.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  3639.         h.concat(results, node.getElementsByTagName('*'));
  3640.       return results;
  3641.     },
  3642.  
  3643.     child: function(nodes) {
  3644.       var h = Selector.handlers;
  3645.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  3646.         for (var j = 0, child; child = node.childNodes[j]; j++)
  3647.           if (child.nodeType == 1 && child.tagName != '!') results.push(child);
  3648.       }
  3649.       return results;
  3650.     },
  3651.  
  3652.     adjacent: function(nodes) {
  3653.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  3654.         var next = this.nextElementSibling(node);
  3655.         if (next) results.push(next);
  3656.       }
  3657.       return results;
  3658.     },
  3659.  
  3660.     laterSibling: function(nodes) {
  3661.       var h = Selector.handlers;
  3662.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  3663.         h.concat(results, Element.nextSiblings(node));
  3664.       return results;
  3665.     },
  3666.  
  3667.     nextElementSibling: function(node) {
  3668.       while (node = node.nextSibling)
  3669.         if (node.nodeType == 1) return node;
  3670.       return null;
  3671.     },
  3672.  
  3673.     previousElementSibling: function(node) {
  3674.       while (node = node.previousSibling)
  3675.         if (node.nodeType == 1) return node;
  3676.       return null;
  3677.     },
  3678.  
  3679.     tagName: function(nodes, root, tagName, combinator) {
  3680.       var uTagName = tagName.toUpperCase();
  3681.       var results = [], h = Selector.handlers;
  3682.       if (nodes) {
  3683.         if (combinator) {
  3684.           if (combinator == "descendant") {
  3685.             for (var i = 0, node; node = nodes[i]; i++)
  3686.               h.concat(results, node.getElementsByTagName(tagName));
  3687.             return results;
  3688.           } else nodes = this[combinator](nodes);
  3689.           if (tagName == "*") return nodes;
  3690.         }
  3691.         for (var i = 0, node; node = nodes[i]; i++)
  3692.           if (node.tagName.toUpperCase() === uTagName) results.push(node);
  3693.         return results;
  3694.       } else return root.getElementsByTagName(tagName);
  3695.     },
  3696.  
  3697.     id: function(nodes, root, id, combinator) {
  3698.       var targetNode = $(id), h = Selector.handlers;
  3699.  
  3700.       if (root == document) {
  3701.         if (!targetNode) return [];
  3702.         if (!nodes) return [targetNode];
  3703.       } else {
  3704.         if (!root.sourceIndex || root.sourceIndex < 1) {
  3705.           var nodes = root.getElementsByTagName('*');
  3706.           for (var j = 0, node; node = nodes[j]; j++) {
  3707.             if (node.id === id) return [node];
  3708.           }
  3709.         }
  3710.       }
  3711.  
  3712.       if (nodes) {
  3713.         if (combinator) {
  3714.           if (combinator == 'child') {
  3715.             for (var i = 0, node; node = nodes[i]; i++)
  3716.               if (targetNode.parentNode == node) return [targetNode];
  3717.           } else if (combinator == 'descendant') {
  3718.             for (var i = 0, node; node = nodes[i]; i++)
  3719.               if (Element.descendantOf(targetNode, node)) return [targetNode];
  3720.           } else if (combinator == 'adjacent') {
  3721.             for (var i = 0, node; node = nodes[i]; i++)
  3722.               if (Selector.handlers.previousElementSibling(targetNode) == node)
  3723.                 return [targetNode];
  3724.           } else nodes = h[combinator](nodes);
  3725.         }
  3726.         for (var i = 0, node; node = nodes[i]; i++)
  3727.           if (node == targetNode) return [targetNode];
  3728.         return [];
  3729.       }
  3730.       return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
  3731.     },
  3732.  
  3733.     className: function(nodes, root, className, combinator) {
  3734.       if (nodes && combinator) nodes = this[combinator](nodes);
  3735.       return Selector.handlers.byClassName(nodes, root, className);
  3736.     },
  3737.  
  3738.     byClassName: function(nodes, root, className) {
  3739.       if (!nodes) nodes = Selector.handlers.descendant([root]);
  3740.       var needle = ' ' + className + ' ';
  3741.       for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
  3742.         nodeClassName = node.className;
  3743.         if (nodeClassName.length == 0) continue;
  3744.         if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
  3745.           results.push(node);
  3746.       }
  3747.       return results;
  3748.     },
  3749.  
  3750.     attrPresence: function(nodes, root, attr, combinator) {
  3751.       if (!nodes) nodes = root.getElementsByTagName("*");
  3752.       if (nodes && combinator) nodes = this[combinator](nodes);
  3753.       var results = [];
  3754.       for (var i = 0, node; node = nodes[i]; i++)
  3755.         if (Element.hasAttribute(node, attr)) results.push(node);
  3756.       return results;
  3757.     },
  3758.  
  3759.     attr: function(nodes, root, attr, value, operator, combinator) {
  3760.       if (!nodes) nodes = root.getElementsByTagName("*");
  3761.       if (nodes && combinator) nodes = this[combinator](nodes);
  3762.       var handler = Selector.operators[operator], results = [];
  3763.       for (var i = 0, node; node = nodes[i]; i++) {
  3764.         var nodeValue = Element.readAttribute(node, attr);
  3765.         if (nodeValue === null) continue;
  3766.         if (handler(nodeValue, value)) results.push(node);
  3767.       }
  3768.       return results;
  3769.     },
  3770.  
  3771.     pseudo: function(nodes, name, value, root, combinator) {
  3772.       if (nodes && combinator) nodes = this[combinator](nodes);
  3773.       if (!nodes) nodes = root.getElementsByTagName("*");
  3774.       return Selector.pseudos[name](nodes, value, root);
  3775.     }
  3776.   },
  3777.  
  3778.   pseudos: {
  3779.     'first-child': function(nodes, value, root) {
  3780.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  3781.         if (Selector.handlers.previousElementSibling(node)) continue;
  3782.           results.push(node);
  3783.       }
  3784.       return results;
  3785.     },
  3786.     'last-child': function(nodes, value, root) {
  3787.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  3788.         if (Selector.handlers.nextElementSibling(node)) continue;
  3789.           results.push(node);
  3790.       }
  3791.       return results;
  3792.     },
  3793.     'only-child': function(nodes, value, root) {
  3794.       var h = Selector.handlers;
  3795.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  3796.         if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
  3797.           results.push(node);
  3798.       return results;
  3799.     },
  3800.     'nth-child':        function(nodes, formula, root) {
  3801.       return Selector.pseudos.nth(nodes, formula, root);
  3802.     },
  3803.     'nth-last-child':   function(nodes, formula, root) {
  3804.       return Selector.pseudos.nth(nodes, formula, root, true);
  3805.     },
  3806.     'nth-of-type':      function(nodes, formula, root) {
  3807.       return Selector.pseudos.nth(nodes, formula, root, false, true);
  3808.     },
  3809.     'nth-last-of-type': function(nodes, formula, root) {
  3810.       return Selector.pseudos.nth(nodes, formula, root, true, true);
  3811.     },
  3812.     'first-of-type':    function(nodes, formula, root) {
  3813.       return Selector.pseudos.nth(nodes, "1", root, false, true);
  3814.     },
  3815.     'last-of-type':     function(nodes, formula, root) {
  3816.       return Selector.pseudos.nth(nodes, "1", root, true, true);
  3817.     },
  3818.     'only-of-type':     function(nodes, formula, root) {
  3819.       var p = Selector.pseudos;
  3820.       return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
  3821.     },
  3822.  
  3823.     getIndices: function(a, b, total) {
  3824.       if (a == 0) return b > 0 ? [b] : [];
  3825.       return $R(1, total).inject([], function(memo, i) {
  3826.         if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
  3827.         return memo;
  3828.       });
  3829.     },
  3830.  
  3831.     nth: function(nodes, formula, root, reverse, ofType) {
  3832.       if (nodes.length == 0) return [];
  3833.       if (formula == 'even') formula = '2n+0';
  3834.       if (formula == 'odd')  formula = '2n+1';
  3835.       var h = Selector.handlers, results = [], indexed = [], m;
  3836.       h.mark(nodes);
  3837.       for (var i = 0, node; node = nodes[i]; i++) {
  3838.         if (!node.parentNode._countedByPrototype) {
  3839.           h.index(node.parentNode, reverse, ofType);
  3840.           indexed.push(node.parentNode);
  3841.         }
  3842.       }
  3843.       if (formula.match(/^\d+$/)) { // just a number
  3844.         formula = Number(formula);
  3845.         for (var i = 0, node; node = nodes[i]; i++)
  3846.           if (node.nodeIndex == formula) results.push(node);
  3847.       } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
  3848.         if (m[1] == "-") m[1] = -1;
  3849.         var a = m[1] ? Number(m[1]) : 1;
  3850.         var b = m[2] ? Number(m[2]) : 0;
  3851.         var indices = Selector.pseudos.getIndices(a, b, nodes.length);
  3852.         for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
  3853.           for (var j = 0; j < l; j++)
  3854.             if (node.nodeIndex == indices[j]) results.push(node);
  3855.         }
  3856.       }
  3857.       h.unmark(nodes);
  3858.       h.unmark(indexed);
  3859.       return results;
  3860.     },
  3861.  
  3862.     'empty': function(nodes, value, root) {
  3863.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  3864.         if (node.tagName == '!' || node.firstChild) continue;
  3865.         results.push(node);
  3866.       }
  3867.       return results;
  3868.     },
  3869.  
  3870.     'not': function(nodes, selector, root) {
  3871.       var h = Selector.handlers, selectorType, m;
  3872.       var exclusions = new Selector(selector).findElements(root);
  3873.       h.mark(exclusions);
  3874.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  3875.         if (!node._countedByPrototype) results.push(node);
  3876.       h.unmark(exclusions);
  3877.       return results;
  3878.     },
  3879.  
  3880.     'enabled': function(nodes, value, root) {
  3881.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  3882.         if (!node.disabled && (!node.type || node.type !== 'hidden'))
  3883.           results.push(node);
  3884.       return results;
  3885.     },
  3886.  
  3887.     'disabled': function(nodes, value, root) {
  3888.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  3889.         if (node.disabled) results.push(node);
  3890.       return results;
  3891.     },
  3892.  
  3893.     'checked': function(nodes, value, root) {
  3894.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  3895.         if (node.checked) results.push(node);
  3896.       return results;
  3897.     }
  3898.   },
  3899.  
  3900.   operators: {
  3901.     '=':  function(nv, v) { return nv == v; },
  3902.     '!=': function(nv, v) { return nv != v; },
  3903.     '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
  3904.     '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
  3905.     '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
  3906.     '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
  3907.     '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
  3908.      '-').include('-' + (v || "").toUpperCase() + '-'); }
  3909.   },
  3910.  
  3911.   split: function(expression) {
  3912.     var expressions = [];
  3913.     expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
  3914.       expressions.push(m[1].strip());
  3915.     });
  3916.     return expressions;
  3917.   },
  3918.  
  3919.   matchElements: function(elements, expression) {
  3920.     var matches = $$(expression), h = Selector.handlers;
  3921.     h.mark(matches);
  3922.     for (var i = 0, results = [], element; element = elements[i]; i++)
  3923.       if (element._countedByPrototype) results.push(element);
  3924.     h.unmark(matches);
  3925.     return results;
  3926.   },
  3927.  
  3928.   findElement: function(elements, expression, index) {
  3929.     if (Object.isNumber(expression)) {
  3930.       index = expression; expression = false;
  3931.     }
  3932.     return Selector.matchElements(elements, expression || '*')[index || 0];
  3933.   },
  3934.  
  3935.   findChildElements: function(element, expressions) {
  3936.     expressions = Selector.split(expressions.join(','));
  3937.     var results = [], h = Selector.handlers;
  3938.     for (var i = 0, l = expressions.length, selector; i < l; i++) {
  3939.       selector = new Selector(expressions[i].strip());
  3940.       h.concat(results, selector.findElements(element));
  3941.     }
  3942.     return (l > 1) ? h.unique(results) : results;
  3943.   }
  3944. });
  3945.  
  3946. if (Prototype.Browser.IE) {
  3947.   Object.extend(Selector.handlers, {
  3948.     concat: function(a, b) {
  3949.       for (var i = 0, node; node = b[i]; i++)
  3950.         if (node.tagName !== "!") a.push(node);
  3951.       return a;
  3952.     }
  3953.   });
  3954. }
  3955.  
  3956. function $$() {
  3957.   return Selector.findChildElements(document, $A(arguments));
  3958. }
  3959.  
  3960. var Form = {
  3961.   reset: function(form) {
  3962.     form = $(form);
  3963.     form.reset();
  3964.     return form;
  3965.   },
  3966.  
  3967.   serializeElements: function(elements, options) {
  3968.     if (typeof options != 'object') options = { hash: !!options };
  3969.     else if (Object.isUndefined(options.hash)) options.hash = true;
  3970.     var key, value, submitted = false, submit = options.submit;
  3971.  
  3972.     var data = elements.inject({ }, function(result, element) {
  3973.       if (!element.disabled && element.name) {
  3974.         key = element.name; value = $(element).getValue();
  3975.         if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
  3976.             submit !== false && (!submit || key == submit) && (submitted = true)))) {
  3977.           if (key in result) {
  3978.             if (!Object.isArray(result[key])) result[key] = [result[key]];
  3979.             result[key].push(value);
  3980.           }
  3981.           else result[key] = value;
  3982.         }
  3983.       }
  3984.       return result;
  3985.     });
  3986.  
  3987.     return options.hash ? data : Object.toQueryString(data);
  3988.   }
  3989. };
  3990.  
  3991. Form.Methods = {
  3992.   serialize: function(form, options) {
  3993.     return Form.serializeElements(Form.getElements(form), options);
  3994.   },
  3995.  
  3996.   getElements: function(form) {
  3997.     var elements = $(form).getElementsByTagName('*'),
  3998.         element,
  3999.         arr = [ ],
  4000.         serializers = Form.Element.Serializers;
  4001.     for (var i = 0; element = elements[i]; i++) {
  4002.       arr.push(element);
  4003.     }
  4004.     return arr.inject([], function(elements, child) {
  4005.       if (serializers[child.tagName.toLowerCase()])
  4006.         elements.push(Element.extend(child));
  4007.       return elements;
  4008.     })
  4009.   },
  4010.  
  4011.   getInputs: function(form, typeName, name) {
  4012.     form = $(form);
  4013.     var inputs = form.getElementsByTagName('input');
  4014.  
  4015.     if (!typeName && !name) return $A(inputs).map(Element.extend);
  4016.  
  4017.     for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
  4018.       var input = inputs[i];
  4019.       if ((typeName && input.type != typeName) || (name && input.name != name))
  4020.         continue;
  4021.       matchingInputs.push(Element.extend(input));
  4022.     }
  4023.  
  4024.     return matchingInputs;
  4025.   },
  4026.  
  4027.   disable: function(form) {
  4028.     form = $(form);
  4029.     Form.getElements(form).invoke('disable');
  4030.     return form;
  4031.   },
  4032.  
  4033.   enable: function(form) {
  4034.     form = $(form);
  4035.     Form.getElements(form).invoke('enable');
  4036.     return form;
  4037.   },
  4038.  
  4039.   findFirstElement: function(form) {
  4040.     var elements = $(form).getElements().findAll(function(element) {
  4041.       return 'hidden' != element.type && !element.disabled;
  4042.     });
  4043.     var firstByIndex = elements.findAll(function(element) {
  4044.       return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
  4045.     }).sortBy(function(element) { return element.tabIndex }).first();
  4046.  
  4047.     return firstByIndex ? firstByIndex : elements.find(function(element) {
  4048.       return /^(?:input|select|textarea)$/i.test(element.tagName);
  4049.     });
  4050.   },
  4051.  
  4052.   focusFirstElement: function(form) {
  4053.     form = $(form);
  4054.     form.findFirstElement().activate();
  4055.     return form;
  4056.   },
  4057.  
  4058.   request: function(form, options) {
  4059.     form = $(form), options = Object.clone(options || { });
  4060.  
  4061.     var params = options.parameters, action = form.readAttribute('action') || '';
  4062.     if (action.blank()) action = window.location.href;
  4063.     options.parameters = form.serialize(true);
  4064.  
  4065.     if (params) {
  4066.       if (Object.isString(params)) params = params.toQueryParams();
  4067.       Object.extend(options.parameters, params);
  4068.     }
  4069.  
  4070.     if (form.hasAttribute('method') && !options.method)
  4071.       options.method = form.method;
  4072.  
  4073.     return new Ajax.Request(action, options);
  4074.   }
  4075. };
  4076.  
  4077. /*--------------------------------------------------------------------------*/
  4078.  
  4079.  
  4080. Form.Element = {
  4081.   focus: function(element) {
  4082.     $(element).focus();
  4083.     return element;
  4084.   },
  4085.  
  4086.   select: function(element) {
  4087.     $(element).select();
  4088.     return element;
  4089.   }
  4090. };
  4091.  
  4092. Form.Element.Methods = {
  4093.  
  4094.   serialize: function(element) {
  4095.     element = $(element);
  4096.     if (!element.disabled && element.name) {
  4097.       var value = element.getValue();
  4098.       if (value != undefined) {
  4099.         var pair = { };
  4100.         pair[element.name] = value;
  4101.         return Object.toQueryString(pair);
  4102.       }
  4103.     }
  4104.     return '';
  4105.   },
  4106.  
  4107.   getValue: function(element) {
  4108.     element = $(element);
  4109.     var method = element.tagName.toLowerCase();
  4110.     return Form.Element.Serializers[method](element);
  4111.   },
  4112.  
  4113.   setValue: function(element, value) {
  4114.     element = $(element);
  4115.     var method = element.tagName.toLowerCase();
  4116.     Form.Element.Serializers[method](element, value);
  4117.     return element;
  4118.   },
  4119.  
  4120.   clear: function(element) {
  4121.     $(element).value = '';
  4122.     return element;
  4123.   },
  4124.  
  4125.   present: function(element) {
  4126.     return $(element).value != '';
  4127.   },
  4128.  
  4129.   activate: function(element) {
  4130.     element = $(element);
  4131.     try {
  4132.       element.focus();
  4133.       if (element.select && (element.tagName.toLowerCase() != 'input' ||
  4134.           !(/^(?:button|reset|submit)$/i.test(element.type))))
  4135.         element.select();
  4136.     } catch (e) { }
  4137.     return element;
  4138.   },
  4139.  
  4140.   disable: function(element) {
  4141.     element = $(element);
  4142.     element.disabled = true;
  4143.     return element;
  4144.   },
  4145.  
  4146.   enable: function(element) {
  4147.     element = $(element);
  4148.     element.disabled = false;
  4149.     return element;
  4150.   }
  4151. };
  4152.  
  4153. /*--------------------------------------------------------------------------*/
  4154.  
  4155. var Field = Form.Element;
  4156.  
  4157. var $F = Form.Element.Methods.getValue;
  4158.  
  4159. /*--------------------------------------------------------------------------*/
  4160.  
  4161. Form.Element.Serializers = {
  4162.   input: function(element, value) {
  4163.     switch (element.type.toLowerCase()) {
  4164.       case 'checkbox':
  4165.       case 'radio':
  4166.         return Form.Element.Serializers.inputSelector(element, value);
  4167.       default:
  4168.         return Form.Element.Serializers.textarea(element, value);
  4169.     }
  4170.   },
  4171.  
  4172.   inputSelector: function(element, value) {
  4173.     if (Object.isUndefined(value)) return element.checked ? element.value : null;
  4174.     else element.checked = !!value;
  4175.   },
  4176.  
  4177.   textarea: function(element, value) {
  4178.     if (Object.isUndefined(value)) return element.value;
  4179.     else element.value = value;
  4180.   },
  4181.  
  4182.   select: function(element, value) {
  4183.     if (Object.isUndefined(value))
  4184.       return this[element.type == 'select-one' ?
  4185.         'selectOne' : 'selectMany'](element);
  4186.     else {
  4187.       var opt, currentValue, single = !Object.isArray(value);
  4188.       for (var i = 0, length = element.length; i < length; i++) {
  4189.         opt = element.options[i];
  4190.         currentValue = this.optionValue(opt);
  4191.         if (single) {
  4192.           if (currentValue == value) {
  4193.             opt.selected = true;
  4194.             return;
  4195.           }
  4196.         }
  4197.         else opt.selected = value.include(currentValue);
  4198.       }
  4199.     }
  4200.   },
  4201.  
  4202.   selectOne: function(element) {
  4203.     var index = element.selectedIndex;
  4204.     return index >= 0 ? this.optionValue(element.options[index]) : null;
  4205.   },
  4206.  
  4207.   selectMany: function(element) {
  4208.     var values, length = element.length;
  4209.     if (!length) return null;
  4210.  
  4211.     for (var i = 0, values = []; i < length; i++) {
  4212.       var opt = element.options[i];
  4213.       if (opt.selected) values.push(this.optionValue(opt));
  4214.     }
  4215.     return values;
  4216.   },
  4217.  
  4218.   optionValue: function(opt) {
  4219.     return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  4220.   }
  4221. };
  4222.  
  4223. /*--------------------------------------------------------------------------*/
  4224.  
  4225.  
  4226. Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
  4227.   initialize: function($super, element, frequency, callback) {
  4228.     $super(callback, frequency);
  4229.     this.element   = $(element);
  4230.     this.lastValue = this.getValue();
  4231.   },
  4232.  
  4233.   execute: function() {
  4234.     var value = this.getValue();
  4235.     if (Object.isString(this.lastValue) && Object.isString(value) ?
  4236.         this.lastValue != value : String(this.lastValue) != String(value)) {
  4237.       this.callback(this.element, value);
  4238.       this.lastValue = value;
  4239.     }
  4240.   }
  4241. });
  4242.  
  4243. Form.Element.Observer = Class.create(Abstract.TimedObserver, {
  4244.   getValue: function() {
  4245.     return Form.Element.getValue(this.element);
  4246.   }
  4247. });
  4248.  
  4249. Form.Observer = Class.create(Abstract.TimedObserver, {
  4250.   getValue: function() {
  4251.     return Form.serialize(this.element);
  4252.   }
  4253. });
  4254.  
  4255. /*--------------------------------------------------------------------------*/
  4256.  
  4257. Abstract.EventObserver = Class.create({
  4258.   initialize: function(element, callback) {
  4259.     this.element  = $(element);
  4260.     this.callback = callback;
  4261.  
  4262.     this.lastValue = this.getValue();
  4263.     if (this.element.tagName.toLowerCase() == 'form')
  4264.       this.registerFormCallbacks();
  4265.     else
  4266.       this.registerCallback(this.element);
  4267.   },
  4268.  
  4269.   onElementEvent: function() {
  4270.     var value = this.getValue();
  4271.     if (this.lastValue != value) {
  4272.       this.callback(this.element, value);
  4273.       this.lastValue = value;
  4274.     }
  4275.   },
  4276.  
  4277.   registerFormCallbacks: function() {
  4278.     Form.getElements(this.element).each(this.registerCallback, this);
  4279.   },
  4280.  
  4281.   registerCallback: function(element) {
  4282.     if (element.type) {
  4283.       switch (element.type.toLowerCase()) {
  4284.         case 'checkbox':
  4285.         case 'radio':
  4286.           Event.observe(element, 'click', this.onElementEvent.bind(this));
  4287.           break;
  4288.         default:
  4289.           Event.observe(element, 'change', this.onElementEvent.bind(this));
  4290.           break;
  4291.       }
  4292.     }
  4293.   }
  4294. });
  4295.  
  4296. Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
  4297.   getValue: function() {
  4298.     return Form.Element.getValue(this.element);
  4299.   }
  4300. });
  4301.  
  4302. Form.EventObserver = Class.create(Abstract.EventObserver, {
  4303.   getValue: function() {
  4304.     return Form.serialize(this.element);
  4305.   }
  4306. });
  4307. (function() {
  4308.  
  4309.   var Event = {
  4310.     KEY_BACKSPACE: 8,
  4311.     KEY_TAB:       9,
  4312.     KEY_RETURN:   13,
  4313.     KEY_ESC:      27,
  4314.     KEY_LEFT:     37,
  4315.     KEY_UP:       38,
  4316.     KEY_RIGHT:    39,
  4317.     KEY_DOWN:     40,
  4318.     KEY_DELETE:   46,
  4319.     KEY_HOME:     36,
  4320.     KEY_END:      35,
  4321.     KEY_PAGEUP:   33,
  4322.     KEY_PAGEDOWN: 34,
  4323.     KEY_INSERT:   45,
  4324.  
  4325.     cache: {}
  4326.   };
  4327.  
  4328.   var docEl = document.documentElement;
  4329.   var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
  4330.     && 'onmouseleave' in docEl;
  4331.  
  4332.   var _isButton;
  4333.   if (Prototype.Browser.IE) {
  4334.     var buttonMap = { 0: 1, 1: 4, 2: 2 };
  4335.     _isButton = function(event, code) {
  4336.       return event.button === buttonMap[code];
  4337.     };
  4338.   } else if (Prototype.Browser.WebKit) {
  4339.     _isButton = function(event, code) {
  4340.       switch (code) {
  4341.         case 0: return event.which == 1 && !event.metaKey;
  4342.         case 1: return event.which == 1 && event.metaKey;
  4343.         default: return false;
  4344.       }
  4345.     };
  4346.   } else {
  4347.     _isButton = function(event, code) {
  4348.       return event.which ? (event.which === code + 1) : (event.button === code);
  4349.     };
  4350.   }
  4351.  
  4352.   function isLeftClick(event)   { return _isButton(event, 0) }
  4353.  
  4354.   function isMiddleClick(event) { return _isButton(event, 1) }
  4355.  
  4356.   function isRightClick(event)  { return _isButton(event, 2) }
  4357.  
  4358.   function element(event) {
  4359.     event = Event.extend(event);
  4360.  
  4361.     var node = event.target, type = event.type,
  4362.      currentTarget = event.currentTarget;
  4363.  
  4364.     if (currentTarget && currentTarget.tagName) {
  4365.       if (type === 'load' || type === 'error' ||
  4366.         (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
  4367.           && currentTarget.type === 'radio'))
  4368.             node = currentTarget;
  4369.     }
  4370.  
  4371.     if (node.nodeType == Node.TEXT_NODE)
  4372.       node = node.parentNode;
  4373.  
  4374.     return Element.extend(node);
  4375.   }
  4376.  
  4377.   function findElement(event, expression) {
  4378.     var element = Event.element(event);
  4379.     if (!expression) return element;
  4380.     var elements = [element].concat(element.ancestors());
  4381.     return Selector.findElement(elements, expression, 0);
  4382.   }
  4383.  
  4384.   function pointer(event) {
  4385.     return { x: pointerX(event), y: pointerY(event) };
  4386.   }
  4387.  
  4388.   function pointerX(event) {
  4389.     var docElement = document.documentElement,
  4390.      body = document.body || { scrollLeft: 0 };
  4391.  
  4392.     return event.pageX || (event.clientX +
  4393.       (docElement.scrollLeft || body.scrollLeft) -
  4394.       (docElement.clientLeft || 0));
  4395.   }
  4396.  
  4397.   function pointerY(event) {
  4398.     var docElement = document.documentElement,
  4399.      body = document.body || { scrollTop: 0 };
  4400.  
  4401.     return  event.pageY || (event.clientY +
  4402.        (docElement.scrollTop || body.scrollTop) -
  4403.        (docElement.clientTop || 0));
  4404.   }
  4405.  
  4406.  
  4407.   function stop(event) {
  4408.     Event.extend(event);
  4409.     event.preventDefault();
  4410.     event.stopPropagation();
  4411.  
  4412.     event.stopped = true;
  4413.   }
  4414.  
  4415.   Event.Methods = {
  4416.     isLeftClick: isLeftClick,
  4417.     isMiddleClick: isMiddleClick,
  4418.     isRightClick: isRightClick,
  4419.  
  4420.     element: element,
  4421.     findElement: findElement,
  4422.  
  4423.     pointer: pointer,
  4424.     pointerX: pointerX,
  4425.     pointerY: pointerY,
  4426.  
  4427.     stop: stop
  4428.   };
  4429.  
  4430.  
  4431.   var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
  4432.     m[name] = Event.Methods[name].methodize();
  4433.     return m;
  4434.   });
  4435.  
  4436.   if (Prototype.Browser.IE) {
  4437.     function _relatedTarget(event) {
  4438.       var element;
  4439.       switch (event.type) {
  4440.         case 'mouseover': element = event.fromElement; break;
  4441.         case 'mouseout':  element = event.toElement;   break;
  4442.         default: return null;
  4443.       }
  4444.       return Element.extend(element);
  4445.     }
  4446.  
  4447.     Object.extend(methods, {
  4448.       stopPropagation: function() { this.cancelBubble = true },
  4449.       preventDefault:  function() { this.returnValue = false },
  4450.       inspect: function() { return '[object Event]' }
  4451.     });
  4452.  
  4453.     Event.extend = function(event, element) {
  4454.       if (!event) return false;
  4455.       if (event._extendedByPrototype) return event;
  4456.  
  4457.       event._extendedByPrototype = Prototype.emptyFunction;
  4458.       var pointer = Event.pointer(event);
  4459.  
  4460.       Object.extend(event, {
  4461.         target: event.srcElement || element,
  4462.         relatedTarget: _relatedTarget(event),
  4463.         pageX:  pointer.x,
  4464.         pageY:  pointer.y
  4465.       });
  4466.  
  4467.       return Object.extend(event, methods);
  4468.     };
  4469.   } else {
  4470.     Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
  4471.     Object.extend(Event.prototype, methods);
  4472.     Event.extend = Prototype.K;
  4473.   }
  4474.  
  4475.   function _createResponder(element, eventName, handler) {
  4476.     var registry = Element.retrieve(element, 'prototype_event_registry');
  4477.  
  4478.     if (Object.isUndefined(registry)) {
  4479.       CACHE.push(element);
  4480.       registry = Element.retrieve(element, 'prototype_event_registry', $H());
  4481.     }
  4482.  
  4483.     var respondersForEvent = registry.get(eventName);
  4484.     if (Object.isUndefined(respondersForEvent)) {
  4485.       respondersForEvent = [];
  4486.       registry.set(eventName, respondersForEvent);
  4487.     }
  4488.  
  4489.     if (respondersForEvent.pluck('handler').include(handler)) return false;
  4490.  
  4491.     var responder;
  4492.     if (eventName.include(":")) {
  4493.       responder = function(event) {
  4494.         if (Object.isUndefined(event.eventName))
  4495.           return false;
  4496.  
  4497.         if (event.eventName !== eventName)
  4498.           return false;
  4499.  
  4500.         Event.extend(event, element);
  4501.         handler.call(element, event);
  4502.       };
  4503.     } else {
  4504.       if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
  4505.        (eventName === "mouseenter" || eventName === "mouseleave")) {
  4506.         if (eventName === "mouseenter" || eventName === "mouseleave") {
  4507.           responder = function(event) {
  4508.             Event.extend(event, element);
  4509.  
  4510.             var parent = event.relatedTarget;
  4511.             while (parent && parent !== element) {
  4512.               try { parent = parent.parentNode; }
  4513.               catch(e) { parent = element; }
  4514.             }
  4515.  
  4516.             if (parent === element) return;
  4517.  
  4518.             handler.call(element, event);
  4519.           };
  4520.         }
  4521.       } else {
  4522.         responder = function(event) {
  4523.           Event.extend(event, element);
  4524.           handler.call(element, event);
  4525.         };
  4526.       }
  4527.     }
  4528.  
  4529.     responder.handler = handler;
  4530.     respondersForEvent.push(responder);
  4531.     return responder;
  4532.   }
  4533.  
  4534.   function _destroyCache() {
  4535.     for (var i = 0, length = CACHE.length; i < length; i++) {
  4536.       Event.stopObserving(CACHE[i]);
  4537.       CACHE[i] = null;
  4538.     }
  4539.   }
  4540.  
  4541.   var CACHE = [];
  4542.  
  4543.   if (Prototype.Browser.IE)
  4544.     window.attachEvent('onunload', _destroyCache);
  4545.  
  4546.   if (Prototype.Browser.WebKit)
  4547.     window.addEventListener('unload', Prototype.emptyFunction, false);
  4548.  
  4549.  
  4550.   var _getDOMEventName = Prototype.K;
  4551.  
  4552.   if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
  4553.     _getDOMEventName = function(eventName) {
  4554.       var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
  4555.       return eventName in translations ? translations[eventName] : eventName;
  4556.     };
  4557.   }
  4558.  
  4559.   function observe(element, eventName, handler) {
  4560.     element = $(element);
  4561.  
  4562.     var responder = _createResponder(element, eventName, handler);
  4563.  
  4564.     if (!responder) return element;
  4565.  
  4566.     if (eventName.include(':')) {
  4567.       if (element.addEventListener)
  4568.         element.addEventListener("dataavailable", responder, false);
  4569.       else {
  4570.         element.attachEvent("ondataavailable", responder);
  4571.         element.attachEvent("onfilterchange", responder);
  4572.       }
  4573.     } else {
  4574.       var actualEventName = _getDOMEventName(eventName);
  4575.  
  4576.       if (element.addEventListener)
  4577.         element.addEventListener(actualEventName, responder, false);
  4578.       else
  4579.         element.attachEvent("on" + actualEventName, responder);
  4580.     }
  4581.  
  4582.     return element;
  4583.   }
  4584.  
  4585.   function stopObserving(element, eventName, handler) {
  4586.     element = $(element);
  4587.  
  4588.     var registry = Element.retrieve(element, 'prototype_event_registry');
  4589.  
  4590.     if (Object.isUndefined(registry)) return element;
  4591.  
  4592.     if (eventName && !handler) {
  4593.       var responders = registry.get(eventName);
  4594.  
  4595.       if (Object.isUndefined(responders)) return element;
  4596.  
  4597.       responders.each( function(r) {
  4598.         Element.stopObserving(element, eventName, r.handler);
  4599.       });
  4600.       return element;
  4601.     } else if (!eventName) {
  4602.       registry.each( function(pair) {
  4603.         var eventName = pair.key, responders = pair.value;
  4604.  
  4605.         responders.each( function(r) {
  4606.           Element.stopObserving(element, eventName, r.handler);
  4607.         });
  4608.       });
  4609.       return element;
  4610.     }
  4611.  
  4612.     var responders = registry.get(eventName);
  4613.  
  4614.     if (!responders) return;
  4615.  
  4616.     var responder = responders.find( function(r) { return r.handler === handler; });
  4617.     if (!responder) return element;
  4618.  
  4619.     var actualEventName = _getDOMEventName(eventName);
  4620.  
  4621.     if (eventName.include(':')) {
  4622.       if (element.removeEventListener)
  4623.         element.removeEventListener("dataavailable", responder, false);
  4624.       else {
  4625.         element.detachEvent("ondataavailable", responder);
  4626.         element.detachEvent("onfilterchange",  responder);
  4627.       }
  4628.     } else {
  4629.       if (element.removeEventListener)
  4630.         element.removeEventListener(actualEventName, responder, false);
  4631.       else
  4632.         element.detachEvent('on' + actualEventName, responder);
  4633.     }
  4634.  
  4635.     registry.set(eventName, responders.without(responder));
  4636.  
  4637.     return element;
  4638.   }
  4639.  
  4640.   function fire(element, eventName, memo, bubble) {
  4641.     element = $(element);
  4642.  
  4643.     if (Object.isUndefined(bubble))
  4644.       bubble = true;
  4645.  
  4646.     if (element == document && document.createEvent && !element.dispatchEvent)
  4647.       element = document.documentElement;
  4648.  
  4649.     var event;
  4650.     if (document.createEvent) {
  4651.       event = document.createEvent('HTMLEvents');
  4652.       event.initEvent('dataavailable', true, true);
  4653.     } else {
  4654.       event = document.createEventObject();
  4655.       event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
  4656.     }
  4657.  
  4658.     event.eventName = eventName;
  4659.     event.memo = memo || { };
  4660.  
  4661.     if (document.createEvent)
  4662.       element.dispatchEvent(event);
  4663.     else
  4664.       element.fireEvent(event.eventType, event);
  4665.  
  4666.     return Event.extend(event);
  4667.   }
  4668.  
  4669.  
  4670.   Object.extend(Event, Event.Methods);
  4671.  
  4672.   Object.extend(Event, {
  4673.     fire:          fire,
  4674.     observe:       observe,
  4675.     stopObserving: stopObserving
  4676.   });
  4677.  
  4678.   Element.addMethods({
  4679.     fire:          fire,
  4680.  
  4681.     observe:       observe,
  4682.  
  4683.     stopObserving: stopObserving
  4684.   });
  4685.  
  4686.   Object.extend(document, {
  4687.     fire:          fire.methodize(),
  4688.  
  4689.     observe:       observe.methodize(),
  4690.  
  4691.     stopObserving: stopObserving.methodize(),
  4692.  
  4693.     loaded:        false
  4694.   });
  4695.  
  4696.   if (window.Event) Object.extend(window.Event, Event);
  4697.   else window.Event = Event;
  4698. })();
  4699.  
  4700. (function() {
  4701.   /* Support for the DOMContentLoaded event is based on work by Dan Webb,
  4702.      Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
  4703.  
  4704.   var timer;
  4705.  
  4706.   function fireContentLoadedEvent() {
  4707.     if (document.loaded) return;
  4708.     if (timer) window.clearTimeout(timer);
  4709.     document.loaded = true;
  4710.     document.fire('dom:loaded');
  4711.   }
  4712.  
  4713.   function checkReadyState() {
  4714.     if (document.readyState === 'complete') {
  4715.       document.stopObserving('readystatechange', checkReadyState);
  4716.       fireContentLoadedEvent();
  4717.     }
  4718.   }
  4719.  
  4720.   function pollDoScroll() {
  4721.     try { document.documentElement.doScroll('left'); }
  4722.     catch(e) {
  4723.       timer = pollDoScroll.defer();
  4724.       return;
  4725.     }
  4726.     fireContentLoadedEvent();
  4727.   }
  4728.  
  4729.   if (document.addEventListener) {
  4730.     document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
  4731.   } else {
  4732.     document.observe('readystatechange', checkReadyState);
  4733.     if (window == top)
  4734.       timer = pollDoScroll.defer();
  4735.   }
  4736.  
  4737.   Event.observe(window, 'load', fireContentLoadedEvent);
  4738. })();
  4739.  
  4740. Element.addMethods();
  4741.  
  4742. /*------------------------------- DEPRECATED -------------------------------*/
  4743.  
  4744. Hash.toQueryString = Object.toQueryString;
  4745.  
  4746. var Toggle = { display: Element.toggle };
  4747.  
  4748. Element.Methods.childOf = Element.Methods.descendantOf;
  4749.  
  4750. var Insertion = {
  4751.   Before: function(element, content) {
  4752.     return Element.insert(element, {before:content});
  4753.   },
  4754.  
  4755.   Top: function(element, content) {
  4756.     return Element.insert(element, {top:content});
  4757.   },
  4758.  
  4759.   Bottom: function(element, content) {
  4760.     return Element.insert(element, {bottom:content});
  4761.   },
  4762.  
  4763.   After: function(element, content) {
  4764.     return Element.insert(element, {after:content});
  4765.   }
  4766. };
  4767.  
  4768. var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
  4769.  
  4770. var Position = {
  4771.   includeScrollOffsets: false,
  4772.  
  4773.   prepare: function() {
  4774.     this.deltaX =  window.pageXOffset
  4775.                 || document.documentElement.scrollLeft
  4776.                 || document.body.scrollLeft
  4777.                 || 0;
  4778.     this.deltaY =  window.pageYOffset
  4779.                 || document.documentElement.scrollTop
  4780.                 || document.body.scrollTop
  4781.                 || 0;
  4782.   },
  4783.  
  4784.   within: function(element, x, y) {
  4785.     if (this.includeScrollOffsets)
  4786.       return this.withinIncludingScrolloffsets(element, x, y);
  4787.     this.xcomp = x;
  4788.     this.ycomp = y;
  4789.     this.offset = Element.cumulativeOffset(element);
  4790.  
  4791.     return (y >= this.offset[1] &&
  4792.             y <  this.offset[1] + element.offsetHeight &&
  4793.             x >= this.offset[0] &&
  4794.             x <  this.offset[0] + element.offsetWidth);
  4795.   },
  4796.  
  4797.   withinIncludingScrolloffsets: function(element, x, y) {
  4798.     var offsetcache = Element.cumulativeScrollOffset(element);
  4799.  
  4800.     this.xcomp = x + offsetcache[0] - this.deltaX;
  4801.     this.ycomp = y + offsetcache[1] - this.deltaY;
  4802.     this.offset = Element.cumulativeOffset(element);
  4803.  
  4804.     return (this.ycomp >= this.offset[1] &&
  4805.             this.ycomp <  this.offset[1] + element.offsetHeight &&
  4806.             this.xcomp >= this.offset[0] &&
  4807.             this.xcomp <  this.offset[0] + element.offsetWidth);
  4808.   },
  4809.  
  4810.   overlap: function(mode, element) {
  4811.     if (!mode) return 0;
  4812.     if (mode == 'vertical')
  4813.       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
  4814.         element.offsetHeight;
  4815.     if (mode == 'horizontal')
  4816.       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
  4817.         element.offsetWidth;
  4818.   },
  4819.  
  4820.  
  4821.   cumulativeOffset: Element.Methods.cumulativeOffset,
  4822.  
  4823.   positionedOffset: Element.Methods.positionedOffset,
  4824.  
  4825.   absolutize: function(element) {
  4826.     Position.prepare();
  4827.     return Element.absolutize(element);
  4828.   },
  4829.  
  4830.   relativize: function(element) {
  4831.     Position.prepare();
  4832.     return Element.relativize(element);
  4833.   },
  4834.  
  4835.   realOffset: Element.Methods.cumulativeScrollOffset,
  4836.  
  4837.   offsetParent: Element.Methods.getOffsetParent,
  4838.  
  4839.   page: Element.Methods.viewportOffset,
  4840.  
  4841.   clone: function(source, target, options) {
  4842.     options = options || { };
  4843.     return Element.clonePosition(target, source, options);
  4844.   }
  4845. };
  4846.  
  4847. /*--------------------------------------------------------------------------*/
  4848.  
  4849. if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  4850.   function iter(name) {
  4851.     return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  4852.   }
  4853.  
  4854.   instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  4855.   function(element, className) {
  4856.     className = className.toString().strip();
  4857.     var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
  4858.     return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  4859.   } : function(element, className) {
  4860.     className = className.toString().strip();
  4861.     var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
  4862.     if (!classNames && !className) return elements;
  4863.  
  4864.     var nodes = $(element).getElementsByTagName('*');
  4865.     className = ' ' + className + ' ';
  4866.  
  4867.     for (var i = 0, child, cn; child = nodes[i]; i++) {
  4868.       if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
  4869.           (classNames && classNames.all(function(name) {
  4870.             return !name.toString().blank() && cn.include(' ' + name + ' ');
  4871.           }))))
  4872.         elements.push(Element.extend(child));
  4873.     }
  4874.     return elements;
  4875.   };
  4876.  
  4877.   return function(className, parentElement) {
  4878.     return $(parentElement || document.body).getElementsByClassName(className);
  4879.   };
  4880. }(Element.Methods);
  4881.  
  4882. /*--------------------------------------------------------------------------*/
  4883.  
  4884. Element.ClassNames = Class.create();
  4885. Element.ClassNames.prototype = {
  4886.   initialize: function(element) {
  4887.     this.element = $(element);
  4888.   },
  4889.  
  4890.   _each: function(iterator) {
  4891.     this.element.className.split(/\s+/).select(function(name) {
  4892.       return name.length > 0;
  4893.     })._each(iterator);
  4894.   },
  4895.  
  4896.   set: function(className) {
  4897.     this.element.className = className;
  4898.   },
  4899.  
  4900.   add: function(classNameToAdd) {
  4901.     if (this.include(classNameToAdd)) return;
  4902.     this.set($A(this).concat(classNameToAdd).join(' '));
  4903.   },
  4904.  
  4905.   remove: function(classNameToRemove) {
  4906.     if (!this.include(classNameToRemove)) return;
  4907.     this.set($A(this).without(classNameToRemove).join(' '));
  4908.   },
  4909.  
  4910.   toString: function() {
  4911.     return $A(this).join(' ');
  4912.   }
  4913. };
  4914.  
  4915. Object.extend(Element.ClassNames.prototype, Enumerable);
  4916.  
  4917. /*--------------------------------------------------------------------------*/
  4918.