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