www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

prototype.js (139854B)


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