prototype.lite.js
上传用户:wangting
上传日期:2020-01-24
资源大小:2226k
文件大小:94k
源码类别:

破解

开发平台:

ASP/ASPX

  1. /*  Prototype JavaScript framework, version 1.5.1
  2.  *  (c) 2005-2007 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. var Prototype = {
  9.   Version: '1.5.1',
  10.   Browser: {
  11.     IE:     !!(window.attachEvent && !window.opera),
  12.     Opera:  !!window.opera,
  13.     WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
  14.     Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
  15.   },
  16.   BrowserFeatures: {
  17.     XPath: !!document.evaluate,
  18.     ElementExtensions: !!window.HTMLElement,
  19.     SpecificElementExtensions:
  20.       (document.createElement('div').__proto__ !==
  21.        document.createElement('form').__proto__)
  22.   },
  23.   ScriptFragment: '<script[^>]*>([u0001-uFFFF]*?)</script>',
  24.   JSONFilter: /^/*-secure-s*(.*)s**/s*$/,
  25.   emptyFunction: function() { },
  26.   K: function(x) { return x }
  27. }
  28. var Class = {
  29.   create: function() {
  30.     return function() {
  31.       this.initialize.apply(this, arguments);
  32.     }
  33.   }
  34. }
  35. var Abstract = new Object();
  36. Object.extend = function(destination, source) {
  37.   for (var property in source) {
  38.     destination[property] = source[property];
  39.   }
  40.   return destination;
  41. }
  42. Object.extend(Object, {
  43.   inspect: function(object) {
  44.     try {
  45.       if (object === undefined) return 'undefined';
  46.       if (object === null) return 'null';
  47.       return object.inspect ? object.inspect() : object.toString();
  48.     } catch (e) {
  49.       if (e instanceof RangeError) return '...';
  50.       throw e;
  51.     }
  52.   },
  53.   toJSON: function(object) {
  54.     var type = typeof object;
  55.     switch(type) {
  56.       case 'undefined':
  57.       case 'function':
  58.       case 'unknown': return;
  59.       case 'boolean': return object.toString();
  60.     }
  61.     if (object === null) return 'null';
  62.     if (object.toJSON) return object.toJSON();
  63.     if (object.ownerDocument === document) return;
  64.     var results = [];
  65.     for (var property in object) {
  66.       var value = Object.toJSON(object[property]);
  67.       if (value !== undefined)
  68.         results.push(property.toJSON() + ': ' + value);
  69.     }
  70.     return '{' + results.join(', ') + '}';
  71.   },
  72.   keys: function(object) {
  73.     var keys = [];
  74.     for (var property in object)
  75.       keys.push(property);
  76.     return keys;
  77.   },
  78.   values: function(object) {
  79.     var values = [];
  80.     for (var property in object)
  81.       values.push(object[property]);
  82.     return values;
  83.   },
  84.   clone: function(object) {
  85.     return Object.extend({}, object);
  86.   }
  87. });
  88. Function.prototype.bind = function() {
  89.   var __method = this, args = $A(arguments), object = args.shift();
  90.   return function() {
  91.     return __method.apply(object, args.concat($A(arguments)));
  92.   }
  93. }
  94. Function.prototype.bindAsEventListener = function(object) {
  95.   var __method = this, args = $A(arguments), object = args.shift();
  96.   return function(event) {
  97.     return __method.apply(object, [event || window.event].concat(args));
  98.   }
  99. }
  100. Object.extend(Number.prototype, {
  101.   toColorPart: function() {
  102.     return this.toPaddedString(2, 16);
  103.   },
  104.   succ: function() {
  105.     return this + 1;
  106.   },
  107.   times: function(iterator) {
  108.     $R(0, this, true).each(iterator);
  109.     return this;
  110.   },
  111.   toPaddedString: function(length, radix) {
  112.     var string = this.toString(radix || 10);
  113.     return '0'.times(length - string.length) + string;
  114.   },
  115.   toJSON: function() {
  116.     return isFinite(this) ? this.toString() : 'null';
  117.   }
  118. });
  119. Date.prototype.toJSON = function() {
  120.   return '"' + this.getFullYear() + '-' +
  121.     (this.getMonth() + 1).toPaddedString(2) + '-' +
  122.     this.getDate().toPaddedString(2) + 'T' +
  123.     this.getHours().toPaddedString(2) + ':' +
  124.     this.getMinutes().toPaddedString(2) + ':' +
  125.     this.getSeconds().toPaddedString(2) + '"';
  126. };
  127. var Try = {
  128.   these: function() {
  129.     var returnValue;
  130.     for (var i = 0, length = arguments.length; i < length; i++) {
  131.       var lambda = arguments[i];
  132.       try {
  133.         returnValue = lambda();
  134.         break;
  135.       } catch (e) {}
  136.     }
  137.     return returnValue;
  138.   }
  139. }
  140. /*--------------------------------------------------------------------------*/
  141. var PeriodicalExecuter = Class.create();
  142. PeriodicalExecuter.prototype = {
  143.   initialize: function(callback, frequency) {
  144.     this.callback = callback;
  145.     this.frequency = frequency;
  146.     this.currentlyExecuting = false;
  147.     this.registerCallback();
  148.   },
  149.   registerCallback: function() {
  150.     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  151.   },
  152.   stop: function() {
  153.     if (!this.timer) return;
  154.     clearInterval(this.timer);
  155.     this.timer = null;
  156.   },
  157.   onTimerEvent: function() {
  158.     if (!this.currentlyExecuting) {
  159.       try {
  160.         this.currentlyExecuting = true;
  161.         this.callback(this);
  162.       } finally {
  163.         this.currentlyExecuting = false;
  164.       }
  165.     }
  166.   }
  167. }
  168. Object.extend(String, {
  169.   interpret: function(value) {
  170.     return value == null ? '' : String(value);
  171.   },
  172.   specialChar: {
  173.     'b': '\b',
  174.     't': '\t',
  175.     'n': '\n',
  176.     'f': '\f',
  177.     'r': '\r',
  178.     '\': '\\'
  179.   }
  180. });
  181. Object.extend(String.prototype, {
  182.   gsub: function(pattern, replacement) {
  183.     var result = '', source = this, match;
  184.     replacement = arguments.callee.prepareReplacement(replacement);
  185.     while (source.length > 0) {
  186.       if (match = source.match(pattern)) {
  187.         result += source.slice(0, match.index);
  188.         result += String.interpret(replacement(match));
  189.         source  = source.slice(match.index + match[0].length);
  190.       } else {
  191.         result += source, source = '';
  192.       }
  193.     }
  194.     return result;
  195.   },
  196.   sub: function(pattern, replacement, count) {
  197.     replacement = this.gsub.prepareReplacement(replacement);
  198.     count = count === undefined ? 1 : count;
  199.     return this.gsub(pattern, function(match) {
  200.       if (--count < 0) return match[0];
  201.       return replacement(match);
  202.     });
  203.   },
  204.   scan: function(pattern, iterator) {
  205.     this.gsub(pattern, iterator);
  206.     return this;
  207.   },
  208.   truncate: function(length, truncation) {
  209.     length = length || 30;
  210.     truncation = truncation === undefined ? '...' : truncation;
  211.     return this.length > length ?
  212.       this.slice(0, length - truncation.length) + truncation : this;
  213.   },
  214.   strip: function() {
  215.     return this.replace(/^s+/, '').replace(/s+$/, '');
  216.   },
  217.   stripTags: function() {
  218.     return this.replace(/</?[^>]+>/gi, '');
  219.   },
  220.   stripScripts: function() {
  221.     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  222.   },
  223.   extractScripts: function() {
  224.     var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
  225.     var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
  226.     return (this.match(matchAll) || []).map(function(scriptTag) {
  227.       return (scriptTag.match(matchOne) || ['', ''])[1];
  228.     });
  229.   },
  230.   evalScripts: function() {
  231.     return this.extractScripts().map(function(script) { return eval(script) });
  232.   },
  233.   escapeHTML: function() {
  234.     var self = arguments.callee;
  235.     self.text.data = this;
  236.     return self.div.innerHTML;
  237.   },
  238.   unescapeHTML: function() {
  239.     var div = document.createElement('div');
  240.     div.innerHTML = this.stripTags();
  241.     return div.childNodes[0] ? (div.childNodes.length > 1 ?
  242.       $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
  243.       div.childNodes[0].nodeValue) : '';
  244.   },
  245.   toQueryParams: function(separator) {
  246.     var match = this.strip().match(/([^?#]*)(#.*)?$/);
  247.     if (!match) return {};
  248.     return match[1].split(separator || '&').inject({}, function(hash, pair) {
  249.       if ((pair = pair.split('='))[0]) {
  250.         var key = decodeURIComponent(pair.shift());
  251.         var value = pair.length > 1 ? pair.join('=') : pair[0];
  252.         if (value != undefined) value = decodeURIComponent(value);
  253.         if (key in hash) {
  254.           if (hash[key].constructor != Array) hash[key] = [hash[key]];
  255.           hash[key].push(value);
  256.         }
  257.         else hash[key] = value;
  258.       }
  259.       return hash;
  260.     });
  261.   },
  262.   toArray: function() {
  263.     return this.split('');
  264.   },
  265.   succ: function() {
  266.     return this.slice(0, this.length - 1) +
  267.       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  268.   },
  269.   times: function(count) {
  270.     var result = '';
  271.     for (var i = 0; i < count; i++) result += this;
  272.     return result;
  273.   },
  274.   camelize: function() {
  275.     var parts = this.split('-'), len = parts.length;
  276.     if (len == 1) return parts[0];
  277.     var camelized = this.charAt(0) == '-'
  278.       ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
  279.       : parts[0];
  280.     for (var i = 1; i < len; i++)
  281.       camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
  282.     return camelized;
  283.   },
  284.   capitalize: function() {
  285.     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  286.   },
  287.   underscore: function() {
  288.     return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-zd])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  289.   },
  290.   dasherize: function() {
  291.     return this.gsub(/_/,'-');
  292.   },
  293.   inspect: function(useDoubleQuotes) {
  294.     var escapedString = this.gsub(/[x00-x1f\]/, function(match) {
  295.       var character = String.specialChar[match[0]];
  296.       return character ? character : '\u00' + match[0].charCodeAt().toPaddedString(2, 16);
  297.     });
  298.     if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\"') + '"';
  299.     return "'" + escapedString.replace(/'/g, '\'') + "'";
  300.   },
  301.   toJSON: function() {
  302.     return this.inspect(true);
  303.   },
  304.   unfilterJSON: function(filter) {
  305.     return this.sub(filter || Prototype.JSONFilter, '#{1}');
  306.   },
  307.   evalJSON: function(sanitize) {
  308.     var json = this.unfilterJSON();
  309.     try {
  310.       if (!sanitize || (/^("(\.|[^"\nr])*?"|[,:{}[]0-9.-+Eaeflnr-u nrt])+?$/.test(json)))
  311.         return eval('(' + json + ')');
  312.     } catch (e) { }
  313.     throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  314.   },
  315.   include: function(pattern) {
  316.     return this.indexOf(pattern) > -1;
  317.   },
  318.   startsWith: function(pattern) {
  319.     return this.indexOf(pattern) === 0;
  320.   },
  321.   endsWith: function(pattern) {
  322.     var d = this.length - pattern.length;
  323.     return d >= 0 && this.lastIndexOf(pattern) === d;
  324.   },
  325.   empty: function() {
  326.     return this == '';
  327.   },
  328.   blank: function() {
  329.     return /^s*$/.test(this);
  330.   }
  331. });
  332. if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  333.   escapeHTML: function() {
  334.     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  335.   },
  336.   unescapeHTML: function() {
  337.     return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  338.   }
  339. });
  340. String.prototype.gsub.prepareReplacement = function(replacement) {
  341.   if (typeof replacement == 'function') return replacement;
  342.   var template = new Template(replacement);
  343.   return function(match) { return template.evaluate(match) };
  344. }
  345. String.prototype.parseQuery = String.prototype.toQueryParams;
  346. Object.extend(String.prototype.escapeHTML, {
  347.   div:  document.createElement('div'),
  348.   text: document.createTextNode('')
  349. });
  350. with (String.prototype.escapeHTML) div.appendChild(text);
  351. var Template = Class.create();
  352. Template.Pattern = /(^|.|r|n)(#{(.*?)})/;
  353. Template.prototype = {
  354.   initialize: function(template, pattern) {
  355.     this.template = template.toString();
  356.     this.pattern  = pattern || Template.Pattern;
  357.   },
  358.   evaluate: function(object) {
  359.     return this.template.gsub(this.pattern, function(match) {
  360.       var before = match[1];
  361.       if (before == '\') return match[2];
  362.       return before + String.interpret(object[match[3]]);
  363.     });
  364.   }
  365. }
  366. var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
  367. var Enumerable = {
  368.   each: function(iterator) {
  369.     var index = 0;
  370.     try {
  371.       this._each(function(value) {
  372.         iterator(value, index++);
  373.       });
  374.     } catch (e) {
  375.       if (e != $break) throw e;
  376.     }
  377.     return this;
  378.   },
  379.   eachSlice: function(number, iterator) {
  380.     var index = -number, slices = [], array = this.toArray();
  381.     while ((index += number) < array.length)
  382.       slices.push(array.slice(index, index+number));
  383.     return slices.map(iterator);
  384.   },
  385.   all: function(iterator) {
  386.     var result = true;
  387.     this.each(function(value, index) {
  388.       result = result && !!(iterator || Prototype.K)(value, index);
  389.       if (!result) throw $break;
  390.     });
  391.     return result;
  392.   },
  393.   any: function(iterator) {
  394.     var result = false;
  395.     this.each(function(value, index) {
  396.       if (result = !!(iterator || Prototype.K)(value, index))
  397.         throw $break;
  398.     });
  399.     return result;
  400.   },
  401.   collect: function(iterator) {
  402.     var results = [];
  403.     this.each(function(value, index) {
  404.       results.push((iterator || Prototype.K)(value, index));
  405.     });
  406.     return results;
  407.   },
  408.   detect: function(iterator) {
  409.     var result;
  410.     this.each(function(value, index) {
  411.       if (iterator(value, index)) {
  412.         result = value;
  413.         throw $break;
  414.       }
  415.     });
  416.     return result;
  417.   },
  418.   findAll: function(iterator) {
  419.     var results = [];
  420.     this.each(function(value, index) {
  421.       if (iterator(value, index))
  422.         results.push(value);
  423.     });
  424.     return results;
  425.   },
  426.   grep: function(pattern, iterator) {
  427.     var results = [];
  428.     this.each(function(value, index) {
  429.       var stringValue = value.toString();
  430.       if (stringValue.match(pattern))
  431.         results.push((iterator || Prototype.K)(value, index));
  432.     })
  433.     return results;
  434.   },
  435.   include: function(object) {
  436.     var found = false;
  437.     this.each(function(value) {
  438.       if (value == object) {
  439.         found = true;
  440.         throw $break;
  441.       }
  442.     });
  443.     return found;
  444.   },
  445.   inGroupsOf: function(number, fillWith) {
  446.     fillWith = fillWith === undefined ? null : fillWith;
  447.     return this.eachSlice(number, function(slice) {
  448.       while(slice.length < number) slice.push(fillWith);
  449.       return slice;
  450.     });
  451.   },
  452.   inject: function(memo, iterator) {
  453.     this.each(function(value, index) {
  454.       memo = iterator(memo, value, index);
  455.     });
  456.     return memo;
  457.   },
  458.   invoke: function(method) {
  459.     var args = $A(arguments).slice(1);
  460.     return this.map(function(value) {
  461.       return value[method].apply(value, args);
  462.     });
  463.   },
  464.   max: function(iterator) {
  465.     var result;
  466.     this.each(function(value, index) {
  467.       value = (iterator || Prototype.K)(value, index);
  468.       if (result == undefined || value >= result)
  469.         result = value;
  470.     });
  471.     return result;
  472.   },
  473.   min: function(iterator) {
  474.     var result;
  475.     this.each(function(value, index) {
  476.       value = (iterator || Prototype.K)(value, index);
  477.       if (result == undefined || value < result)
  478.         result = value;
  479.     });
  480.     return result;
  481.   },
  482.   partition: function(iterator) {
  483.     var trues = [], falses = [];
  484.     this.each(function(value, index) {
  485.       ((iterator || Prototype.K)(value, index) ?
  486.         trues : falses).push(value);
  487.     });
  488.     return [trues, falses];
  489.   },
  490.   pluck: function(property) {
  491.     var results = [];
  492.     this.each(function(value, index) {
  493.       results.push(value[property]);
  494.     });
  495.     return results;
  496.   },
  497.   reject: function(iterator) {
  498.     var results = [];
  499.     this.each(function(value, index) {
  500.       if (!iterator(value, index))
  501.         results.push(value);
  502.     });
  503.     return results;
  504.   },
  505.   sortBy: function(iterator) {
  506.     return this.map(function(value, index) {
  507.       return {value: value, criteria: iterator(value, index)};
  508.     }).sort(function(left, right) {
  509.       var a = left.criteria, b = right.criteria;
  510.       return a < b ? -1 : a > b ? 1 : 0;
  511.     }).pluck('value');
  512.   },
  513.   toArray: function() {
  514.     return this.map();
  515.   },
  516.   zip: function() {
  517.     var iterator = Prototype.K, args = $A(arguments);
  518.     if (typeof args.last() == 'function')
  519.       iterator = args.pop();
  520.     var collections = [this].concat(args).map($A);
  521.     return this.map(function(value, index) {
  522.       return iterator(collections.pluck(index));
  523.     });
  524.   },
  525.   size: function() {
  526.     return this.toArray().length;
  527.   },
  528.   inspect: function() {
  529.     return '#<Enumerable:' + this.toArray().inspect() + '>';
  530.   }
  531. }
  532. Object.extend(Enumerable, {
  533.   map:     Enumerable.collect,
  534.   find:    Enumerable.detect,
  535.   select:  Enumerable.findAll,
  536.   member:  Enumerable.include,
  537.   entries: Enumerable.toArray
  538. });
  539. var $A = Array.from = function(iterable) {
  540.   if (!iterable) return [];
  541.   if (iterable.toArray) {
  542.     return iterable.toArray();
  543.   } else {
  544.     var results = [];
  545.     for (var i = 0, length = iterable.length; i < length; i++)
  546.       results.push(iterable[i]);
  547.     return results;
  548.   }
  549. }
  550. if (Prototype.Browser.WebKit) {
  551.   $A = Array.from = function(iterable) {
  552.     if (!iterable) return [];
  553.     if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
  554.       iterable.toArray) {
  555.       return iterable.toArray();
  556.     } else {
  557.       var results = [];
  558.       for (var i = 0, length = iterable.length; i < length; i++)
  559.         results.push(iterable[i]);
  560.       return results;
  561.     }
  562.   }
  563. }
  564. Object.extend(Array.prototype, Enumerable);
  565. if (!Array.prototype._reverse)
  566.   Array.prototype._reverse = Array.prototype.reverse;
  567. Object.extend(Array.prototype, {
  568.   _each: function(iterator) {
  569.     for (var i = 0, length = this.length; i < length; i++)
  570.       iterator(this[i]);
  571.   },
  572.   clear: function() {
  573.     this.length = 0;
  574.     return this;
  575.   },
  576.   first: function() {
  577.     return this[0];
  578.   },
  579.   last: function() {
  580.     return this[this.length - 1];
  581.   },
  582.   compact: function() {
  583.     return this.select(function(value) {
  584.       return value != null;
  585.     });
  586.   },
  587.   flatten: function() {
  588.     return this.inject([], function(array, value) {
  589.       return array.concat(value && value.constructor == Array ?
  590.         value.flatten() : [value]);
  591.     });
  592.   },
  593.   without: function() {
  594.     var values = $A(arguments);
  595.     return this.select(function(value) {
  596.       return !values.include(value);
  597.     });
  598.   },
  599.   indexOf: function(object) {
  600.     for (var i = 0, length = this.length; i < length; i++)
  601.       if (this[i] == object) return i;
  602.     return -1;
  603.   },
  604.   reverse: function(inline) {
  605.     return (inline !== false ? this : this.toArray())._reverse();
  606.   },
  607.   reduce: function() {
  608.     return this.length > 1 ? this : this[0];
  609.   },
  610.   uniq: function(sorted) {
  611.     return this.inject([], function(array, value, index) {
  612.       if (0 == index || (sorted ? array.last() != value : !array.include(value)))
  613.         array.push(value);
  614.       return array;
  615.     });
  616.   },
  617.   clone: function() {
  618.     return [].concat(this);
  619.   },
  620.   size: function() {
  621.     return this.length;
  622.   },
  623.   inspect: function() {
  624.     return '[' + this.map(Object.inspect).join(', ') + ']';
  625.   },
  626.   toJSON: function() {
  627.     var results = [];
  628.     this.each(function(object) {
  629.       var value = Object.toJSON(object);
  630.       if (value !== undefined) results.push(value);
  631.     });
  632.     return '[' + results.join(', ') + ']';
  633.   }
  634. });
  635. Array.prototype.toArray = Array.prototype.clone;
  636. function $w(string) {
  637.   string = string.strip();
  638.   return string ? string.split(/s+/) : [];
  639. }
  640. if (Prototype.Browser.Opera){
  641.   Array.prototype.concat = function() {
  642.     var array = [];
  643.     for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
  644.     for (var i = 0, length = arguments.length; i < length; i++) {
  645.       if (arguments[i].constructor == Array) {
  646.         for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
  647.           array.push(arguments[i][j]);
  648.       } else {
  649.         array.push(arguments[i]);
  650.       }
  651.     }
  652.     return array;
  653.   }
  654. }
  655. var Hash = function(object) {
  656.   if (object instanceof Hash) this.merge(object);
  657.   else Object.extend(this, object || {});
  658. };
  659. Object.extend(Hash, {
  660.   toQueryString: function(obj) {
  661.     var parts = [];
  662.     parts.add = arguments.callee.addPair;
  663.     this.prototype._each.call(obj, function(pair) {
  664.       if (!pair.key) return;
  665.       var value = pair.value;
  666.       if (value && typeof value == 'object') {
  667.         if (value.constructor == Array) value.each(function(value) {
  668.           parts.add(pair.key, value);
  669.         });
  670.         return;
  671.       }
  672.       parts.add(pair.key, value);
  673.     });
  674.     return parts.join('&');
  675.   },
  676.   toJSON: function(object) {
  677.     var results = [];
  678.     this.prototype._each.call(object, function(pair) {
  679.       var value = Object.toJSON(pair.value);
  680.       if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
  681.     });
  682.     return '{' + results.join(', ') + '}';
  683.   }
  684. });
  685. Hash.toQueryString.addPair = function(key, value, prefix) {
  686.   key = encodeURIComponent(key);
  687.   if (value === undefined) this.push(key);
  688.   else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
  689. }
  690. Object.extend(Hash.prototype, Enumerable);
  691. Object.extend(Hash.prototype, {
  692.   _each: function(iterator) {
  693.     for (var key in this) {
  694.       var value = this[key];
  695.       if (value && value == Hash.prototype[key]) continue;
  696.       var pair = [key, value];
  697.       pair.key = key;
  698.       pair.value = value;
  699.       iterator(pair);
  700.     }
  701.   },
  702.   keys: function() {
  703.     return this.pluck('key');
  704.   },
  705.   values: function() {
  706.     return this.pluck('value');
  707.   },
  708.   merge: function(hash) {
  709.     return $H(hash).inject(this, function(mergedHash, pair) {
  710.       mergedHash[pair.key] = pair.value;
  711.       return mergedHash;
  712.     });
  713.   },
  714.   remove: function() {
  715.     var result;
  716.     for(var i = 0, length = arguments.length; i < length; i++) {
  717.       var value = this[arguments[i]];
  718.       if (value !== undefined){
  719.         if (result === undefined) result = value;
  720.         else {
  721.           if (result.constructor != Array) result = [result];
  722.           result.push(value)
  723.         }
  724.       }
  725.       delete this[arguments[i]];
  726.     }
  727.     return result;
  728.   },
  729.   toQueryString: function() {
  730.     return Hash.toQueryString(this);
  731.   },
  732.   inspect: function() {
  733.     return '#<Hash:{' + this.map(function(pair) {
  734.       return pair.map(Object.inspect).join(': ');
  735.     }).join(', ') + '}>';
  736.   },
  737.   toJSON: function() {
  738.     return Hash.toJSON(this);
  739.   }
  740. });
  741. function $H(object) {
  742.   if (object instanceof Hash) return object;
  743.   return new Hash(object);
  744. };
  745. // Safari iterates over shadowed properties
  746. if (function() {
  747.   var i = 0, Test = function(value) { this.key = value };
  748.   Test.prototype.key = 'foo';
  749.   for (var property in new Test('bar')) i++;
  750.   return i > 1;
  751. }()) Hash.prototype._each = function(iterator) {
  752.   var cache = [];
  753.   for (var key in this) {
  754.     var value = this[key];
  755.     if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
  756.     cache.push(key);
  757.     var pair = [key, value];
  758.     pair.key = key;
  759.     pair.value = value;
  760.     iterator(pair);
  761.   }
  762. };
  763. ObjectRange = Class.create();
  764. Object.extend(ObjectRange.prototype, Enumerable);
  765. Object.extend(ObjectRange.prototype, {
  766.   initialize: function(start, end, exclusive) {
  767.     this.start = start;
  768.     this.end = end;
  769.     this.exclusive = exclusive;
  770.   },
  771.   _each: function(iterator) {
  772.     var value = this.start;
  773.     while (this.include(value)) {
  774.       iterator(value);
  775.       value = value.succ();
  776.     }
  777.   },
  778.   include: function(value) {
  779.     if (value < this.start)
  780.       return false;
  781.     if (this.exclusive)
  782.       return value < this.end;
  783.     return value <= this.end;
  784.   }
  785. });
  786. var $R = function(start, end, exclusive) {
  787.   return new ObjectRange(start, end, exclusive);
  788. }
  789. var Ajax = {
  790.   getTransport: function() {
  791.     return Try.these(
  792.       function() {return new XMLHttpRequest()},
  793.       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
  794.       function() {return new ActiveXObject('Microsoft.XMLHTTP')}
  795.     ) || false;
  796.   },
  797.   activeRequestCount: 0
  798. }
  799. Ajax.Responders = {
  800.   responders: [],
  801.   _each: function(iterator) {
  802.     this.responders._each(iterator);
  803.   },
  804.   register: function(responder) {
  805.     if (!this.include(responder))
  806.       this.responders.push(responder);
  807.   },
  808.   unregister: function(responder) {
  809.     this.responders = this.responders.without(responder);
  810.   },
  811.   dispatch: function(callback, request, transport, json) {
  812.     this.each(function(responder) {
  813.       if (typeof responder[callback] == 'function') {
  814.         try {
  815.           responder[callback].apply(responder, [request, transport, json]);
  816.         } catch (e) {}
  817.       }
  818.     });
  819.   }
  820. };
  821. Object.extend(Ajax.Responders, Enumerable);
  822. Ajax.Responders.register({
  823.   onCreate: function() {
  824.     Ajax.activeRequestCount++;
  825.   },
  826.   onComplete: function() {
  827.     Ajax.activeRequestCount--;
  828.   }
  829. });
  830. Ajax.Base = function() {};
  831. Ajax.Base.prototype = {
  832.   setOptions: function(options) {
  833.     this.options = {
  834.       method:       'post',
  835.       asynchronous: true,
  836.       contentType:  'application/x-www-form-urlencoded',
  837.       encoding:     'UTF-8',
  838.       parameters:   ''
  839.     }
  840.     Object.extend(this.options, options || {});
  841.     this.options.method = this.options.method.toLowerCase();
  842.     if (typeof this.options.parameters == 'string')
  843.       this.options.parameters = this.options.parameters.toQueryParams();
  844.   }
  845. }
  846. Ajax.Request = Class.create();
  847. Ajax.Request.Events =
  848.   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
  849. Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
  850.   _complete: false,
  851.   initialize: function(url, options) {
  852.     this.transport = Ajax.getTransport();
  853.     this.setOptions(options);
  854.     this.request(url);
  855.   },
  856.   request: function(url) {
  857.     this.url = url;
  858.     this.method = this.options.method;
  859.     var params = Object.clone(this.options.parameters);
  860.     if (!['get', 'post'].include(this.method)) {
  861.       // simulate other verbs over post
  862.       params['_method'] = this.method;
  863.       this.method = 'post';
  864.     }
  865.     this.parameters = params;
  866.     if (params = Hash.toQueryString(params)) {
  867.       // when GET, append parameters to URL
  868.       if (this.method == 'get')
  869.         this.url += (this.url.include('?') ? '&' : '?') + params;
  870.       else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
  871.         params += '&_=';
  872.     }
  873.     try {
  874.       if (this.options.onCreate) this.options.onCreate(this.transport);
  875.       Ajax.Responders.dispatch('onCreate', this, this.transport);
  876.       this.transport.open(this.method.toUpperCase(), this.url,
  877.         this.options.asynchronous);
  878.       if (this.options.asynchronous)
  879.         setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
  880.       this.transport.onreadystatechange = this.onStateChange.bind(this);
  881.       this.setRequestHeaders();
  882.       this.body = this.method == 'post' ? (this.options.postBody || params) : null;
  883.       this.transport.send(this.body);
  884.       /* Force Firefox to handle ready state 4 for synchronous requests */
  885.       if (!this.options.asynchronous && this.transport.overrideMimeType)
  886.         this.onStateChange();
  887.     }
  888.     catch (e) {
  889.       this.dispatchException(e);
  890.     }
  891.   },
  892.   onStateChange: function() {
  893.     var readyState = this.transport.readyState;
  894.     if (readyState > 1 && !((readyState == 4) && this._complete))
  895.       this.respondToReadyState(this.transport.readyState);
  896.   },
  897.   setRequestHeaders: function() {
  898.     var headers = {
  899.       'X-Requested-With': 'XMLHttpRequest',
  900.       'X-Prototype-Version': Prototype.Version,
  901.       'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
  902.     };
  903.     if (this.method == 'post') {
  904.       headers['Content-type'] = this.options.contentType +
  905.         (this.options.encoding ? '; charset=' + this.options.encoding : '');
  906.       /* Force "Connection: close" for older Mozilla browsers to work
  907.        * around a bug where XMLHttpRequest sends an incorrect
  908.        * Content-length header. See Mozilla Bugzilla #246651.
  909.        */
  910.       if (this.transport.overrideMimeType &&
  911.           (navigator.userAgent.match(/Gecko/(d{4})/) || [0,2005])[1] < 2005)
  912.             headers['Connection'] = 'close';
  913.     }
  914.     // user-defined headers
  915.     if (typeof this.options.requestHeaders == 'object') {
  916.       var extras = this.options.requestHeaders;
  917.       if (typeof extras.push == 'function')
  918.         for (var i = 0, length = extras.length; i < length; i += 2)
  919.           headers[extras[i]] = extras[i+1];
  920.       else
  921.         $H(extras).each(function(pair) { headers[pair.key] = pair.value });
  922.     }
  923.     for (var name in headers)
  924.       this.transport.setRequestHeader(name, headers[name]);
  925.   },
  926.   success: function() {
  927.     return !this.transport.status
  928.         || (this.transport.status >= 200 && this.transport.status < 300);
  929.   },
  930.   respondToReadyState: function(readyState) {
  931.     var state = Ajax.Request.Events[readyState];
  932.     var transport = this.transport, json = this.evalJSON();
  933.     if (state == 'Complete') {
  934.       try {
  935.         this._complete = true;
  936.         (this.options['on' + this.transport.status]
  937.          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
  938.          || Prototype.emptyFunction)(transport, json);
  939.       } catch (e) {
  940.         this.dispatchException(e);
  941.       }
  942.       var contentType = this.getHeader('Content-type');
  943.       if (contentType && contentType.strip().
  944.         match(/^(text|application)/(x-)?(java|ecma)script(;.*)?$/i))
  945.           this.evalResponse();
  946.     }
  947.     try {
  948.       (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
  949.       Ajax.Responders.dispatch('on' + state, this, transport, json);
  950.     } catch (e) {
  951.       this.dispatchException(e);
  952.     }
  953.     if (state == 'Complete') {
  954.       // avoid memory leak in MSIE: clean up
  955.       this.transport.onreadystatechange = Prototype.emptyFunction;
  956.     }
  957.   },
  958.   getHeader: function(name) {
  959.     try {
  960.       return this.transport.getResponseHeader(name);
  961.     } catch (e) { return null }
  962.   },
  963.   evalJSON: function() {
  964.     try {
  965.       var json = this.getHeader('X-JSON');
  966.       return json ? json.evalJSON() : null;
  967.     } catch (e) { return null }
  968.   },
  969.   evalResponse: function() {
  970.     try {
  971.       return eval((this.transport.responseText || '').unfilterJSON());
  972.     } catch (e) {
  973.       this.dispatchException(e);
  974.     }
  975.   },
  976.   dispatchException: function(exception) {
  977.     (this.options.onException || Prototype.emptyFunction)(this, exception);
  978.     Ajax.Responders.dispatch('onException', this, exception);
  979.   }
  980. });
  981. Ajax.Updater = Class.create();
  982. Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  983.   initialize: function(container, url, options) {
  984.     this.container = {
  985.       success: (container.success || container),
  986.       failure: (container.failure || (container.success ? null : container))
  987.     }
  988.     this.transport = Ajax.getTransport();
  989.     this.setOptions(options);
  990.     var onComplete = this.options.onComplete || Prototype.emptyFunction;
  991.     this.options.onComplete = (function(transport, param) {
  992.       this.updateContent();
  993.       onComplete(transport, param);
  994.     }).bind(this);
  995.     this.request(url);
  996.   },
  997.   updateContent: function() {
  998.     var receiver = this.container[this.success() ? 'success' : 'failure'];
  999.     var response = this.transport.responseText;
  1000.     if (!this.options.evalScripts) response = response.stripScripts();
  1001.     if (receiver = $(receiver)) {
  1002.       if (this.options.insertion)
  1003.         new this.options.insertion(receiver, response);
  1004.       else
  1005.         receiver.update(response);
  1006.     }
  1007.     if (this.success()) {
  1008.       if (this.onComplete)
  1009.         setTimeout(this.onComplete.bind(this), 10);
  1010.     }
  1011.   }
  1012. });
  1013. Ajax.PeriodicalUpdater = Class.create();
  1014. Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  1015.   initialize: function(container, url, options) {
  1016.     this.setOptions(options);
  1017.     this.onComplete = this.options.onComplete;
  1018.     this.frequency = (this.options.frequency || 2);
  1019.     this.decay = (this.options.decay || 1);
  1020.     this.updater = {};
  1021.     this.container = container;
  1022.     this.url = url;
  1023.     this.start();
  1024.   },
  1025.   start: function() {
  1026.     this.options.onComplete = this.updateComplete.bind(this);
  1027.     this.onTimerEvent();
  1028.   },
  1029.   stop: function() {
  1030.     this.updater.options.onComplete = undefined;
  1031.     clearTimeout(this.timer);
  1032.     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  1033.   },
  1034.   updateComplete: function(request) {
  1035.     if (this.options.decay) {
  1036.       this.decay = (request.responseText == this.lastText ?
  1037.         this.decay * this.options.decay : 1);
  1038.       this.lastText = request.responseText;
  1039.     }
  1040.     this.timer = setTimeout(this.onTimerEvent.bind(this),
  1041.       this.decay * this.frequency * 1000);
  1042.   },
  1043.   onTimerEvent: function() {
  1044.     this.updater = new Ajax.Updater(this.container, this.url, this.options);
  1045.   }
  1046. });
  1047. function $(element) {
  1048.   if (arguments.length > 1) {
  1049.     for (var i = 0, elements = [], length = arguments.length; i < length; i++)
  1050.       elements.push($(arguments[i]));
  1051.     return elements;
  1052.   }
  1053.   if (typeof element == 'string')
  1054.     element = document.getElementById(element);
  1055.   return Element.extend(element);
  1056. }
  1057. if (Prototype.BrowserFeatures.XPath) {
  1058.   document._getElementsByXPath = function(expression, parentElement) {
  1059.     var results = [];
  1060.     var query = document.evaluate(expression, $(parentElement) || document,
  1061.       null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  1062.     for (var i = 0, length = query.snapshotLength; i < length; i++)
  1063.       results.push(query.snapshotItem(i));
  1064.     return results;
  1065.   };
  1066.   document.getElementsByClassName = function(className, parentElement) {
  1067.     var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
  1068.     return document._getElementsByXPath(q, parentElement);
  1069.   }
  1070. } else document.getElementsByClassName = function(className, parentElement) {
  1071.   var children = ($(parentElement) || document.body).getElementsByTagName('*');
  1072.   var elements = [], child;
  1073.   for (var i = 0, length = children.length; i < length; i++) {
  1074.     child = children[i];
  1075.     if (Element.hasClassName(child, className))
  1076.       elements.push(Element.extend(child));
  1077.   }
  1078.   return elements;
  1079. };
  1080. /*--------------------------------------------------------------------------*/
  1081. if (!window.Element) var Element = {};
  1082. Element.extend = function(element) {
  1083.   var F = Prototype.BrowserFeatures;
  1084.   if (!element || !element.tagName || element.nodeType == 3 ||
  1085.    element._extended || F.SpecificElementExtensions || element == window)
  1086.     return element;
  1087.   var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
  1088.    T = Element.Methods.ByTag;
  1089.   // extend methods for all tags (Safari doesn't need this)
  1090.   if (!F.ElementExtensions) {
  1091.     Object.extend(methods, Element.Methods),
  1092.     Object.extend(methods, Element.Methods.Simulated);
  1093.   }
  1094.   // extend methods for specific tags
  1095.   if (T[tagName]) Object.extend(methods, T[tagName]);
  1096.   for (var property in methods) {
  1097.     var value = methods[property];
  1098.     if (typeof value == 'function' && !(property in element))
  1099.       element[property] = cache.findOrStore(value);
  1100.   }
  1101.   element._extended = Prototype.emptyFunction;
  1102.   return element;
  1103. };
  1104. Element.extend.cache = {
  1105.   findOrStore: function(value) {
  1106.     return this[value] = this[value] || function() {
  1107.       return value.apply(null, [this].concat($A(arguments)));
  1108.     }
  1109.   }
  1110. };
  1111. Element.Methods = {
  1112.   visible: function(element) {
  1113.     return $(element).style.display != 'none';
  1114.   },
  1115.   toggle: function(element) {
  1116.     element = $(element);
  1117.     Element[Element.visible(element) ? 'hide' : 'show'](element);
  1118.     return element;
  1119.   },
  1120.   hide: function(element) {
  1121.     $(element).style.display = 'none';
  1122.     return element;
  1123.   },
  1124.   show: function(element) {
  1125.     $(element).style.display = '';
  1126.     return element;
  1127.   },
  1128.   remove: function(element) {
  1129.     element = $(element);
  1130.     element.parentNode.removeChild(element);
  1131.     return element;
  1132.   },
  1133.   update: function(element, html) {
  1134.     html = typeof html == 'undefined' ? '' : html.toString();
  1135.     $(element).innerHTML = html.stripScripts();
  1136.     setTimeout(function() {html.evalScripts()}, 10);
  1137.     return element;
  1138.   },
  1139.   replace: function(element, html) {
  1140.     element = $(element);
  1141.     html = typeof html == 'undefined' ? '' : html.toString();
  1142.     if (element.outerHTML) {
  1143.       element.outerHTML = html.stripScripts();
  1144.     } else {
  1145.       var range = element.ownerDocument.createRange();
  1146.       range.selectNodeContents(element);
  1147.       element.parentNode.replaceChild(
  1148.         range.createContextualFragment(html.stripScripts()), element);
  1149.     }
  1150.     setTimeout(function() {html.evalScripts()}, 10);
  1151.     return element;
  1152.   },
  1153.   inspect: function(element) {
  1154.     element = $(element);
  1155.     var result = '<' + element.tagName.toLowerCase();
  1156.     $H({'id': 'id', 'className': 'class'}).each(function(pair) {
  1157.       var property = pair.first(), attribute = pair.last();
  1158.       var value = (element[property] || '').toString();
  1159.       if (value) result += ' ' + attribute + '=' + value.inspect(true);
  1160.     });
  1161.     return result + '>';
  1162.   },
  1163.   recursivelyCollect: function(element, property) {
  1164.     element = $(element);
  1165.     var elements = [];
  1166.     while (element = element[property])
  1167.       if (element.nodeType == 1)
  1168.         elements.push(Element.extend(element));
  1169.     return elements;
  1170.   },
  1171.   ancestors: function(element) {
  1172.     return $(element).recursivelyCollect('parentNode');
  1173.   },
  1174.   descendants: function(element) {
  1175.     return $A($(element).getElementsByTagName('*')).each(Element.extend);
  1176.   },
  1177.   firstDescendant: function(element) {
  1178.     element = $(element).firstChild;
  1179.     while (element && element.nodeType != 1) element = element.nextSibling;
  1180.     return $(element);
  1181.   },
  1182.   immediateDescendants: function(element) {
  1183.     if (!(element = $(element).firstChild)) return [];
  1184.     while (element && element.nodeType != 1) element = element.nextSibling;
  1185.     if (element) return [element].concat($(element).nextSiblings());
  1186.     return [];
  1187.   },
  1188.   previousSiblings: function(element) {
  1189.     return $(element).recursivelyCollect('previousSibling');
  1190.   },
  1191.   nextSiblings: function(element) {
  1192.     return $(element).recursivelyCollect('nextSibling');
  1193.   },
  1194.   siblings: function(element) {
  1195.     element = $(element);
  1196.     return element.previousSiblings().reverse().concat(element.nextSiblings());
  1197.   },
  1198.   match: function(element, selector) {
  1199.     if (typeof selector == 'string')
  1200.       selector = new Selector(selector);
  1201.     return selector.match($(element));
  1202.   },
  1203.   up: function(element, expression, index) {
  1204.     element = $(element);
  1205.     if (arguments.length == 1) return $(element.parentNode);
  1206.     var ancestors = element.ancestors();
  1207.     return expression ? Selector.findElement(ancestors, expression, index) :
  1208.       ancestors[index || 0];
  1209.   },
  1210.   down: function(element, expression, index) {
  1211.     element = $(element);
  1212.     if (arguments.length == 1) return element.firstDescendant();
  1213.     var descendants = element.descendants();
  1214.     return expression ? Selector.findElement(descendants, expression, index) :
  1215.       descendants[index || 0];
  1216.   },
  1217.   previous: function(element, expression, index) {
  1218.     element = $(element);
  1219.     if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
  1220.     var previousSiblings = element.previousSiblings();
  1221.     return expression ? Selector.findElement(previousSiblings, expression, index) :
  1222.       previousSiblings[index || 0];
  1223.   },
  1224.   next: function(element, expression, index) {
  1225.     element = $(element);
  1226.     if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
  1227.     var nextSiblings = element.nextSiblings();
  1228.     return expression ? Selector.findElement(nextSiblings, expression, index) :
  1229.       nextSiblings[index || 0];
  1230.   },
  1231.   getElementsBySelector: function() {
  1232.     var args = $A(arguments), element = $(args.shift());
  1233.     return Selector.findChildElements(element, args);
  1234.   },
  1235.   getElementsByClassName: function(element, className) {
  1236.     return document.getElementsByClassName(className, element);
  1237.   },
  1238.   readAttribute: function(element, name) {
  1239.     element = $(element);
  1240.     if (Prototype.Browser.IE) {
  1241.       if (!element.attributes) return null;
  1242.       var t = Element._attributeTranslations;
  1243.       if (t.values[name]) return t.values[name](element, name);
  1244.       if (t.names[name])  name = t.names[name];
  1245.       var attribute = element.attributes[name];
  1246.       return attribute ? attribute.nodeValue : null;
  1247.     }
  1248.     return element.getAttribute(name);
  1249.   },
  1250.   getHeight: function(element) {
  1251.     return $(element).getDimensions().height;
  1252.   },
  1253.   getWidth: function(element) {
  1254.     return $(element).getDimensions().width;
  1255.   },
  1256.   classNames: function(element) {
  1257.     return new Element.ClassNames(element);
  1258.   },
  1259.   hasClassName: function(element, className) {
  1260.     if (!(element = $(element))) return;
  1261.     var elementClassName = element.className;
  1262.     if (elementClassName.length == 0) return false;
  1263.     if (elementClassName == className ||
  1264.         elementClassName.match(new RegExp("(^|\s)" + className + "(\s|$)")))
  1265.       return true;
  1266.     return false;
  1267.   },
  1268.   addClassName: function(element, className) {
  1269.     if (!(element = $(element))) return;
  1270.     Element.classNames(element).add(className);
  1271.     return element;
  1272.   },
  1273.   removeClassName: function(element, className) {
  1274.     if (!(element = $(element))) return;
  1275.     Element.classNames(element).remove(className);
  1276.     return element;
  1277.   },
  1278.   toggleClassName: function(element, className) {
  1279.     if (!(element = $(element))) return;
  1280.     Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
  1281.     return element;
  1282.   },
  1283.   observe: function() {
  1284.     Event.observe.apply(Event, arguments);
  1285.     return $A(arguments).first();
  1286.   },
  1287.   stopObserving: function() {
  1288.     Event.stopObserving.apply(Event, arguments);
  1289.     return $A(arguments).first();
  1290.   },
  1291.   // removes whitespace-only text node children
  1292.   cleanWhitespace: function(element) {
  1293.     element = $(element);
  1294.     var node = element.firstChild;
  1295.     while (node) {
  1296.       var nextNode = node.nextSibling;
  1297.       if (node.nodeType == 3 && !/S/.test(node.nodeValue))
  1298.         element.removeChild(node);
  1299.       node = nextNode;
  1300.     }
  1301.     return element;
  1302.   },
  1303.   empty: function(element) {
  1304.     return $(element).innerHTML.blank();
  1305.   },
  1306.   descendantOf: function(element, ancestor) {
  1307.     element = $(element), ancestor = $(ancestor);
  1308.     while (element = element.parentNode)
  1309.       if (element == ancestor) return true;
  1310.     return false;
  1311.   },
  1312.   scrollTo: function(element) {
  1313.     element = $(element);
  1314.     var pos = Position.cumulativeOffset(element);
  1315.     window.scrollTo(pos[0], pos[1]);
  1316.     return element;
  1317.   },
  1318.   getStyle: function(element, style) {
  1319.     element = $(element);
  1320.     style = style == 'float' ? 'cssFloat' : style.camelize();
  1321.     var value = element.style[style];
  1322.     if (!value) {
  1323.       var css = document.defaultView.getComputedStyle(element, null);
  1324.       value = css ? css[style] : null;
  1325.     }
  1326.     if (style == 'opacity') return value ? parseFloat(value) : 1.0;
  1327.     return value == 'auto' ? null : value;
  1328.   },
  1329.   getOpacity: function(element) {
  1330.     return $(element).getStyle('opacity');
  1331.   },
  1332.   setStyle: function(element, styles, camelized) {
  1333.     element = $(element);
  1334.     var elementStyle = element.style;
  1335.     for (var property in styles)
  1336.       if (property == 'opacity') element.setOpacity(styles[property])
  1337.       else
  1338.         elementStyle[(property == 'float' || property == 'cssFloat') ?
  1339.           (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
  1340.           (camelized ? property : property.camelize())] = styles[property];
  1341.     return element;
  1342.   },
  1343.   setOpacity: function(element, value) {
  1344.     element = $(element);
  1345.     element.style.opacity = (value == 1 || value === '') ? '' :
  1346.       (value < 0.00001) ? 0 : value;
  1347.     return element;
  1348.   },
  1349.   getDimensions: function(element) {
  1350.     element = $(element);
  1351.     var display = $(element).getStyle('display');
  1352.     if (display != 'none' && display != null) // Safari bug
  1353.       return {width: element.offsetWidth, height: element.offsetHeight};
  1354.     // All *Width and *Height properties give 0 on elements with display none,
  1355.     // so enable the element temporarily
  1356.     var els = element.style;
  1357.     var originalVisibility = els.visibility;
  1358.     var originalPosition = els.position;
  1359.     var originalDisplay = els.display;
  1360.     els.visibility = 'hidden';
  1361.     els.position = 'absolute';
  1362.     els.display = 'block';
  1363.     var originalWidth = element.clientWidth;
  1364.     var originalHeight = element.clientHeight;
  1365.     els.display = originalDisplay;
  1366.     els.position = originalPosition;
  1367.     els.visibility = originalVisibility;
  1368.     return {width: originalWidth, height: originalHeight};
  1369.   },
  1370.   makePositioned: function(element) {
  1371.     element = $(element);
  1372.     var pos = Element.getStyle(element, 'position');
  1373.     if (pos == 'static' || !pos) {
  1374.       element._madePositioned = true;
  1375.       element.style.position = 'relative';
  1376.       // Opera returns the offset relative to the positioning context, when an
  1377.       // element is position relative but top and left have not been defined
  1378.       if (window.opera) {
  1379.         element.style.top = 0;
  1380.         element.style.left = 0;
  1381.       }
  1382.     }
  1383.     return element;
  1384.   },
  1385.   undoPositioned: function(element) {
  1386.     element = $(element);
  1387.     if (element._madePositioned) {
  1388.       element._madePositioned = undefined;
  1389.       element.style.position =
  1390.         element.style.top =
  1391.         element.style.left =
  1392.         element.style.bottom =
  1393.         element.style.right = '';
  1394.     }
  1395.     return element;
  1396.   },
  1397.   makeClipping: function(element) {
  1398.     element = $(element);
  1399.     if (element._overflow) return element;
  1400.     element._overflow = element.style.overflow || 'auto';
  1401.     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
  1402.       element.style.overflow = 'hidden';
  1403.     return element;
  1404.   },
  1405.   undoClipping: function(element) {
  1406.     element = $(element);
  1407.     if (!element._overflow) return element;
  1408.     element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
  1409.     element._overflow = null;
  1410.     return element;
  1411.   }
  1412. };
  1413. Object.extend(Element.Methods, {
  1414.   childOf: Element.Methods.descendantOf,
  1415.   childElements: Element.Methods.immediateDescendants
  1416. });
  1417. if (Prototype.Browser.Opera) {
  1418.   Element.Methods._getStyle = Element.Methods.getStyle;
  1419.   Element.Methods.getStyle = function(element, style) {
  1420.     switch(style) {
  1421.       case 'left':
  1422.       case 'top':
  1423.       case 'right':
  1424.       case 'bottom':
  1425.         if (Element._getStyle(element, 'position') == 'static') return null;
  1426.       default: return Element._getStyle(element, style);
  1427.     }
  1428.   };
  1429. }
  1430. else if (Prototype.Browser.IE) {
  1431.   Element.Methods.getStyle = function(element, style) {
  1432.     element = $(element);
  1433.     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
  1434.     var value = element.style[style];
  1435.     if (!value && element.currentStyle) value = element.currentStyle[style];
  1436.     if (style == 'opacity') {
  1437.       if (value = (element.getStyle('filter') || '').match(/alpha(opacity=(.*))/))
  1438.         if (value[1]) return parseFloat(value[1]) / 100;
  1439.       return 1.0;
  1440.     }
  1441.     if (value == 'auto') {
  1442.       if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
  1443.         return element['offset'+style.capitalize()] + 'px';
  1444.       return null;
  1445.     }
  1446.     return value;
  1447.   };
  1448.   Element.Methods.setOpacity = function(element, value) {
  1449.     element = $(element);
  1450.     var filter = element.getStyle('filter'), style = element.style;
  1451.     if (value == 1 || value === '') {
  1452.       style.filter = filter.replace(/alpha([^)]*)/gi,'');
  1453.       return element;
  1454.     } else if (value < 0.00001) value = 0;
  1455.     style.filter = filter.replace(/alpha([^)]*)/gi, '') +
  1456.       'alpha(opacity=' + (value * 100) + ')';
  1457.     return element;
  1458.   };
  1459.   // IE is missing .innerHTML support for TABLE-related elements
  1460.   Element.Methods.update = function(element, html) {
  1461.     element = $(element);
  1462.     html = typeof html == 'undefined' ? '' : html.toString();
  1463.     var tagName = element.tagName.toUpperCase();
  1464.     if (['THEAD','TBODY','TR','TD'].include(tagName)) {
  1465.       var div = document.createElement('div');
  1466.       switch (tagName) {
  1467.         case 'THEAD':
  1468.         case 'TBODY':
  1469.           div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
  1470.           depth = 2;
  1471.           break;
  1472.         case 'TR':
  1473.           div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
  1474.           depth = 3;
  1475.           break;
  1476.         case 'TD':
  1477.           div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
  1478.           depth = 4;
  1479.       }
  1480.       $A(element.childNodes).each(function(node) { element.removeChild(node) });
  1481.       depth.times(function() { div = div.firstChild });
  1482.       $A(div.childNodes).each(function(node) { element.appendChild(node) });
  1483.     } else {
  1484.       element.innerHTML = html.stripScripts();
  1485.     }
  1486.     setTimeout(function() { html.evalScripts() }, 10);
  1487.     return element;
  1488.   }
  1489. }
  1490. else if (Prototype.Browser.Gecko) {
  1491.   Element.Methods.setOpacity = function(element, value) {
  1492.     element = $(element);
  1493.     element.style.opacity = (value == 1) ? 0.999999 :
  1494.       (value === '') ? '' : (value < 0.00001) ? 0 : value;
  1495.     return element;
  1496.   };
  1497. }
  1498. Element._attributeTranslations = {
  1499.   names: {
  1500.     colspan:   "colSpan",
  1501.     rowspan:   "rowSpan",
  1502.     valign:    "vAlign",
  1503.     datetime:  "dateTime",
  1504.     accesskey: "accessKey",
  1505.     tabindex:  "tabIndex",
  1506.     enctype:   "encType",
  1507.     maxlength: "maxLength",
  1508.     readonly:  "readOnly",
  1509.     longdesc:  "longDesc"
  1510.   },
  1511.   values: {
  1512.     _getAttr: function(element, attribute) {
  1513.       return element.getAttribute(attribute, 2);
  1514.     },
  1515.     _flag: function(element, attribute) {
  1516.       return $(element).hasAttribute(attribute) ? attribute : null;
  1517.     },
  1518.     style: function(element) {
  1519.       return element.style.cssText.toLowerCase();
  1520.     },
  1521.     title: function(element) {
  1522.       var node = element.getAttributeNode('title');
  1523.       return node.specified ? node.nodeValue : null;
  1524.     }
  1525.   }
  1526. };
  1527. (function() {
  1528.   Object.extend(this, {
  1529.     href: this._getAttr,
  1530.     src:  this._getAttr,
  1531.     type: this._getAttr,
  1532.     disabled: this._flag,
  1533.     checked:  this._flag,
  1534.     readonly: this._flag,
  1535.     multiple: this._flag
  1536.   });
  1537. }).call(Element._attributeTranslations.values);
  1538. Element.Methods.Simulated = {
  1539.   hasAttribute: function(element, attribute) {
  1540.     var t = Element._attributeTranslations, node;
  1541.     attribute = t.names[attribute] || attribute;
  1542.     node = $(element).getAttributeNode(attribute);
  1543.     return node && node.specified;
  1544.   }
  1545. };
  1546. Element.Methods.ByTag = {};
  1547. Object.extend(Element, Element.Methods);
  1548. if (!Prototype.BrowserFeatures.ElementExtensions &&
  1549.  document.createElement('div').__proto__) {
  1550.   window.HTMLElement = {};
  1551.   window.HTMLElement.prototype = document.createElement('div').__proto__;
  1552.   Prototype.BrowserFeatures.ElementExtensions = true;
  1553. }
  1554. Element.hasAttribute = function(element, attribute) {
  1555.   if (element.hasAttribute) return element.hasAttribute(attribute);
  1556.   return Element.Methods.Simulated.hasAttribute(element, attribute);
  1557. };
  1558. Element.addMethods = function(methods) {
  1559.   var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
  1560.   if (!methods) {
  1561.     Object.extend(Form, Form.Methods);
  1562.     Object.extend(Form.Element, Form.Element.Methods);
  1563.     Object.extend(Element.Methods.ByTag, {
  1564.       "FORM":     Object.clone(Form.Methods),
  1565.       "INPUT":    Object.clone(Form.Element.Methods),
  1566.       "SELECT":   Object.clone(Form.Element.Methods),
  1567.       "TEXTAREA": Object.clone(Form.Element.Methods)
  1568.     });
  1569.   }
  1570.   if (arguments.length == 2) {
  1571.     var tagName = methods;
  1572.     methods = arguments[1];
  1573.   }
  1574.   if (!tagName) Object.extend(Element.Methods, methods || {});
  1575.   else {
  1576.     if (tagName.constructor == Array) tagName.each(extend);
  1577.     else extend(tagName);
  1578.   }
  1579.   function extend(tagName) {
  1580.     tagName = tagName.toUpperCase();
  1581.     if (!Element.Methods.ByTag[tagName])
  1582.       Element.Methods.ByTag[tagName] = {};
  1583.     Object.extend(Element.Methods.ByTag[tagName], methods);
  1584.   }
  1585.   function copy(methods, destination, onlyIfAbsent) {
  1586.     onlyIfAbsent = onlyIfAbsent || false;
  1587.     var cache = Element.extend.cache;
  1588.     for (var property in methods) {
  1589.       var value = methods[property];
  1590.       if (!onlyIfAbsent || !(property in destination))
  1591.         destination[property] = cache.findOrStore(value);
  1592.     }
  1593.   }
  1594.   function findDOMClass(tagName) {
  1595.     var klass;
  1596.     var trans = {
  1597.       "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
  1598.       "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
  1599.       "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
  1600.       "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
  1601.       "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
  1602.       "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
  1603.       "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
  1604.       "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
  1605.       "FrameSet", "IFRAME": "IFrame"
  1606.     };
  1607.     if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
  1608.     if (window[klass]) return window[klass];
  1609.     klass = 'HTML' + tagName + 'Element';
  1610.     if (window[klass]) return window[klass];
  1611.     klass = 'HTML' + tagName.capitalize() + 'Element';
  1612.     if (window[klass]) return window[klass];
  1613.     window[klass] = {};
  1614.     window[klass].prototype = document.createElement(tagName).__proto__;
  1615.     return window[klass];
  1616.   }
  1617.   if (F.ElementExtensions) {
  1618.     copy(Element.Methods, HTMLElement.prototype);
  1619.     copy(Element.Methods.Simulated, HTMLElement.prototype, true);
  1620.   }
  1621.   if (F.SpecificElementExtensions) {
  1622.     for (var tag in Element.Methods.ByTag) {
  1623.       var klass = findDOMClass(tag);
  1624.       if (typeof klass == "undefined") continue;
  1625.       copy(T[tag], klass.prototype);
  1626.     }
  1627.   }
  1628.   Object.extend(Element, Element.Methods);
  1629.   delete Element.ByTag;
  1630. };
  1631. var Toggle = { display: Element.toggle };
  1632. /*--------------------------------------------------------------------------*/
  1633. Abstract.Insertion = function(adjacency) {
  1634.   this.adjacency = adjacency;
  1635. }
  1636. Abstract.Insertion.prototype = {
  1637.   initialize: function(element, content) {
  1638.     this.element = $(element);
  1639.     this.content = content.stripScripts();
  1640.     if (this.adjacency && this.element.insertAdjacentHTML) {
  1641.       try {
  1642.         this.element.insertAdjacentHTML(this.adjacency, this.content);
  1643.       } catch (e) {
  1644.         var tagName = this.element.tagName.toUpperCase();
  1645.         if (['TBODY', 'TR'].include(tagName)) {
  1646.           this.insertContent(this.contentFromAnonymousTable());
  1647.         } else {
  1648.           throw e;
  1649.         }
  1650.       }
  1651.     } else {
  1652.       this.range = this.element.ownerDocument.createRange();
  1653.       if (this.initializeRange) this.initializeRange();
  1654.       this.insertContent([this.range.createContextualFragment(this.content)]);
  1655.     }
  1656.     setTimeout(function() {content.evalScripts()}, 10);
  1657.   },
  1658.   contentFromAnonymousTable: function() {
  1659.     var div = document.createElement('div');
  1660.     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
  1661.     return $A(div.childNodes[0].childNodes[0].childNodes);
  1662.   }
  1663. }
  1664. var Insertion = new Object();
  1665. Insertion.Before = Class.create();
  1666. Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  1667.   initializeRange: function() {
  1668.     this.range.setStartBefore(this.element);
  1669.   },
  1670.   insertContent: function(fragments) {
  1671.     fragments.each((function(fragment) {
  1672.       this.element.parentNode.insertBefore(fragment, this.element);
  1673.     }).bind(this));
  1674.   }
  1675. });
  1676. Insertion.Top = Class.create();
  1677. Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  1678.   initializeRange: function() {
  1679.     this.range.selectNodeContents(this.element);
  1680.     this.range.collapse(true);
  1681.   },
  1682.   insertContent: function(fragments) {
  1683.     fragments.reverse(false).each((function(fragment) {
  1684.       this.element.insertBefore(fragment, this.element.firstChild);
  1685.     }).bind(this));
  1686.   }
  1687. });
  1688. Insertion.Bottom = Class.create();
  1689. Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  1690.   initializeRange: function() {
  1691.     this.range.selectNodeContents(this.element);
  1692.     this.range.collapse(this.element);
  1693.   },
  1694.   insertContent: function(fragments) {
  1695.     fragments.each((function(fragment) {
  1696.       this.element.appendChild(fragment);
  1697.     }).bind(this));
  1698.   }
  1699. });
  1700. Insertion.After = Class.create();
  1701. Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  1702.   initializeRange: function() {
  1703.     this.range.setStartAfter(this.element);
  1704.   },
  1705.   insertContent: function(fragments) {
  1706.     fragments.each((function(fragment) {
  1707.       this.element.parentNode.insertBefore(fragment,
  1708.         this.element.nextSibling);
  1709.     }).bind(this));
  1710.   }
  1711. });
  1712. /*--------------------------------------------------------------------------*/
  1713. Element.ClassNames = Class.create();
  1714. Element.ClassNames.prototype = {
  1715.   initialize: function(element) {
  1716.     this.element = $(element);
  1717.   },
  1718.   _each: function(iterator) {
  1719.     this.element.className.split(/s+/).select(function(name) {
  1720.       return name.length > 0;
  1721.     })._each(iterator);
  1722.   },
  1723.   set: function(className) {
  1724.     this.element.className = className;
  1725.   },
  1726.   add: function(classNameToAdd) {
  1727.     if (this.include(classNameToAdd)) return;
  1728.     this.set($A(this).concat(classNameToAdd).join(' '));
  1729.   },
  1730.   remove: function(classNameToRemove) {
  1731.     if (!this.include(classNameToRemove)) return;
  1732.     this.set($A(this).without(classNameToRemove).join(' '));
  1733.   },
  1734.   toString: function() {
  1735.     return $A(this).join(' ');
  1736.   }
  1737. };
  1738. Object.extend(Element.ClassNames.prototype, Enumerable);
  1739. /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
  1740.  * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
  1741.  * license.  Please see http://www.yui-ext.com/ for more information. */
  1742. var Selector = Class.create();
  1743. Selector.prototype = {
  1744.   initialize: function(expression) {
  1745.     this.expression = expression.strip();
  1746.     this.compileMatcher();
  1747.   },
  1748.   compileMatcher: function() {
  1749.     // Selectors with namespaced attributes can't use the XPath version
  1750.     if (Prototype.BrowserFeatures.XPath && !(/[[w-]*?:/).test(this.expression))
  1751.       return this.compileXPathMatcher();
  1752.     var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
  1753.         c = Selector.criteria, le, p, m;
  1754.     if (Selector._cache[e]) {
  1755.       this.matcher = Selector._cache[e]; return;
  1756.     }
  1757.     this.matcher = ["this.matcher = function(root) {",
  1758.                     "var r = root, h = Selector.handlers, c = false, n;"];
  1759.     while (e && le != e && (/S/).test(e)) {
  1760.       le = e;
  1761.       for (var i in ps) {
  1762.         p = ps[i];
  1763.         if (m = e.match(p)) {
  1764.           this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
  1765.            new Template(c[i]).evaluate(m));
  1766.           e = e.replace(m[0], '');
  1767.           break;
  1768.         }
  1769.       }
  1770.     }
  1771.     this.matcher.push("return h.unique(n);n}");
  1772.     eval(this.matcher.join('n'));
  1773.     Selector._cache[this.expression] = this.matcher;
  1774.   },
  1775.   compileXPathMatcher: function() {
  1776.     var e = this.expression, ps = Selector.patterns,
  1777.         x = Selector.xpath, le,  m;
  1778.     if (Selector._cache[e]) {
  1779.       this.xpath = Selector._cache[e]; return;
  1780.     }
  1781.     this.matcher = ['.//*'];
  1782.     while (e && le != e && (/S/).test(e)) {
  1783.       le = e;
  1784.       for (var i in ps) {
  1785.         if (m = e.match(ps[i])) {
  1786.           this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
  1787.             new Template(x[i]).evaluate(m));
  1788.           e = e.replace(m[0], '');
  1789.           break;
  1790.         }
  1791.       }
  1792.     }
  1793.     this.xpath = this.matcher.join('');
  1794.     Selector._cache[this.expression] = this.xpath;
  1795.   },
  1796.   findElements: function(root) {
  1797.     root = root || document;
  1798.     if (this.xpath) return document._getElementsByXPath(this.xpath, root);
  1799.     return this.matcher(root);
  1800.   },
  1801.   match: function(element) {
  1802.     return this.findElements(document).include(element);
  1803.   },
  1804.   toString: function() {
  1805.     return this.expression;
  1806.   },
  1807.   inspect: function() {
  1808.     return "#<Selector:" + this.expression.inspect() + ">";
  1809.   }
  1810. };
  1811. Object.extend(Selector, {
  1812.   _cache: {},
  1813.   xpath: {
  1814.     descendant:   "//*",
  1815.     child:        "/*",
  1816.     adjacent:     "/following-sibling::*[1]",
  1817.     laterSibling: '/following-sibling::*',
  1818.     tagName:      function(m) {
  1819.       if (m[1] == '*') return '';
  1820.       return "[local-name()='" + m[1].toLowerCase() +
  1821.              "' or local-name()='" + m[1].toUpperCase() + "']";
  1822.     },
  1823.     className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
  1824.     id:           "[@id='#{1}']",
  1825.     attrPresence: "[@#{1}]",
  1826.     attr: function(m) {
  1827.       m[3] = m[5] || m[6];
  1828.       return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
  1829.     },
  1830.     pseudo: function(m) {
  1831.       var h = Selector.xpath.pseudos[m[1]];
  1832.       if (!h) return '';
  1833.       if (typeof h === 'function') return h(m);
  1834.       return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
  1835.     },
  1836.     operators: {
  1837.       '=':  "[@#{1}='#{3}']",
  1838.       '!=': "[@#{1}!='#{3}']",
  1839.       '^=': "[starts-with(@#{1}, '#{3}')]",
  1840.       '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
  1841.       '*=': "[contains(@#{1}, '#{3}')]",
  1842.       '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
  1843.       '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
  1844.     },
  1845.     pseudos: {
  1846.       'first-child': '[not(preceding-sibling::*)]',
  1847.       'last-child':  '[not(following-sibling::*)]',
  1848.       'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
  1849.       'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' trn', '') = '')]",
  1850.       'checked':     "[@checked]",
  1851.       'disabled':    "[@disabled]",
  1852.       'enabled':     "[not(@disabled)]",
  1853.       'not': function(m) {
  1854.         var e = m[6], p = Selector.patterns,
  1855.             x = Selector.xpath, le, m, v;
  1856.         var exclusion = [];
  1857.         while (e && le != e && (/S/).test(e)) {
  1858.           le = e;
  1859.           for (var i in p) {
  1860.             if (m = e.match(p[i])) {
  1861.               v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
  1862.               exclusion.push("(" + v.substring(1, v.length - 1) + ")");
  1863.               e = e.replace(m[0], '');
  1864.               break;
  1865.             }
  1866.           }
  1867.         }
  1868.         return "[not(" + exclusion.join(" and ") + ")]";
  1869.       },
  1870.       'nth-child':      function(m) {
  1871.         return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
  1872.       },
  1873.       'nth-last-child': function(m) {
  1874.         return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
  1875.       },
  1876.       'nth-of-type':    function(m) {
  1877.         return Selector.xpath.pseudos.nth("position() ", m);
  1878.       },
  1879.       'nth-last-of-type': function(m) {
  1880.         return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
  1881.       },
  1882.       'first-of-type':  function(m) {
  1883.         m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
  1884.       },
  1885.       'last-of-type':   function(m) {
  1886.         m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
  1887.       },
  1888.       'only-of-type':   function(m) {
  1889.         var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
  1890.       },
  1891.       nth: function(fragment, m) {
  1892.         var mm, formula = m[6], predicate;
  1893.         if (formula == 'even') formula = '2n+0';
  1894.         if (formula == 'odd')  formula = '2n+1';
  1895.         if (mm = formula.match(/^(d+)$/)) // digit only
  1896.           return '[' + fragment + "= " + mm[1] + ']';
  1897.         if (mm = formula.match(/^(-?d*)?n(([+-])(d+))?/)) { // an+b
  1898.           if (mm[1] == "-") mm[1] = -1;
  1899.           var a = mm[1] ? Number(mm[1]) : 1;
  1900.           var b = mm[2] ? Number(mm[2]) : 0;
  1901.           predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
  1902.           "((#{fragment} - #{b}) div #{a} >= 0)]";
  1903.           return new Template(predicate).evaluate({
  1904.             fragment: fragment, a: a, b: b });
  1905.         }
  1906.       }
  1907.     }
  1908.   },
  1909.   criteria: {
  1910.     tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
  1911.     className:    'n = h.className(n, r, "#{1}", c); c = false;',
  1912.     id:           'n = h.id(n, r, "#{1}", c);        c = false;',
  1913.     attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
  1914.     attr: function(m) {
  1915.       m[3] = (m[5] || m[6]);
  1916.       return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
  1917.     },
  1918.     pseudo:       function(m) {
  1919.       if (m[6]) m[6] = m[6].replace(/"/g, '\"');
  1920.       return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
  1921.     },
  1922.     descendant:   'c = "descendant";',
  1923.     child:        'c = "child";',
  1924.     adjacent:     'c = "adjacent";',
  1925.     laterSibling: 'c = "laterSibling";'
  1926.   },
  1927.   patterns: {
  1928.     // combinators must be listed first
  1929.     // (and descendant needs to be last combinator)
  1930.     laterSibling: /^s*~s*/,
  1931.     child:        /^s*>s*/,
  1932.     adjacent:     /^s*+s*/,
  1933.     descendant:   /^s/,
  1934.     // selectors follow
  1935.     tagName:      /^s*(*|[w-]+)(b|$)?/,
  1936.     id:           /^#([w-*]+)(b|$)/,
  1937.     className:    /^.([w-*]+)(b|$)/,
  1938.     pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(((.*?)))?(b|$|s|(?=:))/,
  1939.     attrPresence: /^[([w]+)]/,
  1940.     attr:         /[((?:[w-]*:)?[w-]+)s*(?:([!^$*~|]?=)s*((['"])([^]]*?)4|([^'"][^]]*?)))?]/
  1941.   },
  1942.   handlers: {
  1943.     // UTILITY FUNCTIONS
  1944.     // joins two collections
  1945.     concat: function(a, b) {
  1946.       for (var i = 0, node; node = b[i]; i++)
  1947.         a.push(node);
  1948.       return a;
  1949.     },
  1950.     // marks an array of nodes for counting
  1951.     mark: function(nodes) {
  1952.       for (var i = 0, node; node = nodes[i]; i++)
  1953.         node._counted = true;
  1954.       return nodes;
  1955.     },
  1956.     unmark: function(nodes) {
  1957.       for (var i = 0, node; node = nodes[i]; i++)
  1958.         node._counted = undefined;
  1959.       return nodes;
  1960.     },
  1961.     // mark each child node with its position (for nth calls)
  1962.     // "ofType" flag indicates whether we're indexing for nth-of-type
  1963.     // rather than nth-child
  1964.     index: function(parentNode, reverse, ofType) {
  1965.       parentNode._counted = true;
  1966.       if (reverse) {
  1967.         for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
  1968.           node = nodes[i];
  1969.           if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
  1970.         }
  1971.       } else {
  1972.         for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
  1973.           if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
  1974.       }
  1975.     },
  1976.     // filters out duplicates and extends all nodes
  1977.     unique: function(nodes) {
  1978.       if (nodes.length == 0) return nodes;
  1979.       var results = [], n;
  1980.       for (var i = 0, l = nodes.length; i < l; i++)
  1981.         if (!(n = nodes[i])._counted) {
  1982.           n._counted = true;
  1983.           results.push(Element.extend(n));
  1984.         }
  1985.       return Selector.handlers.unmark(results);
  1986.     },
  1987.     // COMBINATOR FUNCTIONS
  1988.     descendant: function(nodes) {
  1989.       var h = Selector.handlers;
  1990.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  1991.         h.concat(results, node.getElementsByTagName('*'));
  1992.       return results;
  1993.     },
  1994.     child: function(nodes) {
  1995.       var h = Selector.handlers;
  1996.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  1997.         for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
  1998.           if (child.nodeType == 1 && child.tagName != '!') results.push(child);
  1999.       }
  2000.       return results;
  2001.     },
  2002.     adjacent: function(nodes) {
  2003.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  2004.         var next = this.nextElementSibling(node);
  2005.         if (next) results.push(next);
  2006.       }
  2007.       return results;
  2008.     },
  2009.     laterSibling: function(nodes) {
  2010.       var h = Selector.handlers;
  2011.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  2012.         h.concat(results, Element.nextSiblings(node));
  2013.       return results;
  2014.     },
  2015.     nextElementSibling: function(node) {
  2016.       while (node = node.nextSibling)
  2017.       if (node.nodeType == 1) return node;
  2018.       return null;
  2019.     },
  2020.     previousElementSibling: function(node) {
  2021.       while (node = node.previousSibling)
  2022.         if (node.nodeType == 1) return node;
  2023.       return null;
  2024.     },
  2025.     // TOKEN FUNCTIONS
  2026.     tagName: function(nodes, root, tagName, combinator) {
  2027.       tagName = tagName.toUpperCase();
  2028.       var results = [], h = Selector.handlers;
  2029.       if (nodes) {
  2030.         if (combinator) {
  2031.           // fastlane for ordinary descendant combinators
  2032.           if (combinator == "descendant") {
  2033.             for (var i = 0, node; node = nodes[i]; i++)
  2034.               h.concat(results, node.getElementsByTagName(tagName));
  2035.             return results;
  2036.           } else nodes = this[combinator](nodes);
  2037.           if (tagName == "*") return nodes;
  2038.         }
  2039.         for (var i = 0, node; node = nodes[i]; i++)
  2040.           if (node.tagName.toUpperCase() == tagName) results.push(node);
  2041.         return results;
  2042.       } else return root.getElementsByTagName(tagName);
  2043.     },
  2044.     id: function(nodes, root, id, combinator) {
  2045.       var targetNode = $(id), h = Selector.handlers;
  2046.       if (!nodes && root == document) return targetNode ? [targetNode] : [];
  2047.       if (nodes) {
  2048.         if (combinator) {
  2049.           if (combinator == 'child') {
  2050.             for (var i = 0, node; node = nodes[i]; i++)
  2051.               if (targetNode.parentNode == node) return [targetNode];
  2052.           } else if (combinator == 'descendant') {
  2053.             for (var i = 0, node; node = nodes[i]; i++)
  2054.               if (Element.descendantOf(targetNode, node)) return [targetNode];
  2055.           } else if (combinator == 'adjacent') {
  2056.             for (var i = 0, node; node = nodes[i]; i++)
  2057.               if (Selector.handlers.previousElementSibling(targetNode) == node)
  2058.                 return [targetNode];
  2059.           } else nodes = h[combinator](nodes);
  2060.         }
  2061.         for (var i = 0, node; node = nodes[i]; i++)
  2062.           if (node == targetNode) return [targetNode];
  2063.         return [];
  2064.       }
  2065.       return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
  2066.     },
  2067.     className: function(nodes, root, className, combinator) {
  2068.       if (nodes && combinator) nodes = this[combinator](nodes);
  2069.       return Selector.handlers.byClassName(nodes, root, className);
  2070.     },
  2071.     byClassName: function(nodes, root, className) {
  2072.       if (!nodes) nodes = Selector.handlers.descendant([root]);
  2073.       var needle = ' ' + className + ' ';
  2074.       for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
  2075.         nodeClassName = node.className;
  2076.         if (nodeClassName.length == 0) continue;
  2077.         if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
  2078.           results.push(node);
  2079.       }
  2080.       return results;
  2081.     },
  2082.     attrPresence: function(nodes, root, attr) {
  2083.       var results = [];
  2084.       for (var i = 0, node; node = nodes[i]; i++)
  2085.         if (Element.hasAttribute(node, attr)) results.push(node);
  2086.       return results;
  2087.     },
  2088.     attr: function(nodes, root, attr, value, operator) {
  2089.       if (!nodes) nodes = root.getElementsByTagName("*");
  2090.       var handler = Selector.operators[operator], results = [];
  2091.       for (var i = 0, node; node = nodes[i]; i++) {
  2092.         var nodeValue = Element.readAttribute(node, attr);
  2093.         if (nodeValue === null) continue;
  2094.         if (handler(nodeValue, value)) results.push(node);
  2095.       }
  2096.       return results;
  2097.     },
  2098.     pseudo: function(nodes, name, value, root, combinator) {
  2099.       if (nodes && combinator) nodes = this[combinator](nodes);
  2100.       if (!nodes) nodes = root.getElementsByTagName("*");
  2101.       return Selector.pseudos[name](nodes, value, root);
  2102.     }
  2103.   },
  2104.   pseudos: {
  2105.     'first-child': function(nodes, value, root) {
  2106.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  2107.         if (Selector.handlers.previousElementSibling(node)) continue;
  2108.           results.push(node);
  2109.       }
  2110.       return results;
  2111.     },
  2112.     'last-child': function(nodes, value, root) {
  2113.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  2114.         if (Selector.handlers.nextElementSibling(node)) continue;
  2115.           results.push(node);
  2116.       }
  2117.       return results;
  2118.     },
  2119.     'only-child': function(nodes, value, root) {
  2120.       var h = Selector.handlers;
  2121.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  2122.         if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
  2123.           results.push(node);
  2124.       return results;
  2125.     },
  2126.     'nth-child':        function(nodes, formula, root) {
  2127.       return Selector.pseudos.nth(nodes, formula, root);
  2128.     },
  2129.     'nth-last-child':   function(nodes, formula, root) {
  2130.       return Selector.pseudos.nth(nodes, formula, root, true);
  2131.     },
  2132.     'nth-of-type':      function(nodes, formula, root) {
  2133.       return Selector.pseudos.nth(nodes, formula, root, false, true);
  2134.     },
  2135.     'nth-last-of-type': function(nodes, formula, root) {
  2136.       return Selector.pseudos.nth(nodes, formula, root, true, true);
  2137.     },
  2138.     'first-of-type':    function(nodes, formula, root) {
  2139.       return Selector.pseudos.nth(nodes, "1", root, false, true);
  2140.     },
  2141.     'last-of-type':     function(nodes, formula, root) {
  2142.       return Selector.pseudos.nth(nodes, "1", root, true, true);
  2143.     },
  2144.     'only-of-type':     function(nodes, formula, root) {
  2145.       var p = Selector.pseudos;
  2146.       return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
  2147.     },
  2148.     // handles the an+b logic
  2149.     getIndices: function(a, b, total) {
  2150.       if (a == 0) return b > 0 ? [b] : [];
  2151.       return $R(1, total).inject([], function(memo, i) {
  2152.         if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
  2153.         return memo;
  2154.       });
  2155.     },
  2156.     // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
  2157.     nth: function(nodes, formula, root, reverse, ofType) {
  2158.       if (nodes.length == 0) return [];
  2159.       if (formula == 'even') formula = '2n+0';
  2160.       if (formula == 'odd')  formula = '2n+1';
  2161.       var h = Selector.handlers, results = [], indexed = [], m;
  2162.       h.mark(nodes);
  2163.       for (var i = 0, node; node = nodes[i]; i++) {
  2164.         if (!node.parentNode._counted) {
  2165.           h.index(node.parentNode, reverse, ofType);
  2166.           indexed.push(node.parentNode);
  2167.         }
  2168.       }
  2169.       if (formula.match(/^d+$/)) { // just a number
  2170.         formula = Number(formula);
  2171.         for (var i = 0, node; node = nodes[i]; i++)
  2172.           if (node.nodeIndex == formula) results.push(node);
  2173.       } else if (m = formula.match(/^(-?d*)?n(([+-])(d+))?/)) { // an+b
  2174.         if (m[1] == "-") m[1] = -1;
  2175.         var a = m[1] ? Number(m[1]) : 1;
  2176.         var b = m[2] ? Number(m[2]) : 0;
  2177.         var indices = Selector.pseudos.getIndices(a, b, nodes.length);
  2178.         for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
  2179.           for (var j = 0; j < l; j++)
  2180.             if (node.nodeIndex == indices[j]) results.push(node);
  2181.         }
  2182.       }
  2183.       h.unmark(nodes);
  2184.       h.unmark(indexed);
  2185.       return results;
  2186.     },
  2187.     'empty': function(nodes, value, root) {
  2188.       for (var i = 0, results = [], node; node = nodes[i]; i++) {
  2189.         // IE treats comments as element nodes
  2190.         if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^s*$/))) continue;
  2191.         results.push(node);
  2192.       }
  2193.       return results;
  2194.     },
  2195.     'not': function(nodes, selector, root) {
  2196.       var h = Selector.handlers, selectorType, m;
  2197.       var exclusions = new Selector(selector).findElements(root);
  2198.       h.mark(exclusions);
  2199.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  2200.         if (!node._counted) results.push(node);
  2201.       h.unmark(exclusions);
  2202.       return results;
  2203.     },
  2204.     'enabled': function(nodes, value, root) {
  2205.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  2206.         if (!node.disabled) results.push(node);
  2207.       return results;
  2208.     },
  2209.     'disabled': function(nodes, value, root) {
  2210.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  2211.         if (node.disabled) results.push(node);
  2212.       return results;
  2213.     },
  2214.     'checked': function(nodes, value, root) {
  2215.       for (var i = 0, results = [], node; node = nodes[i]; i++)
  2216.         if (node.checked) results.push(node);
  2217.       return results;
  2218.     }
  2219.   },
  2220.   operators: {
  2221.     '=':  function(nv, v) { return nv == v; },
  2222.     '!=': function(nv, v) { return nv != v; },
  2223.     '^=': function(nv, v) { return nv.startsWith(v); },
  2224.     '$=': function(nv, v) { return nv.endsWith(v); },
  2225.     '*=': function(nv, v) { return nv.include(v); },
  2226.     '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
  2227.     '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
  2228.   },
  2229.   matchElements: function(elements, expression) {
  2230.     var matches = new Selector(expression).findElements(), h = Selector.handlers;
  2231.     h.mark(matches);
  2232.     for (var i = 0, results = [], element; element = elements[i]; i++)
  2233.       if (element._counted) results.push(element);
  2234.     h.unmark(matches);
  2235.     return results;
  2236.   },
  2237.   findElement: function(elements, expression, index) {
  2238.     if (typeof expression == 'number') {
  2239.       index = expression; expression = false;
  2240.     }
  2241.     return Selector.matchElements(elements, expression || '*')[index || 0];
  2242.   },
  2243.   findChildElements: function(element, expressions) {
  2244.     var exprs = expressions.join(','), expressions = [];
  2245.     exprs.scan(/(([w#:.~>+()s-]+|*|[.*?])+)s*(,|$)/, function(m) {
  2246.       expressions.push(m[1].strip());
  2247.     });
  2248.     var results = [], h = Selector.handlers;
  2249.     for (var i = 0, l = expressions.length, selector; i < l; i++) {
  2250.       selector = new Selector(expressions[i].strip());
  2251.       h.concat(results, selector.findElements(element));
  2252.     }
  2253.     return (l > 1) ? h.unique(results) : results;
  2254.   }
  2255. });
  2256. function $$() {
  2257.   return Selector.findChildElements(document, $A(arguments));
  2258. }
  2259. var Form = {
  2260.   reset: function(form) {
  2261.     $(form).reset();
  2262.     return form;
  2263.   },
  2264.   serializeElements: function(elements, getHash) {
  2265.     var data = elements.inject({}, function(result, element) {
  2266.       if (!element.disabled && element.name) {
  2267.         var key = element.name, value = $(element).getValue();
  2268.         if (value != null) {
  2269.           if (key in result) {
  2270.             if (result[key].constructor != Array) result[key] = [result[key]];
  2271.             result[key].push(value);
  2272.           }
  2273.           else result[key] = value;
  2274.         }
  2275.       }
  2276.       return result;
  2277.     });
  2278.     return getHash ? data : Hash.toQueryString(data);
  2279.   }
  2280. };
  2281. Form.Methods = {
  2282.   serialize: function(form, getHash) {
  2283.     return Form.serializeElements(Form.getElements(form), getHash);
  2284.   },
  2285.   getElements: function(form) {
  2286.     return $A($(form).getElementsByTagName('*')).inject([],
  2287.       function(elements, child) {
  2288.         if (Form.Element.Serializers[child.tagName.toLowerCase()])
  2289.           elements.push(Element.extend(child));
  2290.         return elements;
  2291.       }
  2292.     );
  2293.   },
  2294.   getInputs: function(form, typeName, name) {
  2295.     form = $(form);
  2296.     var inputs = form.getElementsByTagName('input');
  2297.     if (!typeName && !name) return $A(inputs).map(Element.extend);
  2298.     for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
  2299.       var input = inputs[i];
  2300.       if ((typeName && input.type != typeName) || (name && input.name != name))
  2301.         continue;
  2302.       matchingInputs.push(Element.extend(input));
  2303.     }
  2304.     return matchingInputs;
  2305.   },
  2306.   disable: function(form) {
  2307.     form = $(form);
  2308.     Form.getElements(form).invoke('disable');
  2309.     return form;
  2310.   },
  2311.   enable: function(form) {
  2312.     form = $(form);
  2313.     Form.getElements(form).invoke('enable');
  2314.     return form;
  2315.   },
  2316.   findFirstElement: function(form) {
  2317.     return $(form).getElements().find(function(element) {
  2318.       return element.type != 'hidden' && !element.disabled &&
  2319.         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
  2320.     });
  2321.   },
  2322.   focusFirstElement: function(form) {
  2323.     form = $(form);
  2324.     form.findFirstElement().activate();
  2325.     return form;
  2326.   },
  2327.   request: function(form, options) {
  2328.     form = $(form), options = Object.clone(options || {});
  2329.     var params = options.parameters;
  2330.     options.parameters = form.serialize(true);
  2331.     if (params) {
  2332.       if (typeof params == 'string') params = params.toQueryParams();
  2333.       Object.extend(options.parameters, params);
  2334.     }
  2335.     if (form.hasAttribute('method') && !options.method)
  2336.       options.method = form.method;
  2337.     return new Ajax.Request(form.readAttribute('action'), options);
  2338.   }
  2339. }
  2340. /*--------------------------------------------------------------------------*/
  2341. Form.Element = {
  2342.   focus: function(element) {
  2343.     $(element).focus();
  2344.     return element;
  2345.   },
  2346.   select: function(element) {
  2347.     $(element).select();
  2348.     return element;
  2349.   }
  2350. }
  2351. Form.Element.Methods = {
  2352.   serialize: function(element) {
  2353.     element = $(element);
  2354.     if (!element.disabled && element.name) {
  2355.       var value = element.getValue();
  2356.       if (value != undefined) {
  2357.         var pair = {};
  2358.         pair[element.name] = value;
  2359.         return Hash.toQueryString(pair);
  2360.       }
  2361.     }
  2362.     return '';
  2363.   },
  2364.   getValue: function(element) {
  2365.     element = $(element);
  2366.     var method = element.tagName.toLowerCase();
  2367.     return Form.Element.Serializers[method](element);
  2368.   },
  2369.   clear: function(element) {
  2370.     $(element).value = '';
  2371.     return element;
  2372.   },
  2373.   present: function(element) {
  2374.     return $(element).value != '';
  2375.   },
  2376.   activate: function(element) {
  2377.     element = $(element);
  2378.     try {
  2379.       element.focus();
  2380.       if (element.select && (element.tagName.toLowerCase() != 'input' ||
  2381.         !['button', 'reset', 'submit'].include(element.type)))
  2382.         element.select();
  2383.     } catch (e) {}
  2384.     return element;
  2385.   },
  2386.   disable: function(element) {
  2387.     element = $(element);
  2388.     element.blur();
  2389.     element.disabled = true;
  2390.     return element;
  2391.   },
  2392.   enable: function(element) {
  2393.     element = $(element);
  2394.     element.disabled = false;
  2395.     return element;
  2396.   }
  2397. }
  2398. /*--------------------------------------------------------------------------*/
  2399. var Field = Form.Element;
  2400. var $F = Form.Element.Methods.getValue;
  2401. /*--------------------------------------------------------------------------*/
  2402. Form.Element.Serializers = {
  2403.   input: function(element) {
  2404.     switch (element.type.toLowerCase()) {
  2405.       case 'checkbox':
  2406.       case 'radio':
  2407.         return Form.Element.Serializers.inputSelector(element);
  2408.       default:
  2409.         return Form.Element.Serializers.textarea(element);
  2410.     }
  2411.   },
  2412.   inputSelector: function(element) {
  2413.     return element.checked ? element.value : null;
  2414.   },
  2415.   textarea: function(element) {
  2416.     return element.value;
  2417.   },
  2418.   select: function(element) {
  2419.     return this[element.type == 'select-one' ?
  2420.       'selectOne' : 'selectMany'](element);
  2421.   },
  2422.   selectOne: function(element) {
  2423.     var index = element.selectedIndex;
  2424.     return index >= 0 ? this.optionValue(element.options[index]) : null;
  2425.   },
  2426.   selectMany: function(element) {
  2427.     var values, length = element.length;
  2428.     if (!length) return null;
  2429.     for (var i = 0, values = []; i < length; i++) {
  2430.       var opt = element.options[i];
  2431.       if (opt.selected) values.push(this.optionValue(opt));
  2432.     }
  2433.     return values;
  2434.   },
  2435.   optionValue: function(opt) {
  2436.     // extend element because hasAttribute may not be native
  2437.     return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  2438.   }
  2439. }
  2440. /*--------------------------------------------------------------------------*/
  2441. Abstract.TimedObserver = function() {}
  2442. Abstract.TimedObserver.prototype = {
  2443.   initialize: function(element, frequency, callback) {
  2444.     this.frequency = frequency;
  2445.     this.element   = $(element);
  2446.     this.callback  = callback;
  2447.     this.lastValue = this.getValue();
  2448.     this.registerCallback();
  2449.   },
  2450.   registerCallback: function() {
  2451.     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  2452.   },
  2453.   onTimerEvent: function() {
  2454.     var value = this.getValue();
  2455.     var changed = ('string' == typeof this.lastValue && 'string' == typeof value
  2456.       ? this.lastValue != value : String(this.lastValue) != String(value));
  2457.     if (changed) {
  2458.       this.callback(this.element, value);
  2459.       this.lastValue = value;
  2460.     }
  2461.   }
  2462. }
  2463. Form.Element.Observer = Class.create();
  2464. Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  2465.   getValue: function() {
  2466.     return Form.Element.getValue(this.element);
  2467.   }
  2468. });
  2469. Form.Observer = Class.create();
  2470. Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  2471.   getValue: function() {
  2472.     return Form.serialize(this.element);
  2473.   }
  2474. });
  2475. /*--------------------------------------------------------------------------*/
  2476. Abstract.EventObserver = function() {}
  2477. Abstract.EventObserver.prototype = {
  2478.   initialize: function(element, callback) {
  2479.     this.element  = $(element);
  2480.     this.callback = callback;
  2481.     this.lastValue = this.getValue();
  2482.     if (this.element.tagName.toLowerCase() == 'form')
  2483.       this.registerFormCallbacks();
  2484.     else
  2485.       this.registerCallback(this.element);
  2486.   },
  2487.   onElementEvent: function() {
  2488.     var value = this.getValue();
  2489.     if (this.lastValue != value) {
  2490.       this.callback(this.element, value);
  2491.       this.lastValue = value;
  2492.     }
  2493.   },
  2494.   registerFormCallbacks: function() {
  2495.     Form.getElements(this.element).each(this.registerCallback.bind(this));
  2496.   },
  2497.   registerCallback: function(element) {
  2498.     if (element.type) {
  2499.       switch (element.type.toLowerCase()) {
  2500.         case 'checkbox':
  2501.         case 'radio':
  2502.           Event.observe(element, 'click', this.onElementEvent.bind(this));
  2503.           break;
  2504.         default:
  2505.           Event.observe(element, 'change', this.onElementEvent.bind(this));
  2506.           break;
  2507.       }
  2508.     }
  2509.   }
  2510. }
  2511. Form.Element.EventObserver = Class.create();
  2512. Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  2513.   getValue: function() {
  2514.     return Form.Element.getValue(this.element);
  2515.   }
  2516. });
  2517. Form.EventObserver = Class.create();
  2518. Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  2519.   getValue: function() {
  2520.     return Form.serialize(this.element);
  2521.   }
  2522. });
  2523. if (!window.Event) {
  2524.   var Event = new Object();
  2525. }
  2526. Object.extend(Event, {
  2527.   KEY_BACKSPACE: 8,
  2528.   KEY_TAB:       9,
  2529.   KEY_RETURN:   13,
  2530.   KEY_ESC:      27,
  2531.   KEY_LEFT:     37,
  2532.   KEY_UP:       38,
  2533.   KEY_RIGHT:    39,
  2534.   KEY_DOWN:     40,
  2535.   KEY_DELETE:   46,
  2536.   KEY_HOME:     36,
  2537.   KEY_END:      35,
  2538.   KEY_PAGEUP:   33,
  2539.   KEY_PAGEDOWN: 34,
  2540.   element: function(event) {
  2541.     return $(event.target || event.srcElement);
  2542.   },
  2543.   isLeftClick: function(event) {
  2544.     return (((event.which) && (event.which == 1)) ||
  2545.             ((event.button) && (event.button == 1)));
  2546.   },
  2547.   pointerX: function(event) {
  2548.     return event.pageX || (event.clientX +
  2549.       (document.documentElement.scrollLeft || document.body.scrollLeft));
  2550.   },
  2551.   pointerY: function(event) {
  2552.     return event.pageY || (event.clientY +
  2553.       (document.documentElement.scrollTop || document.body.scrollTop));
  2554.   },
  2555.   stop: function(event) {
  2556.     if (event.preventDefault) {
  2557.       event.preventDefault();
  2558.       event.stopPropagation();
  2559.     } else {
  2560.       event.returnValue = false;
  2561.       event.cancelBubble = true;
  2562.     }
  2563.   },
  2564.   // find the first node with the given tagName, starting from the
  2565.   // node the event was triggered on; traverses the DOM upwards
  2566.   findElement: function(event, tagName) {
  2567.     var element = Event.element(event);
  2568.     while (element.parentNode && (!element.tagName ||
  2569.         (element.tagName.toUpperCase() != tagName.toUpperCase())))
  2570.       element = element.parentNode;
  2571.     return element;
  2572.   },
  2573.   observers: false,
  2574.   _observeAndCache: function(element, name, observer, useCapture) {
  2575.     if (!this.observers) this.observers = [];
  2576.     if (element.addEventListener) {
  2577.       this.observers.push([element, name, observer, useCapture]);
  2578.       element.addEventListener(name, observer, useCapture);
  2579.     } else if (element.attachEvent) {
  2580.       this.observers.push([element, name, observer, useCapture]);
  2581.       element.attachEvent('on' + name, observer);
  2582.     }
  2583.   },
  2584.   unloadCache: function() {
  2585.     if (!Event.observers) return;
  2586.     for (var i = 0, length = Event.observers.length; i < length; i++) {
  2587.       Event.stopObserving.apply(this, Event.observers[i]);
  2588.       Event.observers[i][0] = null;
  2589.     }
  2590.     Event.observers = false;
  2591.   },
  2592.   observe: function(element, name, observer, useCapture) {
  2593.     element = $(element);
  2594.     useCapture = useCapture || false;
  2595.     if (name == 'keypress' &&
  2596.       (Prototype.Browser.WebKit || element.attachEvent))
  2597.       name = 'keydown';
  2598.     Event._observeAndCache(element, name, observer, useCapture);
  2599.   },
  2600.   stopObserving: function(element, name, observer, useCapture) {
  2601.     element = $(element);
  2602.     useCapture = useCapture || false;
  2603.     if (name == 'keypress' &&
  2604.         (Prototype.Browser.WebKit || element.attachEvent))
  2605.       name = 'keydown';
  2606.     if (element.removeEventListener) {
  2607.       element.removeEventListener(name, observer, useCapture);
  2608.     } else if (element.detachEvent) {
  2609.       try {
  2610.         element.detachEvent('on' + name, observer);
  2611.       } catch (e) {}
  2612.     }
  2613.   }
  2614. });
  2615. /* prevent memory leaks in IE */
  2616. if (Prototype.Browser.IE)
  2617.   Event.observe(window, 'unload', Event.unloadCache, false);
  2618. var Position = {
  2619.   // set to true if needed, warning: firefox performance problems
  2620.   // NOT neeeded for page scrolling, only if draggable contained in
  2621.   // scrollable elements
  2622.   includeScrollOffsets: false,
  2623.   // must be called before calling withinIncludingScrolloffset, every time the
  2624.   // page is scrolled
  2625.   prepare: function() {
  2626.     this.deltaX =  window.pageXOffset
  2627.                 || document.documentElement.scrollLeft
  2628.                 || document.body.scrollLeft
  2629.                 || 0;
  2630.     this.deltaY =  window.pageYOffset
  2631.                 || document.documentElement.scrollTop
  2632.                 || document.body.scrollTop
  2633.                 || 0;
  2634.   },
  2635.   realOffset: function(element) {
  2636.     var valueT = 0, valueL = 0;
  2637.     do {
  2638.       valueT += element.scrollTop  || 0;
  2639.       valueL += element.scrollLeft || 0;
  2640.       element = element.parentNode;
  2641.     } while (element);
  2642.     return [valueL, valueT];
  2643.   },
  2644.   cumulativeOffset: function(element) {
  2645.     var valueT = 0, valueL = 0;
  2646.     do {
  2647.       valueT += element.offsetTop  || 0;
  2648.       valueL += element.offsetLeft || 0;
  2649.       element = element.offsetParent;
  2650.     } while (element);
  2651.     return [valueL, valueT];
  2652.   },
  2653.   positionedOffset: function(element) {
  2654.     var valueT = 0, valueL = 0;
  2655.     do {
  2656.       valueT += element.offsetTop  || 0;
  2657.       valueL += element.offsetLeft || 0;
  2658.       element = element.offsetParent;
  2659.       if (element) {
  2660.         if(element.tagName=='BODY') break;
  2661.         var p = Element.getStyle(element, 'position');
  2662.         if (p == 'relative' || p == 'absolute') break;
  2663.       }
  2664.     } while (element);
  2665.     return [valueL, valueT];
  2666.   },
  2667.   offsetParent: function(element) {
  2668.     if (element.offsetParent) return element.offsetParent;
  2669.     if (element == document.body) return element;
  2670.     while ((element = element.parentNode) && element != document.body)
  2671.       if (Element.getStyle(element, 'position') != 'static')
  2672.         return element;
  2673.     return document.body;
  2674.   },
  2675.   // caches x/y coordinate pair to use with overlap
  2676.   within: function(element, x, y) {
  2677.     if (this.includeScrollOffsets)
  2678.       return this.withinIncludingScrolloffsets(element, x, y);
  2679.     this.xcomp = x;
  2680.     this.ycomp = y;
  2681.     this.offset = this.cumulativeOffset(element);
  2682.     return (y >= this.offset[1] &&
  2683.             y <  this.offset[1] + element.offsetHeight &&
  2684.             x >= this.offset[0] &&
  2685.             x <  this.offset[0] + element.offsetWidth);
  2686.   },
  2687.   withinIncludingScrolloffsets: function(element, x, y) {
  2688.     var offsetcache = this.realOffset(element);
  2689.     this.xcomp = x + offsetcache[0] - this.deltaX;
  2690.     this.ycomp = y + offsetcache[1] - this.deltaY;
  2691.     this.offset = this.cumulativeOffset(element);
  2692.     return (this.ycomp >= this.offset[1] &&
  2693.             this.ycomp <  this.offset[1] + element.offsetHeight &&
  2694.             this.xcomp >= this.offset[0] &&
  2695.             this.xcomp <  this.offset[0] + element.offsetWidth);
  2696.   },
  2697.   // within must be called directly before
  2698.   overlap: function(mode, element) {
  2699.     if (!mode) return 0;
  2700.     if (mode == 'vertical')
  2701.       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
  2702.         element.offsetHeight;
  2703.     if (mode == 'horizontal')
  2704.       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
  2705.         element.offsetWidth;
  2706.   },
  2707.   page: function(forElement) {
  2708.     var valueT = 0, valueL = 0;
  2709.     var element = forElement;
  2710.     do {
  2711.       valueT += element.offsetTop  || 0;
  2712.       valueL += element.offsetLeft || 0;
  2713.       // Safari fix
  2714.       if (element.offsetParent == document.body)
  2715.         if (Element.getStyle(element,'position')=='absolute') break;
  2716.     } while (element = element.offsetParent);
  2717.     element = forElement;
  2718.     do {
  2719.       if (!window.opera || element.tagName=='BODY') {
  2720.         valueT -= element.scrollTop  || 0;
  2721.         valueL -= element.scrollLeft || 0;
  2722.       }
  2723.     } while (element = element.parentNode);
  2724.     return [valueL, valueT];
  2725.   },
  2726.   clone: function(source, target) {
  2727.     var options = Object.extend({
  2728.       setLeft:    true,
  2729.       setTop:     true,
  2730.       setWidth:   true,
  2731.       setHeight:  true,
  2732.       offsetTop:  0,
  2733.       offsetLeft: 0
  2734.     }, arguments[2] || {})
  2735.     // find page position of source
  2736.     source = $(source);
  2737.     var p = Position.page(source);
  2738.     // find coordinate system to use
  2739.     target = $(target);
  2740.     var delta = [0, 0];
  2741.     var parent = null;
  2742.     // delta [0,0] will do fine with position: fixed elements,
  2743.     // position:absolute needs offsetParent deltas
  2744.     if (Element.getStyle(target,'position') == 'absolute') {
  2745.       parent = Position.offsetParent(target);
  2746.       delta = Position.page(parent);
  2747.     }
  2748.     // correct by body offsets (fixes Safari)
  2749.     if (parent == document.body) {
  2750.       delta[0] -= document.body.offsetLeft;
  2751.       delta[1] -= document.body.offsetTop;
  2752.     }
  2753.     // set position
  2754.     if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
  2755.     if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
  2756.     if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
  2757.     if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  2758.   },
  2759.   absolutize: function(element) {
  2760.     element = $(element);
  2761.     if (element.style.position == 'absolute') return;
  2762.     Position.prepare();
  2763.     var offsets = Position.positionedOffset(element);
  2764.     var top     = offsets[1];
  2765.     var left    = offsets[0];
  2766.     var width   = element.clientWidth;
  2767.     var height  = element.clientHeight;
  2768.     element._originalLeft   = left - parseFloat(element.style.left  || 0);
  2769.     element._originalTop    = top  - parseFloat(element.style.top || 0);
  2770.     element._originalWidth  = element.style.width;
  2771.     element._originalHeight = element.style.height;
  2772.     element.style.position = 'absolute';
  2773.     element.style.top    = top + 'px';
  2774.     element.style.left   = left + 'px';
  2775.     element.style.width  = width + 'px';
  2776.     element.style.height = height + 'px';
  2777.   },
  2778.   relativize: function(element) {
  2779.     element = $(element);
  2780.     if (element.style.position == 'relative') return;
  2781.     Position.prepare();
  2782.     element.style.position = 'relative';
  2783.     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
  2784.     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
  2785.     element.style.top    = top + 'px';
  2786.     element.style.left   = left + 'px';
  2787.     element.style.height = element._originalHeight;
  2788.     element.style.width  = element._originalWidth;
  2789.   }
  2790. }
  2791. // Safari returns margins on body which is incorrect if the child is absolutely
  2792. // positioned.  For performance reasons, redefine Position.cumulativeOffset for
  2793. // KHTML/WebKit only.
  2794. if (Prototype.Browser.WebKit) {
  2795.   Position.cumulativeOffset = function(element) {
  2796.     var valueT = 0, valueL = 0;
  2797.     do {
  2798.       valueT += element.offsetTop  || 0;
  2799.       valueL += element.offsetLeft || 0;
  2800.       if (element.offsetParent == document.body)
  2801.         if (Element.getStyle(element, 'position') == 'absolute') break;
  2802.       element = element.offsetParent;
  2803.     } while (element);
  2804.     return [valueL, valueT];
  2805.   }
  2806. }
  2807. Element.addMethods();