Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 8610

Show
Ignore:
Timestamp:
01/09/08 17:05:29 (6 months ago)
Author:
madrobby
Message:

javascript_test: update prototype/unittest.js to latest 1.6.0.1 versions, generate public/javascripts/name.js file when using the generator, add option to use application-provided prototype.js file instead of the one that comes with the pluging. Fixes #10662, #10667, #10669, #10694, #10697 and #10732.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/javascript_test/assets/prototype.js

    r5058 r8610  
    1 /*  Prototype JavaScript framework, version 1.5.0_rc
    2  *  (c) 2005 Sam Stephenson <sam@conio.net> 
     1/*  Prototype JavaScript framework, version 1.6.0.
     2 *  (c) 2005-2007 Sam Stephenson 
    33 * 
    44 *  Prototype is freely distributable under the terms of an MIT-style license. 
    5  *  For details, see the Prototype web site: http://prototype.conio.net
     5 *  For details, see the Prototype web site: http://www.prototypejs.org
    66 * 
    7 /*--------------------------------------------------------------------------*/ 
     7 *--------------------------------------------------------------------------*/ 
    88 
    99var Prototype = { 
    10   Version: '1.5.0_rc1', 
    11   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', 
    12  
    13   emptyFunction: function() {}, 
    14   K: function(x) {return x} 
    15 
    16  
     10  Version: '1.6.0.1', 
     11 
     12  Browser: { 
     13    IE:     !!(window.attachEvent && !window.opera), 
     14    Opera:  !!window.opera, 
     15    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, 
     16    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, 
     17    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) 
     18  }, 
     19 
     20  BrowserFeatures: { 
     21    XPath: !!document.evaluate, 
     22    ElementExtensions: !!window.HTMLElement, 
     23    SpecificElementExtensions: 
     24      document.createElement('div').__proto__ && 
     25      document.createElement('div').__proto__ !== 
     26        document.createElement('form').__proto__ 
     27  }, 
     28 
     29  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', 
     30  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, 
     31 
     32  emptyFunction: function() { }, 
     33  K: function(x) { return x } 
     34}; 
     35 
     36if (Prototype.Browser.MobileSafari) 
     37  Prototype.BrowserFeatures.SpecificElementExtensions = false; 
     38 
     39 
     40/* Based on Alex Arnell's inheritance implementation. */ 
    1741var Class = { 
    1842  create: function() { 
    19     return function() { 
     43    var parent = null, properties = $A(arguments); 
     44    if (Object.isFunction(properties[0])) 
     45      parent = properties.shift(); 
     46 
     47    function klass() { 
    2048      this.initialize.apply(this, arguments); 
    2149    } 
    22   } 
    23 
    24  
    25 var Abstract = new Object(); 
     50 
     51    Object.extend(klass, Class.Methods); 
     52    klass.superclass = parent; 
     53    klass.subclasses = []; 
     54 
     55    if (parent) { 
     56      var subclass = function() { }; 
     57      subclass.prototype = parent.prototype; 
     58      klass.prototype = new subclass; 
     59      parent.subclasses.push(klass); 
     60    } 
     61 
     62    for (var i = 0; i < properties.length; i++) 
     63      klass.addMethods(properties[i]); 
     64 
     65    if (!klass.prototype.initialize) 
     66      klass.prototype.initialize = Prototype.emptyFunction; 
     67 
     68    klass.prototype.constructor = klass; 
     69 
     70    return klass; 
     71  } 
     72}; 
     73 
     74Class.Methods = { 
     75  addMethods: function(source) { 
     76    var ancestor   = this.superclass && this.superclass.prototype; 
     77    var properties = Object.keys(source); 
     78 
     79    if (!Object.keys({ toString: true }).length) 
     80      properties.push("toString", "valueOf"); 
     81 
     82    for (var i = 0, length = properties.length; i < length; i++) { 
     83      var property = properties[i], value = source[property]; 
     84      if (ancestor && Object.isFunction(value) && 
     85          value.argumentNames().first() == "$super") { 
     86        var method = value, value = Object.extend((function(m) { 
     87          return function() { return ancestor[m].apply(this, arguments) }; 
     88        })(property).wrap(method), { 
     89          valueOf:  function() { return method }, 
     90          toString: function() { return method.toString() } 
     91        }); 
     92      } 
     93      this.prototype[property] = value; 
     94    } 
     95 
     96    return this; 
     97  } 
     98}; 
     99 
     100var Abstract = { }; 
    26101 
    27102Object.extend = function(destination, source) { 
    28   for (var property in source) { 
     103  for (var property in source) 
    29104    destination[property] = source[property]; 
    30   } 
    31105  return destination; 
    32 } 
     106}; 
    33107 
    34108Object.extend(Object, { 
    35109  inspect: function(object) { 
    36110    try { 
    37       if (object == undefined) return 'undefined'; 
    38       if (object == null) return 'null'; 
     111      if (Object.isUndefined(object)) return 'undefined'; 
     112      if (object === null) return 'null'; 
    39113      return object.inspect ? object.inspect() : object.toString(); 
    40114    } catch (e) { 
     
    42116      throw e; 
    43117    } 
     118  }, 
     119 
     120  toJSON: function(object) { 
     121    var type = typeof object; 
     122    switch (type) { 
     123      case 'undefined': 
     124      case 'function': 
     125      case 'unknown': return; 
     126      case 'boolean': return object.toString(); 
     127    } 
     128 
     129    if (object === null) return 'null'; 
     130    if (object.toJSON) return object.toJSON(); 
     131    if (Object.isElement(object)) return; 
     132 
     133    var results = []; 
     134    for (var property in object) { 
     135      var value = Object.toJSON(object[property]); 
     136      if (!Object.isUndefined(value)) 
     137        results.push(property.toJSON() + ': ' + value); 
     138    } 
     139 
     140    return '{' + results.join(', ') + '}'; 
     141  }, 
     142 
     143  toQueryString: function(object) { 
     144    return $H(object).toQueryString(); 
     145  }, 
     146 
     147  toHTML: function(object) { 
     148    return object && object.toHTML ? object.toHTML() : String.interpret(object); 
    44149  }, 
    45150 
     
    59164 
    60165  clone: function(object) { 
    61     return Object.extend({}, object); 
     166    return Object.extend({ }, object); 
     167  }, 
     168 
     169  isElement: function(object) { 
     170    return object && object.nodeType == 1; 
     171  }, 
     172 
     173  isArray: function(object) { 
     174    return object && object.constructor === Array; 
     175  }, 
     176 
     177  isHash: function(object) { 
     178    return object instanceof Hash; 
     179  }, 
     180 
     181  isFunction: function(object) { 
     182    return typeof object == "function"; 
     183  }, 
     184 
     185  isString: function(object) { 
     186    return typeof object == "string"; 
     187  }, 
     188 
     189  isNumber: function(object) { 
     190    return typeof object == "number"; 
     191  }, 
     192 
     193  isUndefined: function(object) { 
     194    return typeof object == "undefined"; 
    62195  } 
    63196}); 
    64197 
    65 Function.prototype.bind = function() { 
    66   var __method = this, args = $A(arguments), object = args.shift(); 
    67   return function() { 
    68     return __method.apply(object, args.concat($A(arguments))); 
    69   } 
    70 
    71  
    72 Function.prototype.bindAsEventListener = function(object) { 
    73   var __method = this, args = $A(arguments), object = args.shift(); 
    74   return function(event) { 
    75     return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); 
    76   } 
    77 
    78  
    79 Object.extend(Number.prototype, { 
    80   toColorPart: function() { 
    81     var digits = this.toString(16); 
    82     if (this < 16) return '0' + digits; 
    83     return digits; 
    84   }, 
    85  
    86   succ: function() { 
    87     return this + 1; 
    88   }, 
    89  
    90   times: function(iterator) { 
    91     $R(0, this, true).each(iterator); 
    92     return this; 
     198Object.extend(Function.prototype, { 
     199  argumentNames: function() { 
     200    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); 
     201    return names.length == 1 && !names[0] ? [] : names; 
     202  }, 
     203 
     204  bind: function() { 
     205    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; 
     206    var __method = this, args = $A(arguments), object = args.shift(); 
     207    return function() { 
     208      return __method.apply(object, args.concat($A(arguments))); 
     209    } 
     210  }, 
     211 
     212  bindAsEventListener: function() { 
     213    var __method = this, args = $A(arguments), object = args.shift(); 
     214    return function(event) { 
     215      return __method.apply(object, [event || window.event].concat(args)); 
     216    } 
     217  }, 
     218 
     219  curry: function() { 
     220    if (!arguments.length) return this; 
     221    var __method = this, args = $A(arguments); 
     222    return function() { 
     223      return __method.apply(this, args.concat($A(arguments))); 
     224    } 
     225  }, 
     226 
     227  delay: function() { 
     228    var __method = this, args = $A(arguments), timeout = args.shift() * 1000; 
     229    return window.setTimeout(function() { 
     230      return __method.apply(__method, args); 
     231    }, timeout); 
     232  }, 
     233 
     234  wrap: function(wrapper) { 
     235    var __method = this; 
     236    return function() { 
     237      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); 
     238    } 
     239  }, 
     240 
     241  methodize: function() { 
     242    if (this._methodized) return this._methodized; 
     243    var __method = this; 
     244    return this._methodized = function() { 
     245      return __method.apply(null, [this].concat($A(arguments))); 
     246    }; 
    93247  } 
    94248}); 
     249 
     250Function.prototype.defer = Function.prototype.delay.curry(0.01); 
     251 
     252Date.prototype.toJSON = function() { 
     253  return '"' + this.getUTCFullYear() + '-' + 
     254    (this.getUTCMonth() + 1).toPaddedString(2) + '-' + 
     255    this.getUTCDate().toPaddedString(2) + 'T' + 
     256    this.getUTCHours().toPaddedString(2) + ':' + 
     257    this.getUTCMinutes().toPaddedString(2) + ':' + 
     258    this.getUTCSeconds().toPaddedString(2) + 'Z"'; 
     259}; 
    95260 
    96261var Try = { 
     
    98263    var returnValue; 
    99264 
    100     for (var i = 0; i < arguments.length; i++) { 
     265    for (var i = 0, length = arguments.length; i < length; i++) { 
    101266      var lambda = arguments[i]; 
    102267      try { 
    103268        returnValue = lambda(); 
    104269        break; 
    105       } catch (e) {
     270      } catch (e) {
    106271    } 
    107272 
    108273    return returnValue; 
    109274  } 
    110 
     275}; 
     276 
     277RegExp.prototype.match = RegExp.prototype.test; 
     278 
     279RegExp.escape = function(str) { 
     280  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 
     281}; 
    111282 
    112283/*--------------------------------------------------------------------------*/ 
    113284 
    114 var PeriodicalExecuter = Class.create(); 
    115 PeriodicalExecuter.prototype = { 
     285var PeriodicalExecuter = Class.create({ 
    116286  initialize: function(callback, frequency) { 
    117287    this.callback = callback; 
     
    126296  }, 
    127297 
     298  execute: function() { 
     299    this.callback(this); 
     300  }, 
     301 
    128302  stop: function() { 
    129303    if (!this.timer) return; 
     
    136310      try { 
    137311        this.currentlyExecuting = true; 
    138         this.callback(this); 
     312        this.execute(); 
    139313      } finally { 
    140314        this.currentlyExecuting = false; 
     
    142316    } 
    143317  } 
    144 
     318}); 
     319Object.extend(String, { 
     320  interpret: function(value) { 
     321    return value == null ? '' : String(value); 
     322  }, 
     323  specialChar: { 
     324    '\b': '\\b', 
     325    '\t': '\\t', 
     326    '\n': '\\n', 
     327    '\f': '\\f', 
     328    '\r': '\\r', 
     329    '\\': '\\\\' 
     330  } 
     331}); 
     332 
    145333Object.extend(String.prototype, { 
    146334  gsub: function(pattern, replacement) { 
     
    151339      if (match = source.match(pattern)) { 
    152340        result += source.slice(0, match.index); 
    153         result += (replacement(match) || '').toString(); 
     341        result += String.interpret(replacement(match)); 
    154342        source  = source.slice(match.index + match[0].length); 
    155343      } else { 
     
    162350  sub: function(pattern, replacement, count) { 
    163351    replacement = this.gsub.prepareReplacement(replacement); 
    164     count = count === undefined ? 1 : count; 
     352    count = Object.isUndefined(count) ? 1 : count; 
    165353 
    166354    return this.gsub(pattern, function(match) { 
     
    172360  scan: function(pattern, iterator) { 
    173361    this.gsub(pattern, iterator); 
    174     return this
     362    return String(this)
    175363  }, 
    176364 
    177365  truncate: function(length, truncation) { 
    178366    length = length || 30; 
    179     truncation = truncation === undefined ? '...' : truncation; 
     367    truncation = Object.isUndefined(truncation) ? '...' : truncation; 
    180368    return this.length > length ? 
    181       this.slice(0, length - truncation.length) + truncation : this
     369      this.slice(0, length - truncation.length) + truncation : String(this)
    182370  }, 
    183371 
     
    207395 
    208396  escapeHTML: function() { 
    209     var div = document.createElement('div'); 
    210     var text = document.createTextNode(this); 
    211     div.appendChild(text); 
    212     return div.innerHTML; 
     397    var self = arguments.callee; 
     398    self.text.data = this; 
     399    return self.div.innerHTML; 
    213400  }, 
    214401 
    215402  unescapeHTML: function() { 
    216     var div = document.createElement('div'); 
     403    var div = new Element('div'); 
    217404    div.innerHTML = this.stripTags(); 
    218     return div.childNodes[0] ? div.childNodes[0].nodeValue : ''; 
    219   }, 
    220  
    221   toQueryParams: function() { 
    222     var pairs = this.match(/^\??(.*)$/)[1].split('&'); 
    223     return pairs.inject({}, function(params, pairString) { 
    224       var pair  = pairString.split('='); 
    225       var value = pair[1] ? decodeURIComponent(pair[1]) : undefined; 
    226       params[decodeURIComponent(pair[0])] = value; 
    227       return params; 
     405    return div.childNodes[0] ? (div.childNodes.length > 1 ? 
     406      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : 
     407      div.childNodes[0].nodeValue) : ''; 
     408  }, 
     409 
     410  toQueryParams: function(separator) { 
     411    var match = this.strip().match(/([^?#]*)(#.*)?$/); 
     412    if (!match) return { }; 
     413 
     414    return match[1].split(separator || '&').inject({ }, function(hash, pair) { 
     415      if ((pair = pair.split('='))[0]) { 
     416        var key = decodeURIComponent(pair.shift()); 
     417        var value = pair.length > 1 ? pair.join('=') : pair[0]; 
     418        if (value != undefined) value = decodeURIComponent(value); 
     419 
     420        if (key in hash) { 
     421          if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; 
     422          hash[key].push(value); 
     423        } 
     424        else hash[key] = value; 
     425      } 
     426      return hash; 
    228427    }); 
    229428  }, 
     
    233432  }, 
    234433 
     434  succ: function() { 
     435    return this.slice(0, this.length - 1) + 
     436      String.fromCharCode(this.charCodeAt(this.length - 1) + 1); 
     437  }, 
     438 
     439  times: function(count) { 
     440    return count < 1 ? '' : new Array(count + 1).join(this); 
     441  }, 
     442 
    235443  camelize: function() { 
    236     var oStringList = this.split('-'); 
    237     if (oStringList.length == 1) return oStringList[0]; 
    238  
    239     var camelizedString = this.indexOf('-') == 0 
    240       ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1) 
    241       : oStringList[0]; 
    242  
    243     for (var i = 1, len = oStringList.length; i < len; i++) { 
    244       var s = oStringList[i]; 
    245       camelizedString += s.charAt(0).toUpperCase() + s.substring(1); 
    246     } 
    247  
    248     return camelizedString; 
     444    var parts = this.split('-'), len = parts.length; 
     445    if (len == 1) return parts[0]; 
     446 
     447    var camelized = this.charAt(0) == '-' 
     448      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) 
     449      : parts[0]; 
     450 
     451    for (var i = 1; i < len; i++) 
     452      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); 
     453 
     454    return camelized; 
     455  }, 
     456 
     457  capitalize: function() { 
     458    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); 
     459  }, 
     460 
     461  underscore: function() { 
     462    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); 
     463  }, 
     464 
     465  dasherize: function() { 
     466    return this.gsub(/_/,'-'); 
    249467  }, 
    250468 
    251469  inspect: function(useDoubleQuotes) { 
    252     var escapedString = this.replace(/\\/g, '\\\\'); 
    253     if (useDoubleQuotes) 
    254       return '"' + escapedString.replace(/"/g, '\\"') + '"'; 
    255     else 
    256       return "'" + escapedString.replace(/'/g, '\\\'') + "'"; 
     470    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { 
     471      var character = String.specialChar[match[0]]; 
     472      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); 
     473    }); 
     474    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; 
     475    return "'" + escapedString.replace(/'/g, '\\\'') + "'"; 
     476  }, 
     477 
     478  toJSON: function() { 
     479    return this.inspect(true); 
     480  }, 
     481 
     482  unfilterJSON: function(filter) { 
     483    return this.sub(filter || Prototype.JSONFilter, '#{1}'); 
     484  }, 
     485 
     486  isJSON: function() { 
     487    var str = this; 
     488    if (str.blank()) return false; 
     489    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); 
     490    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); 
     491  }, 
     492 
     493  evalJSON: function(sanitize) { 
     494    var json = this.unfilterJSON(); 
     495    try { 
     496      if (!sanitize || json.isJSON()) return eval('(' + json + ')'); 
     497    } catch (e) { } 
     498    throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); 
     499  }, 
     500 
     501  include: function(pattern) { 
     502    return this.indexOf(pattern) > -1; 
     503  }, 
     504 
     505  startsWith: function(pattern) { 
     506    return this.indexOf(pattern) === 0; 
     507  }, 
     508 
     509  endsWith: function(pattern) { 
     510    var d = this.length - pattern.length; 
     511    return d >= 0 && this.lastIndexOf(pattern) === d; 
     512  }, 
     513 
     514  empty: function() { 
     515    return this == ''; 
     516  }, 
     517 
     518  blank: function() { 
     519    return /^\s*$/.test(this); 
     520  }, 
     521 
     522  interpolate: function(object, pattern) { 
     523    return new Template(this, pattern).evaluate(object); 
    257524  } 
    258525}); 
    259526 
     527if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { 
     528  escapeHTML: function() { 
     529    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); 
     530  }, 
     531  unescapeHTML: function() { 
     532    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>'); 
     533  } 
     534}); 
     535 
    260536String.prototype.gsub.prepareReplacement = function(replacement) { 
    261   if (typeof replacement == 'function') return replacement; 
     537  if (Object.isFunction(replacement)) return replacement; 
    262538  var template = new Template(replacement); 
    263539  return function(match) { return template.evaluate(match) }; 
    264 } 
     540}; 
    265541 
    266542String.prototype.parseQuery = String.prototype.toQueryParams; 
    267543 
    268 var Template = Class.create(); 
    269 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
    270 Template.prototype = { 
     544Object.extend(String.prototype.escapeHTML, { 
     545  div:  document.createElement('div'), 
     546  text: document.createTextNode('') 
     547}); 
     548 
     549with (String.prototype.escapeHTML) div.appendChild(text); 
     550 
     551var Template = Class.create({ 
    271552  initialize: function(template, pattern) { 
    272553    this.template = template.toString(); 
    273     this.pattern = pattern || Template.Pattern; 
     554    this.pattern = pattern || Template.Pattern; 
    274555  }, 
    275556 
    276557  evaluate: function(object) { 
     558    if (Object.isFunction(object.toTemplateReplacements)) 
     559      object = object.toTemplateReplacements(); 
     560 
    277561    return this.template.gsub(this.pattern, function(match) { 
    278       var before = match[1]; 
     562      if (object == null) return ''; 
     563 
     564      var before = match[1] || ''; 
    279565      if (before == '\\') return match[2]; 
    280       return before + (object[match[3]] || '').toString(); 
    281     }); 
    282   } 
    283 
    284  
    285 var $break    = new Object(); 
    286 var $continue = new Object(); 
     566 
     567      var ctx = object, expr = match[3]; 
     568      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; 
     569      match = pattern.exec(expr); 
     570      if (match == null) return before; 
     571 
     572      while (match != null) { 
     573        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; 
     574        ctx = ctx[comp]; 
     575        if (null == ctx || '' == match[3]) break; 
     576        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); 
     577        match = pattern.exec(expr); 
     578      } 
     579 
     580      return before + String.interpret(ctx); 
     581    }.bind(this)); 
     582  } 
     583}); 
     584Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
     585 
     586var $break = { }; 
    287587 
    288588var Enumerable = { 
    289   each: function(iterator) { 
     589  each: function(iterator, context) { 
    290590    var index = 0; 
     591    iterator = iterator.bind(context); 
    291592    try { 
    292593      this._each(function(value) { 
    293         try { 
    294           iterator(value, index++); 
    295         } catch (e) { 
    296           if (e != $continue) throw e; 
    297         } 
     594        iterator(value, index++); 
    298595      }); 
    299596    } catch (e) { 
    300597      if (e != $break) throw e; 
    301598    } 
    302   }, 
    303  
    304   all: function(iterator) { 
     599    return this; 
     600  }, 
     601 
     602  eachSlice: function(number, iterator, context) { 
     603    iterator = iterator ? iterator.bind(context) : Prototype.K; 
     604    var index = -number, slices = [], array = this.toArray(); 
     605    while ((index += number) < array.length) 
     606      slices.push(array.slice(index, index+number)); 
     607    return slices.collect(iterator, context); 
     608  }, 
     609 
     610  all: function(iterator, context) { 
     611    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    305612    var result = true; 
    306613    this.each(function(value, index) { 
    307       result = result && !!(iterator || Prototype.K)(value, index); 
     614      result = result && !!iterator(value, index); 
    308615      if (!result) throw $break; 
    309616    }); 
     
    311618  }, 
    312619 
    313   any: function(iterator) { 
     620  any: function(iterator, context) { 
     621    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    314622    var result = false; 
    315623    this.each(function(value, index) { 
    316       if (result = !!(iterator || Prototype.K)(value, index)) 
     624      if (result = !!iterator(value, index)) 
    317625        throw $break; 
    318626    }); 
     
    320628  }, 
    321629 
    322   collect: function(iterator) { 
     630  collect: function(iterator, context) { 
     631    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    323632    var results = []; 
    324633    this.each(function(value, index) { 
     
    328637  }, 
    329638 
    330   detect: function (iterator) { 
     639  detect: function(iterator, context) { 
     640    iterator = iterator.bind(context); 
    331641    var result; 
    332642    this.each(function(value, index) { 
     
    339649  }, 
    340650 
    341   findAll: function(iterator) { 
     651  findAll: function(iterator, context) { 
     652    iterator = iterator.bind(context); 
    342653    var results = []; 
    343654    this.each(function(value, index) { 
     
    348659  }, 
    349660 
    350   grep: function(pattern, iterator) { 
     661  grep: function(filter, iterator, context) { 
     662    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    351663    var results = []; 
     664 
     665    if (Object.isString(filter)) 
     666      filter = new RegExp(filter); 
     667 
    352668    this.each(function(value, index) { 
    353       var stringValue = value.toString(); 
    354       if (stringValue.match(pattern)) 
    355         results.push((iterator || Prototype.K)(value, index)); 
    356     }) 
     669      if (filter.match(value)) 
     670        results.push(iterator(value, index)); 
     671    }); 
    357672    return results; 
    358673  }, 
    359674 
    360675  include: function(object) { 
     676    if (Object.isFunction(this.indexOf)) 
     677      if (this.indexOf(object) != -1) return true; 
     678 
    361679    var found = false; 
    362680    this.each(function(value) { 
     
    369687  }, 
    370688 
    371   inject: function(memo, iterator) { 
     689  inGroupsOf: function(number, fillWith) { 
     690    fillWith = Object.isUndefined(fillWith) ? null : fillWith; 
     691    return this.eachSlice(number, function(slice) { 
     692      while(slice.length < number) slice.push(fillWith); 
     693      return slice; 
     694    }); 
     695  }, 
     696 
     697  inject: function(memo, iterator, context) { 
     698    iterator = iterator.bind(context); 
    372699    this.each(function(value, index) { 
    373700      memo = iterator(memo, value, index); 
     
    378705  invoke: function(method) { 
    379706    var args = $A(arguments).slice(1); 
    380     return this.collect(function(value) { 
     707    return this.map(function(value) { 
    381708      return value[method].apply(value, args); 
    382709    }); 
    383710  }, 
    384711 
    385   max: function(iterator) { 
     712  max: function(iterator, context) { 
     713    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    386714    var result; 
    387715    this.each(function(value, index) { 
    388       value = (iterator || Prototype.K)(value, index); 
    389       if (result == undefined || value >= result) 
     716      value = iterator(value, index); 
     717      if (result == null || value >= result) 
    390718        result = value; 
    391719    }); 
     
    393721  }, 
    394722 
    395   min: function(iterator) { 
     723  min: function(iterator, context) { 
     724    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    396725    var result; 
    397726    this.each(function(value, index) { 
    398       value = (iterator || Prototype.K)(value, index); 
    399       if (result == undefined || value < result) 
     727      value = iterator(value, index); 
     728      if (result == null || value < result) 
    400729        result = value; 
    401730    }); 
     
    403732  }, 
    404733 
    405   partition: function(iterator) { 
     734  partition: function(iterator, context) { 
     735    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    406736    var trues = [], falses = []; 
    407737    this.each(function(value, index) { 
    408       ((iterator || Prototype.K)(value, index) ? 
     738      (iterator(value, index) ? 
    409739        trues : falses).push(value); 
    410740    }); 
     
    414744  pluck: function(property) { 
    415745    var results = []; 
    416     this.each(function(value, index) { 
     746    this.each(function(value) { 
    417747      results.push(value[property]); 
    418748    }); 
     
    420750  }, 
    421751 
    422   reject: function(iterator) { 
     752  reject: function(iterator, context) { 
     753    iterator = iterator.bind(context); 
    423754    var results = []; 
    424755    this.each(function(value, index) { 
     
    429760  }, 
    430761 
    431   sortBy: function(iterator) { 
    432     return this.collect(function(value, index) { 
     762  sortBy: function(iterator, context) { 
     763    iterator = iterator.bind(context); 
     764    return this.map(function(value, index) { 
    433765      return {value: value, criteria: iterator(value, index)}; 
    434766    }).sort(function(left, right) { 
     
    439771 
    440772  toArray: function() { 
    441     return this.collect(Prototype.K); 
     773    return this.map(); 
    442774  }, 
    443775 
    444776  zip: function() { 
    445777    var iterator = Prototype.K, args = $A(arguments); 
    446     if (typeof args.last() == 'function'
     778    if (Object.isFunction(args.last())
    447779      iterator = args.pop(); 
    448780 
     
    453785  }, 
    454786 
     787  size: function() { 
     788    return this.toArray().length; 
     789  }, 
     790 
    455791  inspect: function() { 
    456792    return '#<Enumerable:' + this.toArray().inspect() + '>'; 
    457793  } 
    458 } 
     794}; 
    459795 
    460796Object.extend(Enumerable, { 
     
    462798  find:    Enumerable.detect, 
    463799  select:  Enumerable.findAll, 
     800  filter:  Enumerable.findAll, 
    464801  member:  Enumerable.include, 
    465   entries: Enumerable.toArray 
     802  entries: Enumerable.toArray, 
     803  every:   Enumerable.all, 
     804  some:    Enumerable.any 
    466805}); 
    467 var $A = Array.from = function(iterable) { 
     806function $A(iterable) { 
    468807  if (!iterable) return []; 
    469   if (iterable.toArray) { 
    470     return iterable.toArray(); 
    471   } else { 
    472     var results = []; 
    473     for (var i = 0; i < iterable.length; i++) 
    474       results.push(iterable[i]); 
     808  if (iterable.toArray) return iterable.toArray(); 
     809  var length = iterable.length || 0, results = new Array(length); 
     810  while (length--) results[length] = iterable[length]; 
     811  return results; 
     812
     813 
     814if (Prototype.Browser.WebKit) { 
     815  function $A(iterable) { 
     816    if (!iterable) return []; 
     817    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && 
     818        iterable.toArray) return iterable.toArray(); 
     819    var length = iterable.length || 0, results = new Array(length); 
     820    while (length--) results[length] = iterable[length]; 
    475821    return results; 
    476822  } 
    477823} 
    478824 
     825Array.from = $A; 
     826 
    479827Object.extend(Array.prototype, Enumerable); 
    480828 
    481 if (!Array.prototype._reverse) 
    482   Array.prototype._reverse = Array.prototype.reverse; 
     829if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; 
    483830 
    484831Object.extend(Array.prototype, { 
    485832  _each: function(iterator) { 
    486     for (var i = 0; i < this.length; i++) 
     833    for (var i = 0, length = this.length; i < length; i++) 
    487834      iterator(this[i]); 
    488835  }, 
     
    503850  compact: function() { 
    504851    return this.select(function(value) { 
    505       return value != undefined || value != null; 
     852      return value != null; 
    506853    }); 
    507854  }, 
     
    509856  flatten: function() { 
    510857    return this.inject([], function(array, value) { 
    511       return array.concat(value && value.constructor == Array
     858      return array.concat(Object.isArray(value)
    512859        value.flatten() : [value]); 
    513860    }); 
     
    521868  }, 
    522869 
    523   indexOf: function(object) { 
    524     for (var i = 0; i < this.length; i++) 
    525       if (this[i] == object) return i; 
    526     return -1; 
    527   }, 
    528  
    529870  reverse: function(inline) { 
    530871    return (inline !== false ? this : this.toArray())._reverse(); 
     
    535876  }, 
    536877 
    537   uniq: function() { 
    538     return this.inject([], function(array, value) { 
    539       return array.include(value) ? array : array.concat([value]); 
     878  uniq: function(sorted) { 
     879    return this.inject([], function(array, value, index) { 
     880      if (0 == index || (sorted ? array.last() != value : !array.include(value))) 
     881        array.push(value); 
     882      return array; 
    540883    }); 
     884  }, 
     885 
     886  intersect: function(array) { 
     887    return this.uniq().findAll(function(item) { 
     888      return array.detect(function(value) { return item === value }); 
     889    }); 
     890  }, 
     891 
     892  clone: function() { 
     893    return [].concat(this); 
     894  }, 
     895 
     896  size: function() { 
     897    return this.length; 
    541898  }, 
    542899 
    543900  inspect: function() { 
    544901    return '[' + this.map(Object.inspect).join(', ') + ']'; 
     902  }, 
     903 
     904  toJSON: function() { 
     905    var results = []; 
     906    this.each(function(object) { 
     907      var value = Object.toJSON(object); 
     908      if (!Object.isUndefined(value)) results.push(value); 
     909    }); 
     910    return '[' + results.join(', ') + ']'; 
    545911  } 
    546912}); 
    547 var Hash = { 
    548   _each: function(iterator) { 
    549     for (var key in this) { 
    550       var value = this[key]; 
    551       if (typeof value == 'function') continue; 
    552  
    553       var pair = [key, value]; 
    554       pair.key = key; 
    555       pair.value = value; 
    556       iterator(pair); 
    557     } 
    558   }, 
    559  
    560   keys: function() { 
    561     return this.pluck('key'); 
    562   }, 
    563  
    564   values: function() { 
    565     return this.pluck('value'); 
    566   }, 
    567  
    568   merge: function(hash) { 
    569     return $H(hash).inject($H(this), function(mergedHash, pair) { 
    570       mergedHash[pair.key] = pair.value; 
    571       return mergedHash; 
    572     }); 
    573   }, 
    574  
    575   toQueryString: function() { 
    576     return this.map(function(pair) { 
    577       return pair.map(encodeURIComponent).join('='); 
    578     }).join('&'); 
    579   }, 
    580  
    581   inspect: function() { 
    582     return '#<Hash:{' + this.map(function(pair) { 
    583       return pair.map(Object.inspect).join(': '); 
    584     }).join(', ') + '}>'; 
    585   } 
     913 
     914// use native browser JS 1.6 implementation if available 
     915if (Object.isFunction(Array.prototype.forEach)) 
     916  Array.prototype._each = Array.prototype.forEach; 
     917 
     918if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { 
     919  i || (i = 0); 
     920  var length = this.length; 
     921  if (i < 0) i = length + i; 
     922  for (; i < length; i++) 
     923    if (this[i] === item) return i; 
     924  return -1; 
     925}; 
     926 
     927if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { 
     928  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; 
     929  var n = this.slice(0, i).reverse().indexOf(item); 
     930  return (n < 0) ? n : i - n - 1; 
     931}; 
     932 
     933Array.prototype.toArray = Array.prototype.clone; 
     934 
     935function $w(string) { 
     936  if (!Object.isString(string)) return []; 
     937  string = string.strip(); 
     938  return string ? string.split(/\s+/) : []; 
    586939} 
    587940 
     941if (Prototype.Browser.Opera){ 
     942  Array.prototype.concat = function() { 
     943    var array = []; 
     944    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); 
     945    for (var i = 0, length = arguments.length; i < length; i++) { 
     946      if (Object.isArray(arguments[i])) { 
     947        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) 
     948          array.push(arguments[i][j]); 
     949      } else { 
     950        array.push(arguments[i]); 
     951      } 
     952    } 
     953    return array; 
     954  }; 
     955} 
     956Object.extend(Number.prototype, { 
     957  toColorPart: function() { 
     958    return this.toPaddedString(2, 16); 
     959  }, 
     960 
     961  succ: function() { 
     962    return this + 1; 
     963  }, 
     964 
     965  times: function(iterator) { 
     966    $R(0, this, true).each(iterator); 
     967    return this; 
     968  }, 
     969 
     970  toPaddedString: function(length, radix) { 
     971    var string = this.toString(radix || 10); 
     972    return '0'.times(length - string.length) + string; 
     973  }, 
     974 
     975  toJSON: function() { 
     976    return isFinite(this) ? this.toString() : 'null'; 
     977  } 
     978}); 
     979 
     980$w('abs round ceil floor').each(function(method){ 
     981  Number.prototype[method] = Math[method].methodize(); 
     982}); 
    588983function $H(object) { 
    589   var hash = Object.extend({}, object || {}); 
    590   Object.extend(hash, Enumerable); 
    591   Object.extend(hash, Hash); 
    592   return hash; 
    593 
    594 ObjectRange = Class.create(); 
    595 Object.extend(ObjectRange.prototype, Enumerable); 
    596 Object.extend(ObjectRange.prototype, { 
     984  return new Hash(object); 
     985}; 
     986 
     987var Hash = Class.create(Enumerable, (function() { 
     988 
     989  function toQueryPair(key, value) { 
     990    if (Object.isUndefined(value)) return key; 
     991    return key + '=' + encodeURIComponent(String.interpret(value)); 
     992  } 
     993 
     994  return { 
     995    initialize: function(object) { 
     996      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); 
     997    }, 
     998 
     999    _each: function(iterator) { 
     1000      for (var key in this._object) { 
     1001        var value = this._object[key], pair = [key, value]; 
     1002        pair.key = key; 
     1003        pair.value = value; 
     1004        iterator(pair); 
     1005      } 
     1006    }, 
     1007 
     1008    set: function(key, value) { 
     1009      return this._object[key] = value; 
     1010    }, 
     1011 
     1012    get: function(key) { 
     1013      return this._object[key]; 
     1014    }, 
     1015 
     1016    unset: function(key) { 
     1017      var value = this._object[key]; 
     1018      delete this._object[key]; 
     1019      return value; 
     1020    }, 
     1021 
     1022    toObject: function() { 
     1023      return Object.clone(this._object); 
     1024    }, 
     1025 
     1026    keys: function() { 
     1027      return this.pluck('key'); 
     1028    }, 
     1029 
     1030    values: function() { 
     1031      return this.pluck('value'); 
     1032    }, 
     1033 
     1034    index: function(value) { 
     1035      var match = this.detect(function(pair) { 
     1036        return pair.value === value; 
     1037      }); 
     1038      return match && match.key; 
     1039    }, 
     1040 
     1041    merge: function(object) { 
     1042      return this.clone().update(object); 
     1043    }, 
     1044 
     1045    update: function(object) { 
     1046      return new Hash(object).inject(this, function(result, pair) { 
     1047        result.set(pair.key, pair.value); 
     1048        return result; 
     1049      }); 
     1050    }, 
     1051 
     1052    toQueryString: function() { 
     1053      return this.map(function(pair) { 
     1054        var key = encodeURIComponent(pair.key), values = pair.value; 
     1055 
     1056        if (values && typeof values == 'object') { 
     1057          if (Object.isArray(values))