Home | History | Annotate | Download | only in lib
      1 /*  Prototype JavaScript framework, version 1.7
      2  *  (c) 2005-2010 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 
     11   Version: '1.7',
     12 
     13   Browser: (function(){
     14     var ua = navigator.userAgent;
     15     var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
     16     return {
     17       IE:             !!window.attachEvent && !isOpera,
     18       Opera:          isOpera,
     19       WebKit:         ua.indexOf('AppleWebKit/') > -1,
     20       Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
     21       MobileSafari:   /Apple.*Mobile/.test(ua)
     22     }
     23   })(),
     24 
     25   BrowserFeatures: {
     26     XPath: !!document.evaluate,
     27 
     28     SelectorsAPI: !!document.querySelector,
     29 
     30     ElementExtensions: (function() {
     31       var constructor = window.Element || window.HTMLElement;
     32       return !!(constructor && constructor.prototype);
     33     })(),
     34     SpecificElementExtensions: (function() {
     35       if (typeof window.HTMLDivElement !== 'undefined')
     36         return true;
     37 
     38       var div = document.createElement('div'),
     39           form = document.createElement('form'),
     40           isSupported = false;
     41 
     42       if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
     43         isSupported = true;
     44       }
     45 
     46       div = form = null;
     47 
     48       return isSupported;
     49     })()
     50   },
     51 
     52   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
     53   JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
     54 
     55   emptyFunction: function() { },
     56 
     57   K: function(x) { return x }
     58 };
     59 
     60 if (Prototype.Browser.MobileSafari)
     61   Prototype.BrowserFeatures.SpecificElementExtensions = false;
     62 
     63 
     64 var Abstract = { };
     65 
     66 
     67 var Try = {
     68   these: function() {
     69     var returnValue;
     70 
     71     for (var i = 0, length = arguments.length; i < length; i++) {
     72       var lambda = arguments[i];
     73       try {
     74         returnValue = lambda();
     75         break;
     76       } catch (e) { }
     77     }
     78 
     79     return returnValue;
     80   }
     81 };
     82 
     83 /* Based on Alex Arnell's inheritance implementation. */
     84 
     85 var Class = (function() {
     86 
     87   var IS_DONTENUM_BUGGY = (function(){
     88     for (var p in { toString: 1 }) {
     89       if (p === 'toString') return false;
     90     }
     91     return true;
     92   })();
     93 
     94   function subclass() {};
     95   function create() {
     96     var parent = null, properties = $A(arguments);
     97     if (Object.isFunction(properties[0]))
     98       parent = properties.shift();
     99 
    100     function klass() {
    101       this.initialize.apply(this, arguments);
    102     }
    103 
    104     Object.extend(klass, Class.Methods);
    105     klass.superclass = parent;
    106     klass.subclasses = [];
    107 
    108     if (parent) {
    109       subclass.prototype = parent.prototype;
    110       klass.prototype = new subclass;
    111       parent.subclasses.push(klass);
    112     }
    113 
    114     for (var i = 0, length = properties.length; i < length; i++)
    115       klass.addMethods(properties[i]);
    116 
    117     if (!klass.prototype.initialize)
    118       klass.prototype.initialize = Prototype.emptyFunction;
    119 
    120     klass.prototype.constructor = klass;
    121     return klass;
    122   }
    123 
    124   function addMethods(source) {
    125     var ancestor   = this.superclass && this.superclass.prototype,
    126         properties = Object.keys(source);
    127 
    128     if (IS_DONTENUM_BUGGY) {
    129       if (source.toString != Object.prototype.toString)
    130         properties.push("toString");
    131       if (source.valueOf != Object.prototype.valueOf)
    132         properties.push("valueOf");
    133     }
    134 
    135     for (var i = 0, length = properties.length; i < length; i++) {
    136       var property = properties[i], value = source[property];
    137       if (ancestor && Object.isFunction(value) &&
    138           value.argumentNames()[0] == "$super") {
    139         var method = value;
    140         value = (function(m) {
    141           return function() { return ancestor[m].apply(this, arguments); };
    142         })(property).wrap(method);
    143 
    144         value.valueOf = method.valueOf.bind(method);
    145         value.toString = method.toString.bind(method);
    146       }
    147       this.prototype[property] = value;
    148     }
    149 
    150     return this;
    151   }
    152 
    153   return {
    154     create: create,
    155     Methods: {
    156       addMethods: addMethods
    157     }
    158   };
    159 })();
    160 (function() {
    161 
    162   var _toString = Object.prototype.toString,
    163       NULL_TYPE = 'Null',
    164       UNDEFINED_TYPE = 'Undefined',
    165       BOOLEAN_TYPE = 'Boolean',
    166       NUMBER_TYPE = 'Number',
    167       STRING_TYPE = 'String',
    168       OBJECT_TYPE = 'Object',
    169       FUNCTION_CLASS = '[object Function]',
    170       BOOLEAN_CLASS = '[object Boolean]',
    171       NUMBER_CLASS = '[object Number]',
    172       STRING_CLASS = '[object String]',
    173       ARRAY_CLASS = '[object Array]',
    174       DATE_CLASS = '[object Date]',
    175       NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
    176         typeof JSON.stringify === 'function' &&
    177         JSON.stringify(0) === '0' &&
    178         typeof JSON.stringify(Prototype.K) === 'undefined';
    179 
    180   function Type(o) {
    181     switch(o) {
    182       case null: return NULL_TYPE;
    183       case (void 0): return UNDEFINED_TYPE;
    184     }
    185     var type = typeof o;
    186     switch(type) {
    187       case 'boolean': return BOOLEAN_TYPE;
    188       case 'number':  return NUMBER_TYPE;
    189       case 'string':  return STRING_TYPE;
    190     }
    191     return OBJECT_TYPE;
    192   }
    193 
    194   function extend(destination, source) {
    195     for (var property in source)
    196       destination[property] = source[property];
    197     return destination;
    198   }
    199 
    200   function inspect(object) {
    201     try {
    202       if (isUndefined(object)) return 'undefined';
    203       if (object === null) return 'null';
    204       return object.inspect ? object.inspect() : String(object);
    205     } catch (e) {
    206       if (e instanceof RangeError) return '...';
    207       throw e;
    208     }
    209   }
    210 
    211   function toJSON(value) {
    212     return Str('', { '': value }, []);
    213   }
    214 
    215   function Str(key, holder, stack) {
    216     var value = holder[key],
    217         type = typeof value;
    218 
    219     if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
    220       value = value.toJSON(key);
    221     }
    222 
    223     var _class = _toString.call(value);
    224 
    225     switch (_class) {
    226       case NUMBER_CLASS:
    227       case BOOLEAN_CLASS:
    228       case STRING_CLASS:
    229         value = value.valueOf();
    230     }
    231 
    232     switch (value) {
    233       case null: return 'null';
    234       case true: return 'true';
    235       case false: return 'false';
    236     }
    237 
    238     type = typeof value;
    239     switch (type) {
    240       case 'string':
    241         return value.inspect(true);
    242       case 'number':
    243         return isFinite(value) ? String(value) : 'null';
    244       case 'object':
    245 
    246         for (var i = 0, length = stack.length; i < length; i++) {
    247           if (stack[i] === value) { throw new TypeError(); }
    248         }
    249         stack.push(value);
    250 
    251         var partial = [];
    252         if (_class === ARRAY_CLASS) {
    253           for (var i = 0, length = value.length; i < length; i++) {
    254             var str = Str(i, value, stack);
    255             partial.push(typeof str === 'undefined' ? 'null' : str);
    256           }
    257           partial = '[' + partial.join(',') + ']';
    258         } else {
    259           var keys = Object.keys(value);
    260           for (var i = 0, length = keys.length; i < length; i++) {
    261             var key = keys[i], str = Str(key, value, stack);
    262             if (typeof str !== "undefined") {
    263                partial.push(key.inspect(true)+ ':' + str);
    264              }
    265           }
    266           partial = '{' + partial.join(',') + '}';
    267         }
    268         stack.pop();
    269         return partial;
    270     }
    271   }
    272 
    273   function stringify(object) {
    274     return JSON.stringify(object);
    275   }
    276 
    277   function toQueryString(object) {
    278     return $H(object).toQueryString();
    279   }
    280 
    281   function toHTML(object) {
    282     return object && object.toHTML ? object.toHTML() : String.interpret(object);
    283   }
    284 
    285   function keys(object) {
    286     if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
    287     var results = [];
    288     for (var property in object) {
    289       if (object.hasOwnProperty(property)) {
    290         results.push(property);
    291       }
    292     }
    293     return results;
    294   }
    295 
    296   function values(object) {
    297     var results = [];
    298     for (var property in object)
    299       results.push(object[property]);
    300     return results;
    301   }
    302 
    303   function clone(object) {
    304     return extend({ }, object);
    305   }
    306 
    307   function isElement(object) {
    308     return !!(object && object.nodeType == 1);
    309   }
    310 
    311   function isArray(object) {
    312     return _toString.call(object) === ARRAY_CLASS;
    313   }
    314 
    315   var hasNativeIsArray = (typeof Array.isArray == 'function')
    316     && Array.isArray([]) && !Array.isArray({});
    317 
    318   if (hasNativeIsArray) {
    319     isArray = Array.isArray;
    320   }
    321 
    322   function isHash(object) {
    323     return object instanceof Hash;
    324   }
    325 
    326   function isFunction(object) {
    327     return _toString.call(object) === FUNCTION_CLASS;
    328   }
    329 
    330   function isString(object) {
    331     return _toString.call(object) === STRING_CLASS;
    332   }
    333 
    334   function isNumber(object) {
    335     return _toString.call(object) === NUMBER_CLASS;
    336   }
    337 
    338   function isDate(object) {
    339     return _toString.call(object) === DATE_CLASS;
    340   }
    341 
    342   function isUndefined(object) {
    343     return typeof object === "undefined";
    344   }
    345 
    346   extend(Object, {
    347     extend:        extend,
    348     inspect:       inspect,
    349     toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
    350     toQueryString: toQueryString,
    351     toHTML:        toHTML,
    352     keys:          Object.keys || keys,
    353     values:        values,
    354     clone:         clone,
    355     isElement:     isElement,
    356     isArray:       isArray,
    357     isHash:        isHash,
    358     isFunction:    isFunction,
    359     isString:      isString,
    360     isNumber:      isNumber,
    361     isDate:        isDate,
    362     isUndefined:   isUndefined
    363   });
    364 })();
    365 Object.extend(Function.prototype, (function() {
    366   var slice = Array.prototype.slice;
    367 
    368   function update(array, args) {
    369     var arrayLength = array.length, length = args.length;
    370     while (length--) array[arrayLength + length] = args[length];
    371     return array;
    372   }
    373 
    374   function merge(array, args) {
    375     array = slice.call(array, 0);
    376     return update(array, args);
    377   }
    378 
    379   function argumentNames() {
    380     var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
    381       .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
    382       .replace(/\s+/g, '').split(',');
    383     return names.length == 1 && !names[0] ? [] : names;
    384   }
    385 
    386   function bind(context) {
    387     if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    388     var __method = this, args = slice.call(arguments, 1);
    389     return function() {
    390       var a = merge(args, arguments);
    391       return __method.apply(context, a);
    392     }
    393   }
    394 
    395   function bindAsEventListener(context) {
    396     var __method = this, args = slice.call(arguments, 1);
    397     return function(event) {
    398       var a = update([event || window.event], args);
    399       return __method.apply(context, a);
    400     }
    401   }
    402 
    403   function curry() {
    404     if (!arguments.length) return this;
    405     var __method = this, args = slice.call(arguments, 0);
    406     return function() {
    407       var a = merge(args, arguments);
    408       return __method.apply(this, a);
    409     }
    410   }
    411 
    412   function delay(timeout) {
    413     var __method = this, args = slice.call(arguments, 1);
    414     timeout = timeout * 1000;
    415     return window.setTimeout(function() {
    416       return __method.apply(__method, args);
    417     }, timeout);
    418   }
    419 
    420   function defer() {
    421     var args = update([0.01], arguments);
    422     return this.delay.apply(this, args);
    423   }
    424 
    425   function wrap(wrapper) {
    426     var __method = this;
    427     return function() {
    428       var a = update([__method.bind(this)], arguments);
    429       return wrapper.apply(this, a);
    430     }
    431   }
    432 
    433   function methodize() {
    434     if (this._methodized) return this._methodized;
    435     var __method = this;
    436     return this._methodized = function() {
    437       var a = update([this], arguments);
    438       return __method.apply(null, a);
    439     };
    440   }
    441 
    442   return {
    443     argumentNames:       argumentNames,
    444     bind:                bind,
    445     bindAsEventListener: bindAsEventListener,
    446     curry:               curry,
    447     delay:               delay,
    448     defer:               defer,
    449     wrap:                wrap,
    450     methodize:           methodize
    451   }
    452 })());
    453 
    454 
    455 
    456 (function(proto) {
    457 
    458 
    459   function toISOString() {
    460     return this.getUTCFullYear() + '-' +
    461       (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
    462       this.getUTCDate().toPaddedString(2) + 'T' +
    463       this.getUTCHours().toPaddedString(2) + ':' +
    464       this.getUTCMinutes().toPaddedString(2) + ':' +
    465       this.getUTCSeconds().toPaddedString(2) + 'Z';
    466   }
    467 
    468 
    469   function toJSON() {
    470     return this.toISOString();
    471   }
    472 
    473   if (!proto.toISOString) proto.toISOString = toISOString;
    474   if (!proto.toJSON) proto.toJSON = toJSON;
    475 
    476 })(Date.prototype);
    477 
    478 
    479 RegExp.prototype.match = RegExp.prototype.test;
    480 
    481 RegExp.escape = function(str) {
    482   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
    483 };
    484 var PeriodicalExecuter = Class.create({
    485   initialize: function(callback, frequency) {
    486     this.callback = callback;
    487     this.frequency = frequency;
    488     this.currentlyExecuting = false;
    489 
    490     this.registerCallback();
    491   },
    492 
    493   registerCallback: function() {
    494     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
    495   },
    496 
    497   execute: function() {
    498     this.callback(this);
    499   },
    500 
    501   stop: function() {
    502     if (!this.timer) return;
    503     clearInterval(this.timer);
    504     this.timer = null;
    505   },
    506 
    507   onTimerEvent: function() {
    508     if (!this.currentlyExecuting) {
    509       try {
    510         this.currentlyExecuting = true;
    511         this.execute();
    512         this.currentlyExecuting = false;
    513       } catch(e) {
    514         this.currentlyExecuting = false;
    515         throw e;
    516       }
    517     }
    518   }
    519 });
    520 Object.extend(String, {
    521   interpret: function(value) {
    522     return value == null ? '' : String(value);
    523   },
    524   specialChar: {
    525     '\b': '\\b',
    526     '\t': '\\t',
    527     '\n': '\\n',
    528     '\f': '\\f',
    529     '\r': '\\r',
    530     '\\': '\\\\'
    531   }
    532 });
    533 
    534 Object.extend(String.prototype, (function() {
    535   var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
    536     typeof JSON.parse === 'function' &&
    537     JSON.parse('{"test": true}').test;
    538 
    539   function prepareReplacement(replacement) {
    540     if (Object.isFunction(replacement)) return replacement;
    541     var template = new Template(replacement);
    542     return function(match) { return template.evaluate(match) };
    543   }
    544 
    545   function gsub(pattern, replacement) {
    546     var result = '', source = this, match;
    547     replacement = prepareReplacement(replacement);
    548 
    549     if (Object.isString(pattern))
    550       pattern = RegExp.escape(pattern);
    551 
    552     if (!(pattern.length || pattern.source)) {
    553       replacement = replacement('');
    554       return replacement + source.split('').join(replacement) + replacement;
    555     }
    556 
    557     while (source.length > 0) {
    558       if (match = source.match(pattern)) {
    559         result += source.slice(0, match.index);
    560         result += String.interpret(replacement(match));
    561         source  = source.slice(match.index + match[0].length);
    562       } else {
    563         result += source, source = '';
    564       }
    565     }
    566     return result;
    567   }
    568 
    569   function sub(pattern, replacement, count) {
    570     replacement = prepareReplacement(replacement);
    571     count = Object.isUndefined(count) ? 1 : count;
    572 
    573     return this.gsub(pattern, function(match) {
    574       if (--count < 0) return match[0];
    575       return replacement(match);
    576     });
    577   }
    578 
    579   function scan(pattern, iterator) {
    580     this.gsub(pattern, iterator);
    581     return String(this);
    582   }
    583 
    584   function truncate(length, truncation) {
    585     length = length || 30;
    586     truncation = Object.isUndefined(truncation) ? '...' : truncation;
    587     return this.length > length ?
    588       this.slice(0, length - truncation.length) + truncation : String(this);
    589   }
    590 
    591   function strip() {
    592     return this.replace(/^\s+/, '').replace(/\s+$/, '');
    593   }
    594 
    595   function stripTags() {
    596     return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
    597   }
    598 
    599   function stripScripts() {
    600     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
    601   }
    602 
    603   function extractScripts() {
    604     var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
    605         matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    606     return (this.match(matchAll) || []).map(function(scriptTag) {
    607       return (scriptTag.match(matchOne) || ['', ''])[1];
    608     });
    609   }
    610 
    611   function evalScripts() {
    612     return this.extractScripts().map(function(script) { return eval(script) });
    613   }
    614 
    615   function escapeHTML() {
    616     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
    617   }
    618 
    619   function unescapeHTML() {
    620     return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
    621   }
    622 
    623 
    624   function toQueryParams(separator) {
    625     var match = this.strip().match(/([^?#]*)(#.*)?$/);
    626     if (!match) return { };
    627 
    628     return match[1].split(separator || '&').inject({ }, function(hash, pair) {
    629       if ((pair = pair.split('='))[0]) {
    630         var key = decodeURIComponent(pair.shift()),
    631             value = pair.length > 1 ? pair.join('=') : pair[0];
    632 
    633         if (value != undefined) value = decodeURIComponent(value);
    634 
    635         if (key in hash) {
    636           if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
    637           hash[key].push(value);
    638         }
    639         else hash[key] = value;
    640       }
    641       return hash;
    642     });
    643   }
    644 
    645   function toArray() {
    646     return this.split('');
    647   }
    648 
    649   function succ() {
    650     return this.slice(0, this.length - 1) +
    651       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
    652   }
    653 
    654   function times(count) {
    655     return count < 1 ? '' : new Array(count + 1).join(this);
    656   }
    657 
    658   function camelize() {
    659     return this.replace(/-+(.)?/g, function(match, chr) {
    660       return chr ? chr.toUpperCase() : '';
    661     });
    662   }
    663 
    664   function capitalize() {
    665     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
    666   }
    667 
    668   function underscore() {
    669     return this.replace(/::/g, '/')
    670                .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
    671                .replace(/([a-z\d])([A-Z])/g, '$1_$2')
    672                .replace(/-/g, '_')
    673                .toLowerCase();
    674   }
    675 
    676   function dasherize() {
    677     return this.replace(/_/g, '-');
    678   }
    679 
    680   function inspect(useDoubleQuotes) {
    681     var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
    682       if (character in String.specialChar) {
    683         return String.specialChar[character];
    684       }
    685       return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
    686     });
    687     if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    688     return "'" + escapedString.replace(/'/g, '\\\'') + "'";
    689   }
    690 
    691   function unfilterJSON(filter) {
    692     return this.replace(filter || Prototype.JSONFilter, '$1');
    693   }
    694 
    695   function isJSON() {
    696     var str = this;
    697     if (str.blank()) return false;
    698     str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
    699     str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
    700     str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
    701     return (/^[\],:{}\s]*$/).test(str);
    702   }
    703 
    704   function evalJSON(sanitize) {
    705     var json = this.unfilterJSON(),
    706         cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    707     if (cx.test(json)) {
    708       json = json.replace(cx, function (a) {
    709         return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
    710       });
    711     }
    712     try {
    713       if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    714     } catch (e) { }
    715     throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
    716   }
    717 
    718   function parseJSON() {
    719     var json = this.unfilterJSON();
    720     return JSON.parse(json);
    721   }
    722 
    723   function include(pattern) {
    724     return this.indexOf(pattern) > -1;
    725   }
    726 
    727   function startsWith(pattern) {
    728     return this.lastIndexOf(pattern, 0) === 0;
    729   }
    730 
    731   function endsWith(pattern) {
    732     var d = this.length - pattern.length;
    733     return d >= 0 && this.indexOf(pattern, d) === d;
    734   }
    735 
    736   function empty() {
    737     return this == '';
    738   }
    739 
    740   function blank() {
    741     return /^\s*$/.test(this);
    742   }
    743 
    744   function interpolate(object, pattern) {
    745     return new Template(this, pattern).evaluate(object);
    746   }
    747 
    748   return {
    749     gsub:           gsub,
    750     sub:            sub,
    751     scan:           scan,
    752     truncate:       truncate,
    753     strip:          String.prototype.trim || strip,
    754     stripTags:      stripTags,
    755     stripScripts:   stripScripts,
    756     extractScripts: extractScripts,
    757     evalScripts:    evalScripts,
    758     escapeHTML:     escapeHTML,
    759     unescapeHTML:   unescapeHTML,
    760     toQueryParams:  toQueryParams,
    761     parseQuery:     toQueryParams,
    762     toArray:        toArray,
    763     succ:           succ,
    764     times:          times,
    765     camelize:       camelize,
    766     capitalize:     capitalize,
    767     underscore:     underscore,
    768     dasherize:      dasherize,
    769     inspect:        inspect,
    770     unfilterJSON:   unfilterJSON,
    771     isJSON:         isJSON,
    772     evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
    773     include:        include,
    774     startsWith:     startsWith,
    775     endsWith:       endsWith,
    776     empty:          empty,
    777     blank:          blank,
    778     interpolate:    interpolate
    779   };
    780 })());
    781 
    782 var Template = Class.create({
    783   initialize: function(template, pattern) {
    784     this.template = template.toString();
    785     this.pattern = pattern || Template.Pattern;
    786   },
    787 
    788   evaluate: function(object) {
    789     if (object && Object.isFunction(object.toTemplateReplacements))
    790       object = object.toTemplateReplacements();
    791 
    792     return this.template.gsub(this.pattern, function(match) {
    793       if (object == null) return (match[1] + '');
    794 
    795       var before = match[1] || '';
    796       if (before == '\\') return match[2];
    797 
    798       var ctx = object, expr = match[3],
    799           pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
    800 
    801       match = pattern.exec(expr);
    802       if (match == null) return before;
    803 
    804       while (match != null) {
    805         var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
    806         ctx = ctx[comp];
    807         if (null == ctx || '' == match[3]) break;
    808         expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
    809         match = pattern.exec(expr);
    810       }
    811 
    812       return before + String.interpret(ctx);
    813     });
    814   }
    815 });
    816 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
    817 
    818 var $break = { };
    819 
    820 var Enumerable = (function() {
    821   function each(iterator, context) {
    822     var index = 0;
    823     try {
    824       this._each(function(value) {
    825         iterator.call(context, value, index++);
    826       });
    827     } catch (e) {
    828       if (e != $break) throw e;
    829     }
    830     return this;
    831   }
    832 
    833   function eachSlice(number, iterator, context) {
    834     var index = -number, slices = [], array = this.toArray();
    835     if (number < 1) return array;
    836     while ((index += number) < array.length)
    837       slices.push(array.slice(index, index+number));
    838     return slices.collect(iterator, context);
    839   }
    840 
    841   function all(iterator, context) {
    842     iterator = iterator || Prototype.K;
    843     var result = true;
    844     this.each(function(value, index) {
    845       result = result && !!iterator.call(context, value, index);
    846       if (!result) throw $break;
    847     });
    848     return result;
    849   }
    850 
    851   function any(iterator, context) {
    852     iterator = iterator || Prototype.K;
    853     var result = false;
    854     this.each(function(value, index) {
    855       if (result = !!iterator.call(context, value, index))
    856         throw $break;
    857     });
    858     return result;
    859   }
    860 
    861   function collect(iterator, context) {
    862     iterator = iterator || Prototype.K;
    863     var results = [];
    864     this.each(function(value, index) {
    865       results.push(iterator.call(context, value, index));
    866     });
    867     return results;
    868   }
    869 
    870   function detect(iterator, context) {
    871     var result;
    872     this.each(function(value, index) {
    873       if (iterator.call(context, value, index)) {
    874         result = value;
    875         throw $break;
    876       }
    877     });
    878     return result;
    879   }
    880 
    881   function findAll(iterator, context) {
    882     var results = [];
    883     this.each(function(value, index) {
    884       if (iterator.call(context, value, index))
    885         results.push(value);
    886     });
    887     return results;
    888   }
    889 
    890   function grep(filter, iterator, context) {
    891     iterator = iterator || Prototype.K;
    892     var results = [];
    893 
    894     if (Object.isString(filter))
    895       filter = new RegExp(RegExp.escape(filter));
    896 
    897     this.each(function(value, index) {
    898       if (filter.match(value))
    899         results.push(iterator.call(context, value, index));
    900     });
    901     return results;
    902   }
    903 
    904   function include(object) {
    905     if (Object.isFunction(this.indexOf))
    906       if (this.indexOf(object) != -1) return true;
    907 
    908     var found = false;
    909     this.each(function(value) {
    910       if (value == object) {
    911         found = true;
    912         throw $break;
    913       }
    914     });
    915     return found;
    916   }
    917 
    918   function inGroupsOf(number, fillWith) {
    919     fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    920     return this.eachSlice(number, function(slice) {
    921       while(slice.length < number) slice.push(fillWith);
    922       return slice;
    923     });
    924   }
    925 
    926   function inject(memo, iterator, context) {
    927     this.each(function(value, index) {
    928       memo = iterator.call(context, memo, value, index);
    929     });
    930     return memo;
    931   }
    932 
    933   function invoke(method) {
    934     var args = $A(arguments).slice(1);
    935     return this.map(function(value) {
    936       return value[method].apply(value, args);
    937     });
    938   }
    939 
    940   function max(iterator, context) {
    941     iterator = iterator || Prototype.K;
    942     var result;
    943     this.each(function(value, index) {
    944       value = iterator.call(context, value, index);
    945       if (result == null || value >= result)
    946         result = value;
    947     });
    948     return result;
    949   }
    950 
    951   function min(iterator, context) {
    952     iterator = iterator || Prototype.K;
    953     var result;
    954     this.each(function(value, index) {
    955       value = iterator.call(context, value, index);
    956       if (result == null || value < result)
    957         result = value;
    958     });
    959     return result;
    960   }
    961 
    962   function partition(iterator, context) {
    963     iterator = iterator || Prototype.K;
    964     var trues = [], falses = [];
    965     this.each(function(value, index) {
    966       (iterator.call(context, value, index) ?
    967         trues : falses).push(value);
    968     });
    969     return [trues, falses];
    970   }
    971 
    972   function pluck(property) {
    973     var results = [];
    974     this.each(function(value) {
    975       results.push(value[property]);
    976     });
    977     return results;
    978   }
    979 
    980   function reject(iterator, context) {
    981     var results = [];
    982     this.each(function(value, index) {
    983       if (!iterator.call(context, value, index))
    984         results.push(value);
    985     });
    986     return results;
    987   }
    988 
    989   function sortBy(iterator, context) {
    990     return this.map(function(value, index) {
    991       return {
    992         value: value,
    993         criteria: iterator.call(context, value, index)
    994       };
    995     }).sort(function(left, right) {
    996       var a = left.criteria, b = right.criteria;
    997       return a < b ? -1 : a > b ? 1 : 0;
    998     }).pluck('value');
    999   }
   1000 
   1001   function toArray() {
   1002     return this.map();
   1003   }
   1004 
   1005   function zip() {
   1006     var iterator = Prototype.K, args = $A(arguments);
   1007     if (Object.isFunction(args.last()))
   1008       iterator = args.pop();
   1009 
   1010     var collections = [this].concat(args).map($A);
   1011     return this.map(function(value, index) {
   1012       return iterator(collections.pluck(index));
   1013     });
   1014   }
   1015 
   1016   function size() {
   1017     return this.toArray().length;
   1018   }
   1019 
   1020   function inspect() {
   1021     return '#<Enumerable:' + this.toArray().inspect() + '>';
   1022   }
   1023 
   1024 
   1025 
   1026 
   1027 
   1028 
   1029 
   1030 
   1031 
   1032   return {
   1033     each:       each,
   1034     eachSlice:  eachSlice,
   1035     all:        all,
   1036     every:      all,
   1037     any:        any,
   1038     some:       any,
   1039     collect:    collect,
   1040     map:        collect,
   1041     detect:     detect,
   1042     findAll:    findAll,
   1043     select:     findAll,
   1044     filter:     findAll,
   1045     grep:       grep,
   1046     include:    include,
   1047     member:     include,
   1048     inGroupsOf: inGroupsOf,
   1049     inject:     inject,
   1050     invoke:     invoke,
   1051     max:        max,
   1052     min:        min,
   1053     partition:  partition,
   1054     pluck:      pluck,
   1055     reject:     reject,
   1056     sortBy:     sortBy,
   1057     toArray:    toArray,
   1058     entries:    toArray,
   1059     zip:        zip,
   1060     size:       size,
   1061     inspect:    inspect,
   1062     find:       detect
   1063   };
   1064 })();
   1065 
   1066 function $A(iterable) {
   1067   if (!iterable) return [];
   1068   if ('toArray' in Object(iterable)) return iterable.toArray();
   1069   var length = iterable.length || 0, results = new Array(length);
   1070   while (length--) results[length] = iterable[length];
   1071   return results;
   1072 }
   1073 
   1074 
   1075 function $w(string) {
   1076   if (!Object.isString(string)) return [];
   1077   string = string.strip();
   1078   return string ? string.split(/\s+/) : [];
   1079 }
   1080 
   1081 Array.from = $A;
   1082 
   1083 
   1084 (function() {
   1085   var arrayProto = Array.prototype,
   1086       slice = arrayProto.slice,
   1087       _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
   1088 
   1089   function each(iterator, context) {
   1090     for (var i = 0, length = this.length >>> 0; i < length; i++) {
   1091       if (i in this) iterator.call(context, this[i], i, this);
   1092     }
   1093   }
   1094   if (!_each) _each = each;
   1095 
   1096   function clear() {
   1097     this.length = 0;
   1098     return this;
   1099   }
   1100 
   1101   function first() {
   1102     return this[0];
   1103   }
   1104 
   1105   function last() {
   1106     return this[this.length - 1];
   1107   }
   1108 
   1109   function compact() {
   1110     return this.select(function(value) {
   1111       return value != null;
   1112     });
   1113   }
   1114 
   1115   function flatten() {
   1116     return this.inject([], function(array, value) {
   1117       if (Object.isArray(value))
   1118         return array.concat(value.flatten());
   1119       array.push(value);
   1120       return array;
   1121     });
   1122   }
   1123 
   1124   function without() {
   1125     var values = slice.call(arguments, 0);
   1126     return this.select(function(value) {
   1127       return !values.include(value);
   1128     });
   1129   }
   1130 
   1131   function reverse(inline) {
   1132     return (inline === false ? this.toArray() : this)._reverse();
   1133   }
   1134 
   1135   function uniq(sorted) {
   1136     return this.inject([], function(array, value, index) {
   1137       if (0 == index || (sorted ? array.last() != value : !array.include(value)))
   1138         array.push(value);
   1139       return array;
   1140     });
   1141   }
   1142 
   1143   function intersect(array) {
   1144     return this.uniq().findAll(function(item) {
   1145       return array.detect(function(value) { return item === value });
   1146     });
   1147   }
   1148 
   1149 
   1150   function clone() {
   1151     return slice.call(this, 0);
   1152   }
   1153 
   1154   function size() {
   1155     return this.length;
   1156   }
   1157 
   1158   function inspect() {
   1159     return '[' + this.map(Object.inspect).join(', ') + ']';
   1160   }
   1161 
   1162   function indexOf(item, i) {
   1163     i || (i = 0);
   1164     var length = this.length;
   1165     if (i < 0) i = length + i;
   1166     for (; i < length; i++)
   1167       if (this[i] === item) return i;
   1168     return -1;
   1169   }
   1170 
   1171   function lastIndexOf(item, i) {
   1172     i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
   1173     var n = this.slice(0, i).reverse().indexOf(item);
   1174     return (n < 0) ? n : i - n - 1;
   1175   }
   1176 
   1177   function concat() {
   1178     var array = slice.call(this, 0), item;
   1179     for (var i = 0, length = arguments.length; i < length; i++) {
   1180       item = arguments[i];
   1181       if (Object.isArray(item) && !('callee' in item)) {
   1182         for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
   1183           array.push(item[j]);
   1184       } else {
   1185         array.push(item);
   1186       }
   1187     }
   1188     return array;
   1189   }
   1190 
   1191   Object.extend(arrayProto, Enumerable);
   1192 
   1193   if (!arrayProto._reverse)
   1194     arrayProto._reverse = arrayProto.reverse;
   1195 
   1196   Object.extend(arrayProto, {
   1197     _each:     _each,
   1198     clear:     clear,
   1199     first:     first,
   1200     last:      last,
   1201     compact:   compact,
   1202     flatten:   flatten,
   1203     without:   without,
   1204     reverse:   reverse,
   1205     uniq:      uniq,
   1206     intersect: intersect,
   1207     clone:     clone,
   1208     toArray:   clone,
   1209     size:      size,
   1210     inspect:   inspect
   1211   });
   1212 
   1213   var CONCAT_ARGUMENTS_BUGGY = (function() {
   1214     return [].concat(arguments)[0][0] !== 1;
   1215   })(1,2)
   1216 
   1217   if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
   1218 
   1219   if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
   1220   if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
   1221 })();
   1222 function $H(object) {
   1223   return new Hash(object);
   1224 };
   1225 
   1226 var Hash = Class.create(Enumerable, (function() {
   1227   function initialize(object) {
   1228     this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
   1229   }
   1230 
   1231 
   1232   function _each(iterator) {
   1233     for (var key in this._object) {
   1234       var value = this._object[key], pair = [key, value];
   1235       pair.key = key;
   1236       pair.value = value;
   1237       iterator(pair);
   1238     }
   1239   }
   1240 
   1241   function set(key, value) {
   1242     return this._object[key] = value;
   1243   }
   1244 
   1245   function get(key) {
   1246     if (this._object[key] !== Object.prototype[key])
   1247       return this._object[key];
   1248   }
   1249 
   1250   function unset(key) {
   1251     var value = this._object[key];
   1252     delete this._object[key];
   1253     return value;
   1254   }
   1255 
   1256   function toObject() {
   1257     return Object.clone(this._object);
   1258   }
   1259 
   1260 
   1261 
   1262   function keys() {
   1263     return this.pluck('key');
   1264   }
   1265 
   1266   function values() {
   1267     return this.pluck('value');
   1268   }
   1269 
   1270   function index(value) {
   1271     var match = this.detect(function(pair) {
   1272       return pair.value === value;
   1273     });
   1274     return match && match.key;
   1275   }
   1276 
   1277   function merge(object) {
   1278     return this.clone().update(object);
   1279   }
   1280 
   1281   function update(object) {
   1282     return new Hash(object).inject(this, function(result, pair) {
   1283       result.set(pair.key, pair.value);
   1284       return result;
   1285     });
   1286   }
   1287 
   1288   function toQueryPair(key, value) {
   1289     if (Object.isUndefined(value)) return key;
   1290     return key + '=' + encodeURIComponent(String.interpret(value));
   1291   }
   1292 
   1293   function toQueryString() {
   1294     return this.inject([], function(results, pair) {
   1295       var key = encodeURIComponent(pair.key), values = pair.value;
   1296 
   1297       if (values && typeof values == 'object') {
   1298         if (Object.isArray(values)) {
   1299           var queryValues = [];
   1300           for (var i = 0, len = values.length, value; i < len; i++) {
   1301             value = values[i];
   1302             queryValues.push(toQueryPair(key, value));
   1303           }
   1304           return results.concat(queryValues);
   1305         }
   1306       } else results.push(toQueryPair(key, values));
   1307       return results;
   1308     }).join('&');
   1309   }
   1310 
   1311   function inspect() {
   1312     return '#<Hash:{' + this.map(function(pair) {
   1313       return pair.map(Object.inspect).join(': ');
   1314     }).join(', ') + '}>';
   1315   }
   1316 
   1317   function clone() {
   1318     return new Hash(this);
   1319   }
   1320 
   1321   return {
   1322     initialize:             initialize,
   1323     _each:                  _each,
   1324     set:                    set,
   1325     get:                    get,
   1326     unset:                  unset,
   1327     toObject:               toObject,
   1328     toTemplateReplacements: toObject,
   1329     keys:                   keys,
   1330     values:                 values,
   1331     index:                  index,
   1332     merge:                  merge,
   1333     update:                 update,
   1334     toQueryString:          toQueryString,
   1335     inspect:                inspect,
   1336     toJSON:                 toObject,
   1337     clone:                  clone
   1338   };
   1339 })());
   1340 
   1341 Hash.from = $H;
   1342 Object.extend(Number.prototype, (function() {
   1343   function toColorPart() {
   1344     return this.toPaddedString(2, 16);
   1345   }
   1346 
   1347   function succ() {
   1348     return this + 1;
   1349   }
   1350 
   1351   function times(iterator, context) {
   1352     $R(0, this, true).each(iterator, context);
   1353     return this;
   1354   }
   1355 
   1356   function toPaddedString(length, radix) {
   1357     var string = this.toString(radix || 10);
   1358     return '0'.times(length - string.length) + string;
   1359   }
   1360 
   1361   function abs() {
   1362     return Math.abs(this);
   1363   }
   1364 
   1365   function round() {
   1366     return Math.round(this);
   1367   }
   1368 
   1369   function ceil() {
   1370     return Math.ceil(this);
   1371   }
   1372 
   1373   function floor() {
   1374     return Math.floor(this);
   1375   }
   1376 
   1377   return {
   1378     toColorPart:    toColorPart,
   1379     succ:           succ,
   1380     times:          times,
   1381     toPaddedString: toPaddedString,
   1382     abs:            abs,
   1383     round:          round,
   1384     ceil:           ceil,
   1385     floor:          floor
   1386   };
   1387 })());
   1388 
   1389 function $R(start, end, exclusive) {
   1390   return new ObjectRange(start, end, exclusive);
   1391 }
   1392 
   1393 var ObjectRange = Class.create(Enumerable, (function() {
   1394   function initialize(start, end, exclusive) {
   1395     this.start = start;
   1396     this.end = end;
   1397     this.exclusive = exclusive;
   1398   }
   1399 
   1400   function _each(iterator) {
   1401     var value = this.start;
   1402     while (this.include(value)) {
   1403       iterator(value);
   1404       value = value.succ();
   1405     }
   1406   }
   1407 
   1408   function include(value) {
   1409     if (value < this.start)
   1410       return false;
   1411     if (this.exclusive)
   1412       return value < this.end;
   1413     return value <= this.end;
   1414   }
   1415 
   1416   return {
   1417     initialize: initialize,
   1418     _each:      _each,
   1419     include:    include
   1420   };
   1421 })());
   1422 
   1423 
   1424 
   1425 var Ajax = {
   1426   getTransport: function() {
   1427     return Try.these(
   1428       function() {return new XMLHttpRequest()},
   1429       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
   1430       function() {return new ActiveXObject('Microsoft.XMLHTTP')}
   1431     ) || false;
   1432   },
   1433 
   1434   activeRequestCount: 0
   1435 };
   1436 
   1437 Ajax.Responders = {
   1438   responders: [],
   1439 
   1440   _each: function(iterator) {
   1441     this.responders._each(iterator);
   1442   },
   1443 
   1444   register: function(responder) {
   1445     if (!this.include(responder))
   1446       this.responders.push(responder);
   1447   },
   1448 
   1449   unregister: function(responder) {
   1450     this.responders = this.responders.without(responder);
   1451   },
   1452 
   1453   dispatch: function(callback, request, transport, json) {
   1454     this.each(function(responder) {
   1455       if (Object.isFunction(responder[callback])) {
   1456         try {
   1457           responder[callback].apply(responder, [request, transport, json]);
   1458         } catch (e) { }
   1459       }
   1460     });
   1461   }
   1462 };
   1463 
   1464 Object.extend(Ajax.Responders, Enumerable);
   1465 
   1466 Ajax.Responders.register({
   1467   onCreate:   function() { Ajax.activeRequestCount++ },
   1468   onComplete: function() { Ajax.activeRequestCount-- }
   1469 });
   1470 Ajax.Base = Class.create({
   1471   initialize: function(options) {
   1472     this.options = {
   1473       method:       'post',
   1474       asynchronous: true,
   1475       contentType:  'application/x-www-form-urlencoded',
   1476       encoding:     'UTF-8',
   1477       parameters:   '',
   1478       evalJSON:     true,
   1479       evalJS:       true
   1480     };
   1481     Object.extend(this.options, options || { });
   1482 
   1483     this.options.method = this.options.method.toLowerCase();
   1484 
   1485     if (Object.isHash(this.options.parameters))
   1486       this.options.parameters = this.options.parameters.toObject();
   1487   }
   1488 });
   1489 Ajax.Request = Class.create(Ajax.Base, {
   1490   _complete: false,
   1491 
   1492   initialize: function($super, url, options) {
   1493     $super(options);
   1494     this.transport = Ajax.getTransport();
   1495     this.request(url);
   1496   },
   1497 
   1498   request: function(url) {
   1499     this.url = url;
   1500     this.method = this.options.method;
   1501     var params = Object.isString(this.options.parameters) ?
   1502           this.options.parameters :
   1503           Object.toQueryString(this.options.parameters);
   1504 
   1505     if (!['get', 'post'].include(this.method)) {
   1506       params += (params ? '&' : '') + "_method=" + this.method;
   1507       this.method = 'post';
   1508     }
   1509 
   1510     if (params && this.method === 'get') {
   1511       this.url += (this.url.include('?') ? '&' : '?') + params;
   1512     }
   1513 
   1514     this.parameters = params.toQueryParams();
   1515 
   1516     try {
   1517       var response = new Ajax.Response(this);
   1518       if (this.options.onCreate) this.options.onCreate(response);
   1519       Ajax.Responders.dispatch('onCreate', this, response);
   1520 
   1521       this.transport.open(this.method.toUpperCase(), this.url,
   1522         this.options.asynchronous);
   1523 
   1524       if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
   1525 
   1526       this.transport.onreadystatechange = this.onStateChange.bind(this);
   1527       this.setRequestHeaders();
   1528 
   1529       this.body = this.method == 'post' ? (this.options.postBody || params) : null;
   1530       this.transport.send(this.body);
   1531 
   1532       /* Force Firefox to handle ready state 4 for synchronous requests */
   1533       if (!this.options.asynchronous && this.transport.overrideMimeType)
   1534         this.onStateChange();
   1535 
   1536     }
   1537     catch (e) {
   1538       this.dispatchException(e);
   1539     }
   1540   },
   1541 
   1542   onStateChange: function() {
   1543     var readyState = this.transport.readyState;
   1544     if (readyState > 1 && !((readyState == 4) && this._complete))
   1545       this.respondToReadyState(this.transport.readyState);
   1546   },
   1547 
   1548   setRequestHeaders: function() {
   1549     var headers = {
   1550       'X-Requested-With': 'XMLHttpRequest',
   1551       'X-Prototype-Version': Prototype.Version,
   1552       'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
   1553     };
   1554 
   1555     if (this.method == 'post') {
   1556       headers['Content-type'] = this.options.contentType +
   1557         (this.options.encoding ? '; charset=' + this.options.encoding : '');
   1558 
   1559       /* Force "Connection: close" for older Mozilla browsers to work
   1560        * around a bug where XMLHttpRequest sends an incorrect
   1561        * Content-length header. See Mozilla Bugzilla #246651.
   1562        */
   1563       if (this.transport.overrideMimeType &&
   1564           (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
   1565             headers['Connection'] = 'close';
   1566     }
   1567 
   1568     if (typeof this.options.requestHeaders == 'object') {
   1569       var extras = this.options.requestHeaders;
   1570 
   1571       if (Object.isFunction(extras.push))
   1572         for (var i = 0, length = extras.length; i < length; i += 2)
   1573           headers[extras[i]] = extras[i+1];
   1574       else
   1575         $H(extras).each(function(pair) { headers[pair.key] = pair.value });
   1576     }
   1577 
   1578     for (var name in headers)
   1579       this.transport.setRequestHeader(name, headers[name]);
   1580   },
   1581 
   1582   success: function() {
   1583     var status = this.getStatus();
   1584     return !status || (status >= 200 && status < 300) || status == 304;
   1585   },
   1586 
   1587   getStatus: function() {
   1588     try {
   1589       if (this.transport.status === 1223) return 204;
   1590       return this.transport.status || 0;
   1591     } catch (e) { return 0 }
   1592   },
   1593 
   1594   respondToReadyState: function(readyState) {
   1595     var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
   1596 
   1597     if (state == 'Complete') {
   1598       try {
   1599         this._complete = true;
   1600         (this.options['on' + response.status]
   1601          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
   1602          || Prototype.emptyFunction)(response, response.headerJSON);
   1603       } catch (e) {
   1604         this.dispatchException(e);
   1605       }
   1606 
   1607       var contentType = response.getHeader('Content-type');
   1608       if (this.options.evalJS == 'force'
   1609           || (this.options.evalJS && this.isSameOrigin() && contentType
   1610           && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
   1611         this.evalResponse();
   1612     }
   1613 
   1614     try {
   1615       (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
   1616       Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
   1617     } catch (e) {
   1618       this.dispatchException(e);
   1619     }
   1620 
   1621     if (state == 'Complete') {
   1622       this.transport.onreadystatechange = Prototype.emptyFunction;
   1623     }
   1624   },
   1625 
   1626   isSameOrigin: function() {
   1627     var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
   1628     return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
   1629       protocol: location.protocol,
   1630       domain: document.domain,
   1631       port: location.port ? ':' + location.port : ''
   1632     }));
   1633   },
   1634 
   1635   getHeader: function(name) {
   1636     try {
   1637       return this.transport.getResponseHeader(name) || null;
   1638     } catch (e) { return null; }
   1639   },
   1640 
   1641   evalResponse: function() {
   1642     try {
   1643       return eval((this.transport.responseText || '').unfilterJSON());
   1644     } catch (e) {
   1645       this.dispatchException(e);
   1646     }
   1647   },
   1648 
   1649   dispatchException: function(exception) {
   1650     (this.options.onException || Prototype.emptyFunction)(this, exception);
   1651     Ajax.Responders.dispatch('onException', this, exception);
   1652   }
   1653 });
   1654 
   1655 Ajax.Request.Events =
   1656   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
   1657 
   1658 
   1659 
   1660 
   1661 
   1662 
   1663 
   1664 
   1665 Ajax.Response = Class.create({
   1666   initialize: function(request){
   1667     this.request = request;
   1668     var transport  = this.transport  = request.transport,
   1669         readyState = this.readyState = transport.readyState;
   1670 
   1671     if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
   1672       this.status       = this.getStatus();
   1673       this.statusText   = this.getStatusText();
   1674       this.responseText = String.interpret(transport.responseText);
   1675       this.headerJSON   = this._getHeaderJSON();
   1676     }
   1677 
   1678     if (readyState == 4) {
   1679       var xml = transport.responseXML;
   1680       this.responseXML  = Object.isUndefined(xml) ? null : xml;
   1681       this.responseJSON = this._getResponseJSON();
   1682     }
   1683   },
   1684 
   1685   status:      0,
   1686 
   1687   statusText: '',
   1688 
   1689   getStatus: Ajax.Request.prototype.getStatus,
   1690 
   1691   getStatusText: function() {
   1692     try {
   1693       return this.transport.statusText || '';
   1694     } catch (e) { return '' }
   1695   },
   1696 
   1697   getHeader: Ajax.Request.prototype.getHeader,
   1698 
   1699   getAllHeaders: function() {
   1700     try {
   1701       return this.getAllResponseHeaders();
   1702     } catch (e) { return null }
   1703   },
   1704 
   1705   getResponseHeader: function(name) {
   1706     return this.transport.getResponseHeader(name);
   1707   },
   1708 
   1709   getAllResponseHeaders: function() {
   1710     return this.transport.getAllResponseHeaders();
   1711   },
   1712 
   1713   _getHeaderJSON: function() {
   1714     var json = this.getHeader('X-JSON');
   1715     if (!json) return null;
   1716     json = decodeURIComponent(escape(json));
   1717     try {
   1718       return json.evalJSON(this.request.options.sanitizeJSON ||
   1719         !this.request.isSameOrigin());
   1720     } catch (e) {
   1721       this.request.dispatchException(e);
   1722     }
   1723   },
   1724 
   1725   _getResponseJSON: function() {
   1726     var options = this.request.options;
   1727     if (!options.evalJSON || (options.evalJSON != 'force' &&
   1728       !(this.getHeader('Content-type') || '').include('application/json')) ||
   1729         this.responseText.blank())
   1730           return null;
   1731     try {
   1732       return this.responseText.evalJSON(options.sanitizeJSON ||
   1733         !this.request.isSameOrigin());
   1734     } catch (e) {
   1735       this.request.dispatchException(e);
   1736     }
   1737   }
   1738 });
   1739 
   1740 Ajax.Updater = Class.create(Ajax.Request, {
   1741   initialize: function($super, container, url, options) {
   1742     this.container = {
   1743       success: (container.success || container),
   1744       failure: (container.failure || (container.success ? null : container))
   1745     };
   1746 
   1747     options = Object.clone(options);
   1748     var onComplete = options.onComplete;
   1749     options.onComplete = (function(response, json) {
   1750       this.updateContent(response.responseText);
   1751       if (Object.isFunction(onComplete)) onComplete(response, json);
   1752     }).bind(this);
   1753 
   1754     $super(url, options);
   1755   },
   1756 
   1757   updateContent: function(responseText) {
   1758     var receiver = this.container[this.success() ? 'success' : 'failure'],
   1759         options = this.options;
   1760 
   1761     if (!options.evalScripts) responseText = responseText.stripScripts();
   1762 
   1763     if (receiver = $(receiver)) {
   1764       if (options.insertion) {
   1765         if (Object.isString(options.insertion)) {
   1766           var insertion = { }; insertion[options.insertion] = responseText;
   1767           receiver.insert(insertion);
   1768         }
   1769         else options.insertion(receiver, responseText);
   1770       }
   1771       else receiver.update(responseText);
   1772     }
   1773   }
   1774 });
   1775 
   1776 Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
   1777   initialize: function($super, container, url, options) {
   1778     $super(options);
   1779     this.onComplete = this.options.onComplete;
   1780 
   1781     this.frequency = (this.options.frequency || 2);
   1782     this.decay = (this.options.decay || 1);
   1783 
   1784     this.updater = { };
   1785     this.container = container;
   1786     this.url = url;
   1787 
   1788     this.start();
   1789   },
   1790 
   1791   start: function() {
   1792     this.options.onComplete = this.updateComplete.bind(this);
   1793     this.onTimerEvent();
   1794   },
   1795 
   1796   stop: function() {
   1797     this.updater.options.onComplete = undefined;
   1798     clearTimeout(this.timer);
   1799     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
   1800   },
   1801 
   1802   updateComplete: function(response) {
   1803     if (this.options.decay) {
   1804       this.decay = (response.responseText == this.lastText ?
   1805         this.decay * this.options.decay : 1);
   1806 
   1807       this.lastText = response.responseText;
   1808     }
   1809     this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
   1810   },
   1811 
   1812   onTimerEvent: function() {
   1813     this.updater = new Ajax.Updater(this.container, this.url, this.options);
   1814   }
   1815 });
   1816 
   1817 
   1818 function $(element) {
   1819   if (arguments.length > 1) {
   1820     for (var i = 0, elements = [], length = arguments.length; i < length; i++)
   1821       elements.push($(arguments[i]));
   1822     return elements;
   1823   }
   1824   if (Object.isString(element))
   1825     element = document.getElementById(element);
   1826   return Element.extend(element);
   1827 }
   1828 
   1829 if (Prototype.BrowserFeatures.XPath) {
   1830   document._getElementsByXPath = function(expression, parentElement) {
   1831     var results = [];
   1832     var query = document.evaluate(expression, $(parentElement) || document,
   1833       null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
   1834     for (var i = 0, length = query.snapshotLength; i < length; i++)
   1835       results.push(Element.extend(query.snapshotItem(i)));
   1836     return results;
   1837   };
   1838 }
   1839 
   1840 /*--------------------------------------------------------------------------*/
   1841 
   1842 if (!Node) var Node = { };
   1843 
   1844 if (!Node.ELEMENT_NODE) {
   1845   Object.extend(Node, {
   1846     ELEMENT_NODE: 1,
   1847     ATTRIBUTE_NODE: 2,
   1848     TEXT_NODE: 3,
   1849     CDATA_SECTION_NODE: 4,
   1850     ENTITY_REFERENCE_NODE: 5,
   1851     ENTITY_NODE: 6,
   1852     PROCESSING_INSTRUCTION_NODE: 7,
   1853     COMMENT_NODE: 8,
   1854     DOCUMENT_NODE: 9,
   1855     DOCUMENT_TYPE_NODE: 10,
   1856     DOCUMENT_FRAGMENT_NODE: 11,
   1857     NOTATION_NODE: 12
   1858   });
   1859 }
   1860 
   1861 
   1862 
   1863 (function(global) {
   1864   function shouldUseCache(tagName, attributes) {
   1865     if (tagName === 'select') return false;
   1866     if ('type' in attributes) return false;
   1867     return true;
   1868   }
   1869 
   1870   var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
   1871     try {
   1872       var el = document.createElement('<input name="x">');
   1873       return el.tagName.toLowerCase() === 'input' && el.name === 'x';
   1874     }
   1875     catch(err) {
   1876       return false;
   1877     }
   1878   })();
   1879 
   1880   var element = global.Element;
   1881 
   1882   global.Element = function(tagName, attributes) {
   1883     attributes = attributes || { };
   1884     tagName = tagName.toLowerCase();
   1885     var cache = Element.cache;
   1886 
   1887     if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
   1888       tagName = '<' + tagName + ' name="' + attributes.name + '">';
   1889       delete attributes.name;
   1890       return Element.writeAttribute(document.createElement(tagName), attributes);
   1891     }
   1892 
   1893     if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
   1894 
   1895     var node = shouldUseCache(tagName, attributes) ?
   1896      cache[tagName].cloneNode(false) : document.createElement(tagName);
   1897 
   1898     return Element.writeAttribute(node, attributes);
   1899   };
   1900 
   1901   Object.extend(global.Element, element || { });
   1902   if (element) global.Element.prototype = element.prototype;
   1903 
   1904 })(this);
   1905 
   1906 Element.idCounter = 1;
   1907 Element.cache = { };
   1908 
   1909 Element._purgeElement = function(element) {
   1910   var uid = element._prototypeUID;
   1911   if (uid) {
   1912     Element.stopObserving(element);
   1913     element._prototypeUID = void 0;
   1914     delete Element.Storage[uid];
   1915   }
   1916 }
   1917 
   1918 Element.Methods = {
   1919   visible: function(element) {
   1920     return $(element).style.display != 'none';
   1921   },
   1922 
   1923   toggle: function(element) {
   1924     element = $(element);
   1925     Element[Element.visible(element) ? 'hide' : 'show'](element);
   1926     return element;
   1927   },
   1928 
   1929   hide: function(element) {
   1930     element = $(element);
   1931     element.style.display = 'none';
   1932     return element;
   1933   },
   1934 
   1935   show: function(element) {
   1936     element = $(element);
   1937     element.style.display = '';
   1938     return element;
   1939   },
   1940 
   1941   remove: function(element) {
   1942     element = $(element);
   1943     element.parentNode.removeChild(element);
   1944     return element;
   1945   },
   1946 
   1947   update: (function(){
   1948 
   1949     var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
   1950       var el = document.createElement("select"),
   1951           isBuggy = true;
   1952       el.innerHTML = "<option value=\"test\">test</option>";
   1953       if (el.options && el.options[0]) {
   1954         isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
   1955       }
   1956       el = null;
   1957       return isBuggy;
   1958     })();
   1959 
   1960     var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
   1961       try {
   1962         var el = document.createElement("table");
   1963         if (el && el.tBodies) {
   1964           el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
   1965           var isBuggy = typeof el.tBodies[0] == "undefined";
   1966           el = null;
   1967           return isBuggy;
   1968         }
   1969       } catch (e) {
   1970         return true;
   1971       }
   1972     })();
   1973 
   1974     var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
   1975       try {
   1976         var el = document.createElement('div');
   1977         el.innerHTML = "<link>";
   1978         var isBuggy = (el.childNodes.length === 0);
   1979         el = null;
   1980         return isBuggy;
   1981       } catch(e) {
   1982         return true;
   1983       }
   1984     })();
   1985 
   1986     var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
   1987      TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
   1988 
   1989     var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
   1990       var s = document.createElement("script"),
   1991           isBuggy = false;
   1992       try {
   1993         s.appendChild(document.createTextNode(""));
   1994         isBuggy = !s.firstChild ||
   1995           s.firstChild && s.firstChild.nodeType !== 3;
   1996       } catch (e) {
   1997         isBuggy = true;
   1998       }
   1999       s = null;
   2000       return isBuggy;
   2001     })();
   2002 
   2003 
   2004     function update(element, content) {
   2005       element = $(element);
   2006       var purgeElement = Element._purgeElement;
   2007 
   2008       var descendants = element.getElementsByTagName('*'),
   2009        i = descendants.length;
   2010       while (i--) purgeElement(descendants[i]);
   2011 
   2012       if (content && content.toElement)
   2013         content = content.toElement();
   2014 
   2015       if (Object.isElement(content))
   2016         return element.update().insert(content);
   2017 
   2018       content = Object.toHTML(content);
   2019 
   2020       var tagName = element.tagName.toUpperCase();
   2021 
   2022       if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
   2023         element.text = content;
   2024         return element;
   2025       }
   2026 
   2027       if (ANY_INNERHTML_BUGGY) {
   2028         if (tagName in Element._insertionTranslations.tags) {
   2029           while (element.firstChild) {
   2030             element.removeChild(element.firstChild);
   2031           }
   2032           Element._getContentFromAnonymousElement(tagName, content.stripScripts())
   2033             .each(function(node) {
   2034               element.appendChild(node)
   2035             });
   2036         } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
   2037           while (element.firstChild) {
   2038             element.removeChild(element.firstChild);
   2039           }
   2040           var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
   2041           nodes.each(function(node) { element.appendChild(node) });
   2042         }
   2043         else {
   2044           element.innerHTML = content.stripScripts();
   2045         }
   2046       }
   2047       else {
   2048         element.innerHTML = content.stripScripts();
   2049       }
   2050 
   2051       content.evalScripts.bind(content).defer();
   2052       return element;
   2053     }
   2054 
   2055     return update;
   2056   })(),
   2057 
   2058   replace: function(element, content) {
   2059     element = $(element);
   2060     if (content && content.toElement) content = content.toElement();
   2061     else if (!Object.isElement(content)) {
   2062       content = Object.toHTML(content);
   2063       var range = element.ownerDocument.createRange();
   2064       range.selectNode(element);
   2065       content.evalScripts.bind(content).defer();
   2066       content = range.createContextualFragment(content.stripScripts());
   2067     }
   2068     element.parentNode.replaceChild(content, element);
   2069     return element;
   2070   },
   2071 
   2072   insert: function(element, insertions) {
   2073     element = $(element);
   2074 
   2075     if (Object.isString(insertions) || Object.isNumber(insertions) ||
   2076         Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
   2077           insertions = {bottom:insertions};
   2078 
   2079     var content, insert, tagName, childNodes;
   2080 
   2081     for (var position in insertions) {
   2082       content  = insertions[position];
   2083       position = position.toLowerCase();
   2084       insert = Element._insertionTranslations[position];
   2085 
   2086       if (content && content.toElement) content = content.toElement();
   2087       if (Object.isElement(content)) {
   2088         insert(element, content);
   2089         continue;
   2090       }
   2091 
   2092       content = Object.toHTML(content);
   2093 
   2094       tagName = ((position == 'before' || position == 'after')
   2095         ? element.parentNode : element).tagName.toUpperCase();
   2096 
   2097       childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
   2098 
   2099       if (position == 'top' || position == 'after') childNodes.reverse();
   2100       childNodes.each(insert.curry(element));
   2101 
   2102       content.evalScripts.bind(content).defer();
   2103     }
   2104 
   2105     return element;
   2106   },
   2107 
   2108   wrap: function(element, wrapper, attributes) {
   2109     element = $(element);
   2110     if (Object.isElement(wrapper))
   2111       $(wrapper).writeAttribute(attributes || { });
   2112     else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
   2113     else wrapper = new Element('div', wrapper);
   2114     if (element.parentNode)
   2115       element.parentNode.replaceChild(wrapper, element);
   2116     wrapper.appendChild(element);
   2117     return wrapper;
   2118   },
   2119 
   2120   inspect: function(element) {
   2121     element = $(element);
   2122     var result = '<' + element.tagName.toLowerCase();
   2123     $H({'id': 'id', 'className': 'class'}).each(function(pair) {
   2124       var property = pair.first(),
   2125           attribute = pair.last(),
   2126           value = (element[property] || '').toString();
   2127       if (value) result += ' ' + attribute + '=' + value.inspect(true);
   2128     });
   2129     return result + '>';
   2130   },
   2131 
   2132   recursivelyCollect: function(element, property, maximumLength) {
   2133     element = $(element);
   2134     maximumLength = maximumLength || -1;
   2135     var elements = [];
   2136 
   2137     while (element = element[property]) {
   2138       if (element.nodeType == 1)
   2139         elements.push(Element.extend(element));
   2140       if (elements.length == maximumLength)
   2141         break;
   2142     }
   2143 
   2144     return elements;
   2145   },
   2146 
   2147   ancestors: function(element) {
   2148     return Element.recursivelyCollect(element, 'parentNode');
   2149   },
   2150 
   2151   descendants: function(element) {
   2152     return Element.select(element, "*");
   2153   },
   2154 
   2155   firstDescendant: function(element) {
   2156     element = $(element).firstChild;
   2157     while (element && element.nodeType != 1) element = element.nextSibling;
   2158     return $(element);
   2159   },
   2160 
   2161   immediateDescendants: function(element) {
   2162     var results = [], child = $(element).firstChild;
   2163     while (child) {
   2164       if (child.nodeType === 1) {
   2165         results.push(Element.extend(child));
   2166       }
   2167       child = child.nextSibling;
   2168     }
   2169     return results;
   2170   },
   2171 
   2172   previousSiblings: function(element, maximumLength) {
   2173     return Element.recursivelyCollect(element, 'previousSibling');
   2174   },
   2175 
   2176   nextSiblings: function(element) {
   2177     return Element.recursivelyCollect(element, 'nextSibling');
   2178   },
   2179 
   2180   siblings: function(element) {
   2181     element = $(element);
   2182     return Element.previousSiblings(element).reverse()
   2183       .concat(Element.nextSiblings(element));
   2184   },
   2185 
   2186   match: function(element, selector) {
   2187     element = $(element);
   2188     if (Object.isString(selector))
   2189       return Prototype.Selector.match(element, selector);
   2190     return selector.match(element);
   2191   },
   2192 
   2193   up: function(element, expression, index) {
   2194     element = $(element);
   2195     if (arguments.length == 1) return $(element.parentNode);
   2196     var ancestors = Element.ancestors(element);
   2197     return Object.isNumber(expression) ? ancestors[expression] :
   2198       Prototype.Selector.find(ancestors, expression, index);
   2199   },
   2200 
   2201   down: function(element, expression, index) {
   2202     element = $(element);
   2203     if (arguments.length == 1) return Element.firstDescendant(element);
   2204     return Object.isNumber(expression) ? Element.descendants(element)[expression] :
   2205       Element.select(element, expression)[index || 0];
   2206   },
   2207 
   2208   previous: function(element, expression, index) {
   2209     element = $(element);
   2210     if (Object.isNumber(expression)) index = expression, expression = false;
   2211     if (!Object.isNumber(index)) index = 0;
   2212 
   2213     if (expression) {
   2214       return Prototype.Selector.find(element.previousSiblings(), expression, index);
   2215     } else {
   2216       return element.recursivelyCollect("previousSibling", index + 1)[index];
   2217     }
   2218   },
   2219 
   2220   next: function(element, expression, index) {
   2221     element = $(element);
   2222     if (Object.isNumber(expression)) index = expression, expression = false;
   2223     if (!Object.isNumber(index)) index = 0;
   2224 
   2225     if (expression) {
   2226       return Prototype.Selector.find(element.nextSiblings(), expression, index);
   2227     } else {
   2228       var maximumLength = Object.isNumber(index) ? index + 1 : 1;
   2229       return element.recursivelyCollect("nextSibling", index + 1)[index];
   2230     }
   2231   },
   2232 
   2233 
   2234   select: function(element) {
   2235     element = $(element);
   2236     var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
   2237     return Prototype.Selector.select(expressions, element);
   2238   },
   2239 
   2240   adjacent: function(element) {
   2241     element = $(element);
   2242     var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
   2243     return Prototype.Selector.select(expressions, element.parentNode).without(element);
   2244   },
   2245 
   2246   identify: function(element) {
   2247     element = $(element);
   2248     var id = Element.readAttribute(element, 'id');
   2249     if (id) return id;
   2250     do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
   2251     Element.writeAttribute(element, 'id', id);
   2252     return id;
   2253   },
   2254 
   2255   readAttribute: function(element, name) {
   2256     element = $(element);
   2257     if (Prototype.Browser.IE) {
   2258       var t = Element._attributeTranslations.read;
   2259       if (t.values[name]) return t.values[name](element, name);
   2260       if (t.names[name]) name = t.names[name];
   2261       if (name.include(':')) {
   2262         return (!element.attributes || !element.attributes[name]) ? null :
   2263          element.attributes[name].value;
   2264       }
   2265     }
   2266     return element.getAttribute(name);
   2267   },
   2268 
   2269   writeAttribute: function(element, name, value) {
   2270     element = $(element);
   2271     var attributes = { }, t = Element._attributeTranslations.write;
   2272 
   2273     if (typeof name == 'object') attributes = name;
   2274     else attributes[name] = Object.isUndefined(value) ? true : value;
   2275 
   2276     for (var attr in attributes) {
   2277       name = t.names[attr] || attr;
   2278       value = attributes[attr];
   2279       if (t.values[attr]) name = t.values[attr](element, value);
   2280       if (value === false || value === null)
   2281         element.removeAttribute(name);
   2282       else if (value === true)
   2283         element.setAttribute(name, name);
   2284       else element.setAttribute(name, value);
   2285     }
   2286     return element;
   2287   },
   2288 
   2289   getHeight: function(element) {
   2290     return Element.getDimensions(element).height;
   2291   },
   2292 
   2293   getWidth: function(element) {
   2294     return Element.getDimensions(element).width;
   2295   },
   2296 
   2297   classNames: function(element) {
   2298     return new Element.ClassNames(element);
   2299   },
   2300 
   2301   hasClassName: function(element, className) {
   2302     if (!(element = $(element))) return;
   2303     var elementClassName = element.className;
   2304     return (elementClassName.length > 0 && (elementClassName == className ||
   2305       new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
   2306   },
   2307 
   2308   addClassName: function(element, className) {
   2309     if (!(element = $(element))) return;
   2310     if (!Element.hasClassName(element, className))
   2311       element.className += (element.className ? ' ' : '') + className;
   2312     return element;
   2313   },
   2314 
   2315   removeClassName: function(element, className) {
   2316     if (!(element = $(element))) return;
   2317     element.className = element.className.replace(
   2318       new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
   2319     return element;
   2320   },
   2321 
   2322   toggleClassName: function(element, className) {
   2323     if (!(element = $(element))) return;
   2324     return Element[Element.hasClassName(element, className) ?
   2325       'removeClassName' : 'addClassName'](element, className);
   2326   },
   2327 
   2328   cleanWhitespace: function(element) {
   2329     element = $(element);
   2330     var node = element.firstChild;
   2331     while (node) {
   2332       var nextNode = node.nextSibling;
   2333       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
   2334         element.removeChild(node);
   2335       node = nextNode;
   2336     }
   2337     return element;
   2338   },
   2339 
   2340   empty: function(element) {
   2341     return $(element).innerHTML.blank();
   2342   },
   2343 
   2344   descendantOf: function(element, ancestor) {
   2345     element = $(element), ancestor = $(ancestor);
   2346 
   2347     if (element.compareDocumentPosition)
   2348       return (element.compareDocumentPosition(ancestor) & 8) === 8;
   2349 
   2350     if (ancestor.contains)
   2351       return ancestor.contains(element) && ancestor !== element;
   2352 
   2353     while (element = element.parentNode)
   2354       if (element == ancestor) return true;
   2355 
   2356     return false;
   2357   },
   2358 
   2359   scrollTo: function(element) {
   2360     element = $(element);
   2361     var pos = Element.cumulativeOffset(element);
   2362     window.scrollTo(pos[0], pos[1]);
   2363     return element;
   2364   },
   2365 
   2366   getStyle: function(element, style) {
   2367     element = $(element);
   2368     style = style == 'float' ? 'cssFloat' : style.camelize();
   2369     var value = element.style[style];
   2370     if (!value || value == 'auto') {
   2371       var css = document.defaultView.getComputedStyle(element, null);
   2372       value = css ? css[style] : null;
   2373     }
   2374     if (style == 'opacity') return value ? parseFloat(value) : 1.0;
   2375     return value == 'auto' ? null : value;
   2376   },
   2377 
   2378   getOpacity: function(element) {
   2379     return $(element).getStyle('opacity');
   2380   },
   2381 
   2382   setStyle: function(element, styles) {
   2383     element = $(element);
   2384     var elementStyle = element.style, match;
   2385     if (Object.isString(styles)) {
   2386       element.style.cssText += ';' + styles;
   2387       return styles.include('opacity') ?
   2388         element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
   2389     }
   2390     for (var property in styles)
   2391       if (property == 'opacity') element.setOpacity(styles[property]);
   2392       else
   2393         elementStyle[(property == 'float' || property == 'cssFloat') ?
   2394           (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
   2395             property] = styles[property];
   2396 
   2397     return element;
   2398   },
   2399 
   2400   setOpacity: function(element, value) {
   2401     element = $(element);
   2402     element.style.opacity = (value == 1 || value === '') ? '' :
   2403       (value < 0.00001) ? 0 : value;
   2404     return element;
   2405   },
   2406 
   2407   makePositioned: function(element) {
   2408     element = $(element);
   2409     var pos = Element.getStyle(element, 'position');
   2410     if (pos == 'static' || !pos) {
   2411       element._madePositioned = true;
   2412       element.style.position = 'relative';
   2413       if (Prototype.Browser.Opera) {
   2414         element.style.top = 0;
   2415         element.style.left = 0;
   2416       }
   2417     }
   2418     return element;
   2419   },
   2420 
   2421   undoPositioned: function(element) {
   2422     element = $(element);
   2423     if (element._madePositioned) {
   2424       element._madePositioned = undefined;
   2425       element.style.position =
   2426         element.style.top =
   2427         element.style.left =
   2428         element.style.bottom =
   2429         element.style.right = '';
   2430     }
   2431     return element;
   2432   },
   2433 
   2434   makeClipping: function(element) {
   2435     element = $(element);
   2436     if (element._overflow) return element;
   2437     element._overflow = Element.getStyle(element, 'overflow') || 'auto';
   2438     if (element._overflow !== 'hidden')
   2439       element.style.overflow = 'hidden';
   2440     return element;
   2441   },
   2442 
   2443   undoClipping: function(element) {
   2444     element = $(element);
   2445     if (!element._overflow) return element;
   2446     element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
   2447     element._overflow = null;
   2448     return element;
   2449   },
   2450 
   2451   clonePosition: function(element, source) {
   2452     var options = Object.extend({
   2453       setLeft:    true,
   2454       setTop:     true,
   2455       setWidth:   true,
   2456       setHeight:  true,
   2457       offsetTop:  0,
   2458       offsetLeft: 0
   2459     }, arguments[2] || { });
   2460 
   2461     source = $(source);
   2462     var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
   2463 
   2464     element = $(element);
   2465 
   2466     if (Element.getStyle(element, 'position') == 'absolute') {
   2467       parent = Element.getOffsetParent(element);
   2468       delta = Element.viewportOffset(parent);
   2469     }
   2470 
   2471     if (parent == document.body) {
   2472       delta[0] -= document.body.offsetLeft;
   2473       delta[1] -= document.body.offsetTop;
   2474     }
   2475 
   2476     if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
   2477     if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
   2478     if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
   2479     if (options.setHeight) element.style.height = source.offsetHeight + 'px';
   2480     return element;
   2481   }
   2482 };
   2483 
   2484 Object.extend(Element.Methods, {
   2485   getElementsBySelector: Element.Methods.select,
   2486 
   2487   childElements: Element.Methods.immediateDescendants
   2488 });
   2489 
   2490 Element._attributeTranslations = {
   2491   write: {
   2492     names: {
   2493       className: 'class',
   2494       htmlFor:   'for'
   2495     },
   2496     values: { }
   2497   }
   2498 };
   2499 
   2500 if (Prototype.Browser.Opera) {
   2501   Element.Methods.getStyle = Element.Methods.getStyle.wrap(
   2502     function(proceed, element, style) {
   2503       switch (style) {
   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.getStyle = function(element, style) {
   2540     element = $(element);
   2541     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
   2542     var value = element.style[style];
   2543     if (!value && element.currentStyle) value = element.currentStyle[style];
   2544 
   2545     if (style == 'opacity') {
   2546       if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
   2547         if (value[1]) return parseFloat(value[1]) / 100;
   2548       return 1.0;
   2549     }
   2550 
   2551     if (value == 'auto') {
   2552       if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
   2553         return element['offset' + style.capitalize()] + 'px';
   2554       return null;
   2555     }
   2556     return value;
   2557   };
   2558 
   2559   Element.Methods.setOpacity = function(element, value) {
   2560     function stripAlpha(filter){
   2561       return filter.replace(/alpha\([^\)]*\)/gi,'');
   2562     }
   2563     element = $(element);
   2564     var currentStyle = element.currentStyle;
   2565     if ((currentStyle && !currentStyle.hasLayout) ||
   2566       (!currentStyle && element.style.zoom == 'normal'))
   2567         element.style.zoom = 1;
   2568 
   2569     var filter = element.getStyle('filter'), style = element.style;
   2570     if (value == 1 || value === '') {
   2571       (filter = stripAlpha(filter)) ?
   2572         style.filter = filter : style.removeAttribute('filter');
   2573       return element;
   2574     } else if (value < 0.00001) value = 0;
   2575     style.filter = stripAlpha(filter) +
   2576       'alpha(opacity=' + (value * 100) + ')';
   2577     return element;
   2578   };
   2579 
   2580   Element._attributeTranslations = (function(){
   2581 
   2582     var classProp = 'className',
   2583         forProp = 'for',
   2584         el = document.createElement('div');
   2585 
   2586     el.setAttribute(classProp, 'x');
   2587 
   2588     if (el.className !== 'x') {
   2589       el.setAttribute('class', 'x');
   2590       if (el.className === 'x') {
   2591         classProp = 'class';
   2592       }
   2593     }
   2594     el = null;
   2595 
   2596     el = document.createElement('label');
   2597     el.setAttribute(forProp, 'x');
   2598     if (el.htmlFor !== 'x') {
   2599       el.setAttribute('htmlFor', 'x');
   2600       if (el.htmlFor === 'x') {
   2601         forProp = 'htmlFor';
   2602       }
   2603     }
   2604     el = null;
   2605 
   2606     return {
   2607       read: {
   2608         names: {
   2609           'class':      classProp,
   2610           'className':  classProp,
   2611           'for':        forProp,
   2612           'htmlFor':    forProp
   2613         },
   2614         values: {
   2615           _getAttr: function(element, attribute) {
   2616             return element.getAttribute(attribute);
   2617           },
   2618           _getAttr2: function(element, attribute) {
   2619             return element.getAttribute(attribute, 2);
   2620           },
   2621           _getAttrNode: function(element, attribute) {
   2622             var node = element.getAttributeNode(attribute);
   2623             return node ? node.value : "";
   2624           },
   2625           _getEv: (function(){
   2626 
   2627             var el = document.createElement('div'), f;
   2628             el.onclick = Prototype.emptyFunction;
   2629             var value = el.getAttribute('onclick');
   2630 
   2631             if (String(value).indexOf('{') > -1) {
   2632               f = function(element, attribute) {
   2633                 attribute = element.getAttribute(attribute);
   2634                 if (!attribute) return null;
   2635                 attribute = attribute.toString();
   2636                 attribute = attribute.split('{')[1];
   2637                 attribute = attribute.split('}')[0];
   2638                 return attribute.strip();
   2639               };
   2640             }
   2641             else if (value === '') {
   2642               f = function(element, attribute) {
   2643                 attribute = element.getAttribute(attribute);
   2644                 if (!attribute) return null;
   2645                 return attribute.strip();
   2646               };
   2647             }
   2648             el = null;
   2649             return f;
   2650           })(),
   2651           _flag: function(element, attribute) {
   2652             return $(element).hasAttribute(attribute) ? attribute : null;
   2653           },
   2654           style: function(element) {
   2655             return element.style.cssText.toLowerCase();
   2656           },
   2657           title: function(element) {
   2658             return element.title;
   2659           }
   2660         }
   2661       }
   2662     }
   2663   })();
   2664 
   2665   Element._attributeTranslations.write = {
   2666     names: Object.extend({
   2667       cellpadding: 'cellPadding',
   2668       cellspacing: 'cellSpacing'
   2669     }, Element._attributeTranslations.read.names),
   2670     values: {
   2671       checked: function(element, value) {
   2672         element.checked = !!value;
   2673       },
   2674 
   2675       style: function(element, value) {
   2676         element.style.cssText = value ? value : '';
   2677       }
   2678     }
   2679   };
   2680 
   2681   Element._attributeTranslations.has = {};
   2682 
   2683   $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
   2684       'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
   2685     Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
   2686     Element._attributeTranslations.has[attr.toLowerCase()] = attr;
   2687   });
   2688 
   2689   (function(v) {
   2690     Object.extend(v, {
   2691       href:        v._getAttr2,
   2692       src:         v._getAttr2,
   2693       type:        v._getAttr,
   2694       action:      v._getAttrNode,
   2695       disabled:    v._flag,
   2696       checked:     v._flag,
   2697       readonly:    v._flag,
   2698       multiple:    v._flag,
   2699       onload:      v._getEv,
   2700       onunload:    v._getEv,
   2701       onclick:     v._getEv,
   2702       ondblclick:  v._getEv,
   2703       onmousedown: v._getEv,
   2704       onmouseup:   v._getEv,
   2705       onmouseover: v._getEv,
   2706       onmousemove: v._getEv,
   2707       onmouseout:  v._getEv,
   2708       onfocus:     v._getEv,
   2709       onblur:      v._getEv,
   2710       onkeypress:  v._getEv,
   2711       onkeydown:   v._getEv,
   2712       onkeyup:     v._getEv,
   2713       onsubmit:    v._getEv,
   2714       onreset:     v._getEv,
   2715       onselect:    v._getEv,
   2716       onchange:    v._getEv
   2717     });
   2718   })(Element._attributeTranslations.read.values);
   2719 
   2720   if (Prototype.BrowserFeatures.ElementExtensions) {
   2721     (function() {
   2722       function _descendants(element) {
   2723         var nodes = element.getElementsByTagName('*'), results = [];
   2724         for (var i = 0, node; node = nodes[i]; i++)
   2725           if (node.tagName !== "!") // Filter out comment nodes.
   2726             results.push(node);
   2727         return results;
   2728       }
   2729 
   2730       Element.Methods.down = function(element, expression, index) {
   2731         element = $(element);
   2732         if (arguments.length == 1) return element.firstDescendant();
   2733         return Object.isNumber(expression) ? _descendants(element)[expression] :
   2734           Element.select(element, expression)[index || 0];
   2735       }
   2736     })();
   2737   }
   2738 
   2739 }
   2740 
   2741 else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
   2742   Element.Methods.setOpacity = function(element, value) {
   2743     element = $(element);
   2744     element.style.opacity = (value == 1) ? 0.999999 :
   2745       (value === '') ? '' : (value < 0.00001) ? 0 : value;
   2746     return element;
   2747   };
   2748 }
   2749 
   2750 else if (Prototype.Browser.WebKit) {
   2751   Element.Methods.setOpacity = function(element, value) {
   2752     element = $(element);
   2753     element.style.opacity = (value == 1 || value === '') ? '' :
   2754       (value < 0.00001) ? 0 : value;
   2755 
   2756     if (value == 1)
   2757       if (element.tagName.toUpperCase() == 'IMG' && element.width) {
   2758         element.width++; element.width--;
   2759       } else try {
   2760         var n = document.createTextNode(' ');
   2761         element.appendChild(n);
   2762         element.removeChild(n);
   2763       } catch (e) { }
   2764 
   2765     return element;
   2766   };
   2767 }
   2768 
   2769 if ('outerHTML' in document.documentElement) {
   2770   Element.Methods.replace = function(element, content) {
   2771     element = $(element);
   2772 
   2773     if (content && content.toElement) content = content.toElement();
   2774     if (Object.isElement(content)) {
   2775       element.parentNode.replaceChild(content, element);
   2776       return element;
   2777     }
   2778 
   2779     content = Object.toHTML(content);
   2780     var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
   2781 
   2782     if (Element._insertionTranslations.tags[tagName]) {
   2783       var nextSibling = element.next(),
   2784           fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
   2785       parent.removeChild(element);
   2786       if (nextSibling)
   2787         fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
   2788       else
   2789         fragments.each(function(node) { parent.appendChild(node) });
   2790     }
   2791     else element.outerHTML = content.stripScripts();
   2792 
   2793     content.evalScripts.bind(content).defer();
   2794     return element;
   2795   };
   2796 }
   2797 
   2798 Element._returnOffset = function(l, t) {
   2799   var result = [l, t];
   2800   result.left = l;
   2801   result.top = t;
   2802   return result;
   2803 };
   2804 
   2805 Element._getContentFromAnonymousElement = function(tagName, html, force) {
   2806   var div = new Element('div'),
   2807       t = Element._insertionTranslations.tags[tagName];
   2808 
   2809   var workaround = false;
   2810   if (t) workaround = true;
   2811   else if (force) {
   2812     workaround = true;
   2813     t = ['', '', 0];
   2814   }
   2815 
   2816   if (workaround) {
   2817     div.innerHTML = '&nbsp;' + t[0] + html + t[1];
   2818     div.removeChild(div.firstChild);
   2819     for (var i = t[2]; i--; ) {
   2820       div = div.firstChild;
   2821     }
   2822   }
   2823   else {
   2824     div.innerHTML = html;
   2825   }
   2826   return $A(div.childNodes);
   2827 };
   2828 
   2829 Element._insertionTranslations = {
   2830   before: function(element, node) {
   2831     element.parentNode.insertBefore(node, element);
   2832   },
   2833   top: function(element, node) {
   2834     element.insertBefore(node, element.firstChild);
   2835   },
   2836   bottom: function(element, node) {
   2837     element.appendChild(node);
   2838   },
   2839   after: function(element, node) {
   2840     element.parentNode.insertBefore(node, element.nextSibling);
   2841   },
   2842   tags: {
   2843     TABLE:  ['<table>',                '</table>',                   1],
   2844     TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
   2845     TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
   2846     TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
   2847     SELECT: ['<select>',               '</select>',                  1]
   2848   }
   2849 };
   2850 
   2851 (function() {
   2852   var tags = Element._insertionTranslations.tags;
   2853   Object.extend(tags, {
   2854     THEAD: tags.TBODY,
   2855     TFOOT: tags.TBODY,
   2856     TH:    tags.TD
   2857   });
   2858 })();
   2859 
   2860 Element.Methods.Simulated = {
   2861   hasAttribute: function(element, attribute) {
   2862     attribute = Element._attributeTranslations.has[attribute] || attribute;
   2863     var node = $(element).getAttributeNode(attribute);
   2864     return !!(node && node.specified);
   2865   }
   2866 };
   2867 
   2868 Element.Methods.ByTag = { };
   2869 
   2870 Object.extend(Element, Element.Methods);
   2871 
   2872 (function(div) {
   2873 
   2874   if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
   2875     window.HTMLElement = { };
   2876     window.HTMLElement.prototype = div['__proto__'];
   2877     Prototype.BrowserFeatures.ElementExtensions = true;
   2878   }
   2879 
   2880   div = null;
   2881 
   2882 })(document.createElement('div'));
   2883 
   2884 Element.extend = (function() {
   2885 
   2886   function checkDeficiency(tagName) {
   2887     if (typeof window.Element != 'undefined') {
   2888       var proto = window.Element.prototype;
   2889       if (proto) {
   2890         var id = '_' + (Math.random()+'').slice(2),
   2891             el = document.createElement(tagName);
   2892         proto[id] = 'x';
   2893         var isBuggy = (el[id] !== 'x');
   2894         delete proto[id];
   2895         el = null;
   2896         return isBuggy;
   2897       }
   2898     }
   2899     return false;
   2900   }
   2901 
   2902   function extendElementWith(element, methods) {
   2903     for (var property in methods) {
   2904       var value = methods[property];
   2905       if (Object.isFunction(value) && !(property in element))
   2906         element[property] = value.methodize();
   2907     }
   2908   }
   2909 
   2910   var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
   2911 
   2912   if (Prototype.BrowserFeatures.SpecificElementExtensions) {
   2913     if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
   2914       return function(element) {
   2915         if (element && typeof element._extendedByPrototype == 'undefined') {
   2916           var t = element.tagName;
   2917           if (t && (/^(?:object|applet|embed)$/i.test(t))) {
   2918             extendElementWith(element, Element.Methods);
   2919             extendElementWith(element, Element.Methods.Simulated);
   2920             extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
   2921           }
   2922         }
   2923         return element;
   2924       }
   2925     }
   2926     return Prototype.K;
   2927   }
   2928 
   2929   var Methods = { }, ByTag = Element.Methods.ByTag;
   2930 
   2931   var extend = Object.extend(function(element) {
   2932     if (!element || typeof element._extendedByPrototype != 'undefined' ||
   2933         element.nodeType != 1 || element == window) return element;
   2934 
   2935     var methods = Object.clone(Methods),
   2936         tagName = element.tagName.toUpperCase();
   2937 
   2938     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
   2939 
   2940     extendElementWith(element, methods);
   2941 
   2942     element._extendedByPrototype = Prototype.emptyFunction;
   2943     return element;
   2944 
   2945   }, {
   2946     refresh: function() {
   2947       if (!Prototype.BrowserFeatures.ElementExtensions) {
   2948         Object.extend(Methods, Element.Methods);
   2949         Object.extend(Methods, Element.Methods.Simulated);
   2950       }
   2951     }
   2952   });
   2953 
   2954   extend.refresh();
   2955   return extend;
   2956 })();
   2957 
   2958 if (document.documentElement.hasAttribute) {
   2959   Element.hasAttribute = function(element, attribute) {
   2960     return element.hasAttribute(attribute);
   2961   };
   2962 }
   2963 else {
   2964   Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
   2965 }
   2966 
   2967 Element.addMethods = function(methods) {
   2968   var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
   2969 
   2970   if (!methods) {
   2971     Object.extend(Form, Form.Methods);
   2972     Object.extend(Form.Element, Form.Element.Methods);
   2973     Object.extend(Element.Methods.ByTag, {
   2974       "FORM":     Object.clone(Form.Methods),
   2975       "INPUT":    Object.clone(Form.Element.Methods),
   2976       "SELECT":   Object.clone(Form.Element.Methods),
   2977       "TEXTAREA": Object.clone(Form.Element.Methods),
   2978       "BUTTON":   Object.clone(Form.Element.Methods)
   2979     });
   2980   }
   2981 
   2982   if (arguments.length == 2) {
   2983     var tagName = methods;
   2984     methods = arguments[1];
   2985   }
   2986 
   2987   if (!tagName) Object.extend(Element.Methods, methods || { });
   2988   else {
   2989     if (Object.isArray(tagName)) tagName.each(extend);
   2990     else extend(tagName);
   2991   }
   2992 
   2993   function extend(tagName) {
   2994     tagName = tagName.toUpperCase();
   2995     if (!Element.Methods.ByTag[tagName])
   2996       Element.Methods.ByTag[tagName] = { };
   2997     Object.extend(Element.Methods.ByTag[tagName], methods);
   2998   }
   2999 
   3000   function copy(methods, destination, onlyIfAbsent) {
   3001     onlyIfAbsent = onlyIfAbsent || false;
   3002     for (var property in methods) {
   3003       var value = methods[property];
   3004       if (!Object.isFunction(value)) continue;
   3005       if (!onlyIfAbsent || !(property in destination))
   3006         destination[property] = value.methodize();
   3007     }
   3008   }
   3009 
   3010   function findDOMClass(tagName) {
   3011     var klass;
   3012     var trans = {
   3013       "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
   3014       "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
   3015       "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
   3016       "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
   3017       "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
   3018       "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
   3019       "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
   3020       "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
   3021       "FrameSet", "IFRAME": "IFrame"
   3022     };
   3023     if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
   3024     if (window[klass]) return window[klass];
   3025     klass = 'HTML' + tagName + 'Element';
   3026     if (window[klass]) return window[klass];
   3027     klass = 'HTML' + tagName.capitalize() + 'Element';
   3028     if (window[klass]) return window[klass];
   3029 
   3030     var element = document.createElement(tagName),
   3031         proto = element['__proto__'] || element.constructor.prototype;
   3032 
   3033     element = null;
   3034     return proto;
   3035   }
   3036 
   3037   var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
   3038    Element.prototype;
   3039 
   3040   if (F.ElementExtensions) {
   3041     copy(Element.Methods, elementPrototype);
   3042     copy(Element.Methods.Simulated, elementPrototype, true);
   3043   }
   3044 
   3045   if (F.SpecificElementExtensions) {
   3046     for (var tag in Element.Methods.ByTag) {
   3047       var klass = findDOMClass(tag);
   3048       if (Object.isUndefined(klass)) continue;
   3049       copy(T[tag], klass.prototype);
   3050     }
   3051   }
   3052 
   3053   Object.extend(Element, Element.Methods);
   3054   delete Element.ByTag;
   3055 
   3056   if (Element.extend.refresh) Element.extend.refresh();
   3057   Element.cache = { };
   3058 };
   3059 
   3060 
   3061 document.viewport = {
   3062 
   3063   getDimensions: function() {
   3064     return { width: this.getWidth(), height: this.getHeight() };
   3065   },
   3066 
   3067   getScrollOffsets: function() {
   3068     return Element._returnOffset(
   3069       window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
   3070       window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
   3071   }
   3072 };
   3073 
   3074 (function(viewport) {
   3075   var B = Prototype.Browser, doc = document, element, property = {};
   3076 
   3077   function getRootElement() {
   3078     if (B.WebKit && !doc.evaluate)
   3079       return document;
   3080 
   3081     if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
   3082       return document.body;
   3083 
   3084     return document.documentElement;
   3085   }
   3086 
   3087   function define(D) {
   3088     if (!element) element = getRootElement();
   3089 
   3090     property[D] = 'client' + D;
   3091 
   3092     viewport['get' + D] = function() { return element[property[D]] };
   3093     return viewport['get' + D]();
   3094   }
   3095 
   3096   viewport.getWidth  = define.curry('Width');
   3097 
   3098   viewport.getHeight = define.curry('Height');
   3099 })(document.viewport);
   3100 
   3101 
   3102 Element.Storage = {
   3103   UID: 1
   3104 };
   3105 
   3106 Element.addMethods({
   3107   getStorage: function(element) {
   3108     if (!(element = $(element))) return;
   3109 
   3110     var uid;
   3111     if (element === window) {
   3112       uid = 0;
   3113     } else {
   3114       if (typeof element._prototypeUID === "undefined")
   3115         element._prototypeUID = Element.Storage.UID++;
   3116       uid = element._prototypeUID;
   3117     }
   3118 
   3119     if (!Element.Storage[uid])
   3120       Element.Storage[uid] = $H();
   3121 
   3122     return Element.Storage[uid];
   3123   },
   3124 
   3125   store: function(element, key, value) {
   3126     if (!(element = $(element))) return;
   3127 
   3128     if (arguments.length === 2) {
   3129       Element.getStorage(element).update(key);
   3130     } else {
   3131       Element.getStorage(element).set(key, value);
   3132     }
   3133 
   3134     return element;
   3135   },
   3136 
   3137   retrieve: function(element, key, defaultValue) {
   3138     if (!(element = $(element))) return;
   3139     var hash = Element.getStorage(element), value = hash.get(key);
   3140 
   3141     if (Object.isUndefined(value)) {
   3142       hash.set(key, defaultValue);
   3143       value = defaultValue;
   3144     }
   3145 
   3146     return value;
   3147   },
   3148 
   3149   clone: function(element, deep) {
   3150     if (!(element = $(element))) return;
   3151     var clone = element.cloneNode(deep);
   3152     clone._prototypeUID = void 0;
   3153     if (deep) {
   3154       var descendants = Element.select(clone, '*'),
   3155           i = descendants.length;
   3156       while (i--) {
   3157         descendants[i]._prototypeUID = void 0;
   3158       }
   3159     }
   3160     return Element.extend(clone);
   3161   },
   3162 
   3163   purge: function(element) {
   3164     if (!(element = $(element))) return;
   3165     var purgeElement = Element._purgeElement;
   3166 
   3167     purgeElement(element);
   3168 
   3169     var descendants = element.getElementsByTagName('*'),
   3170      i = descendants.length;
   3171 
   3172     while (i--) purgeElement(descendants[i]);
   3173 
   3174     return null;
   3175   }
   3176 });
   3177 
   3178 (function() {
   3179 
   3180   function toDecimal(pctString) {
   3181     var match = pctString.match(/^(\d+)%?$/i);
   3182     if (!match) return null;
   3183     return (Number(match[1]) / 100);
   3184   }
   3185 
   3186   function getPixelValue(value, property, context) {
   3187     var element = null;
   3188     if (Object.isElement(value)) {
   3189       element = value;
   3190       value = element.getStyle(property);
   3191     }
   3192 
   3193     if (value === null) {
   3194       return null;
   3195     }
   3196 
   3197     if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
   3198       return window.parseFloat(value);
   3199     }
   3200 
   3201     var isPercentage = value.include('%'), isViewport = (context === document.viewport);
   3202 
   3203     if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
   3204       var style = element.style.left, rStyle = element.runtimeStyle.left;
   3205       element.runtimeStyle.left = element.currentStyle.left;
   3206       element.style.left = value || 0;
   3207       value = element.style.pixelLeft;
   3208       element.style.left = style;
   3209       element.runtimeStyle.left = rStyle;
   3210 
   3211       return value;
   3212     }
   3213 
   3214     if (element && isPercentage) {
   3215       context = context || element.parentNode;
   3216       var decimal = toDecimal(value);
   3217       var whole = null;
   3218       var position = element.getStyle('position');
   3219 
   3220       var isHorizontal = property.include('left') || property.include('right') ||
   3221        property.include('width');
   3222 
   3223       var isVertical =  property.include('top') || property.include('bottom') ||
   3224         property.include('height');
   3225 
   3226       if (context === document.viewport) {
   3227         if (isHorizontal) {
   3228           whole = document.viewport.getWidth();
   3229         } else if (isVertical) {
   3230           whole = document.viewport.getHeight();
   3231         }
   3232       } else {
   3233         if (isHorizontal) {
   3234           whole = $(context).measure('width');
   3235         } else if (isVertical) {
   3236           whole = $(context).measure('height');
   3237         }
   3238       }
   3239 
   3240       return (whole === null) ? 0 : whole * decimal;
   3241     }
   3242 
   3243     return 0;
   3244   }
   3245 
   3246   function toCSSPixels(number) {
   3247     if (Object.isString(number) && number.endsWith('px')) {
   3248       return number;
   3249     }
   3250     return number + 'px';
   3251   }
   3252 
   3253   function isDisplayed(element) {
   3254     var originalElement = element;
   3255     while (element && element.parentNode) {
   3256       var display = element.getStyle('display');
   3257       if (display === 'none') {
   3258         return false;
   3259       }
   3260       element = $(element.parentNode);
   3261     }
   3262     return true;
   3263   }
   3264 
   3265   var hasLayout = Prototype.K;
   3266   if ('currentStyle' in document.documentElement) {
   3267     hasLayout = function(element) {
   3268       if (!element.currentStyle.hasLayout) {
   3269         element.style.zoom = 1;
   3270       }
   3271       return element;
   3272     };
   3273   }
   3274 
   3275   function cssNameFor(key) {
   3276     if (key.include('border')) key = key + '-width';
   3277     return key.camelize();
   3278   }
   3279 
   3280   Element.Layout = Class.create(Hash, {
   3281     initialize: function($super, element, preCompute) {
   3282       $super();
   3283       this.element = $(element);
   3284 
   3285       Element.Layout.PROPERTIES.each( function(property) {
   3286         this._set(property, null);
   3287       }, this);
   3288 
   3289       if (preCompute) {
   3290         this._preComputing = true;
   3291         this._begin();
   3292         Element.Layout.PROPERTIES.each( this._compute, this );
   3293         this._end();
   3294         this._preComputing = false;
   3295       }
   3296     },
   3297 
   3298     _set: function(property, value) {
   3299       return Hash.prototype.set.call(this, property, value);
   3300     },
   3301 
   3302     set: function(property, value) {
   3303       throw "Properties of Element.Layout are read-only.";
   3304     },
   3305 
   3306     get: function($super, property) {
   3307       var value = $super(property);
   3308       return value === null ? this._compute(property) : value;
   3309     },
   3310 
   3311     _begin: function() {
   3312       if (this._prepared) return;
   3313 
   3314       var element = this.element;
   3315       if (isDisplayed(element)) {
   3316         this._prepared = true;
   3317         return;
   3318       }
   3319 
   3320       var originalStyles = {
   3321         position:   element.style.position   || '',
   3322         width:      element.style.width      || '',
   3323         visibility: element.style.visibility || '',
   3324         display:    element.style.display    || ''
   3325       };
   3326 
   3327       element.store('prototype_original_styles', originalStyles);
   3328 
   3329       var position = element.getStyle('position'),
   3330        width = element.getStyle('width');
   3331 
   3332       if (width === "0px" || width === null) {
   3333         element.style.display = 'block';
   3334         width = element.getStyle('width');
   3335       }
   3336 
   3337       var context = (position === 'fixed') ? document.viewport :
   3338        element.parentNode;
   3339 
   3340       element.setStyle({
   3341         position:   'absolute',
   3342         visibility: 'hidden',
   3343         display:    'block'
   3344       });
   3345 
   3346       var positionedWidth = element.getStyle('width');
   3347 
   3348       var newWidth;
   3349       if (width && (positionedWidth === width)) {
   3350         newWidth = getPixelValue(element, 'width', context);
   3351       } else if (position === 'absolute' || position === 'fixed') {
   3352         newWidth = getPixelValue(element, 'width', context);
   3353       } else {
   3354         var parent = element.parentNode, pLayout = $(parent).getLayout();
   3355 
   3356         newWidth = pLayout.get('width') -
   3357          this.get('margin-left') -
   3358          this.get('border-left') -
   3359          this.get('padding-left') -
   3360          this.get('padding-right') -
   3361          this.get('border-right') -
   3362          this.get('margin-right');
   3363       }
   3364 
   3365       element.setStyle({ width: newWidth + 'px' });
   3366 
   3367       this._prepared = true;
   3368     },
   3369 
   3370     _end: function() {
   3371       var element = this.element;
   3372       var originalStyles = element.retrieve('prototype_original_styles');
   3373       element.store('prototype_original_styles', null);
   3374       element.setStyle(originalStyles);
   3375       this._prepared = false;
   3376     },
   3377 
   3378     _compute: function(property) {
   3379       var COMPUTATIONS = Element.Layout.COMPUTATIONS;
   3380       if (!(property in COMPUTATIONS)) {
   3381         throw "Property not found.";
   3382       }
   3383 
   3384       return this._set(property, COMPUTATIONS[property].call(this, this.element));
   3385     },
   3386 
   3387     toObject: function() {
   3388       var args = $A(arguments);
   3389       var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
   3390        args.join(' ').split(' ');
   3391       var obj = {};
   3392       keys.each( function(key) {
   3393         if (!Element.Layout.PROPERTIES.include(key)) return;
   3394         var value = this.get(key);
   3395         if (value != null) obj[key] = value;
   3396       }, this);
   3397       return obj;
   3398     },
   3399 
   3400     toHash: function() {
   3401       var obj = this.toObject.apply(this, arguments);
   3402       return new Hash(obj);
   3403     },
   3404 
   3405     toCSS: function() {
   3406       var args = $A(arguments);
   3407       var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
   3408        args.join(' ').split(' ');
   3409       var css = {};
   3410 
   3411       keys.each( function(key) {
   3412         if (!Element.Layout.PROPERTIES.include(key)) return;
   3413         if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
   3414 
   3415         var value = this.get(key);
   3416         if (value != null) css[cssNameFor(key)] = value + 'px';
   3417       }, this);
   3418       return css;
   3419     },
   3420 
   3421     inspect: function() {
   3422       return "#<Element.Layout>";
   3423     }
   3424   });
   3425 
   3426   Object.extend(Element.Layout, {
   3427     PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
   3428 
   3429     COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
   3430 
   3431     COMPUTATIONS: {
   3432       'height': function(element) {
   3433         if (!this._preComputing) this._begin();
   3434 
   3435         var bHeight = this.get('border-box-height');
   3436         if (bHeight <= 0) {
   3437           if (!this._preComputing) this._end();
   3438           return 0;
   3439         }
   3440 
   3441         var bTop = this.get('border-top'),
   3442          bBottom = this.get('border-bottom');
   3443 
   3444         var pTop = this.get('padding-top'),
   3445          pBottom = this.get('padding-bottom');
   3446 
   3447         if (!this._preComputing) this._end();
   3448 
   3449         return bHeight - bTop - bBottom - pTop - pBottom;
   3450       },
   3451 
   3452       'width': function(element) {
   3453         if (!this._preComputing) this._begin();
   3454 
   3455         var bWidth = this.get('border-box-width');
   3456         if (bWidth <= 0) {
   3457           if (!this._preComputing) this._end();
   3458           return 0;
   3459         }
   3460 
   3461         var bLeft = this.get('border-left'),
   3462          bRight = this.get('border-right');
   3463 
   3464         var pLeft = this.get('padding-left'),
   3465          pRight = this.get('padding-right');
   3466 
   3467         if (!this._preComputing) this._end();
   3468 
   3469         return bWidth - bLeft - bRight - pLeft - pRight;
   3470       },
   3471 
   3472       'padding-box-height': function(element) {
   3473         var height = this.get('height'),
   3474          pTop = this.get('padding-top'),
   3475          pBottom = this.get('padding-bottom');
   3476 
   3477         return height + pTop + pBottom;
   3478       },
   3479 
   3480       'padding-box-width': function(element) {
   3481         var width = this.get('width'),
   3482          pLeft = this.get('padding-left'),
   3483          pRight = this.get('padding-right');
   3484 
   3485         return width + pLeft + pRight;
   3486       },
   3487 
   3488       'border-box-height': function(element) {
   3489         if (!this._preComputing) this._begin();
   3490         var height = element.offsetHeight;
   3491         if (!this._preComputing) this._end();
   3492         return height;
   3493       },
   3494 
   3495       'border-box-width': function(element) {
   3496         if (!this._preComputing) this._begin();
   3497         var width = element.offsetWidth;
   3498         if (!this._preComputing) this._end();
   3499         return width;
   3500       },
   3501 
   3502       'margin-box-height': function(element) {
   3503         var bHeight = this.get('border-box-height'),
   3504          mTop = this.get('margin-top'),
   3505          mBottom = this.get('margin-bottom');
   3506 
   3507         if (bHeight <= 0) return 0;
   3508 
   3509         return bHeight + mTop + mBottom;
   3510       },
   3511 
   3512       'margin-box-width': function(element) {
   3513         var bWidth = this.get('border-box-width'),
   3514          mLeft = this.get('margin-left'),
   3515          mRight = this.get('margin-right');
   3516 
   3517         if (bWidth <= 0) return 0;
   3518 
   3519         return bWidth + mLeft + mRight;
   3520       },
   3521 
   3522       'top': function(element) {
   3523         var offset = element.positionedOffset();
   3524         return offset.top;
   3525       },
   3526 
   3527       'bottom': function(element) {
   3528         var offset = element.positionedOffset(),
   3529          parent = element.getOffsetParent(),
   3530          pHeight = parent.measure('height');
   3531 
   3532         var mHeight = this.get('border-box-height');
   3533 
   3534         return pHeight - mHeight - offset.top;
   3535       },
   3536 
   3537       'left': function(element) {
   3538         var offset = element.positionedOffset();
   3539         return offset.left;
   3540       },
   3541 
   3542       'right': function(element) {
   3543         var offset = element.positionedOffset(),
   3544          parent = element.getOffsetParent(),
   3545          pWidth = parent.measure('width');
   3546 
   3547         var mWidth = this.get('border-box-width');
   3548 
   3549         return pWidth - mWidth - offset.left;
   3550       },
   3551 
   3552       'padding-top': function(element) {
   3553         return getPixelValue(element, 'paddingTop');
   3554       },
   3555 
   3556       'padding-bottom': function(element) {
   3557         return getPixelValue(element, 'paddingBottom');
   3558       },
   3559 
   3560       'padding-left': function(element) {
   3561         return getPixelValue(element, 'paddingLeft');
   3562       },
   3563 
   3564       'padding-right': function(element) {
   3565         return getPixelValue(element, 'paddingRight');
   3566       },
   3567 
   3568       'border-top': function(element) {
   3569         return getPixelValue(element, 'borderTopWidth');
   3570       },
   3571 
   3572       'border-bottom': function(element) {
   3573         return getPixelValue(element, 'borderBottomWidth');
   3574       },
   3575 
   3576       'border-left': function(element) {
   3577         return getPixelValue(element, 'borderLeftWidth');
   3578       },
   3579 
   3580       'border-right': function(element) {
   3581         return getPixelValue(element, 'borderRightWidth');
   3582       },
   3583 
   3584       'margin-top': function(element) {
   3585         return getPixelValue(element, 'marginTop');
   3586       },
   3587 
   3588       'margin-bottom': function(element) {
   3589         return getPixelValue(element, 'marginBottom');
   3590       },
   3591 
   3592       'margin-left': function(element) {
   3593         return getPixelValue(element, 'marginLeft');
   3594       },
   3595 
   3596       'margin-right': function(element) {
   3597         return getPixelValue(element, 'marginRight');
   3598       }
   3599     }
   3600   });
   3601 
   3602   if ('getBoundingClientRect' in document.documentElement) {
   3603     Object.extend(Element.Layout.COMPUTATIONS, {
   3604       'right': function(element) {
   3605         var parent = hasLayout(element.getOffsetParent());
   3606         var rect = element.getBoundingClientRect(),
   3607          pRect = parent.getBoundingClientRect();
   3608 
   3609         return (pRect.right - rect.right).round();
   3610       },
   3611 
   3612       'bottom': function(element) {
   3613         var parent = hasLayout(element.getOffsetParent());
   3614         var rect = element.getBoundingClientRect(),
   3615          pRect = parent.getBoundingClientRect();
   3616 
   3617         return (pRect.bottom - rect.bottom).round();
   3618       }
   3619     });
   3620   }
   3621 
   3622   Element.Offset = Class.create({
   3623     initialize: function(left, top) {
   3624       this.left = left.round();
   3625       this.top  = top.round();
   3626 
   3627       this[0] = this.left;
   3628       this[1] = this.top;
   3629     },
   3630 
   3631     relativeTo: function(offset) {
   3632       return new Element.Offset(
   3633         this.left - offset.left,
   3634         this.top  - offset.top
   3635       );
   3636     },
   3637 
   3638     inspect: function() {
   3639       return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
   3640     },
   3641 
   3642     toString: function() {
   3643       return "[#{left}, #{top}]".interpolate(this);
   3644     },
   3645 
   3646     toArray: function() {
   3647       return [this.left, this.top];
   3648     }
   3649   });
   3650 
   3651   function getLayout(element, preCompute) {
   3652     return new Element.Layout(element, preCompute);
   3653   }
   3654 
   3655   function measure(element, property) {
   3656     return $(element).getLayout().get(property);
   3657   }
   3658 
   3659   function getDimensions(element) {
   3660     element = $(element);
   3661     var display = Element.getStyle(element, 'display');
   3662 
   3663     if (display && display !== 'none') {
   3664       return { width: element.offsetWidth, height: element.offsetHeight };
   3665     }
   3666 
   3667     var style = element.style;
   3668     var originalStyles = {
   3669       visibility: style.visibility,
   3670       position:   style.position,
   3671       display:    style.display
   3672     };
   3673 
   3674     var newStyles = {
   3675       visibility: 'hidden',
   3676       display:    'block'
   3677     };
   3678 
   3679     if (originalStyles.position !== 'fixed')
   3680       newStyles.position = 'absolute';
   3681 
   3682     Element.setStyle(element, newStyles);
   3683 
   3684     var dimensions = {
   3685       width:  element.offsetWidth,
   3686       height: element.offsetHeight
   3687     };
   3688 
   3689     Element.setStyle(element, originalStyles);
   3690 
   3691     return dimensions;
   3692   }
   3693 
   3694   function getOffsetParent(element) {
   3695     element = $(element);
   3696 
   3697     if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
   3698       return $(document.body);
   3699 
   3700     var isInline = (Element.getStyle(element, 'display') === 'inline');
   3701     if (!isInline && element.offsetParent) return $(element.offsetParent);
   3702 
   3703     while ((element = element.parentNode) && element !== document.body) {
   3704       if (Element.getStyle(element, 'position') !== 'static') {
   3705         return isHtml(element) ? $(document.body) : $(element);
   3706       }
   3707     }
   3708 
   3709     return $(document.body);
   3710   }
   3711 
   3712 
   3713   function cumulativeOffset(element) {
   3714     element = $(element);
   3715     var valueT = 0, valueL = 0;
   3716     if (element.parentNode) {
   3717       do {
   3718         valueT += element.offsetTop  || 0;
   3719         valueL += element.offsetLeft || 0;
   3720         element = element.offsetParent;
   3721       } while (element);
   3722     }
   3723     return new Element.Offset(valueL, valueT);
   3724   }
   3725 
   3726   function positionedOffset(element) {
   3727     element = $(element);
   3728 
   3729     var layout = element.getLayout();
   3730 
   3731     var valueT = 0, valueL = 0;
   3732     do {
   3733       valueT += element.offsetTop  || 0;
   3734       valueL += element.offsetLeft || 0;
   3735       element = element.offsetParent;
   3736       if (element) {
   3737         if (isBody(element)) break;
   3738         var p = Element.getStyle(element, 'position');
   3739         if (p !== 'static') break;
   3740       }
   3741     } while (element);
   3742 
   3743     valueL -= layout.get('margin-top');
   3744     valueT -= layout.get('margin-left');
   3745 
   3746     return new Element.Offset(valueL, valueT);
   3747   }
   3748 
   3749   function cumulativeScrollOffset(element) {
   3750     var valueT = 0, valueL = 0;
   3751     do {
   3752       valueT += element.scrollTop  || 0;
   3753       valueL += element.scrollLeft || 0;
   3754       element = element.parentNode;
   3755     } while (element);
   3756     return new Element.Offset(valueL, valueT);
   3757   }
   3758 
   3759   function viewportOffset(forElement) {
   3760     element = $(element);
   3761     var valueT = 0, valueL = 0, docBody = document.body;
   3762 
   3763     var element = forElement;
   3764     do {
   3765       valueT += element.offsetTop  || 0;
   3766       valueL += element.offsetLeft || 0;
   3767       if (element.offsetParent == docBody &&
   3768         Element.getStyle(element, 'position') == 'absolute') break;
   3769     } while (element = element.offsetParent);
   3770 
   3771     element = forElement;
   3772     do {
   3773       if (element != docBody) {
   3774         valueT -= element.scrollTop  || 0;
   3775         valueL -= element.scrollLeft || 0;
   3776       }
   3777     } while (element = element.parentNode);
   3778     return new Element.Offset(valueL, valueT);
   3779   }
   3780 
   3781   function absolutize(element) {
   3782     element = $(element);
   3783 
   3784     if (Element.getStyle(element, 'position') === 'absolute') {
   3785       return element;
   3786     }
   3787 
   3788     var offsetParent = getOffsetParent(element);
   3789     var eOffset = element.viewportOffset(),
   3790      pOffset = offsetParent.viewportOffset();
   3791 
   3792     var offset = eOffset.relativeTo(pOffset);
   3793     var layout = element.getLayout();
   3794 
   3795     element.store('prototype_absolutize_original_styles', {
   3796       left:   element.getStyle('left'),
   3797       top:    element.getStyle('top'),
   3798       width:  element.getStyle('width'),
   3799       height: element.getStyle('height')
   3800     });
   3801 
   3802     element.setStyle({
   3803       position: 'absolute',
   3804       top:    offset.top + 'px',
   3805       left:   offset.left + 'px',
   3806       width:  layout.get('width') + 'px',
   3807       height: layout.get('height') + 'px'
   3808     });
   3809 
   3810     return element;
   3811   }
   3812 
   3813   function relativize(element) {
   3814     element = $(element);
   3815     if (Element.getStyle(element, 'position') === 'relative') {
   3816       return element;
   3817     }
   3818 
   3819     var originalStyles =
   3820      element.retrieve('prototype_absolutize_original_styles');
   3821 
   3822     if (originalStyles) element.setStyle(originalStyles);
   3823     return element;
   3824   }
   3825 
   3826   if (Prototype.Browser.IE) {
   3827     getOffsetParent = getOffsetParent.wrap(
   3828       function(proceed, element) {
   3829         element = $(element);
   3830 
   3831         if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
   3832           return $(document.body);
   3833 
   3834         var position = element.getStyle('position');
   3835         if (position !== 'static') return proceed(element);
   3836 
   3837         element.setStyle({ position: 'relative' });
   3838         var value = proceed(element);
   3839         element.setStyle({ position: position });
   3840         return value;
   3841       }
   3842     );
   3843 
   3844     positionedOffset = positionedOffset.wrap(function(proceed, element) {
   3845       element = $(element);
   3846       if (!element.parentNode) return new Element.Offset(0, 0);
   3847       var position = element.getStyle('position');
   3848       if (position !== 'static') return proceed(element);
   3849 
   3850       var offsetParent = element.getOffsetParent();
   3851       if (offsetParent && offsetParent.getStyle('position') === 'fixed')
   3852         hasLayout(offsetParent);
   3853 
   3854       element.setStyle({ position: 'relative' });
   3855       var value = proceed(element);
   3856       element.setStyle({ position: position });
   3857       return value;
   3858     });
   3859   } else if (Prototype.Browser.Webkit) {
   3860     cumulativeOffset = function(element) {
   3861       element = $(element);
   3862       var valueT = 0, valueL = 0;
   3863       do {
   3864         valueT += element.offsetTop  || 0;
   3865         valueL += element.offsetLeft || 0;
   3866         if (element.offsetParent == document.body)
   3867           if (Element.getStyle(element, 'position') == 'absolute') break;
   3868 
   3869         element = element.offsetParent;
   3870       } while (element);
   3871 
   3872       return new Element.Offset(valueL, valueT);
   3873     };
   3874   }
   3875 
   3876 
   3877   Element.addMethods({
   3878     getLayout:              getLayout,
   3879     measure:                measure,
   3880     getDimensions:          getDimensions,
   3881     getOffsetParent:        getOffsetParent,
   3882     cumulativeOffset:       cumulativeOffset,
   3883     positionedOffset:       positionedOffset,
   3884     cumulativeScrollOffset: cumulativeScrollOffset,
   3885     viewportOffset:         viewportOffset,
   3886     absolutize:             absolutize,
   3887     relativize:             relativize
   3888   });
   3889 
   3890   function isBody(element) {
   3891     return element.nodeName.toUpperCase() === 'BODY';
   3892   }
   3893 
   3894   function isHtml(element) {
   3895     return element.nodeName.toUpperCase() === 'HTML';
   3896   }
   3897 
   3898   function isDocument(element) {
   3899     return element.nodeType === Node.DOCUMENT_NODE;
   3900   }
   3901 
   3902   function isDetached(element) {
   3903     return element !== document.body &&
   3904      !Element.descendantOf(element, document.body);
   3905   }
   3906 
   3907   if ('getBoundingClientRect' in document.documentElement) {
   3908     Element.addMethods({
   3909       viewportOffset: function(element) {
   3910         element = $(element);
   3911         if (isDetached(element)) return new Element.Offset(0, 0);
   3912 
   3913         var rect = element.getBoundingClientRect(),
   3914          docEl = document.documentElement;
   3915         return new Element.Offset(rect.left - docEl.clientLeft,
   3916          rect.top - docEl.clientTop);
   3917       }
   3918     });
   3919   }
   3920 })();
   3921 window.$$ = function() {
   3922   var expression = $A(arguments).join(', ');
   3923   return Prototype.Selector.select(expression, document);
   3924 };
   3925 
   3926 Prototype.Selector = (function() {
   3927 
   3928   function select() {
   3929     throw new Error('Method "Prototype.Selector.select" must be defined.');
   3930   }
   3931 
   3932   function match() {
   3933     throw new Error('Method "Prototype.Selector.match" must be defined.');
   3934   }
   3935 
   3936   function find(elements, expression, index) {
   3937     index = index || 0;
   3938     var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
   3939 
   3940     for (i = 0; i < length; i++) {
   3941       if (match(elements[i], expression) && index == matchIndex++) {
   3942         return Element.extend(elements[i]);
   3943       }
   3944     }
   3945   }
   3946 
   3947   function extendElements(elements) {
   3948     for (var i = 0, length = elements.length; i < length; i++) {
   3949       Element.extend(elements[i]);
   3950     }
   3951     return elements;
   3952   }
   3953 
   3954 
   3955   var K = Prototype.K;
   3956 
   3957   return {
   3958     select: select,
   3959     match: match,
   3960     find: find,
   3961     extendElements: (Element.extend === K) ? K : extendElements,
   3962     extendElement: Element.extend
   3963   };
   3964 })();
   3965 Prototype._original_property = window.Sizzle;
   3966 /*!
   3967  * Sizzle CSS Selector Engine - v1.0
   3968  *  Copyright 2009, The Dojo Foundation
   3969  *  Released under the MIT, BSD, and GPL Licenses.
   3970  *  More information: http://sizzlejs.com/
   3971  */
   3972 (function(){
   3973 
   3974 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
   3975         done = 0,
   3976         toString = Object.prototype.toString,
   3977         hasDuplicate = false,
   3978         baseHasDuplicate = true;
   3979 
   3980 [0, 0].sort(function(){
   3981         baseHasDuplicate = false;
   3982         return 0;
   3983 });
   3984 
   3985 var Sizzle = function(selector, context, results, seed) {
   3986         results = results || [];
   3987         var origContext = context = context || document;
   3988 
   3989         if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
   3990                 return [];
   3991         }
   3992 
   3993         if ( !selector || typeof selector !== "string" ) {
   3994                 return results;
   3995         }
   3996 
   3997         var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
   3998                 soFar = selector;
   3999 
   4000         while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
   4001                 soFar = m[3];
   4002 
   4003                 parts.push( m[1] );
   4004 
   4005                 if ( m[2] ) {
   4006                         extra = m[3];
   4007                         break;
   4008                 }
   4009         }
   4010 
   4011         if ( parts.length > 1 && origPOS.exec( selector ) ) {
   4012                 if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
   4013                         set = posProcess( parts[0] + parts[1], context );
   4014                 } else {
   4015                         set = Expr.relative[ parts[0] ] ?
   4016                                 [ context ] :
   4017                                 Sizzle( parts.shift(), context );
   4018 
   4019                         while ( parts.length ) {
   4020                                 selector = parts.shift();
   4021 
   4022                                 if ( Expr.relative[ selector ] )
   4023                                         selector += parts.shift();
   4024 
   4025                                 set = posProcess( selector, set );
   4026                         }
   4027                 }
   4028         } else {
   4029                 if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
   4030                                 Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
   4031                         var ret = Sizzle.find( parts.shift(), context, contextXML );
   4032                         context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
   4033                 }
   4034 
   4035                 if ( context ) {
   4036                         var ret = seed ?
   4037                                 { expr: parts.pop(), set: makeArray(seed) } :
   4038                                 Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
   4039                         set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
   4040 
   4041                         if ( parts.length > 0 ) {
   4042                                 checkSet = makeArray(set);
   4043                         } else {
   4044                                 prune = false;
   4045                         }
   4046 
   4047                         while ( parts.length ) {
   4048                                 var cur = parts.pop(), pop = cur;
   4049 
   4050                                 if ( !Expr.relative[ cur ] ) {
   4051                                         cur = "";
   4052                                 } else {
   4053                                         pop = parts.pop();
   4054                                 }
   4055 
   4056                                 if ( pop == null ) {
   4057                                         pop = context;
   4058                                 }
   4059 
   4060                                 Expr.relative[ cur ]( checkSet, pop, contextXML );
   4061                         }
   4062                 } else {
   4063                         checkSet = parts = [];
   4064                 }
   4065         }
   4066 
   4067         if ( !checkSet ) {
   4068                 checkSet = set;
   4069         }
   4070 
   4071         if ( !checkSet ) {
   4072                 throw "Syntax error, unrecognized expression: " + (cur || selector);
   4073         }
   4074 
   4075         if ( toString.call(checkSet) === "[object Array]" ) {
   4076                 if ( !prune ) {
   4077                         results.push.apply( results, checkSet );
   4078                 } else if ( context && context.nodeType === 1 ) {
   4079                         for ( var i = 0; checkSet[i] != null; i++ ) {
   4080                                 if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
   4081                                         results.push( set[i] );
   4082                                 }
   4083                         }
   4084                 } else {
   4085                         for ( var i = 0; checkSet[i] != null; i++ ) {
   4086                                 if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
   4087                                         results.push( set[i] );
   4088                                 }
   4089                         }
   4090                 }
   4091         } else {
   4092                 makeArray( checkSet, results );
   4093         }
   4094 
   4095         if ( extra ) {
   4096                 Sizzle( extra, origContext, results, seed );
   4097                 Sizzle.uniqueSort( results );
   4098         }
   4099 
   4100         return results;
   4101 };
   4102 
   4103 Sizzle.uniqueSort = function(results){
   4104         if ( sortOrder ) {
   4105                 hasDuplicate = baseHasDuplicate;
   4106                 results.sort(sortOrder);
   4107 
   4108                 if ( hasDuplicate ) {
   4109                         for ( var i = 1; i < results.length; i++ ) {
   4110                                 if ( results[i] === results[i-1] ) {
   4111                                         results.splice(i--, 1);
   4112                                 }
   4113                         }
   4114                 }
   4115         }
   4116 
   4117         return results;
   4118 };
   4119 
   4120 Sizzle.matches = function(expr, set){
   4121         return Sizzle(expr, null, null, set);
   4122 };
   4123 
   4124 Sizzle.find = function(expr, context, isXML){
   4125         var set, match;
   4126 
   4127         if ( !expr ) {
   4128                 return [];
   4129         }
   4130 
   4131         for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
   4132                 var type = Expr.order[i], match;
   4133 
   4134                 if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
   4135                         var left = match[1];
   4136                         match.splice(1,1);
   4137 
   4138                         if ( left.substr( left.length - 1 ) !== "\\" ) {
   4139                                 match[1] = (match[1] || "").replace(/\\/g, "");
   4140                                 set = Expr.find[ type ]( match, context, isXML );
   4141                                 if ( set != null ) {
   4142                                         expr = expr.replace( Expr.match[ type ], "" );
   4143                                         break;
   4144                                 }
   4145                         }
   4146                 }
   4147         }
   4148 
   4149         if ( !set ) {
   4150                 set = context.getElementsByTagName("*");
   4151         }
   4152 
   4153         return {set: set, expr: expr};
   4154 };
   4155 
   4156 Sizzle.filter = function(expr, set, inplace, not){
   4157         var old = expr, result = [], curLoop = set, match, anyFound,
   4158                 isXMLFilter = set && set[0] && isXML(set[0]);
   4159 
   4160         while ( expr && set.length ) {
   4161                 for ( var type in Expr.filter ) {
   4162                         if ( (match = Expr.match[ type ].exec( expr )) != null ) {
   4163                                 var filter = Expr.filter[ type ], found, item;
   4164                                 anyFound = false;
   4165 
   4166                                 if ( curLoop == result ) {
   4167                                         result = [];
   4168                                 }
   4169 
   4170                                 if ( Expr.preFilter[ type ] ) {
   4171                                         match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
   4172 
   4173                                         if ( !match ) {
   4174                                                 anyFound = found = true;
   4175                                         } else if ( match === true ) {
   4176                                                 continue;
   4177                                         }
   4178                                 }
   4179 
   4180                                 if ( match ) {
   4181                                         for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
   4182                                                 if ( item ) {
   4183                                                         found = filter( item, match, i, curLoop );
   4184                                                         var pass = not ^ !!found;
   4185 
   4186                                                         if ( inplace && found != null ) {
   4187                                                                 if ( pass ) {
   4188                                                                         anyFound = true;
   4189                                                                 } else {
   4190                                                                         curLoop[i] = false;
   4191                                                                 }
   4192                                                         } else if ( pass ) {
   4193                                                                 result.push( item );
   4194                                                                 anyFound = true;
   4195                                                         }
   4196                                                 }
   4197                                         }
   4198                                 }
   4199 
   4200                                 if ( found !== undefined ) {
   4201                                         if ( !inplace ) {
   4202                                                 curLoop = result;
   4203                                         }
   4204 
   4205                                         expr = expr.replace( Expr.match[ type ], "" );
   4206 
   4207                                         if ( !anyFound ) {
   4208                                                 return [];
   4209                                         }
   4210 
   4211                                         break;
   4212                                 }
   4213                         }
   4214                 }
   4215 
   4216                 if ( expr == old ) {
   4217                         if ( anyFound == null ) {
   4218                                 throw "Syntax error, unrecognized expression: " + expr;
   4219                         } else {
   4220                                 break;
   4221                         }
   4222                 }
   4223 
   4224                 old = expr;
   4225         }
   4226 
   4227         return curLoop;
   4228 };
   4229 
   4230 var Expr = Sizzle.selectors = {
   4231         order: [ "ID", "NAME", "TAG" ],
   4232         match: {
   4233                 ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
   4234                 CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
   4235                 NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
   4236                 ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
   4237                 TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
   4238                 CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
   4239                 POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
   4240                 PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
   4241         },
   4242         leftMatch: {},
   4243         attrMap: {
   4244                 "class": "className",
   4245                 "for": "htmlFor"
   4246         },
   4247         attrHandle: {
   4248                 href: function(elem){
   4249                         return elem.getAttribute("href");
   4250                 }
   4251         },
   4252         relative: {
   4253                 "+": function(checkSet, part, isXML){
   4254                         var isPartStr = typeof part === "string",
   4255                                 isTag = isPartStr && !/\W/.test(part),
   4256                                 isPartStrNotTag = isPartStr && !isTag;
   4257 
   4258                         if ( isTag && !isXML ) {
   4259                                 part = part.toUpperCase();
   4260                         }
   4261 
   4262                         for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
   4263                                 if ( (elem = checkSet[i]) ) {
   4264                                         while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
   4265 
   4266                                         checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
   4267                                                 elem || false :
   4268                                                 elem === part;
   4269                                 }
   4270                         }
   4271 
   4272                         if ( isPartStrNotTag ) {
   4273                                 Sizzle.filter( part, checkSet, true );
   4274                         }
   4275                 },
   4276                 ">": function(checkSet, part, isXML){
   4277                         var isPartStr = typeof part === "string";
   4278 
   4279                         if ( isPartStr && !/\W/.test(part) ) {
   4280                                 part = isXML ? part : part.toUpperCase();
   4281 
   4282                                 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
   4283                                         var elem = checkSet[i];
   4284                                         if ( elem ) {
   4285                                                 var parent = elem.parentNode;
   4286                                                 checkSet[i] = parent.nodeName === part ? parent : false;
   4287                                         }
   4288                                 }
   4289                         } else {
   4290                                 for ( var i = 0, l = checkSet.length; i < l; i++ ) {
   4291                                         var elem = checkSet[i];
   4292                                         if ( elem ) {
   4293                                                 checkSet[i] = isPartStr ?
   4294                                                         elem.parentNode :
   4295                                                         elem.parentNode === part;
   4296                                         }
   4297                                 }
   4298 
   4299                                 if ( isPartStr ) {
   4300                                         Sizzle.filter( part, checkSet, true );
   4301                                 }
   4302                         }
   4303                 },
   4304                 "": function(checkSet, part, isXML){
   4305                         var doneName = done++, checkFn = dirCheck;
   4306 
   4307                         if ( !/\W/.test(part) ) {
   4308                                 var nodeCheck = part = isXML ? part : part.toUpperCase();
   4309                                 checkFn = dirNodeCheck;
   4310                         }
   4311 
   4312                         checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
   4313                 },
   4314                 "~": function(checkSet, part, isXML){
   4315                         var doneName = done++, checkFn = dirCheck;
   4316 
   4317                         if ( typeof part === "string" && !/\W/.test(part) ) {
   4318                                 var nodeCheck = part = isXML ? part : part.toUpperCase();
   4319                                 checkFn = dirNodeCheck;
   4320                         }
   4321 
   4322                         checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
   4323                 }
   4324         },
   4325         find: {
   4326                 ID: function(match, context, isXML){
   4327                         if ( typeof context.getElementById !== "undefined" && !isXML ) {
   4328                                 var m = context.getElementById(match[1]);
   4329                                 return m ? [m] : [];
   4330                         }
   4331                 },
   4332                 NAME: function(match, context, isXML){
   4333                         if ( typeof context.getElementsByName !== "undefined" ) {
   4334                                 var ret = [], results = context.getElementsByName(match[1]);
   4335 
   4336                                 for ( var i = 0, l = results.length; i < l; i++ ) {
   4337                                         if ( results[i].getAttribute("name") === match[1] ) {
   4338                                                 ret.push( results[i] );
   4339                                         }
   4340                                 }
   4341 
   4342                                 return ret.length === 0 ? null : ret;
   4343                         }
   4344                 },
   4345                 TAG: function(match, context){
   4346                         return context.getElementsByTagName(match[1]);
   4347                 }
   4348         },
   4349         preFilter: {
   4350                 CLASS: function(match, curLoop, inplace, result, not, isXML){
   4351                         match = " " + match[1].replace(/\\/g, "") + " ";
   4352 
   4353                         if ( isXML ) {
   4354                                 return match;
   4355                         }
   4356 
   4357                         for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
   4358                                 if ( elem ) {
   4359                                         if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
   4360                                                 if ( !inplace )
   4361                                                         result.push( elem );
   4362                                         } else if ( inplace ) {
   4363                                                 curLoop[i] = false;
   4364                                         }
   4365                                 }
   4366                         }
   4367 
   4368                         return false;
   4369                 },
   4370                 ID: function(match){
   4371                         return match[1].replace(/\\/g, "");
   4372                 },
   4373                 TAG: function(match, curLoop){
   4374                         for ( var i = 0; curLoop[i] === false; i++ ){}
   4375                         return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
   4376                 },
   4377                 CHILD: function(match){
   4378                         if ( match[1] == "nth" ) {
   4379                                 var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
   4380                                         match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
   4381                                         !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
   4382 
   4383                                 match[2] = (test[1] + (test[2] || 1)) - 0;
   4384                                 match[3] = test[3] - 0;
   4385                         }
   4386 
   4387                         match[0] = done++;
   4388 
   4389                         return match;
   4390                 },
   4391                 ATTR: function(match, curLoop, inplace, result, not, isXML){
   4392                         var name = match[1].replace(/\\/g, "");
   4393 
   4394                         if ( !isXML && Expr.attrMap[name] ) {
   4395                                 match[1] = Expr.attrMap[name];
   4396                         }
   4397 
   4398                         if ( match[2] === "~=" ) {
   4399                                 match[4] = " " + match[4] + " ";
   4400                         }
   4401 
   4402                         return match;
   4403                 },
   4404                 PSEUDO: function(match, curLoop, inplace, result, not){
   4405                         if ( match[1] === "not" ) {
   4406                                 if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
   4407                                         match[3] = Sizzle(match[3], null, null, curLoop);
   4408                                 } else {
   4409                                         var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
   4410                                         if ( !inplace ) {
   4411                                                 result.push.apply( result, ret );
   4412                                         }
   4413                                         return false;
   4414                                 }
   4415                         } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
   4416                                 return true;
   4417                         }
   4418 
   4419                         return match;
   4420                 },
   4421                 POS: function(match){
   4422                         match.unshift( true );
   4423                         return match;
   4424                 }
   4425         },
   4426         filters: {
   4427                 enabled: function(elem){
   4428                         return elem.disabled === false && elem.type !== "hidden";
   4429                 },
   4430                 disabled: function(elem){
   4431                         return elem.disabled === true;
   4432                 },
   4433                 checked: function(elem){
   4434                         return elem.checked === true;
   4435                 },
   4436                 selected: function(elem){
   4437                         elem.parentNode.selectedIndex;
   4438                         return elem.selected === true;
   4439                 },
   4440                 parent: function(elem){
   4441                         return !!elem.firstChild;
   4442                 },
   4443                 empty: function(elem){
   4444                         return !elem.firstChild;
   4445                 },
   4446                 has: function(elem, i, match){
   4447                         return !!Sizzle( match[3], elem ).length;
   4448                 },
   4449                 header: function(elem){
   4450                         return /h\d/i.test( elem.nodeName );
   4451                 },
   4452                 text: function(elem){
   4453                         return "text" === elem.type;
   4454                 },
   4455                 radio: function(elem){
   4456                         return "radio" === elem.type;
   4457                 },
   4458                 checkbox: function(elem){
   4459                         return "checkbox" === elem.type;
   4460                 },
   4461                 file: function(elem){
   4462                         return "file" === elem.type;
   4463                 },
   4464                 password: function(elem){
   4465                         return "password" === elem.type;
   4466                 },
   4467                 submit: function(elem){
   4468                         return "submit" === elem.type;
   4469                 },
   4470                 image: function(elem){
   4471                         return "image" === elem.type;
   4472                 },
   4473                 reset: function(elem){
   4474                         return "reset" === elem.type;
   4475                 },
   4476                 button: function(elem){
   4477                         return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
   4478                 },
   4479                 input: function(elem){
   4480                         return /input|select|textarea|button/i.test(elem.nodeName);
   4481                 }
   4482         },
   4483         setFilters: {
   4484                 first: function(elem, i){
   4485                         return i === 0;
   4486                 },
   4487                 last: function(elem, i, match, array){
   4488                         return i === array.length - 1;
   4489                 },
   4490                 even: function(elem, i){
   4491                         return i % 2 === 0;
   4492                 },
   4493                 odd: function(elem, i){
   4494                         return i % 2 === 1;
   4495                 },
   4496                 lt: function(elem, i, match){
   4497                         return i < match[3] - 0;
   4498                 },
   4499                 gt: function(elem, i, match){
   4500                         return i > match[3] - 0;
   4501                 },
   4502                 nth: function(elem, i, match){
   4503                         return match[3] - 0 == i;
   4504                 },
   4505                 eq: function(elem, i, match){
   4506                         return match[3] - 0 == i;
   4507                 }
   4508         },
   4509         filter: {
   4510                 PSEUDO: function(elem, match, i, array){
   4511                         var name = match[1], filter = Expr.filters[ name ];
   4512 
   4513                         if ( filter ) {
   4514                                 return filter( elem, i, match, array );
   4515                         } else if ( name === "contains" ) {
   4516                                 return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
   4517                         } else if ( name === "not" ) {
   4518                                 var not = match[3];
   4519 
   4520                                 for ( var i = 0, l = not.length; i < l; i++ ) {
   4521                                         if ( not[i] === elem ) {
   4522                                                 return false;
   4523                                         }
   4524                                 }
   4525 
   4526                                 return true;
   4527                         }
   4528                 },
   4529                 CHILD: function(elem, match){
   4530                         var type = match[1], node = elem;
   4531                         switch (type) {
   4532                                 case 'only':
   4533                                 case 'first':
   4534                                         while ( (node = node.previousSibling) )  {
   4535                                                 if ( node.nodeType === 1 ) return false;
   4536                                         }
   4537                                         if ( type == 'first') return true;
   4538                                         node = elem;
   4539                                 case 'last':
   4540                                         while ( (node = node.nextSibling) )  {
   4541                                                 if ( node.nodeType === 1 ) return false;
   4542                                         }
   4543                                         return true;
   4544                                 case 'nth':
   4545                                         var first = match[2], last = match[3];
   4546 
   4547                                         if ( first == 1 && last == 0 ) {
   4548                                                 return true;
   4549                                         }
   4550 
   4551                                         var doneName = match[0],
   4552                                                 parent = elem.parentNode;
   4553 
   4554                                         if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
   4555                                                 var count = 0;
   4556                                                 for ( node = parent.firstChild; node; node = node.nextSibling ) {
   4557                                                         if ( node.nodeType === 1 ) {
   4558                                                                 node.nodeIndex = ++count;
   4559                                                         }
   4560                                                 }
   4561                                                 parent.sizcache = doneName;
   4562                                         }
   4563 
   4564                                         var diff = elem.nodeIndex - last;
   4565                                         if ( first == 0 ) {
   4566                                                 return diff == 0;
   4567                                         } else {
   4568                                                 return ( diff % first == 0 && diff / first >= 0 );
   4569                                         }
   4570                         }
   4571                 },
   4572                 ID: function(elem, match){
   4573                         return elem.nodeType === 1 && elem.getAttribute("id") === match;
   4574                 },
   4575                 TAG: function(elem, match){
   4576                         return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
   4577                 },
   4578                 CLASS: function(elem, match){
   4579                         return (" " + (elem.className || elem.getAttribute("class")) + " ")
   4580                                 .indexOf( match ) > -1;
   4581                 },
   4582                 ATTR: function(elem, match){
   4583                         var name = match[1],
   4584                                 result = Expr.attrHandle[ name ] ?
   4585                                         Expr.attrHandle[ name ]( elem ) :
   4586                                         elem[ name ] != null ?
   4587                                                 elem[ name ] :
   4588                                                 elem.getAttribute( name ),
   4589                                 value = result + "",
   4590                                 type = match[2],
   4591                                 check = match[4];
   4592 
   4593                         return result == null ?
   4594                                 type === "!=" :
   4595                                 type === "=" ?
   4596                                 value === check :
   4597                                 type === "*=" ?
   4598                                 value.indexOf(check) >= 0 :
   4599                                 type === "~=" ?
   4600                                 (" " + value + " ").indexOf(check) >= 0 :
   4601                                 !check ?
   4602                                 value && result !== false :
   4603                                 type === "!=" ?
   4604                                 value != check :
   4605                                 type === "^=" ?
   4606                                 value.indexOf(check) === 0 :
   4607                                 type === "$=" ?
   4608                                 value.substr(value.length - check.length) === check :
   4609                                 type === "|=" ?
   4610                                 value === check || value.substr(0, check.length + 1) === check + "-" :
   4611                                 false;
   4612                 },
   4613                 POS: function(elem, match, i, array){
   4614                         var name = match[2], filter = Expr.setFilters[ name ];
   4615 
   4616                         if ( filter ) {
   4617                                 return filter( elem, i, match, array );
   4618                         }
   4619                 }
   4620         }
   4621 };
   4622 
   4623 var origPOS = Expr.match.POS;
   4624 
   4625 for ( var type in Expr.match ) {
   4626         Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
   4627         Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
   4628 }
   4629 
   4630 var makeArray = function(array, results) {
   4631         array = Array.prototype.slice.call( array, 0 );
   4632 
   4633         if ( results ) {
   4634                 results.push.apply( results, array );
   4635                 return results;
   4636         }
   4637 
   4638         return array;
   4639 };
   4640 
   4641 try {
   4642         Array.prototype.slice.call( document.documentElement.childNodes, 0 );
   4643 
   4644 } catch(e){
   4645         makeArray = function(array, results) {
   4646                 var ret = results || [];
   4647 
   4648                 if ( toString.call(array) === "[object Array]" ) {
   4649                         Array.prototype.push.apply( ret, array );
   4650                 } else {
   4651                         if ( typeof array.length === "number" ) {
   4652                                 for ( var i = 0, l = array.length; i < l; i++ ) {
   4653                                         ret.push( array[i] );
   4654                                 }
   4655                         } else {
   4656                                 for ( var i = 0; array[i]; i++ ) {
   4657                                         ret.push( array[i] );
   4658                                 }
   4659                         }
   4660                 }
   4661 
   4662                 return ret;
   4663         };
   4664 }
   4665 
   4666 var sortOrder;
   4667 
   4668 if ( document.documentElement.compareDocumentPosition ) {
   4669         sortOrder = function( a, b ) {
   4670                 if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
   4671                         if ( a == b ) {
   4672                                 hasDuplicate = true;
   4673                         }
   4674                         return 0;
   4675                 }
   4676 
   4677                 var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
   4678                 if ( ret === 0 ) {
   4679                         hasDuplicate = true;
   4680                 }
   4681                 return ret;
   4682         };
   4683 } else if ( "sourceIndex" in document.documentElement ) {
   4684         sortOrder = function( a, b ) {
   4685                 if ( !a.sourceIndex || !b.sourceIndex ) {
   4686                         if ( a == b ) {
   4687                                 hasDuplicate = true;
   4688                         }
   4689                         return 0;
   4690                 }
   4691 
   4692                 var ret = a.sourceIndex - b.sourceIndex;
   4693                 if ( ret === 0 ) {
   4694                         hasDuplicate = true;
   4695                 }
   4696                 return ret;
   4697         };
   4698 } else if ( document.createRange ) {
   4699         sortOrder = function( a, b ) {
   4700                 if ( !a.ownerDocument || !b.ownerDocument ) {
   4701                         if ( a == b ) {
   4702                                 hasDuplicate = true;
   4703                         }
   4704                         return 0;
   4705                 }
   4706 
   4707                 var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
   4708                 aRange.setStart(a, 0);
   4709                 aRange.setEnd(a, 0);
   4710                 bRange.setStart(b, 0);
   4711                 bRange.setEnd(b, 0);
   4712                 var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
   4713                 if ( ret === 0 ) {
   4714                         hasDuplicate = true;
   4715                 }
   4716                 return ret;
   4717         };
   4718 }
   4719 
   4720 (function(){
   4721         var form = document.createElement("div"),
   4722                 id = "script" + (new Date).getTime();
   4723         form.innerHTML = "<a name='" + id + "'/>";
   4724 
   4725         var root = document.documentElement;
   4726         root.insertBefore( form, root.firstChild );
   4727 
   4728         if ( !!document.getElementById( id ) ) {
   4729                 Expr.find.ID = function(match, context, isXML){
   4730                         if ( typeof context.getElementById !== "undefined" && !isXML ) {
   4731                                 var m = context.getElementById(match[1]);
   4732                                 return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
   4733                         }
   4734                 };
   4735 
   4736                 Expr.filter.ID = function(elem, match){
   4737                         var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
   4738                         return elem.nodeType === 1 && node && node.nodeValue === match;
   4739                 };
   4740         }
   4741 
   4742         root.removeChild( form );
   4743         root = form = null; // release memory in IE
   4744 })();
   4745 
   4746 (function(){
   4747 
   4748         var div = document.createElement("div");
   4749         div.appendChild( document.createComment("") );
   4750 
   4751         if ( div.getElementsByTagName("*").length > 0 ) {
   4752                 Expr.find.TAG = function(match, context){
   4753                         var results = context.getElementsByTagName(match[1]);
   4754 
   4755                         if ( match[1] === "*" ) {
   4756                                 var tmp = [];
   4757 
   4758                                 for ( var i = 0; results[i]; i++ ) {
   4759                                         if ( results[i].nodeType === 1 ) {
   4760                                                 tmp.push( results[i] );
   4761                                         }
   4762                                 }
   4763 
   4764                                 results = tmp;
   4765                         }
   4766 
   4767                         return results;
   4768                 };
   4769         }
   4770 
   4771         div.innerHTML = "<a href='#'></a>";
   4772         if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
   4773                         div.firstChild.getAttribute("href") !== "#" ) {
   4774                 Expr.attrHandle.href = function(elem){
   4775                         return elem.getAttribute("href", 2);
   4776                 };
   4777         }
   4778 
   4779         div = null; // release memory in IE
   4780 })();
   4781 
   4782 if ( document.querySelectorAll ) (function(){
   4783         var oldSizzle = Sizzle, div = document.createElement("div");
   4784         div.innerHTML = "<p class='TEST'></p>";
   4785 
   4786         if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
   4787                 return;
   4788         }
   4789 
   4790         Sizzle = function(query, context, extra, seed){
   4791                 context = context || document;
   4792 
   4793                 if ( !seed && context.nodeType === 9 && !isXML(context) ) {
   4794                         try {
   4795                                 return makeArray( context.querySelectorAll(query), extra );
   4796                         } catch(e){}
   4797                 }
   4798 
   4799                 return oldSizzle(query, context, extra, seed);
   4800         };
   4801 
   4802         for ( var prop in oldSizzle ) {
   4803                 Sizzle[ prop ] = oldSizzle[ prop ];
   4804         }
   4805 
   4806         div = null; // release memory in IE
   4807 })();
   4808 
   4809 if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
   4810         var div = document.createElement("div");
   4811         div.innerHTML = "<div class='test e'></div><div class='test'></div>";
   4812 
   4813         if ( div.getElementsByClassName("e").length === 0 )
   4814                 return;
   4815 
   4816         div.lastChild.className = "e";
   4817 
   4818         if ( div.getElementsByClassName("e").length === 1 )
   4819                 return;
   4820 
   4821         Expr.order.splice(1, 0, "CLASS");
   4822         Expr.find.CLASS = function(match, context, isXML) {
   4823                 if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
   4824                         return context.getElementsByClassName(match[1]);
   4825                 }
   4826         };
   4827 
   4828         div = null; // release memory in IE
   4829 })();
   4830 
   4831 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
   4832         var sibDir = dir == "previousSibling" && !isXML;
   4833         for ( var i = 0, l = checkSet.length; i < l; i++ ) {
   4834                 var elem = checkSet[i];
   4835                 if ( elem ) {
   4836                         if ( sibDir && elem.nodeType === 1 ){
   4837                                 elem.sizcache = doneName;
   4838                                 elem.sizset = i;
   4839                         }
   4840                         elem = elem[dir];
   4841                         var match = false;
   4842 
   4843                         while ( elem ) {
   4844                                 if ( elem.sizcache === doneName ) {
   4845                                         match = checkSet[elem.sizset];
   4846                                         break;
   4847                                 }
   4848 
   4849                                 if ( elem.nodeType === 1 && !isXML ){
   4850                                         elem.sizcache = doneName;
   4851                                         elem.sizset = i;
   4852                                 }
   4853 
   4854                                 if ( elem.nodeName === cur ) {
   4855                                         match = elem;
   4856                                         break;
   4857                                 }
   4858 
   4859                                 elem = elem[dir];
   4860                         }
   4861 
   4862                         checkSet[i] = match;
   4863                 }
   4864         }
   4865 }
   4866 
   4867 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
   4868         var sibDir = dir == "previousSibling" && !isXML;
   4869         for ( var i = 0, l = checkSet.length; i < l; i++ ) {
   4870                 var elem = checkSet[i];
   4871                 if ( elem ) {
   4872                         if ( sibDir && elem.nodeType === 1 ) {
   4873                                 elem.sizcache = doneName;
   4874                                 elem.sizset = i;
   4875                         }
   4876                         elem = elem[dir];
   4877                         var match = false;
   4878 
   4879                         while ( elem ) {
   4880                                 if ( elem.sizcache === doneName ) {
   4881                                         match = checkSet[elem.sizset];
   4882                                         break;
   4883                                 }
   4884 
   4885                                 if ( elem.nodeType === 1 ) {
   4886                                         if ( !isXML ) {
   4887                                                 elem.sizcache = doneName;
   4888                                                 elem.sizset = i;
   4889                                         }
   4890                                         if ( typeof cur !== "string" ) {
   4891                                                 if ( elem === cur ) {
   4892                                                         match = true;
   4893                                                         break;
   4894                                                 }
   4895 
   4896                                         } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
   4897                                                 match = elem;
   4898                                                 break;
   4899                                         }
   4900                                 }
   4901 
   4902                                 elem = elem[dir];
   4903                         }
   4904 
   4905                         checkSet[i] = match;
   4906                 }
   4907         }
   4908 }
   4909 
   4910 var contains = document.compareDocumentPosition ?  function(a, b){
   4911         return a.compareDocumentPosition(b) & 16;
   4912 } : function(a, b){
   4913         return a !== b && (a.contains ? a.contains(b) : true);
   4914 };
   4915 
   4916 var isXML = function(elem){
   4917         return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
   4918                 !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
   4919 };
   4920 
   4921 var posProcess = function(selector, context){
   4922         var tmpSet = [], later = "", match,
   4923                 root = context.nodeType ? [context] : context;
   4924 
   4925         while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
   4926                 later += match[0];
   4927                 selector = selector.replace( Expr.match.PSEUDO, "" );
   4928         }
   4929 
   4930         selector = Expr.relative[selector] ? selector + "*" : selector;
   4931 
   4932         for ( var i = 0, l = root.length; i < l; i++ ) {
   4933                 Sizzle( selector, root[i], tmpSet );
   4934         }
   4935 
   4936         return Sizzle.filter( later, tmpSet );
   4937 };
   4938 
   4939 
   4940 window.Sizzle = Sizzle;
   4941 
   4942 })();
   4943 
   4944 ;(function(engine) {
   4945   var extendElements = Prototype.Selector.extendElements;
   4946 
   4947   function select(selector, scope) {
   4948     return extendElements(engine(selector, scope || document));
   4949   }
   4950 
   4951   function match(element, selector) {
   4952     return engine.matches(selector, [element]).length == 1;
   4953   }
   4954 
   4955   Prototype.Selector.engine = engine;
   4956   Prototype.Selector.select = select;
   4957   Prototype.Selector.match = match;
   4958 })(Sizzle);
   4959 
   4960 window.Sizzle = Prototype._original_property;
   4961 delete Prototype._original_property;
   4962 
   4963 var Form = {
   4964   reset: function(form) {
   4965     form = $(form);
   4966     form.reset();
   4967     return form;
   4968   },
   4969 
   4970   serializeElements: function(elements, options) {
   4971     if (typeof options != 'object') options = { hash: !!options };
   4972     else if (Object.isUndefined(options.hash)) options.hash = true;
   4973     var key, value, submitted = false, submit = options.submit, accumulator, initial;
   4974 
   4975     if (options.hash) {
   4976       initial = {};
   4977       accumulator = function(result, key, value) {
   4978         if (key in result) {
   4979           if (!Object.isArray(result[key])) result[key] = [result[key]];
   4980           result[key].push(value);
   4981         } else result[key] = value;
   4982         return result;
   4983       };
   4984     } else {
   4985       initial = '';
   4986       accumulator = function(result, key, value) {
   4987         return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
   4988       }
   4989     }
   4990 
   4991     return elements.inject(initial, function(result, element) {
   4992       if (!element.disabled && element.name) {
   4993         key = element.name; value = $(element).getValue();
   4994         if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
   4995             submit !== false && (!submit || key == submit) && (submitted = true)))) {
   4996           result = accumulator(result, key, value);
   4997         }
   4998       }
   4999       return result;
   5000     });
   5001   }
   5002 };
   5003 
   5004 Form.Methods = {
   5005   serialize: function(form, options) {
   5006     return Form.serializeElements(Form.getElements(form), options);
   5007   },
   5008 
   5009   getElements: function(form) {
   5010     var elements = $(form).getElementsByTagName('*'),
   5011         element,
   5012         arr = [ ],
   5013         serializers = Form.Element.Serializers;
   5014     for (var i = 0; element = elements[i]; i++) {
   5015       arr.push(element);
   5016     }
   5017     return arr.inject([], function(elements, child) {
   5018       if (serializers[child.tagName.toLowerCase()])
   5019         elements.push(Element.extend(child));
   5020       return elements;
   5021     })
   5022   },
   5023 
   5024   getInputs: function(form, typeName, name) {
   5025     form = $(form);
   5026     var inputs = form.getElementsByTagName('input');
   5027 
   5028     if (!typeName && !name) return $A(inputs).map(Element.extend);
   5029 
   5030     for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
   5031       var input = inputs[i];
   5032       if ((typeName && input.type != typeName) || (name && input.name != name))
   5033         continue;
   5034       matchingInputs.push(Element.extend(input));
   5035     }
   5036 
   5037     return matchingInputs;
   5038   },
   5039 
   5040   disable: function(form) {
   5041     form = $(form);
   5042     Form.getElements(form).invoke('disable');
   5043     return form;
   5044   },
   5045 
   5046   enable: function(form) {
   5047     form = $(form);
   5048     Form.getElements(form).invoke('enable');
   5049     return form;
   5050   },
   5051 
   5052   findFirstElement: function(form) {
   5053     var elements = $(form).getElements().findAll(function(element) {
   5054       return 'hidden' != element.type && !element.disabled;
   5055     });
   5056     var firstByIndex = elements.findAll(function(element) {
   5057       return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
   5058     }).sortBy(function(element) { return element.tabIndex }).first();
   5059 
   5060     return firstByIndex ? firstByIndex : elements.find(function(element) {
   5061       return /^(?:input|select|textarea)$/i.test(element.tagName);
   5062     });
   5063   },
   5064 
   5065   focusFirstElement: function(form) {
   5066     form = $(form);
   5067     var element = form.findFirstElement();
   5068     if (element) element.activate();
   5069     return form;
   5070   },
   5071 
   5072   request: function(form, options) {
   5073     form = $(form), options = Object.clone(options || { });
   5074 
   5075     var params = options.parameters, action = form.readAttribute('action') || '';
   5076     if (action.blank()) action = window.location.href;
   5077     options.parameters = form.serialize(true);
   5078 
   5079     if (params) {
   5080       if (Object.isString(params)) params = params.toQueryParams();
   5081       Object.extend(options.parameters, params);
   5082     }
   5083 
   5084     if (form.hasAttribute('method') && !options.method)
   5085       options.method = form.method;
   5086 
   5087     return new Ajax.Request(action, options);
   5088   }
   5089 };
   5090 
   5091 /*--------------------------------------------------------------------------*/
   5092 
   5093 
   5094 Form.Element = {
   5095   focus: function(element) {
   5096     $(element).focus();
   5097     return element;
   5098   },
   5099 
   5100   select: function(element) {
   5101     $(element).select();
   5102     return element;
   5103   }
   5104 };
   5105 
   5106 Form.Element.Methods = {
   5107 
   5108   serialize: function(element) {
   5109     element = $(element);
   5110     if (!element.disabled && element.name) {
   5111       var value = element.getValue();
   5112       if (value != undefined) {
   5113         var pair = { };
   5114         pair[element.name] = value;
   5115         return Object.toQueryString(pair);
   5116       }
   5117     }
   5118     return '';
   5119   },
   5120 
   5121   getValue: function(element) {
   5122     element = $(element);
   5123     var method = element.tagName.toLowerCase();
   5124     return Form.Element.Serializers[method](element);
   5125   },
   5126 
   5127   setValue: function(element, value) {
   5128     element = $(element);
   5129     var method = element.tagName.toLowerCase();
   5130     Form.Element.Serializers[method](element, value);
   5131     return element;
   5132   },
   5133 
   5134   clear: function(element) {
   5135     $(element).value = '';
   5136     return element;
   5137   },
   5138 
   5139   present: function(element) {
   5140     return $(element).value != '';
   5141   },
   5142 
   5143   activate: function(element) {
   5144     element = $(element);
   5145     try {
   5146       element.focus();
   5147       if (element.select && (element.tagName.toLowerCase() != 'input' ||
   5148           !(/^(?:button|reset|submit)$/i.test(element.type))))
   5149         element.select();
   5150     } catch (e) { }
   5151     return element;
   5152   },
   5153 
   5154   disable: function(element) {
   5155     element = $(element);
   5156     element.disabled = true;
   5157     return element;
   5158   },
   5159 
   5160   enable: function(element) {
   5161     element = $(element);
   5162     element.disabled = false;
   5163     return element;
   5164   }
   5165 };
   5166 
   5167 /*--------------------------------------------------------------------------*/
   5168 
   5169 var Field = Form.Element;
   5170 
   5171 var $F = Form.Element.Methods.getValue;
   5172 
   5173 /*--------------------------------------------------------------------------*/
   5174 
   5175 Form.Element.Serializers = (function() {
   5176   function input(element, value) {
   5177     switch (element.type.toLowerCase()) {
   5178       case 'checkbox':
   5179       case 'radio':
   5180         return inputSelector(element, value);
   5181       default:
   5182         return valueSelector(element, value);
   5183     }
   5184   }
   5185 
   5186   function inputSelector(element, value) {
   5187     if (Object.isUndefined(value))
   5188       return element.checked ? element.value : null;
   5189     else element.checked = !!value;
   5190   }
   5191 
   5192   function valueSelector(element, value) {
   5193     if (Object.isUndefined(value)) return element.value;
   5194     else element.value = value;
   5195   }
   5196 
   5197   function select(element, value) {
   5198     if (Object.isUndefined(value))
   5199       return (element.type === 'select-one' ? selectOne : selectMany)(element);
   5200 
   5201     var opt, currentValue, single = !Object.isArray(value);
   5202     for (var i = 0, length = element.length; i < length; i++) {
   5203       opt = element.options[i];
   5204       currentValue = this.optionValue(opt);
   5205       if (single) {
   5206         if (currentValue == value) {
   5207           opt.selected = true;
   5208           return;
   5209         }
   5210       }
   5211       else opt.selected = value.include(currentValue);
   5212     }
   5213   }
   5214 
   5215   function selectOne(element) {
   5216     var index = element.selectedIndex;
   5217     return index >= 0 ? optionValue(element.options[index]) : null;
   5218   }
   5219 
   5220   function selectMany(element) {
   5221     var values, length = element.length;
   5222     if (!length) return null;
   5223 
   5224     for (var i = 0, values = []; i < length; i++) {
   5225       var opt = element.options[i];
   5226       if (opt.selected) values.push(optionValue(opt));
   5227     }
   5228     return values;
   5229   }
   5230 
   5231   function optionValue(opt) {
   5232     return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
   5233   }
   5234 
   5235   return {
   5236     input:         input,
   5237     inputSelector: inputSelector,
   5238     textarea:      valueSelector,
   5239     select:        select,
   5240     selectOne:     selectOne,
   5241     selectMany:    selectMany,
   5242     optionValue:   optionValue,
   5243     button:        valueSelector
   5244   };
   5245 })();
   5246 
   5247 /*--------------------------------------------------------------------------*/
   5248 
   5249 
   5250 Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
   5251   initialize: function($super, element, frequency, callback) {
   5252     $super(callback, frequency);
   5253     this.element   = $(element);
   5254     this.lastValue = this.getValue();
   5255   },
   5256 
   5257   execute: function() {
   5258     var value = this.getValue();
   5259     if (Object.isString(this.lastValue) && Object.isString(value) ?
   5260         this.lastValue != value : String(this.lastValue) != String(value)) {
   5261       this.callback(this.element, value);
   5262       this.lastValue = value;
   5263     }
   5264   }
   5265 });
   5266 
   5267 Form.Element.Observer = Class.create(Abstract.TimedObserver, {
   5268   getValue: function() {
   5269     return Form.Element.getValue(this.element);
   5270   }
   5271 });
   5272 
   5273 Form.Observer = Class.create(Abstract.TimedObserver, {
   5274   getValue: function() {
   5275     return Form.serialize(this.element);
   5276   }
   5277 });
   5278 
   5279 /*--------------------------------------------------------------------------*/
   5280 
   5281 Abstract.EventObserver = Class.create({
   5282   initialize: function(element, callback) {
   5283     this.element  = $(element);
   5284     this.callback = callback;
   5285 
   5286     this.lastValue = this.getValue();
   5287     if (this.element.tagName.toLowerCase() == 'form')
   5288       this.registerFormCallbacks();
   5289     else
   5290       this.registerCallback(this.element);
   5291   },
   5292 
   5293   onElementEvent: function() {
   5294     var value = this.getValue();
   5295     if (this.lastValue != value) {
   5296       this.callback(this.element, value);
   5297       this.lastValue = value;
   5298     }
   5299   },
   5300 
   5301   registerFormCallbacks: function() {
   5302     Form.getElements(this.element).each(this.registerCallback, this);
   5303   },
   5304 
   5305   registerCallback: function(element) {
   5306     if (element.type) {
   5307       switch (element.type.toLowerCase()) {
   5308         case 'checkbox':
   5309         case 'radio':
   5310           Event.observe(element, 'click', this.onElementEvent.bind(this));
   5311           break;
   5312         default:
   5313           Event.observe(element, 'change', this.onElementEvent.bind(this));
   5314           break;
   5315       }
   5316     }
   5317   }
   5318 });
   5319 
   5320 Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
   5321   getValue: function() {
   5322     return Form.Element.getValue(this.element);
   5323   }
   5324 });
   5325 
   5326 Form.EventObserver = Class.create(Abstract.EventObserver, {
   5327   getValue: function() {
   5328     return Form.serialize(this.element);
   5329   }
   5330 });
   5331 (function() {
   5332 
   5333   var Event = {
   5334     KEY_BACKSPACE: 8,
   5335     KEY_TAB:       9,
   5336     KEY_RETURN:   13,
   5337     KEY_ESC:      27,
   5338     KEY_LEFT:     37,
   5339     KEY_UP:       38,
   5340     KEY_RIGHT:    39,
   5341     KEY_DOWN:     40,
   5342     KEY_DELETE:   46,
   5343     KEY_HOME:     36,
   5344     KEY_END:      35,
   5345     KEY_PAGEUP:   33,
   5346     KEY_PAGEDOWN: 34,
   5347     KEY_INSERT:   45,
   5348 
   5349     cache: {}
   5350   };
   5351 
   5352   var docEl = document.documentElement;
   5353   var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
   5354     && 'onmouseleave' in docEl;
   5355 
   5356 
   5357 
   5358   var isIELegacyEvent = function(event) { return false; };
   5359 
   5360   if (window.attachEvent) {
   5361     if (window.addEventListener) {
   5362       isIELegacyEvent = function(event) {
   5363         return !(event instanceof window.Event);
   5364       };
   5365     } else {
   5366       isIELegacyEvent = function(event) { return true; };
   5367     }
   5368   }
   5369 
   5370   var _isButton;
   5371 
   5372   function _isButtonForDOMEvents(event, code) {
   5373     return event.which ? (event.which === code + 1) : (event.button === code);
   5374   }
   5375 
   5376   var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
   5377   function _isButtonForLegacyEvents(event, code) {
   5378     return event.button === legacyButtonMap[code];
   5379   }
   5380 
   5381   function _isButtonForWebKit(event, code) {
   5382     switch (code) {
   5383       case 0: return event.which == 1 && !event.metaKey;
   5384       case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
   5385       case 2: return event.which == 3;
   5386       default: return false;
   5387     }
   5388   }
   5389 
   5390   if (window.attachEvent) {
   5391     if (!window.addEventListener) {
   5392       _isButton = _isButtonForLegacyEvents;
   5393     } else {
   5394       _isButton = function(event, code) {
   5395         return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
   5396          _isButtonForDOMEvents(event, code);
   5397       }
   5398     }
   5399   } else if (Prototype.Browser.WebKit) {
   5400     _isButton = _isButtonForWebKit;
   5401   } else {
   5402     _isButton = _isButtonForDOMEvents;
   5403   }
   5404 
   5405   function isLeftClick(event)   { return _isButton(event, 0) }
   5406 
   5407   function isMiddleClick(event) { return _isButton(event, 1) }
   5408 
   5409   function isRightClick(event)  { return _isButton(event, 2) }
   5410 
   5411   function element(event) {
   5412     event = Event.extend(event);
   5413 
   5414     var node = event.target, type = event.type,
   5415      currentTarget = event.currentTarget;
   5416 
   5417     if (currentTarget && currentTarget.tagName) {
   5418       if (type === 'load' || type === 'error' ||
   5419         (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
   5420           && currentTarget.type === 'radio'))
   5421             node = currentTarget;
   5422     }
   5423 
   5424     if (node.nodeType == Node.TEXT_NODE)
   5425       node = node.parentNode;
   5426 
   5427     return Element.extend(node);
   5428   }
   5429 
   5430   function findElement(event, expression) {
   5431     var element = Event.element(event);
   5432 
   5433     if (!expression) return element;
   5434     while (element) {
   5435       if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
   5436         return Element.extend(element);
   5437       }
   5438       element = element.parentNode;
   5439     }
   5440   }
   5441 
   5442   function pointer(event) {
   5443     return { x: pointerX(event), y: pointerY(event) };
   5444   }
   5445 
   5446   function pointerX(event) {
   5447     var docElement = document.documentElement,
   5448      body = document.body || { scrollLeft: 0 };
   5449 
   5450     return event.pageX || (event.clientX +
   5451       (docElement.scrollLeft || body.scrollLeft) -
   5452       (docElement.clientLeft || 0));
   5453   }
   5454 
   5455   function pointerY(event) {
   5456     var docElement = document.documentElement,
   5457      body = document.body || { scrollTop: 0 };
   5458 
   5459     return  event.pageY || (event.clientY +
   5460        (docElement.scrollTop || body.scrollTop) -
   5461        (docElement.clientTop || 0));
   5462   }
   5463 
   5464 
   5465   function stop(event) {
   5466     Event.extend(event);
   5467     event.preventDefault();
   5468     event.stopPropagation();
   5469 
   5470     event.stopped = true;
   5471   }
   5472 
   5473 
   5474   Event.Methods = {
   5475     isLeftClick:   isLeftClick,
   5476     isMiddleClick: isMiddleClick,
   5477     isRightClick:  isRightClick,
   5478 
   5479     element:     element,
   5480     findElement: findElement,
   5481 
   5482     pointer:  pointer,
   5483     pointerX: pointerX,
   5484     pointerY: pointerY,
   5485 
   5486     stop: stop
   5487   };
   5488 
   5489   var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
   5490     m[name] = Event.Methods[name].methodize();
   5491     return m;
   5492   });
   5493 
   5494   if (window.attachEvent) {
   5495     function _relatedTarget(event) {
   5496       var element;
   5497       switch (event.type) {
   5498         case 'mouseover':
   5499         case 'mouseenter':
   5500           element = event.fromElement;
   5501           break;
   5502         case 'mouseout':
   5503         case 'mouseleave':
   5504           element = event.toElement;
   5505           break;
   5506         default:
   5507           return null;
   5508       }
   5509       return Element.extend(element);
   5510     }
   5511 
   5512     var additionalMethods = {
   5513       stopPropagation: function() { this.cancelBubble = true },
   5514       preventDefault:  function() { this.returnValue = false },
   5515       inspect: function() { return '[object Event]' }
   5516     };
   5517 
   5518     Event.extend = function(event, element) {
   5519       if (!event) return false;
   5520 
   5521       if (!isIELegacyEvent(event)) return event;
   5522 
   5523       if (event._extendedByPrototype) return event;
   5524       event._extendedByPrototype = Prototype.emptyFunction;
   5525 
   5526       var pointer = Event.pointer(event);
   5527 
   5528       Object.extend(event, {
   5529         target: event.srcElement || element,
   5530         relatedTarget: _relatedTarget(event),
   5531         pageX:  pointer.x,
   5532         pageY:  pointer.y
   5533       });
   5534 
   5535       Object.extend(event, methods);
   5536       Object.extend(event, additionalMethods);
   5537 
   5538       return event;
   5539     };
   5540   } else {
   5541     Event.extend = Prototype.K;
   5542   }
   5543 
   5544   if (window.addEventListener) {
   5545     Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
   5546     Object.extend(Event.prototype, methods);
   5547   }
   5548 
   5549   function _createResponder(element, eventName, handler) {
   5550     var registry = Element.retrieve(element, 'prototype_event_registry');
   5551 
   5552     if (Object.isUndefined(registry)) {
   5553       CACHE.push(element);
   5554       registry = Element.retrieve(element, 'prototype_event_registry', $H());
   5555     }
   5556 
   5557     var respondersForEvent = registry.get(eventName);
   5558     if (Object.isUndefined(respondersForEvent)) {
   5559       respondersForEvent = [];
   5560       registry.set(eventName, respondersForEvent);
   5561     }
   5562 
   5563     if (respondersForEvent.pluck('handler').include(handler)) return false;
   5564 
   5565     var responder;
   5566     if (eventName.include(":")) {
   5567       responder = function(event) {
   5568         if (Object.isUndefined(event.eventName))
   5569           return false;
   5570 
   5571         if (event.eventName !== eventName)
   5572           return false;
   5573 
   5574         Event.extend(event, element);
   5575         handler.call(element, event);
   5576       };
   5577     } else {
   5578       if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
   5579        (eventName === "mouseenter" || eventName === "mouseleave")) {
   5580         if (eventName === "mouseenter" || eventName === "mouseleave") {
   5581           responder = function(event) {
   5582             Event.extend(event, element);
   5583 
   5584             var parent = event.relatedTarget;
   5585             while (parent && parent !== element) {
   5586               try { parent = parent.parentNode; }
   5587               catch(e) { parent = element; }
   5588             }
   5589 
   5590             if (parent === element) return;
   5591 
   5592             handler.call(element, event);
   5593           };
   5594         }
   5595       } else {
   5596         responder = function(event) {
   5597           Event.extend(event, element);
   5598           handler.call(element, event);
   5599         };
   5600       }
   5601     }
   5602 
   5603     responder.handler = handler;
   5604     respondersForEvent.push(responder);
   5605     return responder;
   5606   }
   5607 
   5608   function _destroyCache() {
   5609     for (var i = 0, length = CACHE.length; i < length; i++) {
   5610       Event.stopObserving(CACHE[i]);
   5611       CACHE[i] = null;
   5612     }
   5613   }
   5614 
   5615   var CACHE = [];
   5616 
   5617   if (Prototype.Browser.IE)
   5618     window.attachEvent('onunload', _destroyCache);
   5619 
   5620   if (Prototype.Browser.WebKit)
   5621     window.addEventListener('unload', Prototype.emptyFunction, false);
   5622 
   5623 
   5624   var _getDOMEventName = Prototype.K,
   5625       translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
   5626 
   5627   if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
   5628     _getDOMEventName = function(eventName) {
   5629       return (translations[eventName] || eventName);
   5630     };
   5631   }
   5632 
   5633   function observe(element, eventName, handler) {
   5634     element = $(element);
   5635 
   5636     var responder = _createResponder(element, eventName, handler);
   5637 
   5638     if (!responder) return element;
   5639 
   5640     if (eventName.include(':')) {
   5641       if (element.addEventListener)
   5642         element.addEventListener("dataavailable", responder, false);
   5643       else {
   5644         element.attachEvent("ondataavailable", responder);
   5645         element.attachEvent("onlosecapture", responder);
   5646       }
   5647     } else {
   5648       var actualEventName = _getDOMEventName(eventName);
   5649 
   5650       if (element.addEventListener)
   5651         element.addEventListener(actualEventName, responder, false);
   5652       else
   5653         element.attachEvent("on" + actualEventName, responder);
   5654     }
   5655 
   5656     return element;
   5657   }
   5658 
   5659   function stopObserving(element, eventName, handler) {
   5660     element = $(element);
   5661 
   5662     var registry = Element.retrieve(element, 'prototype_event_registry');
   5663     if (!registry) return element;
   5664 
   5665     if (!eventName) {
   5666       registry.each( function(pair) {
   5667         var eventName = pair.key;
   5668         stopObserving(element, eventName);
   5669       });
   5670       return element;
   5671     }
   5672 
   5673     var responders = registry.get(eventName);
   5674     if (!responders) return element;
   5675 
   5676     if (!handler) {
   5677       responders.each(function(r) {
   5678         stopObserving(element, eventName, r.handler);
   5679       });
   5680       return element;
   5681     }
   5682 
   5683     var i = responders.length, responder;
   5684     while (i--) {
   5685       if (responders[i].handler === handler) {
   5686         responder = responders[i];
   5687         break;
   5688       }
   5689     }
   5690     if (!responder) return element;
   5691 
   5692     if (eventName.include(':')) {
   5693       if (element.removeEventListener)
   5694         element.removeEventListener("dataavailable", responder, false);
   5695       else {
   5696         element.detachEvent("ondataavailable", responder);
   5697         element.detachEvent("onlosecapture", responder);
   5698       }
   5699     } else {
   5700       var actualEventName = _getDOMEventName(eventName);
   5701       if (element.removeEventListener)
   5702         element.removeEventListener(actualEventName, responder, false);
   5703       else
   5704         element.detachEvent('on' + actualEventName, responder);
   5705     }
   5706 
   5707     registry.set(eventName, responders.without(responder));
   5708 
   5709     return element;
   5710   }
   5711 
   5712   function fire(element, eventName, memo, bubble) {
   5713     element = $(element);
   5714 
   5715     if (Object.isUndefined(bubble))
   5716       bubble = true;
   5717 
   5718     if (element == document && document.createEvent && !element.dispatchEvent)
   5719       element = document.documentElement;
   5720 
   5721     var event;
   5722     if (document.createEvent) {
   5723       event = document.createEvent('HTMLEvents');
   5724       event.initEvent('dataavailable', bubble, true);
   5725     } else {
   5726       event = document.createEventObject();
   5727       event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
   5728     }
   5729 
   5730     event.eventName = eventName;
   5731     event.memo = memo || { };
   5732 
   5733     if (document.createEvent)
   5734       element.dispatchEvent(event);
   5735     else
   5736       element.fireEvent(event.eventType, event);
   5737 
   5738     return Event.extend(event);
   5739   }
   5740 
   5741   Event.Handler = Class.create({
   5742     initialize: function(element, eventName, selector, callback) {
   5743       this.element   = $(element);
   5744       this.eventName = eventName;
   5745       this.selector  = selector;
   5746       this.callback  = callback;
   5747       this.handler   = this.handleEvent.bind(this);
   5748     },
   5749 
   5750     start: function() {
   5751       Event.observe(this.element, this.eventName, this.handler);
   5752       return this;
   5753     },
   5754 
   5755     stop: function() {
   5756       Event.stopObserving(this.element, this.eventName, this.handler);
   5757       return this;
   5758     },
   5759 
   5760     handleEvent: function(event) {
   5761       var element = Event.findElement(event, this.selector);
   5762       if (element) this.callback.call(this.element, event, element);
   5763     }
   5764   });
   5765 
   5766   function on(element, eventName, selector, callback) {
   5767     element = $(element);
   5768     if (Object.isFunction(selector) && Object.isUndefined(callback)) {
   5769       callback = selector, selector = null;
   5770     }
   5771 
   5772     return new Event.Handler(element, eventName, selector, callback).start();
   5773   }
   5774 
   5775   Object.extend(Event, Event.Methods);
   5776 
   5777   Object.extend(Event, {
   5778     fire:          fire,
   5779     observe:       observe,
   5780     stopObserving: stopObserving,
   5781     on:            on
   5782   });
   5783 
   5784   Element.addMethods({
   5785     fire:          fire,
   5786 
   5787     observe:       observe,
   5788 
   5789     stopObserving: stopObserving,
   5790 
   5791     on:            on
   5792   });
   5793 
   5794   Object.extend(document, {
   5795     fire:          fire.methodize(),
   5796 
   5797     observe:       observe.methodize(),
   5798 
   5799     stopObserving: stopObserving.methodize(),
   5800 
   5801     on:            on.methodize(),
   5802 
   5803     loaded:        false
   5804   });
   5805 
   5806   if (window.Event) Object.extend(window.Event, Event);
   5807   else window.Event = Event;
   5808 })();
   5809 
   5810 (function() {
   5811   /* Support for the DOMContentLoaded event is based on work by Dan Webb,
   5812      Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
   5813 
   5814   var timer;
   5815 
   5816   function fireContentLoadedEvent() {
   5817     if (document.loaded) return;
   5818     if (timer) window.clearTimeout(timer);
   5819     document.loaded = true;
   5820     document.fire('dom:loaded');
   5821   }
   5822 
   5823   function checkReadyState() {
   5824     if (document.readyState === 'complete') {
   5825       document.stopObserving('readystatechange', checkReadyState);
   5826       fireContentLoadedEvent();
   5827     }
   5828   }
   5829 
   5830   function pollDoScroll() {
   5831     try { document.documentElement.doScroll('left'); }
   5832     catch(e) {
   5833       timer = pollDoScroll.defer();
   5834       return;
   5835     }
   5836     fireContentLoadedEvent();
   5837   }
   5838 
   5839   if (document.addEventListener) {
   5840     document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
   5841   } else {
   5842     document.observe('readystatechange', checkReadyState);
   5843     if (window == top)
   5844       timer = pollDoScroll.defer();
   5845   }
   5846 
   5847   Event.observe(window, 'load', fireContentLoadedEvent);
   5848 })();
   5849 
   5850 Element.addMethods();
   5851 
   5852 /*------------------------------- DEPRECATED -------------------------------*/
   5853 
   5854 Hash.toQueryString = Object.toQueryString;
   5855 
   5856 var Toggle = { display: Element.toggle };
   5857 
   5858 Element.Methods.childOf = Element.Methods.descendantOf;
   5859 
   5860 var Insertion = {
   5861   Before: function(element, content) {
   5862     return Element.insert(element, {before:content});
   5863   },
   5864 
   5865   Top: function(element, content) {
   5866     return Element.insert(element, {top:content});
   5867   },
   5868 
   5869   Bottom: function(element, content) {
   5870     return Element.insert(element, {bottom:content});
   5871   },
   5872 
   5873   After: function(element, content) {
   5874     return Element.insert(element, {after:content});
   5875   }
   5876 };
   5877 
   5878 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
   5879 
   5880 var Position = {
   5881   includeScrollOffsets: false,
   5882 
   5883   prepare: function() {
   5884     this.deltaX =  window.pageXOffset
   5885                 || document.documentElement.scrollLeft
   5886                 || document.body.scrollLeft
   5887                 || 0;
   5888     this.deltaY =  window.pageYOffset
   5889                 || document.documentElement.scrollTop
   5890                 || document.body.scrollTop
   5891                 || 0;
   5892   },
   5893 
   5894   within: function(element, x, y) {
   5895     if (this.includeScrollOffsets)
   5896       return this.withinIncludingScrolloffsets(element, x, y);
   5897     this.xcomp = x;
   5898     this.ycomp = y;
   5899     this.offset = Element.cumulativeOffset(element);
   5900 
   5901     return (y >= this.offset[1] &&
   5902             y <  this.offset[1] + element.offsetHeight &&
   5903             x >= this.offset[0] &&
   5904             x <  this.offset[0] + element.offsetWidth);
   5905   },
   5906 
   5907   withinIncludingScrolloffsets: function(element, x, y) {
   5908     var offsetcache = Element.cumulativeScrollOffset(element);
   5909 
   5910     this.xcomp = x + offsetcache[0] - this.deltaX;
   5911     this.ycomp = y + offsetcache[1] - this.deltaY;
   5912     this.offset = Element.cumulativeOffset(element);
   5913 
   5914     return (this.ycomp >= this.offset[1] &&
   5915             this.ycomp <  this.offset[1] + element.offsetHeight &&
   5916             this.xcomp >= this.offset[0] &&
   5917             this.xcomp <  this.offset[0] + element.offsetWidth);
   5918   },
   5919 
   5920   overlap: function(mode, element) {
   5921     if (!mode) return 0;
   5922     if (mode == 'vertical')
   5923       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
   5924         element.offsetHeight;
   5925     if (mode == 'horizontal')
   5926       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
   5927         element.offsetWidth;
   5928   },
   5929 
   5930 
   5931   cumulativeOffset: Element.Methods.cumulativeOffset,
   5932 
   5933   positionedOffset: Element.Methods.positionedOffset,
   5934 
   5935   absolutize: function(element) {
   5936     Position.prepare();
   5937     return Element.absolutize(element);
   5938   },
   5939 
   5940   relativize: function(element) {
   5941     Position.prepare();
   5942     return Element.relativize(element);
   5943   },
   5944 
   5945   realOffset: Element.Methods.cumulativeScrollOffset,
   5946 
   5947   offsetParent: Element.Methods.getOffsetParent,
   5948 
   5949   page: Element.Methods.viewportOffset,
   5950 
   5951   clone: function(source, target, options) {
   5952     options = options || { };
   5953     return Element.clonePosition(target, source, options);
   5954   }
   5955 };
   5956 
   5957 /*--------------------------------------------------------------------------*/
   5958 
   5959 if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
   5960   function iter(name) {
   5961     return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
   5962   }
   5963 
   5964   instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
   5965   function(element, className) {
   5966     className = className.toString().strip();
   5967     var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
   5968     return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
   5969   } : function(element, className) {
   5970     className = className.toString().strip();
   5971     var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
   5972     if (!classNames && !className) return elements;
   5973 
   5974     var nodes = $(element).getElementsByTagName('*');
   5975     className = ' ' + className + ' ';
   5976 
   5977     for (var i = 0, child, cn; child = nodes[i]; i++) {
   5978       if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
   5979           (classNames && classNames.all(function(name) {
   5980             return !name.toString().blank() && cn.include(' ' + name + ' ');
   5981           }))))
   5982         elements.push(Element.extend(child));
   5983     }
   5984     return elements;
   5985   };
   5986 
   5987   return function(className, parentElement) {
   5988     return $(parentElement || document.body).getElementsByClassName(className);
   5989   };
   5990 }(Element.Methods);
   5991 
   5992 /*--------------------------------------------------------------------------*/
   5993 
   5994 Element.ClassNames = Class.create();
   5995 Element.ClassNames.prototype = {
   5996   initialize: function(element) {
   5997     this.element = $(element);
   5998   },
   5999 
   6000   _each: function(iterator) {
   6001     this.element.className.split(/\s+/).select(function(name) {
   6002       return name.length > 0;
   6003     })._each(iterator);
   6004   },
   6005 
   6006   set: function(className) {
   6007     this.element.className = className;
   6008   },
   6009 
   6010   add: function(classNameToAdd) {
   6011     if (this.include(classNameToAdd)) return;
   6012     this.set($A(this).concat(classNameToAdd).join(' '));
   6013   },
   6014 
   6015   remove: function(classNameToRemove) {
   6016     if (!this.include(classNameToRemove)) return;
   6017     this.set($A(this).without(classNameToRemove).join(' '));
   6018   },
   6019 
   6020   toString: function() {
   6021     return $A(this).join(' ');
   6022   }
   6023 };
   6024 
   6025 Object.extend(Element.ClassNames.prototype, Enumerable);
   6026 
   6027 /*--------------------------------------------------------------------------*/
   6028 
   6029 (function() {
   6030   window.Selector = Class.create({
   6031     initialize: function(expression) {
   6032       this.expression = expression.strip();
   6033     },
   6034 
   6035     findElements: function(rootElement) {
   6036       return Prototype.Selector.select(this.expression, rootElement);
   6037     },
   6038 
   6039     match: function(element) {
   6040       return Prototype.Selector.match(element, this.expression);
   6041     },
   6042 
   6043     toString: function() {
   6044       return this.expression;
   6045     },
   6046 
   6047     inspect: function() {
   6048       return "#<Selector: " + this.expression + ">";
   6049     }
   6050   });
   6051 
   6052   Object.extend(Selector, {
   6053     matchElements: function(elements, expression) {
   6054       var match = Prototype.Selector.match,
   6055           results = [];
   6056 
   6057       for (var i = 0, length = elements.length; i < length; i++) {
   6058         var element = elements[i];
   6059         if (match(element, expression)) {
   6060           results.push(Element.extend(element));
   6061         }
   6062       }
   6063       return results;
   6064     },
   6065 
   6066     findElement: function(elements, expression, index) {
   6067       index = index || 0;
   6068       var matchIndex = 0, element;
   6069       for (var i = 0, length = elements.length; i < length; i++) {
   6070         element = elements[i];
   6071         if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
   6072           return Element.extend(element);
   6073         }
   6074       }
   6075     },
   6076 
   6077     findChildElements: function(element, expressions) {
   6078       var selector = expressions.toArray().join(', ');
   6079       return Prototype.Selector.select(selector, element || document);
   6080     }
   6081   });
   6082 })();
   6083