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