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