WordPress.org

Make WordPress Core

Ticket #11594: porototype.update.to.1.6.1.patch

File porototype.update.to.1.6.1.patch, 139.8 KB (added by Simek, 8 years ago)
  • prototype.js

     
    1 /*  Prototype JavaScript framework, version 1.6.0
    2  *  (c) 2005-2007 Sam Stephenson
     1/*  Prototype JavaScript framework, version 1.6.1
     2 *  (c) 2005-2009 Sam Stephenson
    33 *
    44 *  Prototype is freely distributable under the terms of an MIT-style license.
    55 *  For details, see the Prototype web site: http://www.prototypejs.org/
     
    77 *--------------------------------------------------------------------------*/
    88
    99var Prototype = {
    10   Version: '1.6.0',
     10  Version: '1.6.1',
    1111
    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   },
     12  Browser: (function(){
     13    var ua = navigator.userAgent;
     14    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
     15    return {
     16      IE:             !!window.attachEvent && !isOpera,
     17      Opera:          isOpera,
     18      WebKit:         ua.indexOf('AppleWebKit/') > -1,
     19      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
     20      MobileSafari:   /Apple.*Mobile.*Safari/.test(ua)
     21    }
     22  })(),
    1923
    2024  BrowserFeatures: {
    2125    XPath: !!document.evaluate,
    22     ElementExtensions: !!window.HTMLElement,
    23     SpecificElementExtensions:
    24       document.createElement('div').__proto__ &&
    25       document.createElement('div').__proto__ !==
    26         document.createElement('form').__proto__
     26    SelectorsAPI: !!document.querySelector,
     27    ElementExtensions: (function() {
     28      var constructor = window.Element || window.HTMLElement;
     29      return !!(constructor && constructor.prototype);
     30    })(),
     31    SpecificElementExtensions: (function() {
     32      if (typeof window.HTMLDivElement !== 'undefined')
     33        return true;
     34
     35      var div = document.createElement('div');
     36      var form = document.createElement('form');
     37      var isSupported = false;
     38
     39      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
     40        isSupported = true;
     41      }
     42
     43      div = form = null;
     44
     45      return isSupported;
     46    })()
    2747  },
    2848
    2949  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
     
    3656if (Prototype.Browser.MobileSafari)
    3757  Prototype.BrowserFeatures.SpecificElementExtensions = false;
    3858
    39 if (Prototype.Browser.WebKit)
    40   Prototype.BrowserFeatures.XPath = false;
    4159
     60var Abstract = { };
     61
     62
     63var Try = {
     64  these: function() {
     65    var returnValue;
     66
     67    for (var i = 0, length = arguments.length; i < length; i++) {
     68      var lambda = arguments[i];
     69      try {
     70        returnValue = lambda();
     71        break;
     72      } catch (e) { }
     73    }
     74
     75    return returnValue;
     76  }
     77};
     78
    4279/* Based on Alex Arnell's inheritance implementation. */
    43 var Class = {
    44   create: function() {
     80
     81var Class = (function() {
     82  function subclass() {};
     83  function create() {
    4584    var parent = null, properties = $A(arguments);
    4685    if (Object.isFunction(properties[0]))
    4786      parent = properties.shift();
     
    5594    klass.subclasses = [];
    5695
    5796    if (parent) {
    58       var subclass = function() { };
    5997      subclass.prototype = parent.prototype;
    6098      klass.prototype = new subclass;
    6199      parent.subclasses.push(klass);
     
    68106      klass.prototype.initialize = Prototype.emptyFunction;
    69107
    70108    klass.prototype.constructor = klass;
    71 
    72109    return klass;
    73110  }
    74 };
    75111
    76 Class.Methods = {
    77   addMethods: function(source) {
     112  function addMethods(source) {
    78113    var ancestor   = this.superclass && this.superclass.prototype;
    79114    var properties = Object.keys(source);
    80115
    81     if (!Object.keys({ toString: true }).length)
    82       properties.push("toString", "valueOf");
     116    if (!Object.keys({ toString: true }).length) {
     117      if (source.toString != Object.prototype.toString)
     118        properties.push("toString");
     119      if (source.valueOf != Object.prototype.valueOf)
     120        properties.push("valueOf");
     121    }
    83122
    84123    for (var i = 0, length = properties.length; i < length; i++) {
    85124      var property = properties[i], value = source[property];
    86125      if (ancestor && Object.isFunction(value) &&
    87126          value.argumentNames().first() == "$super") {
    88         var method = value, value = Object.extend((function(m) {
    89           return function() { return ancestor[m].apply(this, arguments) };
    90         })(property).wrap(method), {
    91           valueOf:  function() { return method },
    92           toString: function() { return method.toString() }
    93         });
     127        var method = value;
     128        value = (function(m) {
     129          return function() { return ancestor[m].apply(this, arguments); };
     130        })(property).wrap(method);
     131
     132        value.valueOf = method.valueOf.bind(method);
     133        value.toString = method.toString.bind(method);
    94134      }
    95135      this.prototype[property] = value;
    96136    }
    97137
    98138    return this;
    99139  }
    100 };
    101140
    102 var Abstract = { };
     141  return {
     142    create: create,
     143    Methods: {
     144      addMethods: addMethods
     145    }
     146  };
     147})();
     148(function() {
    103149
    104 Object.extend = function(destination, source) {
    105   for (var property in source)
    106     destination[property] = source[property];
    107   return destination;
    108 };
     150  var _toString = Object.prototype.toString;
    109151
    110 Object.extend(Object, {
    111   inspect: function(object) {
     152  function extend(destination, source) {
     153    for (var property in source)
     154      destination[property] = source[property];
     155    return destination;
     156  }
     157
     158  function inspect(object) {
    112159    try {
    113       if (object === undefined) return 'undefined';
     160      if (isUndefined(object)) return 'undefined';
    114161      if (object === null) return 'null';
    115       return object.inspect ? object.inspect() : object.toString();
     162      return object.inspect ? object.inspect() : String(object);
    116163    } catch (e) {
    117164      if (e instanceof RangeError) return '...';
    118165      throw e;
    119166    }
    120   },
     167  }
    121168
    122   toJSON: function(object) {
     169  function toJSON(object) {
    123170    var type = typeof object;
    124171    switch (type) {
    125172      case 'undefined':
     
    130177
    131178    if (object === null) return 'null';
    132179    if (object.toJSON) return object.toJSON();
    133     if (Object.isElement(object)) return;
     180    if (isElement(object)) return;
    134181
    135182    var results = [];
    136183    for (var property in object) {
    137       var value = Object.toJSON(object[property]);
    138       if (value !== undefined)
     184      var value = toJSON(object[property]);
     185      if (!isUndefined(value))
    139186        results.push(property.toJSON() + ': ' + value);
    140187    }
    141188
    142189    return '{' + results.join(', ') + '}';
    143   },
     190  }
    144191
    145   toQueryString: function(object) {
     192  function toQueryString(object) {
    146193    return $H(object).toQueryString();
    147   },
     194  }
    148195
    149   toHTML: function(object) {
     196  function toHTML(object) {
    150197    return object && object.toHTML ? object.toHTML() : String.interpret(object);
    151   },
     198  }
    152199
    153   keys: function(object) {
    154     var keys = [];
     200  function keys(object) {
     201    var results = [];
    155202    for (var property in object)
    156       keys.push(property);
    157     return keys;
    158   },
     203      results.push(property);
     204    return results;
     205  }
    159206
    160   values: function(object) {
    161     var values = [];
     207  function values(object) {
     208    var results = [];
    162209    for (var property in object)
    163       values.push(object[property]);
    164     return values;
    165   },
     210      results.push(object[property]);
     211    return results;
     212  }
    166213
    167   clone: function(object) {
    168     return Object.extend({ }, object);
    169   },
     214  function clone(object) {
     215    return extend({ }, object);
     216  }
    170217
    171   isElement: function(object) {
    172     return object && object.nodeType == 1;
    173   },
     218  function isElement(object) {
     219    return !!(object && object.nodeType == 1);
     220  }
    174221
    175   isArray: function(object) {
    176     return object && object.constructor === Array;
    177   },
     222  function isArray(object) {
     223    return _toString.call(object) == "[object Array]";
     224  }
    178225
    179   isHash: function(object) {
     226
     227  function isHash(object) {
    180228    return object instanceof Hash;
    181   },
     229  }
    182230
    183   isFunction: function(object) {
    184     return typeof object == "function";
    185   },
     231  function isFunction(object) {
     232    return typeof object === "function";
     233  }
    186234
    187   isString: function(object) {
    188     return typeof object == "string";
    189   },
     235  function isString(object) {
     236    return _toString.call(object) == "[object String]";
     237  }
    190238
    191   isNumber: function(object) {
    192     return typeof object == "number";
    193   },
     239  function isNumber(object) {
     240    return _toString.call(object) == "[object Number]";
     241  }
    194242
    195   isUndefined: function(object) {
    196     return typeof object == "undefined";
     243  function isUndefined(object) {
     244    return typeof object === "undefined";
    197245  }
    198 });
    199246
    200 Object.extend(Function.prototype, {
    201   argumentNames: function() {
    202     var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
     247  extend(Object, {
     248    extend:        extend,
     249    inspect:       inspect,
     250    toJSON:        toJSON,
     251    toQueryString: toQueryString,
     252    toHTML:        toHTML,
     253    keys:          keys,
     254    values:        values,
     255    clone:         clone,
     256    isElement:     isElement,
     257    isArray:       isArray,
     258    isHash:        isHash,
     259    isFunction:    isFunction,
     260    isString:      isString,
     261    isNumber:      isNumber,
     262    isUndefined:   isUndefined
     263  });
     264})();
     265Object.extend(Function.prototype, (function() {
     266  var slice = Array.prototype.slice;
     267
     268  function update(array, args) {
     269    var arrayLength = array.length, length = args.length;
     270    while (length--) array[arrayLength + length] = args[length];
     271    return array;
     272  }
     273
     274  function merge(array, args) {
     275    array = slice.call(array, 0);
     276    return update(array, args);
     277  }
     278
     279  function argumentNames() {
     280    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
     281      .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
     282      .replace(/\s+/g, '').split(',');
    203283    return names.length == 1 && !names[0] ? [] : names;
    204   },
     284  }
    205285
    206   bind: function() {
    207     if (arguments.length < 2 && arguments[0] === undefined) return this;
    208     var __method = this, args = $A(arguments), object = args.shift();
     286  function bind(context) {
     287    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
     288    var __method = this, args = slice.call(arguments, 1);
    209289    return function() {
    210       return __method.apply(object, args.concat($A(arguments)));
     290      var a = merge(args, arguments);
     291      return __method.apply(context, a);
    211292    }
    212   },
     293  }
    213294
    214   bindAsEventListener: function() {
    215     var __method = this, args = $A(arguments), object = args.shift();
     295  function bindAsEventListener(context) {
     296    var __method = this, args = slice.call(arguments, 1);
    216297    return function(event) {
    217       return __method.apply(object, [event || window.event].concat(args));
     298      var a = update([event || window.event], args);
     299      return __method.apply(context, a);
    218300    }
    219   },
     301  }
    220302
    221   curry: function() {
     303  function curry() {
    222304    if (!arguments.length) return this;
    223     var __method = this, args = $A(arguments);
     305    var __method = this, args = slice.call(arguments, 0);
    224306    return function() {
    225       return __method.apply(this, args.concat($A(arguments)));
     307      var a = merge(args, arguments);
     308      return __method.apply(this, a);
    226309    }
    227   },
     310  }
    228311
    229   delay: function() {
    230     var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
     312  function delay(timeout) {
     313    var __method = this, args = slice.call(arguments, 1);
     314    timeout = timeout * 1000
    231315    return window.setTimeout(function() {
    232316      return __method.apply(__method, args);
    233317    }, timeout);
    234   },
     318  }
    235319
    236   wrap: function(wrapper) {
     320  function defer() {
     321    var args = update([0.01], arguments);
     322    return this.delay.apply(this, args);
     323  }
     324
     325  function wrap(wrapper) {
    237326    var __method = this;
    238327    return function() {
    239       return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
     328      var a = update([__method.bind(this)], arguments);
     329      return wrapper.apply(this, a);
    240330    }
    241   },
     331  }
    242332
    243   methodize: function() {
     333  function methodize() {
    244334    if (this._methodized) return this._methodized;
    245335    var __method = this;
    246336    return this._methodized = function() {
    247       return __method.apply(null, [this].concat($A(arguments)));
     337      var a = update([this], arguments);
     338      return __method.apply(null, a);
    248339    };
    249340  }
    250 });
    251341
    252 Function.prototype.defer = Function.prototype.delay.curry(0.01);
     342  return {
     343    argumentNames:       argumentNames,
     344    bind:                bind,
     345    bindAsEventListener: bindAsEventListener,
     346    curry:               curry,
     347    delay:               delay,
     348    defer:               defer,
     349    wrap:                wrap,
     350    methodize:           methodize
     351  }
     352})());
    253353
     354
    254355Date.prototype.toJSON = function() {
    255356  return '"' + this.getUTCFullYear() + '-' +
    256357    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
     
    260361    this.getUTCSeconds().toPaddedString(2) + 'Z"';
    261362};
    262363
    263 var Try = {
    264   these: function() {
    265     var returnValue;
    266364
    267     for (var i = 0, length = arguments.length; i < length; i++) {
    268       var lambda = arguments[i];
    269       try {
    270         returnValue = lambda();
    271         break;
    272       } catch (e) { }
    273     }
    274 
    275     return returnValue;
    276   }
    277 };
    278 
    279365RegExp.prototype.match = RegExp.prototype.test;
    280366
    281367RegExp.escape = function(str) {
    282368  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
    283369};
    284 
    285 /*--------------------------------------------------------------------------*/
    286 
    287370var PeriodicalExecuter = Class.create({
    288371  initialize: function(callback, frequency) {
    289372    this.callback = callback;
     
    312395      try {
    313396        this.currentlyExecuting = true;
    314397        this.execute();
    315       } finally {
    316398        this.currentlyExecuting = false;
     399      } catch(e) {
     400        this.currentlyExecuting = false;
     401        throw e;
    317402      }
    318403    }
    319404  }
     
    332417  }
    333418});
    334419
    335 Object.extend(String.prototype, {
    336   gsub: function(pattern, replacement) {
     420Object.extend(String.prototype, (function() {
     421
     422  function prepareReplacement(replacement) {
     423    if (Object.isFunction(replacement)) return replacement;
     424    var template = new Template(replacement);
     425    return function(match) { return template.evaluate(match) };
     426  }
     427
     428  function gsub(pattern, replacement) {
    337429    var result = '', source = this, match;
    338     replacement = arguments.callee.prepareReplacement(replacement);
     430    replacement = prepareReplacement(replacement);
    339431
     432    if (Object.isString(pattern))
     433      pattern = RegExp.escape(pattern);
     434
     435    if (!(pattern.length || pattern.source)) {
     436      replacement = replacement('');
     437      return replacement + source.split('').join(replacement) + replacement;
     438    }
     439
    340440    while (source.length > 0) {
    341441      if (match = source.match(pattern)) {
    342442        result += source.slice(0, match.index);
     
    347447      }
    348448    }
    349449    return result;
    350   },
     450  }
    351451
    352   sub: function(pattern, replacement, count) {
    353     replacement = this.gsub.prepareReplacement(replacement);
    354     count = count === undefined ? 1 : count;
     452  function sub(pattern, replacement, count) {
     453    replacement = prepareReplacement(replacement);
     454    count = Object.isUndefined(count) ? 1 : count;
    355455
    356456    return this.gsub(pattern, function(match) {
    357457      if (--count < 0) return match[0];
    358458      return replacement(match);
    359459    });
    360   },
     460  }
    361461
    362   scan: function(pattern, iterator) {
     462  function scan(pattern, iterator) {
    363463    this.gsub(pattern, iterator);
    364464    return String(this);
    365   },
     465  }
    366466
    367   truncate: function(length, truncation) {
     467  function truncate(length, truncation) {
    368468    length = length || 30;
    369     truncation = truncation === undefined ? '...' : truncation;
     469    truncation = Object.isUndefined(truncation) ? '...' : truncation;
    370470    return this.length > length ?
    371471      this.slice(0, length - truncation.length) + truncation : String(this);
    372   },
     472  }
    373473
    374   strip: function() {
     474  function strip() {
    375475    return this.replace(/^\s+/, '').replace(/\s+$/, '');
    376   },
     476  }
    377477
    378   stripTags: function() {
    379     return this.replace(/<\/?[^>]+>/gi, '');
    380   },
     478  function stripTags() {
     479    return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
     480  }
    381481
    382   stripScripts: function() {
     482  function stripScripts() {
    383483    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
    384   },
     484  }
    385485
    386   extractScripts: function() {
     486  function extractScripts() {
    387487    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    388488    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    389489    return (this.match(matchAll) || []).map(function(scriptTag) {
    390490      return (scriptTag.match(matchOne) || ['', ''])[1];
    391491    });
    392   },
     492  }
    393493
    394   evalScripts: function() {
     494  function evalScripts() {
    395495    return this.extractScripts().map(function(script) { return eval(script) });
    396   },
     496  }
    397497
    398   escapeHTML: function() {
    399     var self = arguments.callee;
    400     self.text.data = this;
    401     return self.div.innerHTML;
    402   },
     498  function escapeHTML() {
     499    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
     500  }
    403501
    404   unescapeHTML: function() {
    405     var div = new Element('div');
    406     div.innerHTML = this.stripTags();
    407     return div.childNodes[0] ? (div.childNodes.length > 1 ?
    408       $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
    409       div.childNodes[0].nodeValue) : '';
    410   },
     502  function unescapeHTML() {
     503    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
     504  }
    411505
    412   toQueryParams: function(separator) {
     506
     507  function toQueryParams(separator) {
    413508    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    414509    if (!match) return { };
    415510
     
    427522      }
    428523      return hash;
    429524    });
    430   },
     525  }
    431526
    432   toArray: function() {
     527  function toArray() {
    433528    return this.split('');
    434   },
     529  }
    435530
    436   succ: function() {
     531  function succ() {
    437532    return this.slice(0, this.length - 1) +
    438533      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
    439   },
     534  }
    440535
    441   times: function(count) {
     536  function times(count) {
    442537    return count < 1 ? '' : new Array(count + 1).join(this);
    443   },
     538  }
    444539
    445   camelize: function() {
     540  function camelize() {
    446541    var parts = this.split('-'), len = parts.length;
    447542    if (len == 1) return parts[0];
    448543
     
    454549      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
    455550
    456551    return camelized;
    457   },
     552  }
    458553
    459   capitalize: function() {
     554  function capitalize() {
    460555    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
    461   },
     556  }
    462557
    463   underscore: function() {
    464     return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
    465   },
     558  function underscore() {
     559    return this.replace(/::/g, '/')
     560               .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
     561               .replace(/([a-z\d])([A-Z])/g, '$1_$2')
     562               .replace(/-/g, '_')
     563               .toLowerCase();
     564  }
    466565
    467   dasherize: function() {
    468     return this.gsub(/_/,'-');
    469   },
     566  function dasherize() {
     567    return this.replace(/_/g, '-');
     568  }
    470569
    471   inspect: function(useDoubleQuotes) {
    472     var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
    473       var character = String.specialChar[match[0]];
    474       return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
     570  function inspect(useDoubleQuotes) {
     571    var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
     572      if (character in String.specialChar) {
     573        return String.specialChar[character];
     574      }
     575      return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
    475576    });
    476577    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    477578    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
    478   },
     579  }
    479580
    480   toJSON: function() {
     581  function toJSON() {
    481582    return this.inspect(true);
    482   },
     583  }
    483584
    484   unfilterJSON: function(filter) {
    485     return this.sub(filter || Prototype.JSONFilter, '#{1}');
    486   },
     585  function unfilterJSON(filter) {
     586    return this.replace(filter || Prototype.JSONFilter, '$1');
     587  }
    487588
    488   isJSON: function() {
    489     var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
     589  function isJSON() {
     590    var str = this;
     591    if (str.blank()) return false;
     592    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    490593    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
    491   },
     594  }
    492595
    493   evalJSON: function(sanitize) {
     596  function evalJSON(sanitize) {
    494597    var json = this.unfilterJSON();
    495598    try {
    496599      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    497600    } catch (e) { }
    498601    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
    499   },
     602  }
    500603
    501   include: function(pattern) {
     604  function include(pattern) {
    502605    return this.indexOf(pattern) > -1;
    503   },
     606  }
    504607
    505   startsWith: function(pattern) {
     608  function startsWith(pattern) {
    506609    return this.indexOf(pattern) === 0;
    507   },
     610  }
    508611
    509   endsWith: function(pattern) {
     612  function endsWith(pattern) {
    510613    var d = this.length - pattern.length;
    511614    return d >= 0 && this.lastIndexOf(pattern) === d;
    512   },
     615  }
    513616
    514   empty: function() {
     617  function empty() {
    515618    return this == '';
    516   },
     619  }
    517620
    518   blank: function() {
     621  function blank() {
    519622    return /^\s*$/.test(this);
    520   },
     623  }
    521624
    522   interpolate: function(object, pattern) {
     625  function interpolate(object, pattern) {
    523626    return new Template(this, pattern).evaluate(object);
    524627  }
    525 });
    526628
    527 if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
    528   escapeHTML: function() {
    529     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
    530   },
    531   unescapeHTML: function() {
    532     return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
    533   }
    534 });
     629  return {
     630    gsub:           gsub,
     631    sub:            sub,
     632    scan:           scan,
     633    truncate:       truncate,
     634    strip:          String.prototype.trim ? String.prototype.trim : strip,
     635    stripTags:      stripTags,
     636    stripScripts:   stripScripts,
     637    extractScripts: extractScripts,
     638    evalScripts:    evalScripts,
     639    escapeHTML:     escapeHTML,
     640    unescapeHTML:   unescapeHTML,
     641    toQueryParams:  toQueryParams,
     642    parseQuery:     toQueryParams,
     643    toArray:        toArray,
     644    succ:           succ,
     645    times:          times,
     646    camelize:       camelize,
     647    capitalize:     capitalize,
     648    underscore:     underscore,
     649    dasherize:      dasherize,
     650    inspect:        inspect,
     651    toJSON:         toJSON,
     652    unfilterJSON:   unfilterJSON,
     653    isJSON:         isJSON,
     654    evalJSON:       evalJSON,
     655    include:        include,
     656    startsWith:     startsWith,
     657    endsWith:       endsWith,
     658    empty:          empty,
     659    blank:          blank,
     660    interpolate:    interpolate
     661  };
     662})());
    535663
    536 String.prototype.gsub.prepareReplacement = function(replacement) {
    537   if (Object.isFunction(replacement)) return replacement;
    538   var template = new Template(replacement);
    539   return function(match) { return template.evaluate(match) };
    540 };
    541 
    542 String.prototype.parseQuery = String.prototype.toQueryParams;
    543 
    544 Object.extend(String.prototype.escapeHTML, {
    545   div:  document.createElement('div'),
    546   text: document.createTextNode('')
    547 });
    548 
    549 with (String.prototype.escapeHTML) div.appendChild(text);
    550 
    551664var Template = Class.create({
    552665  initialize: function(template, pattern) {
    553666    this.template = template.toString();
     
    555668  },
    556669
    557670  evaluate: function(object) {
    558     if (Object.isFunction(object.toTemplateReplacements))
     671    if (object && Object.isFunction(object.toTemplateReplacements))
    559672      object = object.toTemplateReplacements();
    560673
    561674    return this.template.gsub(this.pattern, function(match) {
    562       if (object == null) return '';
     675      if (object == null) return (match[1] + '');
    563676
    564677      var before = match[1] || '';
    565678      if (before == '\\') return match[2];
    566679
    567680      var ctx = object, expr = match[3];
    568       var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
     681      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
     682      match = pattern.exec(expr);
    569683      if (match == null) return before;
    570684
    571685      while (match != null) {
    572         var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
     686        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
    573687        ctx = ctx[comp];
    574688        if (null == ctx || '' == match[3]) break;
    575689        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
     
    577691      }
    578692
    579693      return before + String.interpret(ctx);
    580     }.bind(this));
     694    });
    581695  }
    582696});
    583697Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
    584698
    585699var $break = { };
    586700
    587 var Enumerable = {
    588   each: function(iterator, context) {
     701var Enumerable = (function() {
     702  function each(iterator, context) {
    589703    var index = 0;
    590     iterator = iterator.bind(context);
    591704    try {
    592705      this._each(function(value) {
    593         iterator(value, index++);
     706        iterator.call(context, value, index++);
    594707      });
    595708    } catch (e) {
    596709      if (e != $break) throw e;
    597710    }
    598711    return this;
    599   },
     712  }
    600713
    601   eachSlice: function(number, iterator, context) {
    602     iterator = iterator ? iterator.bind(context) : Prototype.K;
     714  function eachSlice(number, iterator, context) {
    603715    var index = -number, slices = [], array = this.toArray();
     716    if (number < 1) return array;
    604717    while ((index += number) < array.length)
    605718      slices.push(array.slice(index, index+number));
    606719    return slices.collect(iterator, context);
    607   },
     720  }
    608721
    609   all: function(iterator, context) {
    610     iterator = iterator ? iterator.bind(context) : Prototype.K;
     722  function all(iterator, context) {
     723    iterator = iterator || Prototype.K;
    611724    var result = true;
    612725    this.each(function(value, index) {
    613       result = result && !!iterator(value, index);
     726      result = result && !!iterator.call(context, value, index);
    614727      if (!result) throw $break;
    615728    });
    616729    return result;
    617   },
     730  }
    618731
    619   any: function(iterator, context) {
    620     iterator = iterator ? iterator.bind(context) : Prototype.K;
     732  function any(iterator, context) {
     733    iterator = iterator || Prototype.K;
    621734    var result = false;
    622735    this.each(function(value, index) {
    623       if (result = !!iterator(value, index))
     736      if (result = !!iterator.call(context, value, index))
    624737        throw $break;
    625738    });
    626739    return result;
    627   },
     740  }
    628741
    629   collect: function(iterator, context) {
    630     iterator = iterator ? iterator.bind(context) : Prototype.K;
     742  function collect(iterator, context) {
     743    iterator = iterator || Prototype.K;
    631744    var results = [];
    632745    this.each(function(value, index) {
    633       results.push(iterator(value, index));
     746      results.push(iterator.call(context, value, index));
    634747    });
    635748    return results;
    636   },
     749  }
    637750
    638   detect: function(iterator, context) {
    639     iterator = iterator.bind(context);
     751  function detect(iterator, context) {
    640752    var result;
    641753    this.each(function(value, index) {
    642       if (iterator(value, index)) {
     754      if (iterator.call(context, value, index)) {
    643755        result = value;
    644756        throw $break;
    645757      }
    646758    });
    647759    return result;
    648   },
     760  }
    649761
    650   findAll: function(iterator, context) {
    651     iterator = iterator.bind(context);
     762  function findAll(iterator, context) {
    652763    var results = [];
    653764    this.each(function(value, index) {
    654       if (iterator(value, index))
     765      if (iterator.call(context, value, index))
    655766        results.push(value);
    656767    });
    657768    return results;
    658   },
     769  }
    659770
    660   grep: function(filter, iterator, context) {
    661     iterator = iterator ? iterator.bind(context) : Prototype.K;
     771  function grep(filter, iterator, context) {
     772    iterator = iterator || Prototype.K;
    662773    var results = [];
    663774
    664775    if (Object.isString(filter))
    665       filter = new RegExp(filter);
     776      filter = new RegExp(RegExp.escape(filter));
    666777
    667778    this.each(function(value, index) {
    668779      if (filter.match(value))
    669         results.push(iterator(value, index));
     780        results.push(iterator.call(context, value, index));
    670781    });
    671782    return results;
    672   },
     783  }
    673784
    674   include: function(object) {
     785  function include(object) {
    675786    if (Object.isFunction(this.indexOf))
    676787      if (this.indexOf(object) != -1) return true;
    677788
     
    683794      }
    684795    });
    685796    return found;
    686   },
     797  }
    687798
    688   inGroupsOf: function(number, fillWith) {
    689     fillWith = fillWith === undefined ? null : fillWith;
     799  function inGroupsOf(number, fillWith) {
     800    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    690801    return this.eachSlice(number, function(slice) {
    691802      while(slice.length < number) slice.push(fillWith);
    692803      return slice;
    693804    });
    694   },
     805  }
    695806
    696   inject: function(memo, iterator, context) {
    697     iterator = iterator.bind(context);
     807  function inject(memo, iterator, context) {
    698808    this.each(function(value, index) {
    699       memo = iterator(memo, value, index);
     809      memo = iterator.call(context, memo, value, index);
    700810    });
    701811    return memo;
    702   },
     812  }
    703813
    704   invoke: function(method) {
     814  function invoke(method) {
    705815    var args = $A(arguments).slice(1);
    706816    return this.map(function(value) {
    707817      return value[method].apply(value, args);
    708818    });
    709   },
     819  }
    710820
    711   max: function(iterator, context) {
    712     iterator = iterator ? iterator.bind(context) : Prototype.K;
     821  function max(iterator, context) {
     822    iterator = iterator || Prototype.K;
    713823    var result;
    714824    this.each(function(value, index) {
    715       value = iterator(value, index);
    716       if (result == undefined || value >= result)
     825      value = iterator.call(context, value, index);
     826      if (result == null || value >= result)
    717827        result = value;
    718828    });
    719829    return result;
    720   },
     830  }
    721831
    722   min: function(iterator, context) {
    723     iterator = iterator ? iterator.bind(context) : Prototype.K;
     832  function min(iterator, context) {
     833    iterator = iterator || Prototype.K;
    724834    var result;
    725835    this.each(function(value, index) {
    726       value = iterator(value, index);
    727       if (result == undefined || value < result)
     836      value = iterator.call(context, value, index);
     837      if (result == null || value < result)
    728838        result = value;
    729839    });
    730840    return result;
    731   },
     841  }
    732842
    733   partition: function(iterator, context) {
    734     iterator = iterator ? iterator.bind(context) : Prototype.K;
     843  function partition(iterator, context) {
     844    iterator = iterator || Prototype.K;
    735845    var trues = [], falses = [];
    736846    this.each(function(value, index) {
    737       (iterator(value, index) ?
     847      (iterator.call(context, value, index) ?
    738848        trues : falses).push(value);
    739849    });
    740850    return [trues, falses];
    741   },
     851  }
    742852
    743   pluck: function(property) {
     853  function pluck(property) {
    744854    var results = [];
    745855    this.each(function(value) {
    746856      results.push(value[property]);
    747857    });
    748858    return results;
    749   },
     859  }
    750860
    751   reject: function(iterator, context) {
    752     iterator = iterator.bind(context);
     861  function reject(iterator, context) {
    753862    var results = [];
    754863    this.each(function(value, index) {
    755       if (!iterator(value, index))
     864      if (!iterator.call(context, value, index))
    756865        results.push(value);
    757866    });
    758867    return results;
    759   },
     868  }
    760869
    761   sortBy: function(iterator, context) {
    762     iterator = iterator.bind(context);
     870  function sortBy(iterator, context) {
    763871    return this.map(function(value, index) {
    764       return {value: value, criteria: iterator(value, index)};
     872      return {
     873        value: value,
     874        criteria: iterator.call(context, value, index)
     875      };
    765876    }).sort(function(left, right) {
    766877      var a = left.criteria, b = right.criteria;
    767878      return a < b ? -1 : a > b ? 1 : 0;
    768879    }).pluck('value');
    769   },
     880  }
    770881
    771   toArray: function() {
     882  function toArray() {
    772883    return this.map();
    773   },
     884  }
    774885
    775   zip: function() {
     886  function zip() {
    776887    var iterator = Prototype.K, args = $A(arguments);
    777888    if (Object.isFunction(args.last()))
    778889      iterator = args.pop();
     
    781892    return this.map(function(value, index) {
    782893      return iterator(collections.pluck(index));
    783894    });
    784   },
     895  }
    785896
    786   size: function() {
     897  function size() {
    787898    return this.toArray().length;
    788   },
     899  }
    789900
    790   inspect: function() {
     901  function inspect() {
    791902    return '#<Enumerable:' + this.toArray().inspect() + '>';
    792903  }
    793 };
    794904
    795 Object.extend(Enumerable, {
    796   map:     Enumerable.collect,
    797   find:    Enumerable.detect,
    798   select:  Enumerable.findAll,
    799   filter:  Enumerable.findAll,
    800   member:  Enumerable.include,
    801   entries: Enumerable.toArray,
    802   every:   Enumerable.all,
    803   some:    Enumerable.any
    804 });
     905
     906
     907
     908
     909
     910
     911
     912
     913  return {
     914    each:       each,
     915    eachSlice:  eachSlice,
     916    all:        all,
     917    every:      all,
     918    any:        any,
     919    some:       any,
     920    collect:    collect,
     921    map:        collect,
     922    detect:     detect,
     923    findAll:    findAll,
     924    select:     findAll,
     925    filter:     findAll,
     926    grep:       grep,
     927    include:    include,
     928    member:     include,
     929    inGroupsOf: inGroupsOf,
     930    inject:     inject,
     931    invoke:     invoke,
     932    max:        max,
     933    min:        min,
     934    partition:  partition,
     935    pluck:      pluck,
     936    reject:     reject,
     937    sortBy:     sortBy,
     938    toArray:    toArray,
     939    entries:    toArray,
     940    zip:        zip,
     941    size:       size,
     942    inspect:    inspect,
     943    find:       detect
     944  };
     945})();
    805946function $A(iterable) {
    806947  if (!iterable) return [];
    807   if (iterable.toArray) return iterable.toArray();
    808   var length = iterable.length, results = new Array(length);
     948  if ('toArray' in Object(iterable)) return iterable.toArray();
     949  var length = iterable.length || 0, results = new Array(length);
    809950  while (length--) results[length] = iterable[length];
    810951  return results;
    811952}
    812953
    813 if (Prototype.Browser.WebKit) {
    814   function $A(iterable) {
    815     if (!iterable) return [];
    816     if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
    817         iterable.toArray) return iterable.toArray();
    818     var length = iterable.length, results = new Array(length);
    819     while (length--) results[length] = iterable[length];
    820     return results;
    821   }
     954function $w(string) {
     955  if (!Object.isString(string)) return [];
     956  string = string.strip();
     957  return string ? string.split(/\s+/) : [];
    822958}
    823959
    824960Array.from = $A;
    825961
    826 Object.extend(Array.prototype, Enumerable);
    827962
    828 if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
     963(function() {
     964  var arrayProto = Array.prototype,
     965      slice = arrayProto.slice,
     966      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
    829967
    830 Object.extend(Array.prototype, {
    831   _each: function(iterator) {
     968  function each(iterator) {
    832969    for (var i = 0, length = this.length; i < length; i++)
    833970      iterator(this[i]);
    834   },
     971  }
     972  if (!_each) _each = each;
    835973
    836   clear: function() {
     974  function clear() {
    837975    this.length = 0;
    838976    return this;
    839   },
     977  }
    840978
    841   first: function() {
     979  function first() {
    842980    return this[0];
    843   },
     981  }
    844982
    845   last: function() {
     983  function last() {
    846984    return this[this.length - 1];
    847   },
     985  }
    848986
    849   compact: function() {
     987  function compact() {
    850988    return this.select(function(value) {
    851989      return value != null;
    852990    });
    853   },
     991  }
    854992
    855   flatten: function() {
     993  function flatten() {
    856994    return this.inject([], function(array, value) {
    857       return array.concat(Object.isArray(value) ?
    858         value.flatten() : [value]);
     995      if (Object.isArray(value))
     996        return array.concat(value.flatten());
     997      array.push(value);
     998      return array;
    859999    });
    860   },
     1000  }
    8611001
    862   without: function() {
    863     var values = $A(arguments);
     1002  function without() {
     1003    var values = slice.call(arguments, 0);
    8641004    return this.select(function(value) {
    8651005      return !values.include(value);
    8661006    });
    867   },
     1007  }
    8681008
    869   reverse: function(inline) {
     1009  function reverse(inline) {
    8701010    return (inline !== false ? this : this.toArray())._reverse();
    871   },
     1011  }
    8721012
    873   reduce: function() {
    874     return this.length > 1 ? this : this[0];
    875   },
    876 
    877   uniq: function(sorted) {
     1013  function uniq(sorted) {
    8781014    return this.inject([], function(array, value, index) {
    8791015      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
    8801016        array.push(value);
    8811017      return array;
    8821018    });
    883   },
     1019  }
    8841020
    885   intersect: function(array) {
     1021  function intersect(array) {
    8861022    return this.uniq().findAll(function(item) {
    8871023      return array.detect(function(value) { return item === value });
    8881024    });
    889   },
     1025  }
    8901026
    891   clone: function() {
    892     return [].concat(this);
    893   },
    8941027
    895   size: function() {
     1028  function clone() {
     1029    return slice.call(this, 0);
     1030  }
     1031
     1032  function size() {
    8961033    return this.length;
    897   },
     1034  }
    8981035
    899   inspect: function() {
     1036  function inspect() {
    9001037    return '[' + this.map(Object.inspect).join(', ') + ']';
    901   },
     1038  }
    9021039
    903   toJSON: function() {
     1040  function toJSON() {
    9041041    var results = [];
    9051042    this.each(function(object) {
    9061043      var value = Object.toJSON(object);
    907       if (value !== undefined) results.push(value);
     1044      if (!Object.isUndefined(value)) results.push(value);
    9081045    });
    9091046    return '[' + results.join(', ') + ']';
    9101047  }
    911 });
    9121048
    913 // use native browser JS 1.6 implementation if available
    914 if (Object.isFunction(Array.prototype.forEach))
    915   Array.prototype._each = Array.prototype.forEach;
     1049  function indexOf(item, i) {
     1050    i || (i = 0);
     1051    var length = this.length;
     1052    if (i < 0) i = length + i;
     1053    for (; i < length; i++)
     1054      if (this[i] === item) return i;
     1055    return -1;
     1056  }
    9161057
    917 if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
    918   i || (i = 0);
    919   var length = this.length;
    920   if (i < 0) i = length + i;
    921   for (; i < length; i++)
    922     if (this[i] === item) return i;
    923   return -1;
    924 };
     1058  function lastIndexOf(item, i) {
     1059    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
     1060    var n = this.slice(0, i).reverse().indexOf(item);
     1061    return (n < 0) ? n : i - n - 1;
     1062  }
    9251063
    926 if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
    927   i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
    928   var n = this.slice(0, i).reverse().indexOf(item);
    929   return (n < 0) ? n : i - n - 1;
    930 };
    931 
    932 Array.prototype.toArray = Array.prototype.clone;
    933 
    934 function $w(string) {
    935   if (!Object.isString(string)) return [];
    936   string = string.strip();
    937   return string ? string.split(/\s+/) : [];
    938 }
    939 
    940 if (Prototype.Browser.Opera){
    941   Array.prototype.concat = function() {
    942     var array = [];
    943     for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
     1064  function concat() {
     1065    var array = slice.call(this, 0), item;
    9441066    for (var i = 0, length = arguments.length; i < length; i++) {
    945       if (Object.isArray(arguments[i])) {
    946         for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
    947           array.push(arguments[i][j]);
     1067      item = arguments[i];
     1068      if (Object.isArray(item) && !('callee' in item)) {
     1069        for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
     1070          array.push(item[j]);
    9481071      } else {
    949         array.push(arguments[i]);
     1072        array.push(item);
    9501073      }
    9511074    }
    9521075    return array;
    953   };
    954 }
    955 Object.extend(Number.prototype, {
    956   toColorPart: function() {
    957     return this.toPaddedString(2, 16);
    958   },
     1076  }
    9591077
    960   succ: function() {
    961     return this + 1;
    962   },
     1078  Object.extend(arrayProto, Enumerable);
    9631079
    964   times: function(iterator) {
    965     $R(0, this, true).each(iterator);
    966     return this;
    967   },
     1080  if (!arrayProto._reverse)
     1081    arrayProto._reverse = arrayProto.reverse;
    9681082
    969   toPaddedString: function(length, radix) {
    970     var string = this.toString(radix || 10);
    971     return '0'.times(length - string.length) + string;
    972   },
     1083  Object.extend(arrayProto, {
     1084    _each:     _each,
     1085    clear:     clear,
     1086    first:     first,
     1087    last:      last,
     1088    compact:   compact,
     1089    flatten:   flatten,
     1090    without:   without,
     1091    reverse:   reverse,
     1092    uniq:      uniq,
     1093    intersect: intersect,
     1094    clone:     clone,
     1095    toArray:   clone,
     1096    size:      size,
     1097    inspect:   inspect,
     1098    toJSON:    toJSON
     1099  });
    9731100
    974   toJSON: function() {
    975     return isFinite(this) ? this.toString() : 'null';
    976   }
    977 });
     1101  var CONCAT_ARGUMENTS_BUGGY = (function() {
     1102    return [].concat(arguments)[0][0] !== 1;
     1103  })(1,2)
    9781104
    979 $w('abs round ceil floor').each(function(method){
    980   Number.prototype[method] = Math[method].methodize();
    981 });
     1105  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
     1106
     1107  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
     1108  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
     1109})();
    9821110function $H(object) {
    9831111  return new Hash(object);
    9841112};
    9851113
    9861114var Hash = Class.create(Enumerable, (function() {
    987   if (function() {
    988     var i = 0, Test = function(value) { this.key = value };
    989     Test.prototype.key = 'foo';
    990     for (var property in new Test('bar')) i++;
    991     return i > 1;
    992   }()) {
    993     function each(iterator) {
    994       var cache = [];
    995       for (var key in this._object) {
    996         var value = this._object[key];
    997         if (cache.include(key)) continue;
    998         cache.push(key);
    999         var pair = [key, value];
    1000         pair.key = key;
    1001         pair.value = value;
    1002         iterator(pair);
    1003       }
     1115  function initialize(object) {
     1116    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
     1117  }
     1118
     1119  function _each(iterator) {
     1120    for (var key in this._object) {
     1121      var value = this._object[key], pair = [key, value];
     1122      pair.key = key;
     1123      pair.value = value;
     1124      iterator(pair);
    10041125    }
    1005   } else {
    1006     function each(iterator) {
    1007       for (var key in this._object) {
    1008         var value = this._object[key], pair = [key, value];
    1009         pair.key = key;
    1010         pair.value = value;
    1011         iterator(pair);
    1012       }
    1013     }
    10141126  }
    10151127
     1128  function set(key, value) {
     1129    return this._object[key] = value;
     1130  }
     1131
     1132  function get(key) {
     1133    if (this._object[key] !== Object.prototype[key])
     1134      return this._object[key];
     1135  }
     1136
     1137  function unset(key) {
     1138    var value = this._object[key];
     1139    delete this._object[key];
     1140    return value;
     1141  }
     1142
     1143  function toObject() {
     1144    return Object.clone(this._object);
     1145  }
     1146
     1147  function keys() {
     1148    return this.pluck('key');
     1149  }
     1150
     1151  function values() {
     1152    return this.pluck('value');
     1153  }
     1154
     1155  function index(value) {
     1156    var match = this.detect(function(pair) {
     1157      return pair.value === value;
     1158    });
     1159    return match && match.key;
     1160  }
     1161
     1162  function merge(object) {
     1163    return this.clone().update(object);
     1164  }
     1165
     1166  function update(object) {
     1167    return new Hash(object).inject(this, function(result, pair) {
     1168      result.set(pair.key, pair.value);
     1169      return result;
     1170    });
     1171  }
     1172
    10161173  function toQueryPair(key, value) {
    10171174    if (Object.isUndefined(value)) return key;
    10181175    return key + '=' + encodeURIComponent(String.interpret(value));
    10191176  }
    10201177
    1021   return {
    1022     initialize: function(object) {
    1023       this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    1024     },
     1178  function toQueryString() {
     1179    return this.inject([], function(results, pair) {
     1180      var key = encodeURIComponent(pair.key), values = pair.value;
    10251181
    1026     _each: each,
     1182      if (values && typeof values == 'object') {
     1183        if (Object.isArray(values))
     1184          return results.concat(values.map(toQueryPair.curry(key)));
     1185      } else results.push(toQueryPair(key, values));
     1186      return results;
     1187    }).join('&');
     1188  }
    10271189
    1028     set: function(key, value) {
    1029       return this._object[key] = value;
    1030     },
     1190  function inspect() {
     1191    return '#<Hash:{' + this.map(function(pair) {
     1192      return pair.map(Object.inspect).join(': ');
     1193    }).join(', ') + '}>';
     1194  }
    10311195
    1032     get: function(key) {
    1033       return this._object[key];
    1034     },
     1196  function toJSON() {
     1197    return Object.toJSON(this.toObject());
     1198  }
    10351199
    1036     unset: function(key) {
    1037       var value = this._object[key];
    1038       delete this._object[key];
    1039       return value;
    1040     },
     1200  function clone() {
     1201    return new Hash(this);
     1202  }
    10411203
    1042     toObject: function() {
    1043       return Object.clone(this._object);
    1044     },
     1204  return {
     1205    initialize:             initialize,
     1206    _each:                  _each,
     1207    set:                    set,
     1208    get:                    get,
     1209    unset:                  unset,
     1210    toObject:               toObject,
     1211    toTemplateReplacements: toObject,
     1212    keys:                   keys,
     1213    values:                 values,
     1214    index:                  index,
     1215    merge:                  merge,
     1216    update:                 update,
     1217    toQueryString:          toQueryString,
     1218    inspect:                inspect,
     1219    toJSON:                 toJSON,
     1220    clone:                  clone
     1221  };
     1222})());
    10451223
    1046     keys: function() {
    1047       return this.pluck('key');
    1048     },
     1224Hash.from = $H;
     1225Object.extend(Number.prototype, (function() {
     1226  function toColorPart() {
     1227    return this.toPaddedString(2, 16);
     1228  }
    10491229
    1050     values: function() {
    1051       return this.pluck('value');
    1052     },
     1230  function succ() {
     1231    return this + 1;
     1232  }
    10531233
    1054     index: function(value) {
    1055       var match = this.detect(function(pair) {
    1056         return pair.value === value;
    1057       });
    1058       return match && match.key;
    1059     },
     1234  function times(iterator, context) {
     1235    $R(0, this, true).each(iterator, context);
     1236    return this;
     1237  }
    10601238
    1061     merge: function(object) {
    1062       return this.clone().update(object);
    1063     },
     1239  function toPaddedString(length, radix) {
     1240    var string = this.toString(radix || 10);
     1241    return '0'.times(length - string.length) + string;
     1242  }
    10641243
    1065     update: function(object) {
    1066       return new Hash(object).inject(this, function(result, pair) {
    1067         result.set(pair.key, pair.value);
    1068         return result;
    1069       });
    1070     },
     1244  function toJSON() {
     1245    return isFinite(this) ? this.toString() : 'null';
     1246  }
    10711247
    1072     toQueryString: function() {
    1073       return this.map(function(pair) {
    1074         var key = encodeURIComponent(pair.key), values = pair.value;
     1248  function abs() {
     1249    return Math.abs(this);
     1250  }
    10751251
    1076         if (values && typeof values == 'object') {
    1077           if (Object.isArray(values))
    1078             return values.map(toQueryPair.curry(key)).join('&');
    1079         }
    1080         return toQueryPair(key, values);
    1081       }).join('&');
    1082     },
     1252  function round() {
     1253    return Math.round(this);
     1254  }
    10831255
    1084     inspect: function() {
    1085       return '#<Hash:{' + this.map(function(pair) {
    1086         return pair.map(Object.inspect).join(': ');
    1087       }).join(', ') + '}>';
    1088     },
     1256  function ceil() {
     1257    return Math.ceil(this);
     1258  }
    10891259
    1090     toJSON: function() {
    1091       return Object.toJSON(this.toObject());
    1092     },
     1260  function floor() {
     1261    return Math.floor(this);
     1262  }
    10931263
    1094     clone: function() {
    1095       return new Hash(this);
    1096     }
    1097   }
     1264  return {
     1265    toColorPart:    toColorPart,
     1266    succ:           succ,
     1267    times:          times,
     1268    toPaddedString: toPaddedString,
     1269    toJSON:         toJSON,
     1270    abs:            abs,
     1271    round:          round,
     1272    ceil:           ceil,
     1273    floor:          floor
     1274  };
    10981275})());
    10991276
    1100 Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
    1101 Hash.from = $H;
    1102 var ObjectRange = Class.create(Enumerable, {
    1103   initialize: function(start, end, exclusive) {
     1277function $R(start, end, exclusive) {
     1278  return new ObjectRange(start, end, exclusive);
     1279}
     1280
     1281var ObjectRange = Class.create(Enumerable, (function() {
     1282  function initialize(start, end, exclusive) {
    11041283    this.start = start;
    11051284    this.end = end;
    11061285    this.exclusive = exclusive;
    1107   },
     1286  }
    11081287
    1109   _each: function(iterator) {
     1288  function _each(iterator) {
    11101289    var value = this.start;
    11111290    while (this.include(value)) {
    11121291      iterator(value);
    11131292      value = value.succ();
    11141293    }
    1115   },
     1294  }
    11161295
    1117   include: function(value) {
     1296  function include(value) {
    11181297    if (value < this.start)
    11191298      return false;
    11201299    if (this.exclusive)
    11211300      return value < this.end;
    11221301    return value <= this.end;
    11231302  }
    1124 });
    11251303
    1126 var $R = function(start, end, exclusive) {
    1127   return new ObjectRange(start, end, exclusive);
    1128 };
     1304  return {
     1305    initialize: initialize,
     1306    _each:      _each,
     1307    include:    include
     1308  };
     1309})());
    11291310
     1311
     1312
    11301313var Ajax = {
    11311314  getTransport: function() {
    11321315    return Try.these(
     
    11721355  onCreate:   function() { Ajax.activeRequestCount++ },
    11731356  onComplete: function() { Ajax.activeRequestCount-- }
    11741357});
    1175 
    11761358Ajax.Base = Class.create({
    11771359  initialize: function(options) {
    11781360    this.options = {
     
    11871369    Object.extend(this.options, options || { });
    11881370
    11891371    this.options.method = this.options.method.toLowerCase();
     1372
    11901373    if (Object.isString(this.options.parameters))
    11911374      this.options.parameters = this.options.parameters.toQueryParams();
     1375    else if (Object.isHash(this.options.parameters))
     1376      this.options.parameters = this.options.parameters.toObject();
    11921377  }
    11931378});
    1194 
    11951379Ajax.Request = Class.create(Ajax.Base, {
    11961380  _complete: false,
    11971381
     
    12071391    var params = Object.clone(this.options.parameters);
    12081392
    12091393    if (!['get', 'post'].include(this.method)) {
    1210       // simulate other verbs over post
    12111394      params['_method'] = this.method;
    12121395      this.method = 'post';
    12131396    }
     
    12151398    this.parameters = params;
    12161399
    12171400    if (params = Object.toQueryString(params)) {
    1218       // when GET, append parameters to URL
    12191401      if (this.method == 'get')
    12201402        this.url += (this.url.include('?') ? '&' : '?') + params;
    12211403      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
     
    12741456            headers['Connection'] = 'close';
    12751457    }
    12761458
    1277     // user-defined headers
    12781459    if (typeof this.options.requestHeaders == 'object') {
    12791460      var extras = this.options.requestHeaders;
    12801461
     
    13151496
    13161497      var contentType = response.getHeader('Content-type');
    13171498      if (this.options.evalJS == 'force'
    1318           || (this.options.evalJS && contentType
     1499          || (this.options.evalJS && this.isSameOrigin() && contentType
    13191500          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
    13201501        this.evalResponse();
    13211502    }
     
    13281509    }
    13291510
    13301511    if (state == 'Complete') {
    1331       // avoid memory leak in MSIE: clean up
    13321512      this.transport.onreadystatechange = Prototype.emptyFunction;
    13331513    }
    13341514  },
    13351515
     1516  isSameOrigin: function() {
     1517    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
     1518    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
     1519      protocol: location.protocol,
     1520      domain: document.domain,
     1521      port: location.port ? ':' + location.port : ''
     1522    }));
     1523  },
     1524
    13361525  getHeader: function(name) {
    13371526    try {
    1338       return this.transport.getResponseHeader(name);
    1339     } catch (e) { return null }
     1527      return this.transport.getResponseHeader(name) || null;
     1528    } catch (e) { return null; }
    13401529  },
    13411530
    13421531  evalResponse: function() {
     
    13561545Ajax.Request.Events =
    13571546  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
    13581547
     1548
     1549
     1550
     1551
     1552
     1553
     1554
    13591555Ajax.Response = Class.create({
    13601556  initialize: function(request){
    13611557    this.request = request;
     
    13711567
    13721568    if(readyState == 4) {
    13731569      var xml = transport.responseXML;
    1374       this.responseXML  = xml === undefined ? null : xml;
     1570      this.responseXML  = Object.isUndefined(xml) ? null : xml;
    13751571      this.responseJSON = this._getResponseJSON();
    13761572    }
    13771573  },
    13781574
    13791575  status:      0,
     1576
    13801577  statusText: '',
    13811578
    13821579  getStatus: Ajax.Request.prototype.getStatus,
     
    14081605    if (!json) return null;
    14091606    json = decodeURIComponent(escape(json));
    14101607    try {
    1411       return json.evalJSON(this.request.options.sanitizeJSON);
     1608      return json.evalJSON(this.request.options.sanitizeJSON ||
     1609        !this.request.isSameOrigin());
    14121610    } catch (e) {
    14131611      this.request.dispatchException(e);
    14141612    }
     
    14171615  _getResponseJSON: function() {
    14181616    var options = this.request.options;
    14191617    if (!options.evalJSON || (options.evalJSON != 'force' &&
    1420       !(this.getHeader('Content-type') || '').include('application/json')))
    1421         return null;
     1618      !(this.getHeader('Content-type') || '').include('application/json')) ||
     1619        this.responseText.blank())
     1620          return null;
    14221621    try {
    1423       return this.transport.responseText.evalJSON(options.sanitizeJSON);
     1622      return this.responseText.evalJSON(options.sanitizeJSON ||
     1623        !this.request.isSameOrigin());
    14241624    } catch (e) {
    14251625      this.request.dispatchException(e);
    14261626    }
     
    14341634      failure: (container.failure || (container.success ? null : container))
    14351635    };
    14361636
    1437     options = options || { };
     1637    options = Object.clone(options);
    14381638    var onComplete = options.onComplete;
    1439     options.onComplete = (function(response, param) {
     1639    options.onComplete = (function(response, json) {
    14401640      this.updateContent(response.responseText);
    1441       if (Object.isFunction(onComplete)) onComplete(response, param);
     1641      if (Object.isFunction(onComplete)) onComplete(response, json);
    14421642    }).bind(this);
    14431643
    14441644    $super(url, options);
     
    14601660      }
    14611661      else receiver.update(responseText);
    14621662    }
    1463 
    1464     if (this.success()) {
    1465       if (this.onComplete) this.onComplete.bind(this).defer();
    1466     }
    14671663  }
    14681664});
    14691665
     
    15071703    this.updater = new Ajax.Updater(this.container, this.url, this.options);
    15081704  }
    15091705});
     1706
     1707
     1708
    15101709function $(element) {
    15111710  if (arguments.length > 1) {
    15121711    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
     
    15341733if (!window.Node) var Node = { };
    15351734
    15361735if (!Node.ELEMENT_NODE) {
    1537   // DOM level 2 ECMAScript Language Binding
    15381736  Object.extend(Node, {
    15391737    ELEMENT_NODE: 1,
    15401738    ATTRIBUTE_NODE: 2,
     
    15511749  });
    15521750}
    15531751
    1554 (function() {
    1555   var element = this.Element;
    1556   this.Element = function(tagName, attributes) {
     1752
     1753(function(global) {
     1754
     1755  var SETATTRIBUTE_IGNORES_NAME = (function(){
     1756    var elForm = document.createElement("form");
     1757    var elInput = document.createElement("input");
     1758    var root = document.documentElement;
     1759    elInput.setAttribute("name", "test");
     1760    elForm.appendChild(elInput);
     1761    root.appendChild(elForm);
     1762    var isBuggy = elForm.elements
     1763      ? (typeof elForm.elements.test == "undefined")
     1764      : null;
     1765    root.removeChild(elForm);
     1766    elForm = elInput = null;
     1767    return isBuggy;
     1768  })();
     1769
     1770  var element = global.Element;
     1771  global.Element = function(tagName, attributes) {
    15571772    attributes = attributes || { };
    15581773    tagName = tagName.toLowerCase();
    15591774    var cache = Element.cache;
    1560     if (Prototype.Browser.IE && attributes.name) {
     1775    if (SETATTRIBUTE_IGNORES_NAME && attributes.name) {
    15611776      tagName = '<' + tagName + ' name="' + attributes.name + '">';
    15621777      delete attributes.name;
    15631778      return Element.writeAttribute(document.createElement(tagName), attributes);
     
    15651780    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    15661781    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
    15671782  };
    1568   Object.extend(this.Element, element || { });
    1569 }).call(window);
     1783  Object.extend(global.Element, element || { });
     1784  if (element) global.Element.prototype = element.prototype;
     1785})(this);
    15701786
    15711787Element.cache = { };
     1788Element.idCounter = 1;
    15721789
    15731790Element.Methods = {
    15741791  visible: function(element) {
     
    15811798    return element;
    15821799  },
    15831800
     1801
    15841802  hide: function(element) {
    1585     $(element).style.display = 'none';
     1803    element = $(element);
     1804    element.style.display = 'none';
    15861805    return element;
    15871806  },
    15881807
    15891808  show: function(element) {
    1590     $(element).style.display = '';
     1809    element = $(element);
     1810    element.style.display = '';
    15911811    return element;
    15921812  },
    15931813
     
    15971817    return element;
    15981818  },
    15991819
    1600   update: function(element, content) {
    1601     element = $(element);
    1602     if (content && content.toElement) content = content.toElement();
    1603     if (Object.isElement(content)) return element.update().insert(content);
    1604     content = Object.toHTML(content);
    1605     element.innerHTML = content.stripScripts();
    1606     content.evalScripts.bind(content).defer();
    1607     return element;
    1608   },
     1820  update: (function(){
    16091821
     1822    var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
     1823      var el = document.createElement("select"),
     1824          isBuggy = true;
     1825      el.innerHTML = "<option value=\"test\">test</option>";
     1826      if (el.options && el.options[0]) {
     1827        isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
     1828      }
     1829      el = null;
     1830      return isBuggy;
     1831    })();
     1832
     1833    var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
     1834      try {
     1835        var el = document.createElement("table");
     1836        if (el && el.tBodies) {
     1837          el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
     1838          var isBuggy = typeof el.tBodies[0] == "undefined";
     1839          el = null;
     1840          return isBuggy;
     1841        }
     1842      } catch (e) {
     1843        return true;
     1844      }
     1845    })();
     1846
     1847    var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
     1848      var s = document.createElement("script"),
     1849          isBuggy = false;
     1850      try {
     1851        s.appendChild(document.createTextNode(""));
     1852        isBuggy = !s.firstChild ||
     1853          s.firstChild && s.firstChild.nodeType !== 3;
     1854      } catch (e) {
     1855        isBuggy = true;
     1856      }
     1857      s = null;
     1858      return isBuggy;
     1859    })();
     1860
     1861    function update(element, content) {
     1862      element = $(element);
     1863
     1864      if (content && content.toElement)
     1865        content = content.toElement();
     1866
     1867      if (Object.isElement(content))
     1868        return element.update().insert(content);
     1869
     1870      content = Object.toHTML(content);
     1871
     1872      var tagName = element.tagName.toUpperCase();
     1873
     1874      if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
     1875        element.text = content;
     1876        return element;
     1877      }
     1878
     1879      if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
     1880        if (tagName in Element._insertionTranslations.tags) {
     1881          while (element.firstChild) {
     1882            element.removeChild(element.firstChild);
     1883          }
     1884          Element._getContentFromAnonymousElement(tagName, content.stripScripts())
     1885            .each(function(node) {
     1886              element.appendChild(node)
     1887            });
     1888        }
     1889        else {
     1890          element.innerHTML = content.stripScripts();
     1891        }
     1892      }
     1893      else {
     1894        element.innerHTML = content.stripScripts();
     1895      }
     1896
     1897      content.evalScripts.bind(content).defer();
     1898      return element;
     1899    }
     1900
     1901    return update;
     1902  })(),
     1903
    16101904  replace: function(element, content) {
    16111905    element = $(element);
    16121906    if (content && content.toElement) content = content.toElement();
     
    16281922        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
    16291923          insertions = {bottom:insertions};
    16301924
    1631     var content, t, range;
     1925    var content, insert, tagName, childNodes;
    16321926
    1633     for (position in insertions) {
     1927    for (var position in insertions) {
    16341928      content  = insertions[position];
    16351929      position = position.toLowerCase();
    1636       t = Element._insertionTranslations[position];
     1930      insert = Element._insertionTranslations[position];
    16371931
    16381932      if (content && content.toElement) content = content.toElement();
    16391933      if (Object.isElement(content)) {
    1640         t.insert(element, content);
     1934        insert(element, content);
    16411935        continue;
    16421936      }
    16431937
    16441938      content = Object.toHTML(content);
    16451939
    1646       range = element.ownerDocument.createRange();
    1647       t.initializeRange(element, range);
    1648       t.insert(element, range.createContextualFragment(content.stripScripts()));
     1940      tagName = ((position == 'before' || position == 'after')
     1941        ? element.parentNode : element).tagName.toUpperCase();
    16491942
     1943      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
     1944
     1945      if (position == 'top' || position == 'after') childNodes.reverse();
     1946      childNodes.each(insert.curry(element));
     1947
    16501948      content.evalScripts.bind(content).defer();
    16511949    }
    16521950
     
    16861984  },
    16871985
    16881986  ancestors: function(element) {
    1689     return $(element).recursivelyCollect('parentNode');
     1987    return Element.recursivelyCollect(element, 'parentNode');
    16901988  },
    16911989
    16921990  descendants: function(element) {
    1693     return $A($(element).getElementsByTagName('*')).each(Element.extend);
     1991    return Element.select(element, "*");
    16941992  },
    16951993
    16961994  firstDescendant: function(element) {
     
    17072005  },
    17082006
    17092007  previousSiblings: function(element) {
    1710     return $(element).recursivelyCollect('previousSibling');
     2008    return Element.recursivelyCollect(element, 'previousSibling');
    17112009  },
    17122010
    17132011  nextSiblings: function(element) {
    1714     return $(element).recursivelyCollect('nextSibling');
     2012    return Element.recursivelyCollect(element, 'nextSibling');
    17152013  },
    17162014
    17172015  siblings: function(element) {
    17182016    element = $(element);
    1719     return element.previousSiblings().reverse().concat(element.nextSiblings());
     2017    return Element.previousSiblings(element).reverse()
     2018      .concat(Element.nextSiblings(element));
    17202019  },
    17212020
    17222021  match: function(element, selector) {
     
    17282027  up: function(element, expression, index) {
    17292028    element = $(element);
    17302029    if (arguments.length == 1) return $(element.parentNode);
    1731     var ancestors = element.ancestors();
    1732     return expression ? Selector.findElement(ancestors, expression, index) :
    1733       ancestors[index || 0];
     2030    var ancestors = Element.ancestors(element);
     2031    return Object.isNumber(expression) ? ancestors[expression] :
     2032      Selector.findElement(ancestors, expression, index);
    17342033  },
    17352034
    17362035  down: function(element, expression, index) {
    17372036    element = $(element);
    1738     if (arguments.length == 1) return element.firstDescendant();
    1739     var descendants = element.descendants();
    1740     return expression ? Selector.findElement(descendants, expression, index) :
    1741       descendants[index || 0];
     2037    if (arguments.length == 1) return Element.firstDescendant(element);
     2038    return Object.isNumber(expression) ? Element.descendants(element)[expression] :
     2039      Element.select(element, expression)[index || 0];
    17422040  },
    17432041
    17442042  previous: function(element, expression, index) {
    17452043    element = $(element);
    17462044    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
    1747     var previousSiblings = element.previousSiblings();
    1748     return expression ? Selector.findElement(previousSiblings, expression, index) :
    1749       previousSiblings[index || 0];
     2045    var previousSiblings = Element.previousSiblings(element);
     2046    return Object.isNumber(expression) ? previousSiblings[expression] :
     2047      Selector.findElement(previousSiblings, expression, index);
    17502048  },
    17512049
    17522050  next: function(element, expression, index) {
    17532051    element = $(element);
    17542052    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
    1755     var nextSiblings = element.nextSiblings();
    1756     return expression ? Selector.findElement(nextSiblings, expression, index) :
    1757       nextSiblings[index || 0];
     2053    var nextSiblings = Element.nextSiblings(element);
     2054    return Object.isNumber(expression) ? nextSiblings[expression] :
     2055      Selector.findElement(nextSiblings, expression, index);
    17582056  },
    17592057
    1760   select: function() {
    1761     var args = $A(arguments), element = $(args.shift());
     2058
     2059  select: function(element) {
     2060    var args = Array.prototype.slice.call(arguments, 1);
    17622061    return Selector.findChildElements(element, args);
    17632062  },
    17642063
    1765   adjacent: function() {
    1766     var args = $A(arguments), element = $(args.shift());
     2064  adjacent: function(element) {
     2065    var args = Array.prototype.slice.call(arguments, 1);
    17672066    return Selector.findChildElements(element.parentNode, args).without(element);
    17682067  },
    17692068
    17702069  identify: function(element) {
    17712070    element = $(element);
    1772     var id = element.readAttribute('id'), self = arguments.callee;
     2071    var id = Element.readAttribute(element, 'id');
    17732072    if (id) return id;
    1774     do { id = 'anonymous_element_' + self.counter++ } while ($(id));
    1775     element.writeAttribute('id', id);
     2073    do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
     2074    Element.writeAttribute(element, 'id', id);
    17762075    return id;
    17772076  },
    17782077
     
    17952094    var attributes = { }, t = Element._attributeTranslations.write;
    17962095
    17972096    if (typeof name == 'object') attributes = name;
    1798     else attributes[name] = value === undefined ? true : value;
     2097    else attributes[name] = Object.isUndefined(value) ? true : value;
    17992098
    18002099    for (var attr in attributes) {
    1801       var name = t.names[attr] || attr, value = attributes[attr];
     2100      name = t.names[attr] || attr;
     2101      value = attributes[attr];
    18022102      if (t.values[attr]) name = t.values[attr](element, value);
    18032103      if (value === false || value === null)
    18042104        element.removeAttribute(name);
     
    18102110  },
    18112111
    18122112  getHeight: function(element) {
    1813     return $(element).getDimensions().height;
     2113    return Element.getDimensions(element).height;
    18142114  },
    18152115
    18162116  getWidth: function(element) {
    1817     return $(element).getDimensions().width;
     2117    return Element.getDimensions(element).width;
    18182118  },
    18192119
    18202120  classNames: function(element) {
     
    18302130
    18312131  addClassName: function(element, className) {
    18322132    if (!(element = $(element))) return;
    1833     if (!element.hasClassName(className))
     2133    if (!Element.hasClassName(element, className))
    18342134      element.className += (element.className ? ' ' : '') + className;
    18352135    return element;
    18362136  },
     
    18442144
    18452145  toggleClassName: function(element, className) {
    18462146    if (!(element = $(element))) return;
    1847     return element[element.hasClassName(className) ?
    1848       'removeClassName' : 'addClassName'](className);
     2147    return Element[Element.hasClassName(element, className) ?
     2148      'removeClassName' : 'addClassName'](element, className);
    18492149  },
    18502150
    1851   // removes whitespace-only text node children
    18522151  cleanWhitespace: function(element) {
    18532152    element = $(element);
    18542153    var node = element.firstChild;
     
    18712170    if (element.compareDocumentPosition)
    18722171      return (element.compareDocumentPosition(ancestor) & 8) === 8;
    18732172
    1874     if (element.sourceIndex && !Prototype.Browser.Opera) {
    1875       var e = element.sourceIndex, a = ancestor.sourceIndex,
    1876        nextAncestor = ancestor.nextSibling;
    1877       if (!nextAncestor) {
    1878         do { ancestor = ancestor.parentNode; }
    1879         while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
    1880       }
    1881       if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
    1882     }
     2173    if (ancestor.contains)
     2174      return ancestor.contains(element) && ancestor !== element;
    18832175
    18842176    while (element = element.parentNode)
    18852177      if (element == ancestor) return true;
     2178
    18862179    return false;
    18872180  },
    18882181
    18892182  scrollTo: function(element) {
    18902183    element = $(element);
    1891     var pos = element.cumulativeOffset();
     2184    var pos = Element.cumulativeOffset(element);
    18922185    window.scrollTo(pos[0], pos[1]);
    18932186    return element;
    18942187  },
     
    18972190    element = $(element);
    18982191    style = style == 'float' ? 'cssFloat' : style.camelize();
    18992192    var value = element.style[style];
    1900     if (!value) {
     2193    if (!value || value == 'auto') {
    19012194      var css = document.defaultView.getComputedStyle(element, null);
    19022195      value = css ? css[style] : null;
    19032196    }
     
    19212214      if (property == 'opacity') element.setOpacity(styles[property]);
    19222215      else
    19232216        elementStyle[(property == 'float' || property == 'cssFloat') ?
    1924           (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
     2217          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
    19252218            property] = styles[property];
    19262219
    19272220    return element;
     
    19362229
    19372230  getDimensions: function(element) {
    19382231    element = $(element);
    1939     var display = $(element).getStyle('display');
     2232    var display = Element.getStyle(element, 'display');
    19402233    if (display != 'none' && display != null) // Safari bug
    19412234      return {width: element.offsetWidth, height: element.offsetHeight};
    19422235
    1943     // All *Width and *Height properties give 0 on elements with display none,
    1944     // so enable the element temporarily
    19452236    var els = element.style;
    19462237    var originalVisibility = els.visibility;
    19472238    var originalPosition = els.position;
    19482239    var originalDisplay = els.display;
    19492240    els.visibility = 'hidden';
    1950     els.position = 'absolute';
     2241    if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
     2242      els.position = 'absolute';
    19512243    els.display = 'block';
    19522244    var originalWidth = element.clientWidth;
    19532245    var originalHeight = element.clientHeight;
     
    19632255    if (pos == 'static' || !pos) {
    19642256      element._madePositioned = true;
    19652257      element.style.position = 'relative';
    1966       // Opera returns the offset relative to the positioning context, when an
    1967       // element is position relative but top and left have not been defined
    1968       if (window.opera) {
     2258      if (Prototype.Browser.Opera) {
    19692259        element.style.top = 0;
    19702260        element.style.left = 0;
    19712261      }
     
    20202310      valueL += element.offsetLeft || 0;
    20212311      element = element.offsetParent;
    20222312      if (element) {
    2023         if (element.tagName == 'BODY') break;
     2313        if (element.tagName.toUpperCase() == 'BODY') break;
    20242314        var p = Element.getStyle(element, 'position');
    2025         if (p == 'relative' || p == 'absolute') break;
     2315        if (p !== 'static') break;
    20262316      }
    20272317    } while (element);
    20282318    return Element._returnOffset(valueL, valueT);
     
    20302320
    20312321  absolutize: function(element) {
    20322322    element = $(element);
    2033     if (element.getStyle('position') == 'absolute') return;
    2034     // Position.prepare(); // To be done manually by Scripty when it needs it.
     2323    if (Element.getStyle(element, 'position') == 'absolute') return element;
    20352324
    2036     var offsets = element.positionedOffset();
     2325    var offsets = Element.positionedOffset(element);
    20372326    var top     = offsets[1];
    20382327    var left    = offsets[0];
    20392328    var width   = element.clientWidth;
     
    20542343
    20552344  relativize: function(element) {
    20562345    element = $(element);
    2057     if (element.getStyle('position') == 'relative') return;
    2058     // Position.prepare(); // To be done manually by Scripty when it needs it.
     2346    if (Element.getStyle(element, 'position') == 'relative') return element;
    20592347
    20602348    element.style.position = 'relative';
    20612349    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
     
    20972385      valueT += element.offsetTop  || 0;
    20982386      valueL += element.offsetLeft || 0;
    20992387
    2100       // Safari fix
    21012388      if (element.offsetParent == document.body &&
    21022389        Element.getStyle(element, 'position') == 'absolute') break;
    21032390
     
    21052392
    21062393    element = forElement;
    21072394    do {
    2108       if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
     2395      if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
    21092396        valueT -= element.scrollTop  || 0;
    21102397        valueL -= element.scrollLeft || 0;
    21112398      }
     
    21242411      offsetLeft: 0
    21252412    }, arguments[2] || { });
    21262413
    2127     // find page position of source
    21282414    source = $(source);
    2129     var p = source.viewportOffset();
     2415    var p = Element.viewportOffset(source);
    21302416
    2131     // find coordinate system to use
    21322417    element = $(element);
    21332418    var delta = [0, 0];
    21342419    var parent = null;
    2135     // delta [0,0] will do fine with position: fixed elements,
    2136     // position:absolute needs offsetParent deltas
    21372420    if (Element.getStyle(element, 'position') == 'absolute') {
    2138       parent = element.getOffsetParent();
    2139       delta = parent.viewportOffset();
     2421      parent = Element.getOffsetParent(element);
     2422      delta = Element.viewportOffset(parent);
    21402423    }
    21412424
    2142     // correct by body offsets (fixes Safari)
    21432425    if (parent == document.body) {
    21442426      delta[0] -= document.body.offsetLeft;
    21452427      delta[1] -= document.body.offsetTop;
    21462428    }
    21472429
    2148     // set position
    21492430    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    21502431    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    21512432    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
     
    21542435  }
    21552436};
    21562437
    2157 Element.Methods.identify.counter = 1;
    2158 
    21592438Object.extend(Element.Methods, {
    21602439  getElementsBySelector: Element.Methods.select,
     2440
    21612441  childElements: Element.Methods.immediateDescendants
    21622442});
    21632443
     
    21712451  }
    21722452};
    21732453
     2454if (Prototype.Browser.Opera) {
     2455  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
     2456    function(proceed, element, style) {
     2457      switch (style) {
     2458        case 'left': case 'top': case 'right': case 'bottom':
     2459          if (proceed(element, 'position') === 'static') return null;
     2460        case 'height': case 'width':
     2461          if (!Element.visible(element)) return null;
    21742462
    2175 if (!document.createRange || Prototype.Browser.Opera) {
    2176   Element.Methods.insert = function(element, insertions) {
    2177     element = $(element);
     2463          var dim = parseInt(proceed(element, style), 10);
    21782464
    2179     if (Object.isString(insertions) || Object.isNumber(insertions) ||
    2180         Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
    2181           insertions = { bottom: insertions };
     2465          if (dim !== element['offset' + style.capitalize()])
     2466            return dim + 'px';
    21822467
    2183     var t = Element._insertionTranslations, content, position, pos, tagName;
    2184 
    2185     for (position in insertions) {
    2186       content  = insertions[position];
    2187       position = position.toLowerCase();
    2188       pos      = t[position];
    2189 
    2190       if (content && content.toElement) content = content.toElement();
    2191       if (Object.isElement(content)) {
    2192         pos.insert(element, content);
    2193         continue;
     2468          var properties;
     2469          if (style === 'height') {
     2470            properties = ['border-top-width', 'padding-top',
     2471             'padding-bottom', 'border-bottom-width'];
     2472          }
     2473          else {
     2474            properties = ['border-left-width', 'padding-left',
     2475             'padding-right', 'border-right-width'];
     2476          }
     2477          return properties.inject(dim, function(memo, property) {
     2478            var val = proceed(element, property);
     2479            return val === null ? memo : memo - parseInt(val, 10);
     2480          }) + 'px';
     2481        default: return proceed(element, style);
    21942482      }
     2483    }
     2484  );
    21952485
    2196       content = Object.toHTML(content);
    2197       tagName = ((position == 'before' || position == 'after')
    2198         ? element.parentNode : element).tagName.toUpperCase();
    2199 
    2200       if (t.tags[tagName]) {
    2201         var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
    2202         if (position == 'top' || position == 'after') fragments.reverse();
    2203         fragments.each(pos.insert.curry(element));
    2204       }
    2205       else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
    2206 
    2207       content.evalScripts.bind(content).defer();
     2486  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
     2487    function(proceed, element, attribute) {
     2488      if (attribute === 'title') return element.title;
     2489      return proceed(element, attribute);
    22082490    }
    2209 
    2210     return element;
    2211   };
     2491  );
    22122492}
    22132493
    2214 if (Prototype.Browser.Opera) {
    2215   Element.Methods._getStyle = Element.Methods.getStyle;
    2216   Element.Methods.getStyle = function(element, style) {
    2217     switch(style) {
    2218       case 'left':
    2219       case 'top':
    2220       case 'right':
    2221       case 'bottom':
    2222         if (Element._getStyle(element, 'position') == 'static') return null;
    2223       default: return Element._getStyle(element, style);
     2494else if (Prototype.Browser.IE) {
     2495  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
     2496    function(proceed, element) {
     2497      element = $(element);
     2498      try { element.offsetParent }
     2499      catch(e) { return $(document.body) }
     2500      var position = element.getStyle('position');
     2501      if (position !== 'static') return proceed(element);
     2502      element.setStyle({ position: 'relative' });
     2503      var value = proceed(element);
     2504      element.setStyle({ position: position });
     2505      return value;
    22242506    }
    2225   };
    2226   Element.Methods._readAttribute = Element.Methods.readAttribute;
    2227   Element.Methods.readAttribute = function(element, attribute) {
    2228     if (attribute == 'title') return element.title;
    2229     return Element._readAttribute(element, attribute);
    2230   };
    2231 }
     2507  );
    22322508
    2233 else if (Prototype.Browser.IE) {
    2234   $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
     2509  $w('positionedOffset viewportOffset').each(function(method) {
    22352510    Element.Methods[method] = Element.Methods[method].wrap(
    22362511      function(proceed, element) {
    22372512        element = $(element);
     2513        try { element.offsetParent }
     2514        catch(e) { return Element._returnOffset(0,0) }
    22382515        var position = element.getStyle('position');
    2239         if (position != 'static') return proceed(element);
     2516        if (position !== 'static') return proceed(element);
     2517        var offsetParent = element.getOffsetParent();
     2518        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
     2519          offsetParent.setStyle({ zoom: 1 });
    22402520        element.setStyle({ position: 'relative' });
    22412521        var value = proceed(element);
    22422522        element.setStyle({ position: position });
     
    22452525    );
    22462526  });
    22472527
     2528  Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
     2529    function(proceed, element) {
     2530      try { element.offsetParent }
     2531      catch(e) { return Element._returnOffset(0,0) }
     2532      return proceed(element);
     2533    }
     2534  );
     2535
    22482536  Element.Methods.getStyle = function(element, style) {
    22492537    element = $(element);
    22502538    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
     
    22862574    return element;
    22872575  };
    22882576
    2289   Element._attributeTranslations = {
    2290     read: {
    2291       names: {
    2292         'class': 'className',
    2293         'for':   'htmlFor'
    2294       },
    2295       values: {
    2296         _getAttr: function(element, attribute) {
    2297           return element.getAttribute(attribute, 2);
     2577  Element._attributeTranslations = (function(){
     2578
     2579    var classProp = 'className';
     2580    var forProp = 'for';
     2581
     2582    var el = document.createElement('div');
     2583
     2584    el.setAttribute(classProp, 'x');
     2585
     2586    if (el.className !== 'x') {
     2587      el.setAttribute('class', 'x');
     2588      if (el.className === 'x') {
     2589        classProp = 'class';
     2590      }
     2591    }
     2592    el = null;
     2593
     2594    el = document.createElement('label');
     2595    el.setAttribute(forProp, 'x');
     2596    if (el.htmlFor !== 'x') {
     2597      el.setAttribute('htmlFor', 'x');
     2598      if (el.htmlFor === 'x') {
     2599        forProp = 'htmlFor';
     2600      }
     2601    }
     2602    el = null;
     2603
     2604    return {
     2605      read: {
     2606        names: {
     2607          'class':      classProp,
     2608          'className':  classProp,
     2609          'for':        forProp,
     2610          'htmlFor':    forProp
    22982611        },
    2299         _getAttrNode: function(element, attribute) {
    2300           var node = element.getAttributeNode(attribute);
    2301           return node ? node.value : "";
    2302         },
    2303         _getEv: function(element, attribute) {
    2304           var attribute = element.getAttribute(attribute);
    2305           return attribute ? attribute.toString().slice(23, -2) : null;
    2306         },
    2307         _flag: function(element, attribute) {
    2308           return $(element).hasAttribute(attribute) ? attribute : null;
    2309         },
    2310         style: function(element) {
    2311           return element.style.cssText.toLowerCase();
    2312         },
    2313         title: function(element) {
    2314           return element.title;
     2612        values: {
     2613          _getAttr: function(element, attribute) {
     2614            return element.getAttribute(attribute);
     2615          },
     2616          _getAttr2: function(element, attribute) {
     2617            return element.getAttribute(attribute, 2);
     2618          },
     2619          _getAttrNode: function(element, attribute) {
     2620            var node = element.getAttributeNode(attribute);
     2621            return node ? node.value : "";
     2622          },
     2623          _getEv: (function(){
     2624
     2625            var el = document.createElement('div');
     2626            el.onclick = Prototype.emptyFunction;
     2627            var value = el.getAttribute('onclick');
     2628            var f;
     2629
     2630            if (String(value).indexOf('{') > -1) {
     2631              f = function(element, attribute) {
     2632                attribute = element.getAttribute(attribute);
     2633                if (!attribute) return null;
     2634                attribute = attribute.toString();
     2635                attribute = attribute.split('{')[1];
     2636                attribute = attribute.split('}')[0];
     2637                return attribute.strip();
     2638              };
     2639            }
     2640            else if (value === '') {
     2641              f = function(element, attribute) {
     2642                attribute = element.getAttribute(attribute);
     2643                if (!attribute) return null;
     2644                return attribute.strip();
     2645              };
     2646            }
     2647            el = null;
     2648            return f;
     2649          })(),
     2650          _flag: function(element, attribute) {
     2651            return $(element).hasAttribute(attribute) ? attribute : null;
     2652          },
     2653          style: function(element) {
     2654            return element.style.cssText.toLowerCase();
     2655          },
     2656          title: function(element) {
     2657            return element.title;
     2658          }
    23152659        }
    23162660      }
    23172661    }
    2318   };
     2662  })();
    23192663
    23202664  Element._attributeTranslations.write = {
    2321     names: Object.clone(Element._attributeTranslations.read.names),
     2665    names: Object.extend({
     2666      cellpadding: 'cellPadding',
     2667      cellspacing: 'cellSpacing'
     2668    }, Element._attributeTranslations.read.names),
    23222669    values: {
    23232670      checked: function(element, value) {
    23242671        element.checked = !!value;
     
    23332680  Element._attributeTranslations.has = {};
    23342681
    23352682  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
    2336       'encType maxLength readOnly longDesc').each(function(attr) {
     2683      'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
    23372684    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    23382685    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
    23392686  });
    23402687
    23412688  (function(v) {
    23422689    Object.extend(v, {
    2343       href:        v._getAttr,
    2344       src:         v._getAttr,
     2690      href:        v._getAttr2,
     2691      src:         v._getAttr2,
    23452692      type:        v._getAttr,
    23462693      action:      v._getAttrNode,
    23472694      disabled:    v._flag,
     
    23682715      onchange:    v._getEv
    23692716    });
    23702717  })(Element._attributeTranslations.read.values);
     2718
     2719  if (Prototype.BrowserFeatures.ElementExtensions) {
     2720    (function() {
     2721      function _descendants(element) {
     2722        var nodes = element.getElementsByTagName('*'), results = [];
     2723        for (var i = 0, node; node = nodes[i]; i++)
     2724          if (node.tagName !== "!") // Filter out comment nodes.
     2725            results.push(node);
     2726        return results;
     2727      }
     2728
     2729      Element.Methods.down = function(element, expression, index) {
     2730        element = $(element);
     2731        if (arguments.length == 1) return element.firstDescendant();
     2732        return Object.isNumber(expression) ? _descendants(element)[expression] :
     2733          Element.select(element, expression)[index || 0];
     2734      }
     2735    })();
     2736  }
     2737
    23712738}
    23722739
    23732740else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
     
    23862753      (value < 0.00001) ? 0 : value;
    23872754
    23882755    if (value == 1)
    2389       if(element.tagName == 'IMG' && element.width) {
     2756      if(element.tagName.toUpperCase() == 'IMG' && element.width) {
    23902757        element.width++; element.width--;
    23912758      } else try {
    23922759        var n = document.createTextNode(' ');
     
    23972764    return element;
    23982765  };
    23992766
    2400   // Safari returns margins on body which is incorrect if the child is absolutely
    2401   // positioned.  For performance reasons, redefine Position.cumulativeOffset for
    2402   // KHTML/WebKit only.
    24032767  Element.Methods.cumulativeOffset = function(element) {
    24042768    var valueT = 0, valueL = 0;
    24052769    do {
     
    24152779  };
    24162780}
    24172781
    2418 if (Prototype.Browser.IE || Prototype.Browser.Opera) {
    2419   // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
    2420   Element.Methods.update = function(element, content) {
    2421     element = $(element);
    2422 
    2423     if (content && content.toElement) content = content.toElement();
    2424     if (Object.isElement(content)) return element.update().insert(content);
    2425 
    2426     content = Object.toHTML(content);
    2427     var tagName = element.tagName.toUpperCase();
    2428 
    2429     if (tagName in Element._insertionTranslations.tags) {
    2430       $A(element.childNodes).each(function(node) { element.removeChild(node) });
    2431       Element._getContentFromAnonymousElement(tagName, content.stripScripts())
    2432         .each(function(node) { element.appendChild(node) });
    2433     }
    2434     else element.innerHTML = content.stripScripts();
    2435 
    2436     content.evalScripts.bind(content).defer();
    2437     return element;
    2438   };
    2439 }
    2440 
    2441 if (document.createElement('div').outerHTML) {
     2782if ('outerHTML' in document.documentElement) {
    24422783  Element.Methods.replace = function(element, content) {
    24432784    element = $(element);
    24442785
     
    24762817
    24772818Element._getContentFromAnonymousElement = function(tagName, html) {
    24782819  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
    2479   div.innerHTML = t[0] + html + t[1];
    2480   t[2].times(function() { div = div.firstChild });
     2820  if (t) {
     2821    div.innerHTML = t[0] + html + t[1];
     2822    t[2].times(function() { div = div.firstChild });
     2823  } else div.innerHTML = html;
    24812824  return $A(div.childNodes);
    24822825};
    24832826
    24842827Element._insertionTranslations = {
    2485   before: {
    2486     adjacency: 'beforeBegin',
    2487     insert: function(element, node) {
    2488       element.parentNode.insertBefore(node, element);
    2489     },
    2490     initializeRange: function(element, range) {
    2491       range.setStartBefore(element);
    2492     }
     2828  before: function(element, node) {
     2829    element.parentNode.insertBefore(node, element);
    24932830  },
    2494   top: {
    2495     adjacency: 'afterBegin',
    2496     insert: function(element, node) {
    2497       element.insertBefore(node, element.firstChild);
    2498     },
    2499     initializeRange: function(element, range) {
    2500       range.selectNodeContents(element);
    2501       range.collapse(true);
    2502     }
     2831  top: function(element, node) {
     2832    element.insertBefore(node, element.firstChild);
    25032833  },
    2504   bottom: {
    2505     adjacency: 'beforeEnd',
    2506     insert: function(element, node) {
    2507       element.appendChild(node);
    2508     }
     2834  bottom: function(element, node) {
     2835    element.appendChild(node);
    25092836  },
    2510   after: {
    2511     adjacency: 'afterEnd',
    2512     insert: function(element, node) {
    2513       element.parentNode.insertBefore(node, element.nextSibling);
    2514     },
    2515     initializeRange: function(element, range) {
    2516       range.setStartAfter(element);
    2517     }
     2837  after: function(element, node) {
     2838    element.parentNode.insertBefore(node, element.nextSibling);
    25182839  },
    25192840  tags: {
    25202841    TABLE:  ['<table>',                '</table>',                   1],
     
    25262847};
    25272848
    25282849(function() {
    2529   this.bottom.initializeRange = this.top.initializeRange;
    2530   Object.extend(this.tags, {
    2531     THEAD: this.tags.TBODY,
    2532     TFOOT: this.tags.TBODY,
    2533     TH:    this.tags.TD
     2850  var tags = Element._insertionTranslations.tags;
     2851  Object.extend(tags, {
     2852    THEAD: tags.TBODY,
     2853    TFOOT: tags.TBODY,
     2854    TH:    tags.TD
    25342855  });
    2535 }).call(Element._insertionTranslations);
     2856})();
    25362857
    25372858Element.Methods.Simulated = {
    25382859  hasAttribute: function(element, attribute) {
    25392860    attribute = Element._attributeTranslations.has[attribute] || attribute;
    25402861    var node = $(element).getAttributeNode(attribute);
    2541     return node && node.specified;
     2862    return !!(node && node.specified);
    25422863  }
    25432864};
    25442865
     
    25462867
    25472868Object.extend(Element, Element.Methods);
    25482869
    2549 if (!Prototype.BrowserFeatures.ElementExtensions &&
    2550     document.createElement('div').__proto__) {
    2551   window.HTMLElement = { };
    2552   window.HTMLElement.prototype = document.createElement('div').__proto__;
    2553   Prototype.BrowserFeatures.ElementExtensions = true;
    2554 }
     2870(function(div) {
    25552871
     2872  if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
     2873    window.HTMLElement = { };
     2874    window.HTMLElement.prototype = div['__proto__'];
     2875    Prototype.BrowserFeatures.ElementExtensions = true;
     2876  }
     2877
     2878  div = null;
     2879
     2880})(document.createElement('div'))
     2881
    25562882Element.extend = (function() {
    2557   if (Prototype.BrowserFeatures.SpecificElementExtensions)
     2883
     2884  function checkDeficiency(tagName) {
     2885    if (typeof window.Element != 'undefined') {
     2886      var proto = window.Element.prototype;
     2887      if (proto) {
     2888        var id = '_' + (Math.random()+'').slice(2);
     2889        var el = document.createElement(tagName);
     2890        proto[id] = 'x';
     2891        var isBuggy = (el[id] !== 'x');
     2892        delete proto[id];
     2893        el = null;
     2894        return isBuggy;
     2895      }
     2896    }
     2897    return false;
     2898  }
     2899
     2900  function extendElementWith(element, methods) {
     2901    for (var property in methods) {
     2902      var value = methods[property];
     2903      if (Object.isFunction(value) && !(property in element))
     2904        element[property] = value.methodize();
     2905    }
     2906  }
     2907
     2908  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
     2909
     2910  if (Prototype.BrowserFeatures.SpecificElementExtensions) {
     2911    if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
     2912      return function(element) {
     2913        if (element && typeof element._extendedByPrototype == 'undefined') {
     2914          var t = element.tagName;
     2915          if (t && (/^(?:object|applet|embed)$/i.test(t))) {
     2916            extendElementWith(element, Element.Methods);
     2917            extendElementWith(element, Element.Methods.Simulated);
     2918            extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
     2919          }
     2920        }
     2921        return element;
     2922      }
     2923    }
    25582924    return Prototype.K;
     2925  }
    25592926
    25602927  var Methods = { }, ByTag = Element.Methods.ByTag;
    25612928
    25622929  var extend = Object.extend(function(element) {
    2563     if (!element || element._extendedByPrototype ||
     2930    if (!element || typeof element._extendedByPrototype != 'undefined' ||
    25642931        element.nodeType != 1 || element == window) return element;
    25652932
    25662933    var methods = Object.clone(Methods),
    2567       tagName = element.tagName, property, value;
     2934        tagName = element.tagName.toUpperCase();
    25682935
    2569     // extend methods for specific tags
    25702936    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
    25712937
    2572     for (property in methods) {
    2573       value = methods[property];
    2574       if (Object.isFunction(value) && !(property in element))
    2575         element[property] = value.methodize();
    2576     }
     2938    extendElementWith(element, methods);
    25772939
    25782940    element._extendedByPrototype = Prototype.emptyFunction;
    25792941    return element;
    25802942
    25812943  }, {
    25822944    refresh: function() {
    2583       // extend methods for all tags (Safari doesn't need this)
    25842945      if (!Prototype.BrowserFeatures.ElementExtensions) {
    25852946        Object.extend(Methods, Element.Methods);
    25862947        Object.extend(Methods, Element.Methods.Simulated);
     
    26593020    klass = 'HTML' + tagName.capitalize() + 'Element';
    26603021    if (window[klass]) return window[klass];
    26613022
    2662     window[klass] = { };
    2663     window[klass].prototype = document.createElement(tagName).__proto__;
    2664     return window[klass];
     3023    var element = document.createElement(tagName);
     3024    var proto = element['__proto__'] || element.constructor.prototype;
     3025    element = null;
     3026    return proto;
    26653027  }
    26663028
     3029  var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
     3030   Element.prototype;
     3031
    26673032  if (F.ElementExtensions) {
    2668     copy(Element.Methods, HTMLElement.prototype);
    2669     copy(Element.Methods.Simulated, HTMLElement.prototype, true);
     3033    copy(Element.Methods, elementPrototype);
     3034    copy(Element.Methods.Simulated, elementPrototype, true);
    26703035  }
    26713036
    26723037  if (F.SpecificElementExtensions) {
     
    26843049  Element.cache = { };
    26853050};
    26863051
     3052
    26873053document.viewport = {
     3054
    26883055  getDimensions: function() {
    2689     var dimensions = { };
    2690     $w('width height').each(function(d) {
    2691       var D = d.capitalize();
    2692       dimensions[d] = self['inner' + D] ||
    2693        (document.documentElement['client' + D] || document.body['client' + D]);
    2694     });
    2695     return dimensions;
     3056    return { width: this.getWidth(), height: this.getHeight() };
    26963057  },
    26973058
    2698   getWidth: function() {
    2699     return this.getDimensions().width;
     3059  getScrollOffsets: function() {
     3060    return Element._returnOffset(
     3061      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
     3062      window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
     3063  }
     3064};
     3065
     3066(function(viewport) {
     3067  var B = Prototype.Browser, doc = document, element, property = {};
     3068
     3069  function getRootElement() {
     3070    if (B.WebKit && !doc.evaluate)
     3071      return document;
     3072
     3073    if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
     3074      return document.body;
     3075
     3076    return document.documentElement;
     3077  }
     3078
     3079  function define(D) {
     3080    if (!element) element = getRootElement();
     3081
     3082    property[D] = 'client' + D;
     3083
     3084    viewport['get' + D] = function() { return element[property[D]] };
     3085    return viewport['get' + D]();
     3086  }
     3087
     3088  viewport.getWidth  = define.curry('Width');
     3089
     3090  viewport.getHeight = define.curry('Height');
     3091})(document.viewport);
     3092
     3093
     3094Element.Storage = {
     3095  UID: 1
     3096};
     3097
     3098Element.addMethods({
     3099  getStorage: function(element) {
     3100    if (!(element = $(element))) return;
     3101
     3102    var uid;
     3103    if (element === window) {
     3104      uid = 0;
     3105    } else {
     3106      if (typeof element._prototypeUID === "undefined")
     3107        element._prototypeUID = [Element.Storage.UID++];
     3108      uid = element._prototypeUID[0];
     3109    }
     3110
     3111    if (!Element.Storage[uid])
     3112      Element.Storage[uid] = $H();
     3113
     3114    return Element.Storage[uid];
    27003115  },
    27013116
    2702   getHeight: function() {
    2703     return this.getDimensions().height;
     3117  store: function(element, key, value) {
     3118    if (!(element = $(element))) return;
     3119
     3120    if (arguments.length === 2) {
     3121      Element.getStorage(element).update(key);
     3122    } else {
     3123      Element.getStorage(element).set(key, value);
     3124    }
     3125
     3126    return element;
    27043127  },
    27053128
    2706   getScrollOffsets: function() {
    2707     return Element._returnOffset(
    2708       window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
    2709       window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
     3129  retrieve: function(element, key, defaultValue) {
     3130    if (!(element = $(element))) return;
     3131    var hash = Element.getStorage(element), value = hash.get(key);
     3132
     3133    if (Object.isUndefined(value)) {
     3134      hash.set(key, defaultValue);
     3135      value = defaultValue;
     3136    }
     3137
     3138    return value;
     3139  },
     3140
     3141  clone: function(element, deep) {
     3142    if (!(element = $(element))) return;
     3143    var clone = element.cloneNode(deep);
     3144    clone._prototypeUID = void 0;
     3145    if (deep) {
     3146      var descendants = Element.select(clone, '*'),
     3147          i = descendants.length;
     3148      while (i--) {
     3149        descendants[i]._prototypeUID = void 0;
     3150      }
     3151    }
     3152    return Element.extend(clone);
    27103153  }
    2711 };
    2712 /* Portions of the Selector class are derived from Jack Slocums DomQuery,
     3154});
     3155/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
    27133156 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
    27143157 * license.  Please see http://www.yui-ext.com/ for more information. */
    27153158
    27163159var Selector = Class.create({
    27173160  initialize: function(expression) {
    27183161    this.expression = expression.strip();
    2719     this.compileMatcher();
     3162
     3163    if (this.shouldUseSelectorsAPI()) {
     3164      this.mode = 'selectorsAPI';
     3165    } else if (this.shouldUseXPath()) {
     3166      this.mode = 'xpath';
     3167      this.compileXPathMatcher();
     3168    } else {
     3169      this.mode = "normal";
     3170      this.compileMatcher();
     3171    }
     3172
    27203173  },
    27213174
     3175  shouldUseXPath: (function() {
     3176
     3177    var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
     3178      var isBuggy = false;
     3179      if (document.evaluate && window.XPathResult) {
     3180        var el = document.createElement('div');
     3181        el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';
     3182
     3183        var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
     3184          "//*[local-name()='li' or local-name()='LI']";
     3185
     3186        var result = document.evaluate(xpath, el, null,
     3187          XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
     3188
     3189        isBuggy = (result.snapshotLength !== 2);
     3190        el = null;
     3191      }
     3192      return isBuggy;
     3193    })();
     3194
     3195    return function() {
     3196      if (!Prototype.BrowserFeatures.XPath) return false;
     3197
     3198      var e = this.expression;
     3199
     3200      if (Prototype.Browser.WebKit &&
     3201       (e.include("-of-type") || e.include(":empty")))
     3202        return false;
     3203
     3204      if ((/(\[[\w-]*?:|:checked)/).test(e))
     3205        return false;
     3206
     3207      if (IS_DESCENDANT_SELECTOR_BUGGY) return false;
     3208
     3209      return true;
     3210    }
     3211
     3212  })(),
     3213
     3214  shouldUseSelectorsAPI: function() {
     3215    if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
     3216
     3217    if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;
     3218
     3219    if (!Selector._div) Selector._div = new Element('div');
     3220
     3221    try {
     3222      Selector._div.querySelector(this.expression);
     3223    } catch(e) {
     3224      return false;
     3225    }
     3226
     3227    return true;
     3228  },
     3229
    27223230  compileMatcher: function() {
    2723     // Selectors with namespaced attributes can't use the XPath version
    2724     if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
    2725       return this.compileXPathMatcher();
    2726 
    27273231    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
    2728         c = Selector.criteria, le, p, m;
     3232        c = Selector.criteria, le, p, m, len = ps.length, name;
    27293233
    27303234    if (Selector._cache[e]) {
    27313235      this.matcher = Selector._cache[e];
     
    27373241
    27383242    while (e && le != e && (/\S/).test(e)) {
    27393243      le = e;
    2740       for (var i in ps) {
    2741         p = ps[i];
     3244      for (var i = 0; i<len; i++) {
     3245        p = ps[i].re;
     3246        name = ps[i].name;
    27423247        if (m = e.match(p)) {
    2743           this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
    2744               new Template(c[i]).evaluate(m));
     3248          this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :
     3249            new Template(c[name]).evaluate(m));
    27453250          e = e.replace(m[0], '');
    27463251          break;
    27473252        }
     
    27553260
    27563261  compileXPathMatcher: function() {
    27573262    var e = this.expression, ps = Selector.patterns,
    2758         x = Selector.xpath, le, m;
     3263        x = Selector.xpath, le, m, len = ps.length, name;
    27593264
    27603265    if (Selector._cache[e]) {
    27613266      this.xpath = Selector._cache[e]; return;
     
    27643269    this.matcher = ['.//*'];
    27653270    while (e && le != e && (/\S/).test(e)) {
    27663271      le = e;
    2767       for (var i in ps) {
    2768         if (m = e.match(ps[i])) {
    2769           this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
    2770             new Template(x[i]).evaluate(m));
     3272      for (var i = 0; i<len; i++) {
     3273        name = ps[i].name;
     3274        if (m = e.match(ps[i].re)) {
     3275          this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
     3276            new Template(x[name]).evaluate(m));
    27713277          e = e.replace(m[0], '');
    27723278          break;
    27733279        }
     
    27803286
    27813287  findElements: function(root) {
    27823288    root = root || document;
    2783     if (this.xpath) return document._getElementsByXPath(this.xpath, root);
    2784     return this.matcher(root);
     3289    var e = this.expression, results;
     3290
     3291    switch (this.mode) {
     3292      case 'selectorsAPI':
     3293        if (root !== document) {
     3294          var oldId = root.id, id = $(root).identify();
     3295          id = id.replace(/([\.:])/g, "\\$1");
     3296          e = "#" + id + " " + e;
     3297        }
     3298
     3299        results = $A(root.querySelectorAll(e)).map(Element.extend);
     3300        root.id = oldId;
     3301
     3302        return results;
     3303      case 'xpath':
     3304        return document._getElementsByXPath(this.xpath, root);
     3305      default:
     3306       return this.matcher(root);
     3307    }
    27853308  },
    27863309
    27873310  match: function(element) {
    27883311    this.tokens = [];
    27893312
    27903313    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
    2791     var le, p, m;
     3314    var le, p, m, len = ps.length, name;
    27923315
    27933316    while (e && le !== e && (/\S/).test(e)) {
    27943317      le = e;
    2795       for (var i in ps) {
    2796         p = ps[i];
     3318      for (var i = 0; i<len; i++) {
     3319        p = ps[i].re;
     3320        name = ps[i].name;
    27973321        if (m = e.match(p)) {
    2798           // use the Selector.assertions methods unless the selector
    2799           // is too complex.
    2800           if (as[i]) {
    2801             this.tokens.push([i, Object.clone(m)]);
     3322          if (as[name]) {
     3323            this.tokens.push([name, Object.clone(m)]);
    28023324            e = e.replace(m[0], '');
    28033325          } else {
    2804             // reluctantly do a document-wide search
    2805             // and look for a match in the array
    28063326            return this.findElements(document).include(element);
    28073327          }
    28083328        }
     
    28293349  }
    28303350});
    28313351
     3352if (Prototype.BrowserFeatures.SelectorsAPI &&
     3353 document.compatMode === 'BackCompat') {
     3354  Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
     3355    var div = document.createElement('div'),
     3356     span = document.createElement('span');
     3357
     3358    div.id = "prototype_test_id";
     3359    span.className = 'Test';
     3360    div.appendChild(span);
     3361    var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
     3362    div = span = null;
     3363    return isIgnored;
     3364  })();
     3365}
     3366
    28323367Object.extend(Selector, {
    28333368  _cache: { },
    28343369
     
    28443379    },
    28453380    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    28463381    id:           "[@id='#{1}']",
    2847     attrPresence: "[@#{1}]",
     3382    attrPresence: function(m) {
     3383      m[1] = m[1].toLowerCase();
     3384      return new Template("[@#{1}]").evaluate(m);
     3385    },
    28483386    attr: function(m) {
     3387      m[1] = m[1].toLowerCase();
    28493388      m[3] = m[5] || m[6];
    28503389      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    28513390    },
     
    28683407      'first-child': '[not(preceding-sibling::*)]',
    28693408      'last-child':  '[not(following-sibling::*)]',
    28703409      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
    2871       'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
     3410      'empty':       "[count(*) = 0 and (count(text()) = 0)]",
    28723411      'checked':     "[@checked]",
    2873       'disabled':    "[@disabled]",
    2874       'enabled':     "[not(@disabled)]",
     3412      'disabled':    "[(@disabled) and (@type!='hidden')]",
     3413      'enabled':     "[not(@disabled) and (@type!='hidden')]",
    28753414      'not': function(m) {
    28763415        var e = m[6], p = Selector.patterns,
    2877             x = Selector.xpath, le, m, v;
     3416            x = Selector.xpath, le, v, len = p.length, name;
    28783417
    28793418        var exclusion = [];
    28803419        while (e && le != e && (/\S/).test(e)) {
    28813420          le = e;
    2882           for (var i in p) {
    2883             if (m = e.match(p[i])) {
    2884               v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
     3421          for (var i = 0; i<len; i++) {
     3422            name = p[i].name
     3423            if (m = e.match(p[i].re)) {
     3424              v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m);
    28853425              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
    28863426              e = e.replace(m[0], '');
    28873427              break;
     
    29313471  },
    29323472
    29333473  criteria: {
    2934     tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
    2935     className:    'n = h.className(n, r, "#{1}", c); c = false;',
    2936     id:           'n = h.id(n, r, "#{1}", c);        c = false;',
    2937     attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
     3474    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
     3475    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
     3476    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
     3477    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
    29383478    attr: function(m) {
    29393479      m[3] = (m[5] || m[6]);
    2940       return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
     3480      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
    29413481    },
    29423482    pseudo: function(m) {
    29433483      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
     
    29493489    laterSibling: 'c = "laterSibling";'
    29503490  },
    29513491
    2952   patterns: {
    2953     // combinators must be listed first
    2954     // (and descendant needs to be last combinator)
    2955     laterSibling: /^\s*~\s*/,
    2956     child:        /^\s*>\s*/,
    2957     adjacent:     /^\s*\+\s*/,
    2958     descendant:   /^\s/,
     3492  patterns: [
     3493    { name: 'laterSibling', re: /^\s*~\s*/ },
     3494    { name: 'child',        re: /^\s*>\s*/ },
     3495    { name: 'adjacent',     re: /^\s*\+\s*/ },
     3496    { name: 'descendant',   re: /^\s/ },
    29593497
    2960     // selectors follow
    2961     tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
    2962     id:           /^#([\w\-\*]+)(\b|$)/,
    2963     className:    /^\.([\w\-\*]+)(\b|$)/,
    2964     pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
    2965     attrPresence: /^\[([\w]+)\]/,
    2966     attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
    2967   },
     3498    { name: 'tagName',      re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
     3499    { name: 'id',           re: /^#([\w\-\*]+)(\b|$)/ },
     3500    { name: 'className',    re: /^\.([\w\-\*]+)(\b|$)/ },
     3501    { name: 'pseudo',       re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
     3502    { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
     3503    { name: 'attr',         re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
     3504  ],
    29683505
    2969   // for Selector.match and Element#match
    29703506  assertions: {
    29713507    tagName: function(element, matches) {
    29723508      return matches[1].toUpperCase() == element.tagName.toUpperCase();
     
    29863522
    29873523    attr: function(element, matches) {
    29883524      var nodeValue = Element.readAttribute(element, matches[1]);
    2989       return Selector.operators[matches[2]](nodeValue, matches[3]);
     3525      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    29903526    }
    29913527  },
    29923528
    29933529  handlers: {
    2994     // UTILITY FUNCTIONS
    2995     // joins two collections
    29963530    concat: function(a, b) {
    29973531      for (var i = 0, node; node = b[i]; i++)
    29983532        a.push(node);
    29993533      return a;
    30003534    },
    30013535
    3002     // marks an array of nodes for counting
    30033536    mark: function(nodes) {
     3537      var _true = Prototype.emptyFunction;
    30043538      for (var i = 0, node; node = nodes[i]; i++)
    3005         node._counted = true;
     3539        node._countedByPrototype = _true;
    30063540      return nodes;
    30073541    },
    30083542
    3009     unmark: function(nodes) {
    3010       for (var i = 0, node; node = nodes[i]; i++)
    3011         node._counted = undefined;
    3012       return nodes;
    3013     },
     3543    unmark: (function(){
    30143544
    3015     // mark each child node with its position (for nth calls)
    3016     // "ofType" flag indicates whether we're indexing for nth-of-type
    3017     // rather than nth-child
     3545      var PROPERTIES_ATTRIBUTES_MAP = (function(){
     3546        var el = document.createElement('div'),
     3547            isBuggy = false,
     3548            propName = '_countedByPrototype',
     3549            value = 'x'
     3550        el[propName] = value;
     3551        isBuggy = (el.getAttribute(propName) === value);
     3552        el = null;
     3553        return isBuggy;
     3554      })();
     3555
     3556      return PROPERTIES_ATTRIBUTES_MAP ?
     3557        function(nodes) {
     3558          for (var i = 0, node; node = nodes[i]; i++)
     3559            node.removeAttribute('_countedByPrototype');
     3560          return nodes;
     3561        } :
     3562        function(nodes) {
     3563          for (var i = 0, node; node = nodes[i]; i++)
     3564            node._countedByPrototype = void 0;
     3565          return nodes;
     3566        }
     3567    })(),
     3568
    30183569    index: function(parentNode, reverse, ofType) {
    3019       parentNode._counted = true;
     3570      parentNode._countedByPrototype = Prototype.emptyFunction;
    30203571      if (reverse) {
    30213572        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
    30223573          var node = nodes[i];
    3023           if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
     3574          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
    30243575        }
    30253576      } else {
    30263577        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
    3027           if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
     3578          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
    30283579      }
    30293580    },
    30303581
    3031     // filters out duplicates and extends all nodes
    30323582    unique: function(nodes) {
    30333583      if (nodes.length == 0) return nodes;
    30343584      var results = [], n;
    30353585      for (var i = 0, l = nodes.length; i < l; i++)
    3036         if (!(n = nodes[i])._counted) {
    3037           n._counted = true;
     3586        if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
     3587          n._countedByPrototype = Prototype.emptyFunction;
    30383588          results.push(Element.extend(n));
    30393589        }
    30403590      return Selector.handlers.unmark(results);
    30413591    },
    30423592
    3043     // COMBINATOR FUNCTIONS
    30443593    descendant: function(nodes) {
    30453594      var h = Selector.handlers;
    30463595      for (var i = 0, results = [], node; node = nodes[i]; i++)
     
    30513600    child: function(nodes) {
    30523601      var h = Selector.handlers;
    30533602      for (var i = 0, results = [], node; node = nodes[i]; i++) {
    3054         for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
     3603        for (var j = 0, child; child = node.childNodes[j]; j++)
    30553604          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
    30563605      }
    30573606      return results;
     
    30743623
    30753624    nextElementSibling: function(node) {
    30763625      while (node = node.nextSibling)
    3077               if (node.nodeType == 1) return node;
     3626        if (node.nodeType == 1) return node;
    30783627      return null;
    30793628    },
    30803629
     
    30843633      return null;
    30853634    },
    30863635
    3087     // TOKEN FUNCTIONS
    30883636    tagName: function(nodes, root, tagName, combinator) {
    3089       tagName = tagName.toUpperCase();
     3637      var uTagName = tagName.toUpperCase();
    30903638      var results = [], h = Selector.handlers;
    30913639      if (nodes) {
    30923640        if (combinator) {
    3093           // fastlane for ordinary descendant combinators
    30943641          if (combinator == "descendant") {
    30953642            for (var i = 0, node; node = nodes[i]; i++)
    30963643              h.concat(results, node.getElementsByTagName(tagName));
     
    30993646          if (tagName == "*") return nodes;
    31003647        }
    31013648        for (var i = 0, node; node = nodes[i]; i++)
    3102           if (node.tagName.toUpperCase() == tagName) results.push(node);
     3649          if (node.tagName.toUpperCase() === uTagName) results.push(node);
    31033650        return results;
    31043651      } else return root.getElementsByTagName(tagName);
    31053652    },
    31063653
    31073654    id: function(nodes, root, id, combinator) {
    31083655      var targetNode = $(id), h = Selector.handlers;
    3109       if (!targetNode) return [];
    3110       if (!nodes && root == document) return [targetNode];
     3656
     3657      if (root == document) {
     3658        if (!targetNode) return [];
     3659        if (!nodes) return [targetNode];
     3660      } else {
     3661        if (!root.sourceIndex || root.sourceIndex < 1) {
     3662          var nodes = root.getElementsByTagName('*');
     3663          for (var j = 0, node; node = nodes[j]; j++) {
     3664            if (node.id === id) return [node];
     3665          }
     3666        }
     3667      }
     3668
    31113669      if (nodes) {
    31123670        if (combinator) {
    31133671          if (combinator == 'child') {
     
    31463704      return results;
    31473705    },
    31483706
    3149     attrPresence: function(nodes, root, attr) {
     3707    attrPresence: function(nodes, root, attr, combinator) {
    31503708      if (!nodes) nodes = root.getElementsByTagName("*");
     3709      if (nodes && combinator) nodes = this[combinator](nodes);
    31513710      var results = [];
    31523711      for (var i = 0, node; node = nodes[i]; i++)
    31533712        if (Element.hasAttribute(node, attr)) results.push(node);
    31543713      return results;
    31553714    },
    31563715
    3157     attr: function(nodes, root, attr, value, operator) {
     3716    attr: function(nodes, root, attr, value, operator, combinator) {
    31583717      if (!nodes) nodes = root.getElementsByTagName("*");
     3718      if (nodes && combinator) nodes = this[combinator](nodes);
    31593719      var handler = Selector.operators[operator], results = [];
    31603720      for (var i = 0, node; node = nodes[i]; i++) {
    31613721        var nodeValue = Element.readAttribute(node, attr);
     
    32173777      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    32183778    },
    32193779
    3220     // handles the an+b logic
    32213780    getIndices: function(a, b, total) {
    32223781      if (a == 0) return b > 0 ? [b] : [];
    32233782      return $R(1, total).inject([], function(memo, i) {
     
    32263785      });
    32273786    },
    32283787
    3229     // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
    32303788    nth: function(nodes, formula, root, reverse, ofType) {
    32313789      if (nodes.length == 0) return [];
    32323790      if (formula == 'even') formula = '2n+0';
     
    32343792      var h = Selector.handlers, results = [], indexed = [], m;
    32353793      h.mark(nodes);
    32363794      for (var i = 0, node; node = nodes[i]; i++) {
    3237         if (!node.parentNode._counted) {
     3795        if (!node.parentNode._countedByPrototype) {
    32383796          h.index(node.parentNode, reverse, ofType);
    32393797          indexed.push(node.parentNode);
    32403798        }
     
    32603818
    32613819    'empty': function(nodes, value, root) {
    32623820      for (var i = 0, results = [], node; node = nodes[i]; i++) {
    3263         // IE treats comments as element nodes
    3264         if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
     3821        if (node.tagName == '!' || node.firstChild) continue;
    32653822        results.push(node);
    32663823      }
    32673824      return results;
     
    32723829      var exclusions = new Selector(selector).findElements(root);
    32733830      h.mark(exclusions);
    32743831      for (var i = 0, results = [], node; node = nodes[i]; i++)
    3275         if (!node._counted) results.push(node);
     3832        if (!node._countedByPrototype) results.push(node);
    32763833      h.unmark(exclusions);
    32773834      return results;
    32783835    },
    32793836
    32803837    'enabled': function(nodes, value, root) {
    32813838      for (var i = 0, results = [], node; node = nodes[i]; i++)
    3282         if (!node.disabled) results.push(node);
     3839        if (!node.disabled && (!node.type || node.type !== 'hidden'))
     3840          results.push(node);
    32833841      return results;
    32843842    },
    32853843
     
    32993857  operators: {
    33003858    '=':  function(nv, v) { return nv == v; },
    33013859    '!=': function(nv, v) { return nv != v; },
    3302     '^=': function(nv, v) { return nv.startsWith(v); },
    3303     '$=': function(nv, v) { return nv.endsWith(v); },
    3304     '*=': function(nv, v) { return nv.include(v); },
     3860    '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
     3861    '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
     3862    '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
    33053863    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    3306     '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
     3864    '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
     3865     '-').include('-' + (v || "").toUpperCase() + '-'); }
    33073866  },
    33083867
     3868  split: function(expression) {
     3869    var expressions = [];
     3870    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
     3871      expressions.push(m[1].strip());
     3872    });
     3873    return expressions;
     3874  },
     3875
    33093876  matchElements: function(elements, expression) {
    3310     var matches = new Selector(expression).findElements(), h = Selector.handlers;
     3877    var matches = $$(expression), h = Selector.handlers;
    33113878    h.mark(matches);
    33123879    for (var i = 0, results = [], element; element = elements[i]; i++)
    3313       if (element._counted) results.push(element);
     3880      if (element._countedByPrototype) results.push(element);
    33143881    h.unmark(matches);
    33153882    return results;
    33163883  },
     
    33233890  },
    33243891
    33253892  findChildElements: function(element, expressions) {
    3326     var exprs = expressions.join(','), expressions = [];
    3327     exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
    3328       expressions.push(m[1].strip());
    3329     });
     3893    expressions = Selector.split(expressions.join(','));
    33303894    var results = [], h = Selector.handlers;
    33313895    for (var i = 0, l = expressions.length, selector; i < l; i++) {
    33323896      selector = new Selector(expressions[i].strip());
     
    33363900  }
    33373901});
    33383902
     3903if (Prototype.Browser.IE) {
     3904  Object.extend(Selector.handlers, {
     3905    concat: function(a, b) {
     3906      for (var i = 0, node; node = b[i]; i++)
     3907        if (node.tagName !== "!") a.push(node);
     3908      return a;
     3909    }
     3910  });
     3911}
     3912
    33393913function $$() {
    33403914  return Selector.findChildElements(document, $A(arguments));
    33413915}
     3916
    33423917var Form = {
    33433918  reset: function(form) {
    3344     $(form).reset();
     3919    form = $(form);
     3920    form.reset();
    33453921    return form;
    33463922  },
    33473923
    33483924  serializeElements: function(elements, options) {
    33493925    if (typeof options != 'object') options = { hash: !!options };
    3350     else if (options.hash === undefined) options.hash = true;
     3926    else if (Object.isUndefined(options.hash)) options.hash = true;
    33513927    var key, value, submitted = false, submit = options.submit;
    33523928
    33533929    var data = elements.inject({ }, function(result, element) {
    33543930      if (!element.disabled && element.name) {
    33553931        key = element.name; value = $(element).getValue();
    3356         if (value != null && (element.type != 'submit' || (!submitted &&
     3932        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
    33573933            submit !== false && (!submit || key == submit) && (submitted = true)))) {
    33583934          if (key in result) {
    3359             // a key is already present; construct an array of values
    33603935            if (!Object.isArray(result[key])) result[key] = [result[key]];
    33613936            result[key].push(value);
    33623937          }
     
    33763951  },
    33773952
    33783953  getElements: function(form) {
    3379     return $A($(form).getElementsByTagName('*')).inject([],
    3380       function(elements, child) {
    3381         if (Form.Element.Serializers[child.tagName.toLowerCase()])
    3382           elements.push(Element.extend(child));
    3383         return elements;
    3384       }
    3385     );
     3954    var elements = $(form).getElementsByTagName('*'),
     3955        element,
     3956        arr = [ ],
     3957        serializers = Form.Element.Serializers;
     3958    for (var i = 0; element = elements[i]; i++) {
     3959      arr.push(element);
     3960    }
     3961    return arr.inject([], function(elements, child) {
     3962      if (serializers[child.tagName.toLowerCase()])
     3963        elements.push(Element.extend(child));
     3964      return elements;
     3965    })
    33863966  },
    33873967
    33883968  getInputs: function(form, typeName, name) {
     
    34224002    }).sortBy(function(element) { return element.tabIndex }).first();
    34234003
    34244004    return firstByIndex ? firstByIndex : elements.find(function(element) {
    3425       return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
     4005      return /^(?:input|select|textarea)$/i.test(element.tagName);
    34264006    });
    34274007  },
    34284008
     
    34534033
    34544034/*--------------------------------------------------------------------------*/
    34554035
     4036
    34564037Form.Element = {
    34574038  focus: function(element) {
    34584039    $(element).focus();
     
    34664047};
    34674048
    34684049Form.Element.Methods = {
     4050
    34694051  serialize: function(element) {
    34704052    element = $(element);
    34714053    if (!element.disabled && element.name) {
     
    35064088    try {
    35074089      element.focus();
    35084090      if (element.select && (element.tagName.toLowerCase() != 'input' ||
    3509           !['button', 'reset', 'submit'].include(element.type)))
     4091          !(/^(?:button|reset|submit)$/i.test(element.type))))
    35104092        element.select();
    35114093    } catch (e) { }
    35124094    return element;
     
    35144096
    35154097  disable: function(element) {
    35164098    element = $(element);
    3517     element.blur();
    35184099    element.disabled = true;
    35194100    return element;
    35204101  },
     
    35294110/*--------------------------------------------------------------------------*/
    35304111
    35314112var Field = Form.Element;
     4113
    35324114var $F = Form.Element.Methods.getValue;
    35334115
    35344116/*--------------------------------------------------------------------------*/
     
    35454127  },
    35464128
    35474129  inputSelector: function(element, value) {
    3548     if (value === undefined) return element.checked ? element.value : null;
     4130    if (Object.isUndefined(value)) return element.checked ? element.value : null;
    35494131    else element.checked = !!value;
    35504132  },
    35514133
    35524134  textarea: function(element, value) {
    3553     if (value === undefined) return element.value;
     4135    if (Object.isUndefined(value)) return element.value;
    35544136    else element.value = value;
    35554137  },
    35564138
    3557   select: function(element, index) {
    3558     if (index === undefined)
     4139  select: function(element, value) {
     4140    if (Object.isUndefined(value))
    35594141      return this[element.type == 'select-one' ?
    35604142        'selectOne' : 'selectMany'](element);
    35614143    else {
    3562       var opt, value, single = !Object.isArray(index);
     4144      var opt, currentValue, single = !Object.isArray(value);
    35634145      for (var i = 0, length = element.length; i < length; i++) {
    35644146        opt = element.options[i];
    3565         value = this.optionValue(opt);
     4147        currentValue = this.optionValue(opt);
    35664148        if (single) {
    3567           if (value == index) {
     4149          if (currentValue == value) {
    35684150            opt.selected = true;
    35694151            return;
    35704152          }
    35714153        }
    3572         else opt.selected = index.include(value);
     4154        else opt.selected = value.include(currentValue);
    35734155      }
    35744156    }
    35754157  },
     
    35914173  },
    35924174
    35934175  optionValue: function(opt) {
    3594     // extend element because hasAttribute may not be native
    35954176    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
    35964177  }
    35974178};
    35984179
    35994180/*--------------------------------------------------------------------------*/
    36004181
     4182
    36014183Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
    36024184  initialize: function($super, element, frequency, callback) {
    36034185    $super(callback, frequency);
     
    36794261    return Form.serialize(this.element);
    36804262  }
    36814263});
    3682 if (!window.Event) var Event = { };
     4264(function() {
    36834265
    3684 Object.extend(Event, {
    3685   KEY_BACKSPACE: 8,
    3686   KEY_TAB:       9,
    3687   KEY_RETURN:   13,
    3688   KEY_ESC:      27,
    3689   KEY_LEFT:     37,
    3690   KEY_UP:       38,
    3691   KEY_RIGHT:    39,
    3692   KEY_DOWN:     40,
    3693   KEY_DELETE:   46,
    3694   KEY_HOME:     36,
    3695   KEY_END:      35,
    3696   KEY_PAGEUP:   33,
    3697   KEY_PAGEDOWN: 34,
    3698   KEY_INSERT:   45,
     4266  var Event = {
     4267    KEY_BACKSPACE: 8,
     4268    KEY_TAB:       9,
     4269    KEY_RETURN:   13,
     4270    KEY_ESC:      27,
     4271    KEY_LEFT:     37,
     4272    KEY_UP:       38,
     4273    KEY_RIGHT:    39,
     4274    KEY_DOWN:     40,
     4275    KEY_DELETE:   46,
     4276    KEY_HOME:     36,
     4277    KEY_END:      35,
     4278    KEY_PAGEUP:   33,
     4279    KEY_PAGEDOWN: 34,
     4280    KEY_INSERT:   45,
    36994281
    3700   cache: { },
     4282    cache: {}
     4283  };
    37014284
    3702   relatedTarget: function(event) {
    3703     var element;
    3704     switch(event.type) {
    3705       case 'mouseover': element = event.fromElement; break;
    3706       case 'mouseout':  element = event.toElement;   break;
    3707       default: return null;
    3708     }
    3709     return Element.extend(element);
    3710   }
    3711 });
     4285  var docEl = document.documentElement;
     4286  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
     4287    && 'onmouseleave' in docEl;
    37124288
    3713 Event.Methods = (function() {
    3714   var isButton;
    3715 
     4289  var _isButton;
    37164290  if (Prototype.Browser.IE) {
    37174291    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    3718     isButton = function(event, code) {
    3719       return event.button == buttonMap[code];
     4292    _isButton = function(event, code) {
     4293      return event.button === buttonMap[code];
    37204294    };
    3721 
    37224295  } else if (Prototype.Browser.WebKit) {
    3723     isButton = function(event, code) {
     4296    _isButton = function(event, code) {
    37244297      switch (code) {
    37254298        case 0: return event.which == 1 && !event.metaKey;
    37264299        case 1: return event.which == 1 && event.metaKey;
    37274300        default: return false;
    37284301      }
    37294302    };
    3730 
    37314303  } else {
    3732     isButton = function(event, code) {
     4304    _isButton = function(event, code) {
    37334305      return event.which ? (event.which === code + 1) : (event.button === code);
    37344306    };
    37354307  }
    37364308
    3737   return {
    3738     isLeftClick:   function(event) { return isButton(event, 0) },
    3739     isMiddleClick: function(event) { return isButton(event, 1) },
    3740     isRightClick:  function(event) { return isButton(event, 2) },
     4309  function isLeftClick(event)   { return _isButton(event, 0) }
    37414310
    3742     element: function(event) {
    3743       var node = Event.extend(event).target;
    3744       return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
    3745     },
     4311  function isMiddleClick(event) { return _isButton(event, 1) }
    37464312
    3747     findElement: function(event, expression) {
    3748       var element = Event.element(event);
    3749       return element.match(expression) ? element : element.up(expression);
    3750     },
     4313  function isRightClick(event)  { return _isButton(event, 2) }
    37514314
    3752     pointer: function(event) {
    3753       return {
    3754         x: event.pageX || (event.clientX +
    3755           (document.documentElement.scrollLeft || document.body.scrollLeft)),
    3756         y: event.pageY || (event.clientY +
    3757           (document.documentElement.scrollTop || document.body.scrollTop))
    3758       };
    3759     },
     4315  function element(event) {
     4316    event = Event.extend(event);
    37604317
    3761     pointerX: function(event) { return Event.pointer(event).x },
    3762     pointerY: function(event) { return Event.pointer(event).y },
     4318    var node = event.target, type = event.type,
     4319     currentTarget = event.currentTarget;
    37634320
    3764     stop: function(event) {
    3765       Event.extend(event);
    3766       event.preventDefault();
    3767       event.stopPropagation();
    3768       event.stopped = true;
     4321    if (currentTarget && currentTarget.tagName) {
     4322      if (type === 'load' || type === 'error' ||
     4323        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
     4324          && currentTarget.type === 'radio'))
     4325            node = currentTarget;
    37694326    }
     4327
     4328    if (node.nodeType == Node.TEXT_NODE)
     4329      node = node.parentNode;
     4330
     4331    return Element.extend(node);
     4332  }
     4333
     4334  function findElement(event, expression) {
     4335    var element = Event.element(event);
     4336    if (!expression) return element;
     4337    var elements = [element].concat(element.ancestors());
     4338    return Selector.findElement(elements, expression, 0);
     4339  }
     4340
     4341  function pointer(event) {
     4342    return { x: pointerX(event), y: pointerY(event) };
     4343  }
     4344
     4345  function pointerX(event) {
     4346    var docElement = document.documentElement,
     4347     body = document.body || { scrollLeft: 0 };
     4348
     4349    return event.pageX || (event.clientX +
     4350      (docElement.scrollLeft || body.scrollLeft) -
     4351      (docElement.clientLeft || 0));
     4352  }
     4353
     4354  function pointerY(event) {
     4355    var docElement = document.documentElement,
     4356     body = document.body || { scrollTop: 0 };
     4357
     4358    return  event.pageY || (event.clientY +
     4359       (docElement.scrollTop || body.scrollTop) -
     4360       (docElement.clientTop || 0));
     4361  }
     4362
     4363
     4364  function stop(event) {
     4365    Event.extend(event);
     4366    event.preventDefault();
     4367    event.stopPropagation();
     4368
     4369    event.stopped = true;
     4370  }
     4371
     4372  Event.Methods = {
     4373    isLeftClick: isLeftClick,
     4374    isMiddleClick: isMiddleClick,
     4375    isRightClick: isRightClick,
     4376
     4377    element: element,
     4378    findElement: findElement,
     4379
     4380    pointer: pointer,
     4381    pointerX: pointerX,
     4382    pointerY: pointerY,
     4383
     4384    stop: stop
    37704385  };
    3771 })();
    37724386
    3773 Event.extend = (function() {
     4387
    37744388  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    37754389    m[name] = Event.Methods[name].methodize();
    37764390    return m;
    37774391  });
    37784392
    37794393  if (Prototype.Browser.IE) {
     4394    function _relatedTarget(event) {
     4395      var element;
     4396      switch (event.type) {
     4397        case 'mouseover': element = event.fromElement; break;
     4398        case 'mouseout':  element = event.toElement;   break;
     4399        default: return null;
     4400      }
     4401      return Element.extend(element);
     4402    }
     4403
    37804404    Object.extend(methods, {
    37814405      stopPropagation: function() { this.cancelBubble = true },
    37824406      preventDefault:  function() { this.returnValue = false },
    3783       inspect: function() { return "[object Event]" }
     4407      inspect: function() { return '[object Event]' }
    37844408    });
    37854409
    3786     return function(event) {
     4410    Event.extend = function(event, element) {
    37874411      if (!event) return false;
    37884412      if (event._extendedByPrototype) return event;
    37894413
    37904414      event._extendedByPrototype = Prototype.emptyFunction;
    37914415      var pointer = Event.pointer(event);
     4416
    37924417      Object.extend(event, {
    3793         target: event.srcElement,
    3794         relatedTarget: Event.relatedTarget(event),
     4418        target: event.srcElement || element,
     4419        relatedTarget: _relatedTarget(event),
    37954420        pageX:  pointer.x,
    37964421        pageY:  pointer.y
    37974422      });
     4423
    37984424      return Object.extend(event, methods);
    37994425    };
    3800 
    38014426  } else {
    3802     Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
     4427    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
    38034428    Object.extend(Event.prototype, methods);
    3804     return Prototype.K;
     4429    Event.extend = Prototype.K;
    38054430  }
    3806 })();
    38074431
    3808 Object.extend(Event, (function() {
    3809   var cache = Event.cache;
     4432  function _createResponder(element, eventName, handler) {
     4433    var registry = Element.retrieve(element, 'prototype_event_registry');
    38104434
    3811   function getEventID(element) {
    3812     if (element._eventID) return element._eventID;
    3813     arguments.callee.id = arguments.callee.id || 1;
    3814     return element._eventID = ++arguments.callee.id;
    3815   }
     4435    if (Object.isUndefined(registry)) {
     4436      CACHE.push(element);
     4437      registry = Element.retrieve(element, 'prototype_event_registry', $H());
     4438    }
    38164439
    3817   function getDOMEventName(eventName) {
    3818     if (eventName && eventName.include(':')) return "dataavailable";
    3819     return eventName;
    3820   }
     4440    var respondersForEvent = registry.get(eventName);
     4441    if (Object.isUndefined(respondersForEvent)) {
     4442      respondersForEvent = [];
     4443      registry.set(eventName, respondersForEvent);
     4444    }
    38214445
    3822   function getCacheForID(id) {
    3823     return cache[id] = cache[id] || { };
    3824   }
     4446    if (respondersForEvent.pluck('handler').include(handler)) return false;
    38254447
    3826   function getWrappersForEventName(id, eventName) {
    3827     var c = getCacheForID(id);
    3828     return c[eventName] = c[eventName] || [];
    3829   }
     4448    var responder;
     4449    if (eventName.include(":")) {
     4450      responder = function(event) {
     4451        if (Object.isUndefined(event.eventName))
     4452          return false;
    38304453
    3831   function createWrapper(element, eventName, handler) {
    3832     var id = getEventID(element);
    3833     var c = getWrappersForEventName(id, eventName);
    3834     if (c.pluck("handler").include(handler)) return false;
    3835 
    3836     var wrapper = function(event) {
    3837       if (!Event || !Event.extend ||
    3838         (event.eventName && event.eventName != eventName))
     4454        if (event.eventName !== eventName)
    38394455          return false;
    38404456
    3841       Event.extend(event);
    3842       handler.call(element, event)
    3843     };
     4457        Event.extend(event, element);
     4458        handler.call(element, event);
     4459      };
     4460    } else {
     4461      if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
     4462       (eventName === "mouseenter" || eventName === "mouseleave")) {
     4463        if (eventName === "mouseenter" || eventName === "mouseleave") {
     4464          responder = function(event) {
     4465            Event.extend(event, element);
    38444466
    3845     wrapper.handler = handler;
    3846     c.push(wrapper);
    3847     return wrapper;
    3848   }
     4467            var parent = event.relatedTarget;
     4468            while (parent && parent !== element) {
     4469              try { parent = parent.parentNode; }
     4470              catch(e) { parent = element; }
     4471            }
    38494472
    3850   function findWrapper(id, eventName, handler) {
    3851     var c = getWrappersForEventName(id, eventName);
    3852     return c.find(function(wrapper) { return wrapper.handler == handler });
    3853   }
     4473            if (parent === element) return;
    38544474
    3855   function destroyWrapper(id, eventName, handler) {
    3856     var c = getCacheForID(id);
    3857     if (!c[eventName]) return false;
    3858     c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
     4475            handler.call(element, event);
     4476          };
     4477        }
     4478      } else {
     4479        responder = function(event) {
     4480          Event.extend(event, element);
     4481          handler.call(element, event);
     4482        };
     4483      }
     4484    }
     4485
     4486    responder.handler = handler;
     4487    respondersForEvent.push(responder);
     4488    return responder;
    38594489  }
    38604490
    3861   function destroyCache() {
    3862     for (var id in cache)
    3863       for (var eventName in cache[id])
    3864         cache[id][eventName] = null;
     4491  function _destroyCache() {
     4492    for (var i = 0, length = CACHE.length; i < length; i++) {
     4493      Event.stopObserving(CACHE[i]);
     4494      CACHE[i] = null;
     4495    }
    38654496  }
    38664497
    3867   if (window.attachEvent) {
    3868     window.attachEvent("onunload", destroyCache);
     4498  var CACHE = [];
     4499
     4500  if (Prototype.Browser.IE)
     4501    window.attachEvent('onunload', _destroyCache);
     4502
     4503  if (Prototype.Browser.WebKit)
     4504    window.addEventListener('unload', Prototype.emptyFunction, false);
     4505
     4506
     4507  var _getDOMEventName = Prototype.K;
     4508
     4509  if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
     4510    _getDOMEventName = function(eventName) {
     4511      var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
     4512      return eventName in translations ? translations[eventName] : eventName;
     4513    };
    38694514  }
    38704515
    3871   return {
    3872     observe: function(element, eventName, handler) {
    3873       element = $(element);
    3874       var name = getDOMEventName(eventName);
     4516  function observe(element, eventName, handler) {
     4517    element = $(element);
    38754518
    3876       var wrapper = createWrapper(element, eventName, handler);
    3877       if (!wrapper) return element;
     4519    var responder = _createResponder(element, eventName, handler);
    38784520
    3879       if (element.addEventListener) {
    3880         element.addEventListener(name, wrapper, false);
    3881       } else {
    3882         element.attachEvent("on" + name, wrapper);
     4521    if (!responder) return element;
     4522
     4523    if (eventName.include(':')) {
     4524      if (element.addEventListener)
     4525        element.addEventListener("dataavailable", responder, false);
     4526      else {
     4527        element.attachEvent("ondataavailable", responder);
     4528        element.attachEvent("onfilterchange", responder);
    38834529      }
     4530    } else {
     4531      var actualEventName = _getDOMEventName(eventName);
    38844532
    3885       return element;
    3886     },
     4533      if (element.addEventListener)
     4534        element.addEventListener(actualEventName, responder, false);
     4535      else
     4536        element.attachEvent("on" + actualEventName, responder);
     4537    }
    38874538
    3888     stopObserving: function(element, eventName, handler) {
    3889       element = $(element);
    3890       var id = getEventID(element), name = getDOMEventName(eventName);
     4539    return element;
     4540  }
    38914541
    3892       if (!handler && eventName) {
    3893         getWrappersForEventName(id, eventName).each(function(wrapper) {
    3894           element.stopObserving(eventName, wrapper.handler);
    3895         });
    3896         return element;
     4542  function stopObserving(element, eventName, handler) {
     4543    element = $(element);
    38974544
    3898       } else if (!eventName) {
    3899         Object.keys(getCacheForID(id)).each(function(eventName) {
    3900           element.stopObserving(eventName);
    3901         });
    3902         return element;
    3903       }
     4545    var registry = Element.retrieve(element, 'prototype_event_registry');
    39044546
    3905       var wrapper = findWrapper(id, eventName, handler);
    3906       if (!wrapper) return element;
     4547    if (Object.isUndefined(registry)) return element;
    39074548
    3908       if (element.removeEventListener) {
    3909         element.removeEventListener(name, wrapper, false);
    3910       } else {
    3911         element.detachEvent("on" + name, wrapper);
    3912       }
     4549    if (eventName && !handler) {
     4550      var responders = registry.get(eventName);
    39134551
    3914       destroyWrapper(id, eventName, handler);
     4552      if (Object.isUndefined(responders)) return element;
    39154553
     4554      responders.each( function(r) {
     4555        Element.stopObserving(element, eventName, r.handler);
     4556      });
    39164557      return element;
    3917     },
     4558    } else if (!eventName) {
     4559      registry.each( function(pair) {
     4560        var eventName = pair.key, responders = pair.value;
    39184561
    3919     fire: function(element, eventName, memo) {
    3920       element = $(element);
    3921       if (element == document && document.createEvent && !element.dispatchEvent)
    3922         element = document.documentElement;
     4562        responders.each( function(r) {
     4563          Element.stopObserving(element, eventName, r.handler);
     4564        });
     4565      });
     4566      return element;
     4567    }
    39234568
    3924       if (document.createEvent) {
    3925         var event = document.createEvent("HTMLEvents");
    3926         event.initEvent("dataavailable", true, true);
    3927       } else {
    3928         var event = document.createEventObject();
    3929         event.eventType = "ondataavailable";
    3930       }
     4569    var responders = registry.get(eventName);
    39314570
    3932       event.eventName = eventName;
    3933       event.memo = memo || { };
     4571    if (!responders) return;
    39344572
    3935       if (document.createEvent) {
    3936         element.dispatchEvent(event);
    3937       } else {
    3938         element.fireEvent(event.eventType, event);
     4573    var responder = responders.find( function(r) { return r.handler === handler; });
     4574    if (!responder) return element;
     4575
     4576    var actualEventName = _getDOMEventName(eventName);
     4577
     4578    if (eventName.include(':')) {
     4579      if (element.removeEventListener)
     4580        element.removeEventListener("dataavailable", responder, false);
     4581      else {
     4582        element.detachEvent("ondataavailable", responder);
     4583        element.detachEvent("onfilterchange",  responder);
    39394584      }
     4585    } else {
     4586      if (element.removeEventListener)
     4587        element.removeEventListener(actualEventName, responder, false);
     4588      else
     4589        element.detachEvent('on' + actualEventName, responder);
     4590    }
    39404591
    3941       return event;
     4592    registry.set(eventName, responders.without(responder));
     4593
     4594    return element;
     4595  }
     4596
     4597  function fire(element, eventName, memo, bubble) {
     4598    element = $(element);
     4599
     4600    if (Object.isUndefined(bubble))
     4601      bubble = true;
     4602
     4603    if (element == document && document.createEvent && !element.dispatchEvent)
     4604      element = document.documentElement;
     4605
     4606    var event;
     4607    if (document.createEvent) {
     4608      event = document.createEvent('HTMLEvents');
     4609      event.initEvent('dataavailable', true, true);
     4610    } else {
     4611      event = document.createEventObject();
     4612      event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
    39424613    }
    3943   };
    3944 })());
    39454614
    3946 Object.extend(Event, Event.Methods);
     4615    event.eventName = eventName;
     4616    event.memo = memo || { };
    39474617
    3948 Element.addMethods({
    3949   fire:          Event.fire,
    3950   observe:       Event.observe,
    3951   stopObserving: Event.stopObserving
    3952 });
     4618    if (document.createEvent)
     4619      element.dispatchEvent(event);
     4620    else
     4621      element.fireEvent(event.eventType, event);
    39534622
    3954 Object.extend(document, {
    3955   fire:          Element.Methods.fire.methodize(),
    3956   observe:       Element.Methods.observe.methodize(),
    3957   stopObserving: Element.Methods.stopObserving.methodize()
    3958 });
     4623    return Event.extend(event);
     4624  }
    39594625
     4626
     4627  Object.extend(Event, Event.Methods);
     4628
     4629  Object.extend(Event, {
     4630    fire:          fire,
     4631    observe:       observe,
     4632    stopObserving: stopObserving
     4633  });
     4634
     4635  Element.addMethods({
     4636    fire:          fire,
     4637
     4638    observe:       observe,
     4639
     4640    stopObserving: stopObserving
     4641  });
     4642
     4643  Object.extend(document, {
     4644    fire:          fire.methodize(),
     4645
     4646    observe:       observe.methodize(),
     4647
     4648    stopObserving: stopObserving.methodize(),
     4649
     4650    loaded:        false
     4651  });
     4652
     4653  if (window.Event) Object.extend(window.Event, Event);
     4654  else window.Event = Event;
     4655})();
     4656
    39604657(function() {
    39614658  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
    3962      Matthias Miller, Dean Edwards and John Resig. */
     4659     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
    39634660
    3964   var timer, fired = false;
     4661  var timer;
    39654662
    39664663  function fireContentLoadedEvent() {
    3967     if (fired) return;
    3968     if (timer) window.clearInterval(timer);
    3969     document.fire("dom:loaded");
    3970     fired = true;
     4664    if (document.loaded) return;
     4665    if (timer) window.clearTimeout(timer);
     4666    document.loaded = true;
     4667    document.fire('dom:loaded');
    39714668  }
    39724669
    3973   if (document.addEventListener) {
    3974     if (Prototype.Browser.WebKit) {
    3975       timer = window.setInterval(function() {
    3976         if (/loaded|complete/.test(document.readyState))
    3977           fireContentLoadedEvent();
    3978       }, 0);
     4670  function checkReadyState() {
     4671    if (document.readyState === 'complete') {
     4672      document.stopObserving('readystatechange', checkReadyState);
     4673      fireContentLoadedEvent();
     4674    }
     4675  }
    39794676
    3980       Event.observe(window, "load", fireContentLoadedEvent);
    3981 
    3982     } else {
    3983       document.addEventListener("DOMContentLoaded",
    3984         fireContentLoadedEvent, false);
     4677  function pollDoScroll() {
     4678    try { document.documentElement.doScroll('left'); }
     4679    catch(e) {
     4680      timer = pollDoScroll.defer();
     4681      return;
    39854682    }
     4683    fireContentLoadedEvent();
     4684  }
    39864685
     4686  if (document.addEventListener) {
     4687    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
    39874688  } else {
    3988     document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    3989     $("__onDOMContentLoaded").onreadystatechange = function() {
    3990       if (this.readyState == "complete") {
    3991         this.onreadystatechange = null;
    3992         fireContentLoadedEvent();
    3993       }
    3994     };
     4689    document.observe('readystatechange', checkReadyState);
     4690    if (window == top)
     4691      timer = pollDoScroll.defer();
    39954692  }
     4693
     4694  Event.observe(window, 'load', fireContentLoadedEvent);
    39964695})();
     4696
     4697Element.addMethods();
     4698
    39974699/*------------------------------- DEPRECATED -------------------------------*/
    39984700
    39994701Hash.toQueryString = Object.toQueryString;
     
    40224724
    40234725var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
    40244726
    4025 // This should be moved to script.aculo.us; notice the deprecated methods
    4026 // further below, that map to the newer Element methods.
    40274727var Position = {
    4028   // set to true if needed, warning: firefox performance problems
    4029   // NOT neeeded for page scrolling, only if draggable contained in
    4030   // scrollable elements
    40314728  includeScrollOffsets: false,
    40324729
    4033   // must be called before calling withinIncludingScrolloffset, every time the
    4034   // page is scrolled
    40354730  prepare: function() {
    40364731    this.deltaX =  window.pageXOffset
    40374732                || document.documentElement.scrollLeft
     
    40434738                || 0;
    40444739  },
    40454740
    4046   // caches x/y coordinate pair to use with overlap
    40474741  within: function(element, x, y) {
    40484742    if (this.includeScrollOffsets)
    40494743      return this.withinIncludingScrolloffsets(element, x, y);
     
    40704764            this.xcomp <  this.offset[0] + element.offsetWidth);
    40714765  },
    40724766
    4073   // within must be called directly before
    40744767  overlap: function(mode, element) {
    40754768    if (!mode) return 0;
    40764769    if (mode == 'vertical')
     
    40814774        element.offsetWidth;
    40824775  },
    40834776
    4084   // Deprecation layer -- use newer Element methods now (1.5.2).
    40854777
    40864778  cumulativeOffset: Element.Methods.cumulativeOffset,
    40874779
     
    41804872Object.extend(Element.ClassNames.prototype, Enumerable);
    41814873
    41824874/*--------------------------------------------------------------------------*/
    4183 
    4184 Element.addMethods();
    4185  No newline at end of file