Changeset 18


Ignore:
Timestamp:
10/07/08 15:50:43 (4 years ago)
Author:
krux
Message:

updated prototype version

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/prototype.js

    r2 r18  
    1 /*  Prototype JavaScript framework, version 1.5.0 
     1/*  Prototype JavaScript framework, version 1.6.0.1 
    22 *  (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', 
     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 
    1120  BrowserFeatures: { 
    12     XPath: !!document.evaluate 
    13   }, 
    14  
    15   ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)', 
    16   emptyFunction: function() {}, 
     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() { }, 
    1733  K: function(x) { return x } 
    18 } 
    19  
     34}; 
     35 
     36if (Prototype.Browser.MobileSafari) 
     37  Prototype.BrowserFeatures.SpecificElementExtensions = false; 
     38 
     39 
     40/* Based on Alex Arnell's inheritance implementation. */ 
    2041var Class = { 
    2142  create: function() { 
    22     return function() { 
     43    var parent = null, properties = $A(arguments); 
     44    if (Object.isFunction(properties[0])) 
     45      parent = properties.shift(); 
     46 
     47    function klass() { 
    2348      this.initialize.apply(this, arguments); 
    2449    } 
    25   } 
    26 } 
    27  
    28 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 = { }; 
    29101 
    30102Object.extend = function(destination, source) { 
    31   for (var property in source) { 
     103  for (var property in source) 
    32104    destination[property] = source[property]; 
    33   } 
    34105  return destination; 
    35 } 
     106}; 
    36107 
    37108Object.extend(Object, { 
    38109  inspect: function(object) { 
    39110    try { 
    40       if (object === undefined) return 'undefined'; 
     111      if (Object.isUndefined(object)) return 'undefined'; 
    41112      if (object === null) return 'null'; 
    42113      return object.inspect ? object.inspect() : object.toString(); 
     
    47118  }, 
    48119 
     120  toJSON: function(object) { 
     121    var type = typeof object; 
     122    switch (type) { 
     123      case 'undefined': 
     124      case 'function': 
     125      case 'unknown': return; 
     126      case 'boolean': return object.toString(); 
     127    } 
     128 
     129    if (object === null) return 'null'; 
     130    if (object.toJSON) return object.toJSON(); 
     131    if (Object.isElement(object)) return; 
     132 
     133    var results = []; 
     134    for (var property in object) { 
     135      var value = Object.toJSON(object[property]); 
     136      if (!Object.isUndefined(value)) 
     137        results.push(property.toJSON() + ': ' + value); 
     138    } 
     139 
     140    return '{' + results.join(', ') + '}'; 
     141  }, 
     142 
     143  toQueryString: function(object) { 
     144    return $H(object).toQueryString(); 
     145  }, 
     146 
     147  toHTML: function(object) { 
     148    return object && object.toHTML ? object.toHTML() : String.interpret(object); 
     149  }, 
     150 
    49151  keys: function(object) { 
    50152    var keys = []; 
     
    62164 
    63165  clone: function(object) { 
    64     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"; 
    65195  } 
    66196}); 
    67197 
    68 Function.prototype.bind = function() { 
    69   var __method = this, args = $A(arguments), object = args.shift(); 
    70   return function() { 
    71     return __method.apply(object, args.concat($A(arguments))); 
    72   } 
    73 } 
    74  
    75 Function.prototype.bindAsEventListener = function(object) { 
    76   var __method = this, args = $A(arguments), object = args.shift(); 
    77   return function(event) { 
    78     return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); 
    79   } 
    80 } 
    81  
    82 Object.extend(Number.prototype, { 
    83   toColorPart: function() { 
    84     var digits = this.toString(16); 
    85     if (this < 16) return '0' + digits; 
    86     return digits; 
    87   }, 
    88  
    89   succ: function() { 
    90     return this + 1; 
    91   }, 
    92  
    93   times: function(iterator) { 
    94     $R(0, this, true).each(iterator); 
    95     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    }; 
    96247  } 
    97248}); 
     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}; 
    98260 
    99261var Try = { 
     
    106268        returnValue = lambda(); 
    107269        break; 
    108       } catch (e) {} 
     270      } catch (e) { } 
    109271    } 
    110272 
    111273    return returnValue; 
    112274  } 
    113 } 
     275}; 
     276 
     277RegExp.prototype.match = RegExp.prototype.test; 
     278 
     279RegExp.escape = function(str) { 
     280  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); 
     281}; 
    114282 
    115283/*--------------------------------------------------------------------------*/ 
    116284 
    117 var PeriodicalExecuter = Class.create(); 
    118 PeriodicalExecuter.prototype = { 
     285var PeriodicalExecuter = Class.create({ 
    119286  initialize: function(callback, frequency) { 
    120287    this.callback = callback; 
     
    129296  }, 
    130297 
     298  execute: function() { 
     299    this.callback(this); 
     300  }, 
     301 
    131302  stop: function() { 
    132303    if (!this.timer) return; 
     
    139310      try { 
    140311        this.currentlyExecuting = true; 
    141         this.callback(this); 
     312        this.execute(); 
    142313      } finally { 
    143314        this.currentlyExecuting = false; 
     
    145316    } 
    146317  } 
    147 } 
    148 String.interpret = function(value){ 
    149   return value == null ? '' : String(value); 
    150 } 
     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}); 
    151332 
    152333Object.extend(String.prototype, { 
     
    169350  sub: function(pattern, replacement, count) { 
    170351    replacement = this.gsub.prepareReplacement(replacement); 
    171     count = count === undefined ? 1 : count; 
     352    count = Object.isUndefined(count) ? 1 : count; 
    172353 
    173354    return this.gsub(pattern, function(match) { 
     
    179360  scan: function(pattern, iterator) { 
    180361    this.gsub(pattern, iterator); 
    181     return this; 
     362    return String(this); 
    182363  }, 
    183364 
    184365  truncate: function(length, truncation) { 
    185366    length = length || 30; 
    186     truncation = truncation === undefined ? '...' : truncation; 
     367    truncation = Object.isUndefined(truncation) ? '...' : truncation; 
    187368    return this.length > length ? 
    188       this.slice(0, length - truncation.length) + truncation : this; 
     369      this.slice(0, length - truncation.length) + truncation : String(this); 
    189370  }, 
    190371 
     
    214395 
    215396  escapeHTML: function() { 
    216     var div = document.createElement('div'); 
    217     var text = document.createTextNode(this); 
    218     div.appendChild(text); 
    219     return div.innerHTML; 
     397    var self = arguments.callee; 
     398    self.text.data = this; 
     399    return self.div.innerHTML; 
    220400  }, 
    221401 
    222402  unescapeHTML: function() { 
    223     var div = document.createElement('div'); 
     403    var div = new Element('div'); 
    224404    div.innerHTML = this.stripTags(); 
    225405    return div.childNodes[0] ? (div.childNodes.length > 1 ? 
    226       $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) : 
     406      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : 
    227407      div.childNodes[0].nodeValue) : ''; 
    228408  }, 
     
    230410  toQueryParams: function(separator) { 
    231411    var match = this.strip().match(/([^?#]*)(#.*)?$/); 
    232     if (!match) return {}; 
    233  
    234     return match[1].split(separator || '&').inject({}, function(hash, pair) { 
     412    if (!match) return { }; 
     413 
     414    return match[1].split(separator || '&').inject({ }, function(hash, pair) { 
    235415      if ((pair = pair.split('='))[0]) { 
    236         var name = decodeURIComponent(pair[0]); 
    237         var value = pair[1] ? decodeURIComponent(pair[1]) : undefined; 
    238  
    239         if (hash[name] !== undefined) { 
    240           if (hash[name].constructor != Array) 
    241             hash[name] = [hash[name]]; 
    242           if (value) hash[name].push(value); 
     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); 
    243423        } 
    244         else hash[name] = value; 
     424        else hash[key] = value; 
    245425      } 
    246426      return hash; 
     
    257437  }, 
    258438 
     439  times: function(count) { 
     440    return count < 1 ? '' : new Array(count + 1).join(this); 
     441  }, 
     442 
    259443  camelize: function() { 
    260444    var parts = this.split('-'), len = parts.length; 
     
    271455  }, 
    272456 
    273   capitalize: function(){ 
     457  capitalize: function() { 
    274458    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); 
    275459  }, 
     
    284468 
    285469  inspect: function(useDoubleQuotes) { 
    286     var escapedString = this.replace(/\\/g, '\\\\'); 
    287     if (useDoubleQuotes) 
    288       return '"' + escapedString.replace(/"/g, '\\"') + '"'; 
    289     else 
    290       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); 
    291524  } 
    292525}); 
    293526 
     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 
    294536String.prototype.gsub.prepareReplacement = function(replacement) { 
    295   if (typeof replacement == 'function') return replacement; 
     537  if (Object.isFunction(replacement)) return replacement; 
    296538  var template = new Template(replacement); 
    297539  return function(match) { return template.evaluate(match) }; 
    298 } 
     540}; 
    299541 
    300542String.prototype.parseQuery = String.prototype.toQueryParams; 
    301543 
    302 var Template = Class.create(); 
    303 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; 
    304 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({ 
    305552  initialize: function(template, pattern) { 
    306553    this.template = template.toString(); 
    307     this.pattern  = pattern || Template.Pattern; 
     554    this.pattern = pattern || Template.Pattern; 
    308555  }, 
    309556 
    310557  evaluate: function(object) { 
     558    if (Object.isFunction(object.toTemplateReplacements)) 
     559      object = object.toTemplateReplacements(); 
     560 
    311561    return this.template.gsub(this.pattern, function(match) { 
    312       var before = match[1]; 
     562      if (object == null) return ''; 
     563 
     564      var before = match[1] || ''; 
    313565      if (before == '\\') return match[2]; 
    314       return before + String.interpret(object[match[3]]); 
    315     }); 
    316   } 
    317 } 
    318  
    319 var $break    = new Object(); 
    320 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 = { }; 
    321587 
    322588var Enumerable = { 
    323   each: function(iterator) { 
     589  each: function(iterator, context) { 
    324590    var index = 0; 
     591    iterator = iterator.bind(context); 
    325592    try { 
    326593      this._each(function(value) { 
    327         try { 
    328           iterator(value, index++); 
    329         } catch (e) { 
    330           if (e != $continue) throw e; 
    331         } 
     594        iterator(value, index++); 
    332595      }); 
    333596    } catch (e) { 
     
    337600  }, 
    338601 
    339   eachSlice: function(number, iterator) { 
     602  eachSlice: function(number, iterator, context) { 
     603    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    340604    var index = -number, slices = [], array = this.toArray(); 
    341605    while ((index += number) < array.length) 
    342606      slices.push(array.slice(index, index+number)); 
    343     return slices.map(iterator); 
    344   }, 
    345  
    346   all: function(iterator) { 
     607    return slices.collect(iterator, context); 
     608  }, 
     609 
     610  all: function(iterator, context) { 
     611    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    347612    var result = true; 
    348613    this.each(function(value, index) { 
    349       result = result && !!(iterator || Prototype.K)(value, index); 
     614      result = result && !!iterator(value, index); 
    350615      if (!result) throw $break; 
    351616    }); 
     
    353618  }, 
    354619 
    355   any: function(iterator) { 
     620  any: function(iterator, context) { 
     621    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    356622    var result = false; 
    357623    this.each(function(value, index) { 
    358       if (result = !!(iterator || Prototype.K)(value, index)) 
     624      if (result = !!iterator(value, index)) 
    359625        throw $break; 
    360626    }); 
     
    362628  }, 
    363629 
    364   collect: function(iterator) { 
     630  collect: function(iterator, context) { 
     631    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    365632    var results = []; 
    366633    this.each(function(value, index) { 
    367       results.push((iterator || Prototype.K)(value, index)); 
     634      results.push(iterator(value, index)); 
    368635    }); 
    369636    return results; 
    370637  }, 
    371638 
    372   detect: function(iterator) { 
     639  detect: function(iterator, context) { 
     640    iterator = iterator.bind(context); 
    373641    var result; 
    374642    this.each(function(value, index) { 
     
    381649  }, 
    382650 
    383   findAll: function(iterator) { 
     651  findAll: function(iterator, context) { 
     652    iterator = iterator.bind(context); 
    384653    var results = []; 
    385654    this.each(function(value, index) { 
     
    390659  }, 
    391660 
    392   grep: function(pattern, iterator) { 
     661  grep: function(filter, iterator, context) { 
     662    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    393663    var results = []; 
     664 
     665    if (Object.isString(filter)) 
     666      filter = new RegExp(filter); 
     667 
    394668    this.each(function(value, index) { 
    395       var stringValue = value.toString(); 
    396       if (stringValue.match(pattern)) 
    397         results.push((iterator || Prototype.K)(value, index)); 
    398     }) 
     669      if (filter.match(value)) 
     670        results.push(iterator(value, index)); 
     671    }); 
    399672    return results; 
    400673  }, 
    401674 
    402675  include: function(object) { 
     676    if (Object.isFunction(this.indexOf)) 
     677      if (this.indexOf(object) != -1) return true; 
     678 
    403679    var found = false; 
    404680    this.each(function(value) { 
     
    412688 
    413689  inGroupsOf: function(number, fillWith) { 
    414     fillWith = fillWith === undefined ? null : fillWith; 
     690    fillWith = Object.isUndefined(fillWith) ? null : fillWith; 
    415691    return this.eachSlice(number, function(slice) { 
    416692      while(slice.length < number) slice.push(fillWith); 
     
    419695  }, 
    420696 
    421   inject: function(memo, iterator) { 
     697  inject: function(memo, iterator, context) { 
     698    iterator = iterator.bind(context); 
    422699    this.each(function(value, index) { 
    423700      memo = iterator(memo, value, index); 
     
    433710  }, 
    434711 
    435   max: function(iterator) { 
     712  max: function(iterator, context) { 
     713    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    436714    var result; 
    437715    this.each(function(value, index) { 
    438       value = (iterator || Prototype.K)(value, index); 
    439       if (result == undefined || value >= result) 
     716      value = iterator(value, index); 
     717      if (result == null || value >= result) 
    440718        result = value; 
    441719    }); 
     
    443721  }, 
    444722 
    445   min: function(iterator) { 
     723  min: function(iterator, context) { 
     724    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    446725    var result; 
    447726    this.each(function(value, index) { 
    448       value = (iterator || Prototype.K)(value, index); 
    449       if (result == undefined || value < result) 
     727      value = iterator(value, index); 
     728      if (result == null || value < result) 
    450729        result = value; 
    451730    }); 
     
    453732  }, 
    454733 
    455   partition: function(iterator) { 
     734  partition: function(iterator, context) { 
     735    iterator = iterator ? iterator.bind(context) : Prototype.K; 
    456736    var trues = [], falses = []; 
    457737    this.each(function(value, index) { 
    458       ((iterator || Prototype.K)(value, index) ? 
     738      (iterator(value, index) ? 
    459739        trues : falses).push(value); 
    460740    }); 
     
    464744  pluck: function(property) { 
    465745    var results = []; 
    466     this.each(function(value, index) { 
     746    this.each(function(value) { 
    467747      results.push(value[property]); 
    468748    }); 
     
    470750  }, 
    471751 
    472   reject: function(iterator) { 
     752  reject: function(iterator, context) { 
     753    iterator = iterator.bind(context); 
    473754    var results = []; 
    474755    this.each(function(value, index) { 
     
    479760  }, 
    480761 
    481   sortBy: function(iterator) { 
     762  sortBy: function(iterator, context) { 
     763    iterator = iterator.bind(context); 
    482764    return this.map(function(value, index) { 
    483765      return {value: value, criteria: iterator(value, index)}; 
     
    494776  zip: function() { 
    495777    var iterator = Prototype.K, args = $A(arguments); 
    496     if (typeof args.last() == 'function') 
     778    if (Object.isFunction(args.last())) 
    497779      iterator = args.pop(); 
    498780 
     
    510792    return '#<Enumerable:' + this.toArray().inspect() + '>'; 
    511793  } 
    512 } 
     794}; 
    513795 
    514796Object.extend(Enumerable, { 
     
    516798  find:    Enumerable.detect, 
    517799  select:  Enumerable.findAll, 
     800  filter:  Enumerable.findAll, 
    518801  member:  Enumerable.include, 
    519   entries: Enumerable.toArray 
     802  entries: Enumerable.toArray, 
     803  every:   Enumerable.all, 
     804  some:    Enumerable.any 
    520805}); 
    521 var $A = Array.from = function(iterable) { 
     806function $A(iterable) { 
    522807  if (!iterable) return []; 
    523   if (iterable.toArray) { 
    524     return iterable.toArray(); 
    525   } else { 
    526     var results = []; 
    527     for (var i = 0, length = iterable.length; i < length; i++) 
    528       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]; 
    529821    return results; 
    530822  } 
    531823} 
    532824 
     825Array.from = $A; 
     826 
    533827Object.extend(Array.prototype, Enumerable); 
    534828 
    535 if (!Array.prototype._reverse) 
    536   Array.prototype._reverse = Array.prototype.reverse; 
     829if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; 
    537830 
    538831Object.extend(Array.prototype, { 
     
    563856  flatten: function() { 
    564857    return this.inject([], function(array, value) { 
    565       return array.concat(value && value.constructor == Array ? 
     858      return array.concat(Object.isArray(value) ? 
    566859        value.flatten() : [value]); 
    567860    }); 
     
    575868  }, 
    576869 
    577   indexOf: function(object) { 
    578     for (var i = 0, length = this.length; i < length; i++) 
    579       if (this[i] == object) return i; 
    580     return -1; 
    581   }, 
    582  
    583870  reverse: function(inline) { 
    584871    return (inline !== false ? this : this.toArray())._reverse(); 
     
    589876  }, 
    590877 
    591   uniq: function() { 
    592     return this.inject([], function(array, value) { 
    593       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; 
     883    }); 
     884  }, 
     885 
     886  intersect: function(array) { 
     887    return this.uniq().findAll(function(item) { 
     888      return array.detect(function(value) { return item === value }); 
    594889    }); 
    595890  }, 
     
    605900  inspect: function() { 
    606901    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(', ') + ']'; 
    607911  } 
    608912}); 
    609913 
     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 
    610933Array.prototype.toArray = Array.prototype.clone; 
    611934 
    612 function $w(string){ 
     935function $w(string) { 
     936  if (!Object.isString(string)) return []; 
    613937  string = string.strip(); 
    614938  return string ? string.split(/\s+/) : []; 
    615939} 
    616940 
    617 if(window.opera){ 
    618   Array.prototype.concat = function(){ 
     941if (Prototype.Browser.Opera){ 
     942  Array.prototype.concat = function() { 
    619943    var array = []; 
    620     for(var i = 0, length = this.length; i < length; i++) array.push(this[i]); 
    621     for(var i = 0, length = arguments.length; i < length; i++) { 
    622       if(arguments[i].constructor == Array) { 
    623         for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) 
     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++) 
    624948          array.push(arguments[i][j]); 
    625949      } else { 
     
    628952    } 
    629953    return array; 
    630   } 
     954  }; 
    631955} 
    632 var Hash = function(obj) { 
    633   Object.extend(this, obj || {}); 
    634 }; 
    635  
    636 Object.extend(Hash, { 
    637   toQueryString: function(obj) { 
    638     var parts = []; 
    639  
    640           this.prototype._each.call(obj, function(pair) { 
    641       if (!pair.key) return; 
    642  
    643       if (pair.value && pair.value.constructor == Array) { 
    644         var values = pair.value.compact(); 
    645         if (values.length < 2) pair.value = values.reduce(); 
    646         else { 
    647                 key = encodeURIComponent(pair.key); 
    648           values.each(function(value) { 
    649             value = value != undefined ? encodeURIComponent(value) : ''; 
    650             parts.push(key + '=' + encodeURIComponent(value)); 
    651           }); 
    652           return; 
    653         } 
    654       } 
    655       if (pair.value == undefined) pair[1] = ''; 
    656       parts.push(pair.map(encodeURIComponent).join('=')); 
    657           }); 
    658  
    659     return parts.join('&'); 
     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'; 
    660977  } 
    661978}); 
    662979 
    663 Object.extend(Hash.prototype, Enumerable); 
    664 Object.extend(Hash.prototype, { 
    665   _each: function(iterator) { 
    666     for (var key in this) { 
    667       var value = this[key]; 
    668       if (value && value == Hash.prototype[key]) continue; 
    669  
    670       var pair = [key, value]; 
    671       pair.key = key; 
    672       pair.value = value; 
    673       iterator(pair); 
    674     } 
    675   }, 
    676  
    677   keys: function() { 
    678     return this.pluck('key'); 
    679   }, 
    680  
    681   values: function() { 
    682     return this.pluck('value'); 
    683   }, 
    684  
    685   merge: function(hash) { 
    686     return $H(hash).inject(this, function(mergedHash, pair) { 
    687       mergedHash[pair.key] = pair.value; 
    688       return mergedHash; 
    689     }); 
    690   }, 
    691  
    692   remove: function() { 
    693     var result; 
    694     for(var i = 0, length = arguments.length; i < length; i++) { 
    695       var value = this[arguments[i]]; 
    696       if (value !== undefined){ 
    697         if (result === undefined) result = value; 
    698         else { 
    699           if (result.constructor != Array) result = [result]; 
    700           result.push(value) 
    701         } 
    702       } 
    703       delete this[arguments[i]]; 
    704     } 
    705     return result; 
    706   }, 
    707  
    708   toQueryString: function() { 
    709     return Hash.toQueryString(this); 
    710   }, 
    711  
    712   inspect: function() { 
    713     return '#<Hash:{' + this.map(function(pair) { 
    714       return pair.map(Object.inspect).join(': '); 
    715     }).join(', ') + '}>'; 
    716   } 
     980$w('abs round ceil floor').each(function(method){ 
     981  Number.prototype[method] = Math[method].methodize(); 
    717982}); 
    718  
    719983function $H(object) { 
    720   if (object && object.constructor == Hash) return object; 
    721984  return new Hash(object); 
    722985}; 
    723 ObjectRange = Class.create(); 
    724 Object.extend(ObjectRange.prototype, Enumerable); 
    725 Object.extend(ObjectRange.prototype, { 
     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)) 
     1058            return values.map(toQueryPair.curry(key)).join('&'); 
     1059        } 
     1060        return toQueryPair(key, values); 
     1061      }).join('&'); 
     1062    }, 
     1063 
     1064    inspect: function() { 
     1065      return '#<Hash:{' + this.map(function(pair) { 
     1066        return pair.map(Object.inspect).join(': '); 
     1067      }).join(', ') + '}>'; 
     1068    }, 
     1069 
     1070    toJSON: function() { 
     1071      return Object.toJSON(this.toObject()); 
     1072    }, 
     1073 
     1074    clone: function() { 
     1075      return new Hash(this); 
     1076    } 
     1077  } 
     1078})()); 
     1079 
     1080Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; 
     1081Hash.from = $H; 
     1082var ObjectRange = Class.create(Enumerable, { 
    7261083  initialize: function(start, end, exclusive) { 
    7271084    this.start = start; 
     
    7491106var $R = function(start, end, exclusive) { 
    7501107  return new ObjectRange(start, end, exclusive); 
    751 } 
     1108}; 
    7521109 
    7531110var Ajax = { 
     
    7611118 
    7621119  activeRequestCount: 0 
    763 } 
     1120}; 
    7641121 
    7651122Ajax.Responders = { 
     
    7811138  dispatch: function(callback, request, transport, json) { 
    7821139    this.each(function(responder) { 
    783       if (typeof responder[callback] == 'function') { 
     1140      if (Object.isFunction(responder[callback])) { 
    7841141        try { 
    7851142          responder[callback].apply(responder, [request, transport, json]); 
    786         } catch (e) {} 
     1143        } catch (e) { } 
    7871144      } 
    7881145    }); 
     
    7931150 
    7941151Ajax.Responders.register({ 
    795   onCreate: function() { 
    796     Ajax.activeRequestCount++; 
    797   }, 
    798   onComplete: function() { 
    799     Ajax.activeRequestCount--; 
    800   } 
     1152  onCreate:   function() { Ajax.activeRequestCount++ }, 
     1153  onComplete: function() { Ajax.activeRequestCount-- } 
    8011154}); 
    8021155 
    803 Ajax.Base = function() {}; 
    804 Ajax.Base.prototype = { 
    805   setOptions: function(options) { 
     1156Ajax.Base = Class.create({ 
     1157  initialize: function(options) { 
    8061158    this.options = { 
    8071159      method:       'post', 
     
    8091161      contentType:  'application/x-www-form-urlencoded', 
    8101162      encoding:     'UTF-8', 
    811       parameters:   '' 
    812     } 
    813     Object.extend(this.options, options || {}); 
     1163      parameters:   '', 
     1164      evalJSON:     true, 
     1165      evalJS:       true 
     1166    }; 
     1167    Object.extend(this.options, options || { }); 
    8141168 
    8151169    this.options.method = this.options.method.toLowerCase(); 
    816     if (typeof this.options.parameters == 'string') 
     1170 
     1171    if (Object.isString(this.options.parameters)) 
    8171172      this.options.parameters = this.options.parameters.toQueryParams(); 
    818   } 
    819 } 
    820  
    821 Ajax.Request = Class.create(); 
    822 Ajax.Request.Events = 
    823   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
    824  
    825 Ajax.Request.prototype = Object.extend(new Ajax.Base(), { 
     1173    else if (Object.isHash(this.options.parameters)) 
     1174      this.options.parameters = this.options.parameters.toObject(); 
     1175  } 
     1176}); 
     1177 
     1178Ajax.Request = Class.create(Ajax.Base, { 
    8261179  _complete: false, 
    8271180 
    828   initialize: function(url, options) { 
     1181  initialize: function($super, url, options) { 
     1182    $super(options); 
    8291183    this.transport = Ajax.getTransport(); 
    830     this.setOptions(options); 
    8311184    this.request(url); 
    8321185  }, 
     
    8351188    this.url = url; 
    8361189    this.method = this.options.method; 
    837     var params = this.options.parameters; 
     1190    var params = Object.clone(this.options.parameters); 
    8381191 
    8391192    if (!['get', 'post'].include(this.method)) { 
     
    8431196    } 
    8441197 
    845     params = Hash.toQueryString(params); 
    846     if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_=' 
    847  
    848     // when GET, append parameters to URL 
    849     if (this.method == 'get' && params) 
    850       this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params; 
     1198    this.parameters = params; 
     1199 
     1200    if (params = Object.toQueryString(params)) { 
     1201      // when GET, append parameters to URL 
     1202      if (this.method == 'get') 
     1203        this.url += (this.url.include('?') ? '&' : '?') + params; 
     1204      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) 
     1205        params += '&_='; 
     1206    } 
    8511207 
    8521208    try { 
    853       Ajax.Responders.dispatch('onCreate', this, this.transport); 
     1209      var response = new Ajax.Response(this); 
     1210      if (this.options.onCreate) this.options.onCreate(response); 
     1211      Ajax.Responders.dispatch('onCreate', this, response); 
    8541212 
    8551213      this.transport.open(this.method.toUpperCase(), this.url, 
    8561214        this.options.asynchronous); 
    8571215 
    858       if (this.options.asynchronous) 
    859         setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10); 
     1216      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); 
    8601217 
    8611218      this.transport.onreadystatechange = this.onStateChange.bind(this); 
    8621219      this.setRequestHeaders(); 
    8631220 
    864       var body = this.method == 'post' ? (this.options.postBody || params) : null; 
    865  
    866       this.transport.send(body); 
     1221      this.body = this.method == 'post' ? (this.options.postBody || params) : null; 
     1222      this.transport.send(this.body); 
    8671223 
    8681224      /* Force Firefox to handle ready state 4 for synchronous requests */ 
     
    9061262      var extras = this.options.requestHeaders; 
    9071263 
    908       if (typeof extras.push == 'function') 
     1264      if (Object.isFunction(extras.push)) 
    9091265        for (var i = 0, length = extras.length; i < length; i += 2) 
    9101266          headers[extras[i]] = extras[i+1]; 
     
    9181274 
    9191275  success: function() { 
    920     return !this.transport.status 
    921         || (this.transport.status >= 200 && this.transport.status < 300); 
     1276    var status = this.getStatus(); 
     1277    return !status || (status >= 200 && status < 300); 
     1278  }, 
     1279 
     1280  getStatus: function() { 
     1281    try { 
     1282      return this.transport.status || 0; 
     1283    } catch (e) { return 0 } 
    9221284  }, 
    9231285 
    9241286  respondToReadyState: function(readyState) { 
    925     var state = Ajax.Request.Events[readyState]; 
    926     var transport = this.transport, json = this.evalJSON(); 
     1287    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); 
    9271288 
    9281289    if (state == 'Complete') { 
    9291290      try { 
    9301291        this._complete = true; 
    931         (this.options['on' + this.transport.status] 
     1292        (this.options['on' + response.status] 
    9321293         || this.options['on' + (this.success() ? 'Success' : 'Failure')] 
    933          || Prototype.emptyFunction)(transport, json); 
     1294         || Prototype.emptyFunction)(response, response.headerJSON); 
    9341295      } catch (e) { 
    9351296        this.dispatchException(e); 
    9361297      } 
    9371298 
    938       if ((this.getHeader('Content-type') || 'text/javascript').strip(). 
    939         match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i)) 
    940           this.evalResponse(); 
     1299      var contentType = response.getHeader('Content-type'); 
     1300      if (this.options.evalJS == 'force' 
     1301          || (this.options.evalJS && contentType 
     1302          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) 
     1303        this.evalResponse(); 
    9411304    } 
    9421305 
    9431306    try { 
    944       (this.options['on' + state] || Prototype.emptyFunction)(transport, json); 
    945       Ajax.Responders.dispatch('on' + state, this, transport, json); 
     1307      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); 
     1308      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); 
    9461309    } catch (e) { 
    9471310      this.dispatchException(e); 
     
    9561319  getHeader: function(name) { 
    9571320    try { 
    958       return this.transport.getResponseHeader(name); 
    959     } catch (e) { return null } 
    960   }, 
    961  
    962   evalJSON: function() { 
    963     try { 
    964       var json = this.getHeader('X-JSON'); 
    965       return json ? eval('(' + json + ')') : null; 
     1321      return this.transport.getResponseHeader(name) || null; 
    9661322    } catch (e) { return null } 
    9671323  }, 
     
    9691325  evalResponse: function() { 
    9701326    try { 
    971       return eval(this.transport.responseText); 
     1327      return eval((this.transport.responseText || '').unfilterJSON()); 
    9721328    } catch (e) { 
    9731329      this.dispatchException(e); 
     
    9811337}); 
    9821338 
    983 Ajax.Updater = Class.create(); 
    984  
    985 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), { 
    986   initialize: function(container, url, options) { 
     1339Ajax.Request.Events = 
     1340  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; 
     1341 
     1342Ajax.Response = Class.create({ 
     1343  initialize: function(request){ 
     1344    this.request = request; 
     1345    var transport  = this.transport  = request.transport, 
     1346        readyState = this.readyState = transport.readyState; 
     1347 
     1348    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { 
     1349      this.status       = this.getStatus(); 
     1350      this.statusText   = this.getStatusText(); 
     1351      this.responseText = String.interpret(transport.responseText); 
     1352      this.headerJSON   = this._getHeaderJSON(); 
     1353    } 
     1354 
     1355    if(readyState == 4) { 
     1356      var xml = transport.responseXML; 
     1357      this.responseXML  = Object.isUndefined(xml) ? null : xml; 
     1358      this.responseJSON = this._getResponseJSON(); 
     1359    } 
     1360  }, 
     1361 
     1362  status:      0, 
     1363  statusText: '', 
     1364 
     1365  getStatus: Ajax.Request.prototype.getStatus, 
     1366 
     1367  getStatusText: function() { 
     1368    try { 
     1369      return this.transport.statusText || ''; 
     1370    } catch (e) { return '' } 
     1371  }, 
     1372 
     1373  getHeader: Ajax.Request.prototype.getHeader, 
     1374 
     1375  getAllHeaders: function() { 
     1376    try { 
     1377      return this.getAllResponseHeaders(); 
     1378    } catch (e) { return null } 
     1379  }, 
     1380 
     1381  getResponseHeader: function(name) { 
     1382    return this.transport.getResponseHeader(name); 
     1383  }, 
     1384 
     1385  getAllResponseHeaders: function() { 
     1386    return this.transport.getAllResponseHeaders(); 
     1387  }, 
     1388 
     1389  _getHeaderJSON: function() { 
     1390    var json = this.getHeader('X-JSON'); 
     1391    if (!json) return null; 
     1392    json = decodeURIComponent(escape(json)); 
     1393    try { 
     1394      return json.evalJSON(this.request.options.sanitizeJSON); 
     1395    } catch (e) { 
     1396      this.request.dispatchException(e); 
     1397    } 
     1398  }, 
     1399 
     1400  _getResponseJSON: function() { 
     1401    var options = this.request.options; 
     1402    if (!options.evalJSON || (options.evalJSON != 'force' && 
     1403      !(this.getHeader('Content-type') || '').include('application/json')) || 
     1404        this.responseText.blank()) 
     1405          return null; 
     1406    try { 
     1407      return this.responseText.evalJSON(options.sanitizeJSON); 
     1408    } catch (e) { 
     1409      this.request.dispatchException(e); 
     1410    } 
     1411  } 
     1412}); 
     1413 
     1414Ajax.Updater = Class.create(Ajax.Request, { 
     1415  initialize: function($super, container, url, options) { 
    9871416    this.container = { 
    9881417      success: (container.success || container), 
    9891418      failure: (container.failure || (container.success ? null : container)) 
    990     } 
    991  
    992     this.transport = Ajax.getTransport(); 
    993     this.setOptions(options); 
    994  
    995     var onComplete = this.options.onComplete || Prototype.emptyFunction; 
    996     this.options.onComplete = (function(transport, param) { 
    997       this.updateContent(); 
    998       onComplete(transport, param); 
     1419    }; 
     1420 
     1421    options = Object.clone(options); 
     1422    var onComplete = options.onComplete; 
     1423    options.onComplete = (function(response, json) { 
     1424      this.updateContent(response.responseText); 
     1425      if (Object.isFunction(onComplete)) onComplete(response, json); 
    9991426    }).bind(this); 
    10001427 
    1001     this.request(url); 
    1002   }, 
    1003  
    1004   updateContent: function() { 
    1005     var receiver = this.container[this.success() ? 'success' : 'failure']; 
    1006     var response = this.transport.responseText; 
    1007  
    1008     if (!this.options.evalScripts) response = response.stripScripts(); 
     1428    $super(url, options); 
     1429  }, 
     1430 
     1431  updateContent: function(responseText) { 
     1432    var receiver = this.container[this.success() ? 'success' : 'failure'], 
     1433        options = this.options; 
     1434 
     1435    if (!options.evalScripts) responseText = responseText.stripScripts(); 
    10091436 
    10101437    if (receiver = $(receiver)) { 
    1011       if (this.options.insertion) 
    1012         new this.options.insertion(receiver, response); 
    1013       else 
    1014         receiver.update(response); 
    1015     } 
    1016  
    1017     if (this.success()) { 
    1018       if (this.onComplete) 
    1019         setTimeout(this.onComplete.bind(this), 10); 
     1438      if (options.insertion) { 
     1439        if (Object.isString(options.insertion)) { 
     1440          var insertion = { }; insertion[options.insertion] = responseText; 
     1441          receiver.insert(insertion); 
     1442        } 
     1443        else options.insertion(receiver, responseText); 
     1444      } 
     1445      else receiver.update(responseText); 
    10201446    } 
    10211447  } 
    10221448}); 
    10231449 
    1024 Ajax.PeriodicalUpdater = Class.create(); 
    1025 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), { 
    1026   initialize: function(container, url, options) { 
    1027     this.setOptions(options); 
     1450Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { 
     1451  initialize: function($super, container, url, options) { 
     1452    $super(options); 
    10281453    this.onComplete = this.options.onComplete; 
    10291454 
     
    10311456    this.decay = (this.options.decay || 1); 
    10321457 
    1033     this.updater = {}; 
     1458    this.updater = { }; 
    10341459    this.container = container; 
    10351460    this.url = url; 
     
    10491474  }, 
    10501475 
    1051   updateComplete: function(request) { 
     1476  updateComplete: function(response) { 
    10521477    if (this.options.decay) { 
    1053       this.decay = (request.responseText == this.lastText ? 
     1478      this.decay = (response.responseText == this.lastText ? 
    10541479        this.decay * this.options.decay : 1); 
    10551480 
    1056       this.lastText = request.responseText; 
    1057     } 
    1058     this.timer = setTimeout(this.onTimerEvent.bind(this), 
    1059       this.decay * this.frequency * 1000); 
     1481      this.lastText = response.responseText; 
     1482    } 
     1483    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); 
    10601484  }, 
    10611485 
     
    10701494    return elements; 
    10711495  } 
    1072   if (typeof element == 'string') 
     1496  if (Object.isString(element)) 
    10731497    element = document.getElementById(element); 
    10741498  return Element.extend(element); 
     
    10811505      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); 
    10821506    for (var i = 0, length = query.snapshotLength; i < length; i++) 
    1083       results.push(query.snapshotItem(i)); 
     1507      results.push(Element.extend(query.snapshotItem(i))); 
    10841508    return results; 
    10851509  }; 
    10861510} 
    10871511 
    1088 document.getElementsByClassName = function(className, parentElement) { 
    1089   if (Prototype.BrowserFeatures.XPath) { 
    1090     var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]"; 
    1091     return document._getElementsByXPath(q, parentElement); 
    1092   } else { 
    1093     var children = ($(parentElement) || document.body).getElementsByTagName('*'); 
    1094     var elements = [], child; 
    1095     for (var i = 0, length = children.length; i < length; i++) { 
    1096       child = children[i]; 
    1097       if (Element.hasClassName(child, className)) 
    1098         elements.push(Element.extend(child)); 
    1099     } 
    1100     return elements; 
    1101   } 
    1102 }; 
    1103  
    11041512/*--------------------------------------------------------------------------*/ 
    11051513 
    1106 if (!window.Element) 
    1107   var Element = new Object(); 
    1108  
    1109 Element.extend = function(element) { 
    1110   if (!element || _nativeExtensions || element.nodeType == 3) return element; 
    1111  
    1112   if (!element._extended && element.tagName && element != window) { 
    1113     var methods = Object.clone(Element.Methods), cache = Element.extend.cache; 
    1114  
    1115     if (element.tagName == 'FORM') 
    1116       Object.extend(methods, Form.Methods); 
    1117     if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName)) 
    1118       Object.extend(methods, Form.Element.Methods); 
    1119  
    1120     Object.extend(methods, Element.Methods.Simulated); 
    1121  
    1122     for (var property in methods) { 
    1123       var value = methods[property]; 
    1124       if (typeof value == 'function' && !(property in element)) 
    1125         element[property] = cache.findOrStore(value); 
    1126     } 
    1127   } 
    1128  
    1129   element._extended = true; 
    1130   return element; 
    1131 }; 
    1132  
    1133 Element.extend.cache = { 
    1134   findOrStore: function(value) { 
    1135     return this[value] = this[value] || function() { 
    1136       return value.apply(null, [this].concat($A(arguments))); 
    1137     } 
    1138   } 
    1139 }; 
     1514if (!window.Node) var Node = { }; 
     1515 
     1516if (!Node.ELEMENT_NODE) { 
     1517  // DOM level 2 ECMAScript Language Binding 
     1518  Object.extend(Node, { 
     1519    ELEMENT_NODE: 1, 
     1520    ATTRIBUTE_NODE: 2, 
     1521    TEXT_NODE: 3, 
     1522    CDATA_SECTION_NODE: 4, 
     1523    ENTITY_REFERENCE_NODE: 5, 
     1524    ENTITY_NODE: 6, 
     1525    PROCESSING_INSTRUCTION_NODE: 7, 
     1526    COMMENT_NODE: 8, 
     1527    DOCUMENT_NODE: 9, 
     1528    DOCUMENT_TYPE_NODE: 10, 
     1529    DOCUMENT_FRAGMENT_NODE: 11, 
     1530    NOTATION_NODE: 12 
     1531  }); 
     1532} 
     1533 
     1534(function() { 
     1535  var element = this.Element; 
     1536  this.Element = function(tagName, attributes) { 
     1537    attributes = attributes || { }; 
     1538    tagName = tagName.toLowerCase(); 
     1539    var cache = Element.cache; 
     1540    if (Prototype.Browser.IE && attributes.name) { 
     1541      tagName = '<' + tagName + ' name="' + attributes.name + '">'; 
     1542      delete attributes.name; 
     1543      return Element.writeAttribute(document.createElement(tagName), attributes); 
     1544    } 
     1545    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); 
     1546    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); 
     1547  }; 
     1548  Object.extend(this.Element, element || { }); 
     1549}).call(window); 
     1550 
     1551Element.cache = { }; 
    11401552 
    11411553Element.Methods = { 
     
    11661578  }, 
    11671579 
    1168   update: function(element, html) { 
    1169     html = typeof html == 'undefined' ? '' : html.toString(); 
    1170     $(element).innerHTML = html.stripScripts(); 
    1171     setTimeout(function() {html.evalScripts()}, 10); 
     1580  update: function(element, content) { 
     1581    element = $(element); 
     1582    if (content && content.toElement) content = content.toElement(); 
     1583    if (Object.isElement(content)) return element.update().insert(content); 
     1584    content = Object.toHTML(content); 
     1585    element.innerHTML = content.stripScripts(); 
     1586    content.evalScripts.bind(content).defer(); 
    11721587    return element; 
    11731588  }, 
    11741589 
    1175   replace: function(element, html) { 
     1590  replace: function(element, content) { 
    11761591    element = $(element); 
    1177     html = typeof html == 'undefined' ? '' : html.toString(); 
    1178     if (element.outerHTML) { 
    1179       element.outerHTML = html.stripScripts(); 
    1180     } else { 
     1592    if (content && content.toElement) content = content.toElement(); 
     1593    else if (!Object.isElement(content)) { 
     1594      content = Object.toHTML(content); 
    11811595      var range = element.ownerDocument.createRange(); 
    1182       range.selectNodeContents(element); 
    1183       element.parentNode.replaceChild( 
    1184         range.createContextualFragment(html.stripScripts()), element); 
    1185     } 
    1186     setTimeout(function() {html.evalScripts()}, 10); 
     1596      range.selectNode(element); 
     1597      content.evalScripts.bind(content).defer(); 
     1598      content = range.createContextualFragment(content.stripScripts()); 
     1599    } 
     1600    element.parentNode.replaceChild(content, element); 
    11871601    return element; 
     1602  }, 
     1603 
     1604  insert: function(element, insertions) { 
     1605    element = $(element); 
     1606 
     1607    if (Object.isString(insertions) || Object.isNumber(insertions) || 
     1608        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) 
     1609          insertions = {bottom:insertions}; 
     1610 
     1611    var content, insert, tagName, childNodes; 
     1612 
     1613    for (position in insertions) { 
     1614      content  = insertions[position]; 
     1615      position = position.toLowerCase(); 
     1616      insert = Element._insertionTranslations[position]; 
     1617 
     1618      if (content && content.toElement) content = content.toElement(); 
     1619      if (Object.isElement(content)) { 
     1620        insert(element, content); 
     1621        continue; 
     1622      } 
     1623 
     1624      content = Object.toHTML(content); 
     1625 
     1626      tagName = ((position == 'before' || position == 'after') 
     1627        ? element.parentNode : element).tagName.toUpperCase(); 
     1628 
     1629      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 
     1630 
     1631      if (position == 'top' || position == 'after') childNodes.reverse(); 
     1632      childNodes.each(insert.curry(element)); 
     1633 
     1634      content.evalScripts.bind(content).defer(); 
     1635    } 
     1636 
     1637    return element; 
     1638  }, 
     1639 
     1640  wrap: function(element, wrapper, attributes) { 
     1641    element = $(element); 
     1642    if (Object.isElement(wrapper)) 
     1643      $(wrapper).writeAttribute(attributes || { }); 
     1644    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); 
     1645    else wrapper = new Element('div', wrapper); 
     1646    if (element.parentNode) 
     1647      element.parentNode.replaceChild(wrapper, element); 
     1648    wrapper.appendChild(element); 
     1649    return wrapper; 
    11881650  }, 
    11891651 
     
    12131675 
    12141676  descendants: function(element) { 
    1215     return $A($(element).getElementsByTagName('*')); 
     1677    return $(element).getElementsBySelector("*"); 
     1678  }, 
     1679 
     1680  firstDescendant: function(element) { 
     1681    element = $(element).firstChild; 
     1682    while (element && element.nodeType != 1) element = element.nextSibling; 
     1683    return $(element); 
    12161684  }, 
    12171685 
     
    12371705 
    12381706  match: function(element, selector) { 
    1239     if (typeof selector == 'string') 
     1707    if (Object.isString(selector)) 
    12401708      selector = new Selector(selector); 
    12411709    return selector.match($(element)); 
     
    12431711 
    12441712  up: function(element, expression, index) { 
    1245     return Selector.findElement($(element).ancestors(), expression, index); 
     1713    element = $(element); 
     1714    if (arguments.length == 1) return $(element.parentNode); 
     1715    var ancestors = element.ancestors(); 
     1716    return Object.isNumber(expression) ? ancestors[expression] : 
     1717      Selector.findElement(ancestors, expression, index); 
    12461718  }, 
    12471719 
    12481720  down: function(element, expression, index) { 
    1249     return Selector.findElement($(element).descendants(), expression, index); 
     1721    element = $(element); 
     1722    if (arguments.length == 1) return element.firstDescendant(); 
     1723    return Object.isNumber(expression) ? element.descendants()[expression] : 
     1724      element.select(expression)[index || 0]; 
    12501725  }, 
    12511726 
    12521727  previous: function(element, expression, index) { 
    1253     return Selector.findElement($(element).previousSiblings(), expression, index); 
     1728    element = $(element); 
     1729    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); 
     1730    var previousSiblings = element.previousSiblings(); 
     1731    return Object.isNumber(expression) ? previousSiblings[expression] : 
     1732      Selector.findElement(previousSiblings, expression, index); 
    12541733  }, 
    12551734 
    12561735  next: function(element, expression, index) { 
    1257     return Selector.findElement($(element).nextSiblings(), expression, index); 
    1258   }, 
    1259  
    1260   getElementsBySelector: function() { 
     1736    element = $(element); 
     1737    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); 
     1738    var nextSiblings = element.nextSiblings(); 
     1739    return Object.isNumber(expression) ? nextSiblings[expression] : 
     1740      Selector.findElement(nextSiblings, expression, index); 
     1741  }, 
     1742 
     1743  select: function() { 
    12611744    var args = $A(arguments), element = $(args.shift()); 
    12621745    return Selector.findChildElements(element, args); 
    12631746  }, 
    12641747 
    1265   getElementsByClassName: function(element, className) { 
    1266     return document.getElementsByClassName(className, element); 
     1748  adjacent: function() { 
     1749    var args = $A(arguments), element = $(args.shift()); 
     1750    return Selector.findChildElements(element.parentNode, args).without(element); 
     1751  }, 
     1752 
     1753  identify: function(element) { 
     1754    element = $(element); 
     1755    var id = element.readAttribute('id'), self = arguments.callee; 
     1756    if (id) return id; 
     1757    do { id = 'anonymous_element_' + self.counter++ } while ($(id)); 
     1758    element.writeAttribute('id', id); 
     1759    return id; 
    12671760  }, 
    12681761 
    12691762  readAttribute: function(element, name) { 
    12701763    element = $(element); 
    1271     if (document.all && !window.opera) { 
    1272       var t = Element._attributeTranslations; 
     1764    if (Prototype.Browser.IE) { 
     1765      var t = Element._attributeTranslations.read; 
    12731766      if (t.values[name]) return t.values[name](element, name); 
    1274       if (t.names[name])  name = t.names[name]; 
    1275       var attribute = element.attributes[name]; 
    1276       if(attribute) return attribute.nodeValue; 
     1767      if (t.names[name]) name = t.names[name]; 
     1768      if (name.include(':')) { 
     1769        return (!element.attributes || !element.attributes[name]) ? null : 
     1770         element.attributes[name].value; 
     1771      } 
    12771772    } 
    12781773    return element.getAttribute(name); 
     1774  }, 
     1775 
     1776  writeAttribute: function(element, name, value) { 
     1777    element = $(element); 
     1778    var attributes = { }, t = Element._attributeTranslations.write; 
     1779 
     1780    if (typeof name == 'object') attributes = name; 
     1781    else attributes[name] = Object.isUndefined(value) ? true : value; 
     1782 
     1783    for (var attr in attributes) { 
     1784      name = t.names[attr] || attr; 
     1785      value = attributes[attr]; 
     1786      if (t.values[attr]) name = t.values[attr](element, value); 
     1787      if (value === false || value === null) 
     1788        element.removeAttribute(name); 
     1789      else if (value === true) 
     1790        element.setAttribute(name, name); 
     1791      else element.setAttribute(name, value); 
     1792    } 
     1793    return element; 
    12791794  }, 
    12801795 
     
    12941809    if (!(element = $(element))) return; 
    12951810    var elementClassName = element.className; 
    1296     if (elementClassName.length == 0) return false; 
    1297     if (elementClassName == className || 
    1298         elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))) 
    1299       return true; 
    1300     return false; 
     1811    return (elementClassName.length > 0 && (elementClassName == className || 
     1812      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); 
    13011813  }, 
    13021814 
    13031815  addClassName: function(element, className) { 
    13041816    if (!(element = $(element))) return; 
    1305     Element.classNames(element).add(className); 
     1817    if (!element.hasClassName(className)) 
     1818      element.className += (element.className ? ' ' : '') + className; 
    13061819    return element; 
    13071820  }, 
     
    13091822  removeClassName: function(element, className) { 
    13101823    if (!(element = $(element))) return; 
    1311     Element.classNames(element).remove(className); 
     1824    element.className = element.className.replace( 
     1825      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); 
    13121826    return element; 
    13131827  }, 
     
    13151829  toggleClassName: function(element, className) { 
    13161830    if (!(element = $(element))) return; 
    1317     Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className); 
    1318     return element; 
    1319   }, 
    1320  
    1321   observe: function() { 
    1322     Event.observe.apply(Event, arguments); 
    1323     return $A(arguments).first(); 
    1324   }, 
    1325  
    1326   stopObserving: function() { 
    1327     Event.stopObserving.apply(Event, arguments); 
    1328     return $A(arguments).first(); 
     1831    return element[element.hasClassName(className) ? 
     1832      'removeClassName' : 'addClassName'](className); 
    13291833  }, 
    13301834 
     
    13431847 
    13441848  empty: function(element) { 
    1345     return $(element).innerHTML.match(/^\s*$/); 
     1849    return $(element).innerHTML.blank(); 
    13461850  }, 
    13471851 
    13481852  descendantOf: function(element, ancestor) { 
    13491853    element = $(element), ancestor = $(ancestor); 
     1854    var originalAncestor = ancestor; 
     1855 
     1856    if (element.compareDocumentPosition) 
     1857      return (element.compareDocumentPosition(ancestor) & 8) === 8; 
     1858 
     1859    if (element.sourceIndex && !Prototype.Browser.Opera) { 
     1860      var e = element.sourceIndex, a = ancestor.sourceIndex, 
     1861       nextAncestor = ancestor.nextSibling; 
     1862      if (!nextAncestor) { 
     1863        do { ancestor = ancestor.parentNode; } 
     1864        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); 
     1865      } 
     1866      if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex); 
     1867    } 
     1868 
    13501869    while (element = element.parentNode) 
    1351       if (element == ancestor) return true; 
     1870      if (element == originalAncestor) return true; 
    13521871    return false; 
    13531872  }, 
     
    13551874  scrollTo: function(element) { 
    13561875    element = $(element); 
    1357     var pos = Position.cumulativeOffset(element); 
     1876    var pos = element.cumulativeOffset(); 
    13581877    window.scrollTo(pos[0], pos[1]); 
    13591878    return element; 
     
    13621881  getStyle: function(element, style) { 
    13631882    element = $(element); 
    1364     if (['float','cssFloat'].include(style)) 
    1365       style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat'); 
    1366     style = style.camelize(); 
     1883    style = style == 'float' ? 'cssFloat' : style.camelize(); 
    13671884    var value = element.style[style]; 
    13681885    if (!value) { 
    1369       if (document.defaultView && document.defaultView.getComputedStyle) { 
    1370         var css = document.defaultView.getComputedStyle(element, null); 
    1371         value = css ? css[style] : null; 
    1372       } else if (element.currentStyle) { 
    1373         value = element.currentStyle[style]; 
    1374       } 
    1375     } 
    1376  
    1377     if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none')) 
    1378       value = element['offset'+style.capitalize()] + 'px'; 
    1379  
    1380     if (window.opera && ['left', 'top', 'right', 'bottom'].include(style)) 
    1381       if (Element.getStyle(element, 'position') == 'static') value = 'auto'; 
    1382     if(style == 'opacity') { 
    1383       if(value) return parseFloat(value); 
    1384       if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) 
    1385         if(value[1]) return parseFloat(value[1]) / 100; 
    1386       return 1.0; 
    1387     } 
     1886      var css = document.defaultView.getComputedStyle(element, null); 
     1887      value = css ? css[style] : null; 
     1888    } 
     1889    if (style == 'opacity') return value ? parseFloat(value) : 1.0; 
    13881890    return value == 'auto' ? null : value; 
    13891891  }, 
    13901892 
    1391   setStyle: function(element, style) { 
     1893  getOpacity: function(element) { 
     1894    return $(element).getStyle('opacity'); 
     1895  }, 
     1896 
     1897  setStyle: function(element, styles) { 
    13921898    element = $(element); 
    1393     for (var name in style) { 
    1394       var value = style[name]; 
    1395       if(name == 'opacity') { 
    1396         if (value == 1) { 
    1397           value = (/Gecko/.test(navigator.userAgent) && 
    1398             !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0; 
    1399           if(/MSIE/.test(navigator.userAgent) && !window.opera) 
    1400             element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,''); 
    1401         } else if(value == '') { 
    1402           if(/MSIE/.test(navigator.userAgent) && !window.opera) 
    1403             element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,''); 
    1404         } else { 
    1405           if(value < 0.00001) value = 0; 
    1406           if(/MSIE/.test(navigator.userAgent) && !window.opera) 
    1407             element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') + 
    1408               'alpha(opacity='+value*100+')'; 
    1409         } 
    1410       } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat'; 
    1411       element.style[name.camelize()] = value; 
    1412     } 
     1899    var elementStyle = element.style, match; 
     1900    if (Object.isString(styles)) { 
     1901      element.style.cssText += ';' + styles; 
     1902      return styles.include('opacity') ? 
     1903        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; 
     1904    } 
     1905    for (var property in styles) 
     1906      if (property == 'opacity') element.setOpacity(styles[property]); 
     1907      else 
     1908        elementStyle[(property == 'float' || property == 'cssFloat') ? 
     1909          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : 
     1910            property] = styles[property]; 
     1911 
     1912    return element; 
     1913  }, 
     1914 
     1915  setOpacity: function(element, value) { 
     1916    element = $(element); 
     1917    element.style.opacity = (value == 1 || value === '') ? '' : 
     1918      (value < 0.00001) ? 0 : value; 
    14131919    return element; 
    14141920  }, 
     
    14691975    element = $(element); 
    14701976    if (element._overflow) return element; 
    1471     element._overflow = element.style.overflow || 'auto'; 
    1472     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden') 
     1977    element._overflow = Element.getStyle(element, 'overflow') || 'auto'; 
     1978    if (element._overflow !== 'hidden') 
    14731979      element.style.overflow = 'hidden'; 
    14741980    return element; 
     
    14811987    element._overflow = null; 
    14821988    return element; 
     1989  }, 
     1990 
     1991  cumulativeOffset: function(element) { 
     1992    var valueT = 0, valueL = 0; 
     1993    do { 
     1994      valueT += element.offsetTop  || 0; 
     1995      valueL += element.offsetLeft || 0; 
     1996      element = element.offsetParent; 
     1997    } while (element); 
     1998    return Element._returnOffset(valueL, valueT); 
     1999  }, 
     2000 
     2001  positionedOffset: function(element) { 
     2002    var valueT = 0, valueL = 0; 
     2003    do { 
     2004      valueT += element.offsetTop  || 0; 
     2005      valueL += element.offsetLeft || 0; 
     2006      element = element.offsetParent; 
     2007      if (element) { 
     2008        if (element.tagName == 'BODY') break; 
     2009        var p = Element.getStyle(element, 'position'); 
     2010        if (p == 'relative' || p == 'absolute') break; 
     2011      } 
     2012    } while (element); 
     2013    return Element._returnOffset(valueL, valueT); 
     2014  }, 
     2015 
     2016  absolutize: function(element) { 
     2017    element = $(element); 
     2018    if (element.getStyle('position') == 'absolute') return; 
     2019    // Position.prepare(); // To be done manually by Scripty when it needs it. 
     2020 
     2021    var offsets = element.positionedOffset(); 
     2022    var top     = offsets[1]; 
     2023    var left    = offsets[0]; 
     2024    var width   = element.clientWidth; 
     2025    var height  = element.clientHeight; 
     2026 
     2027    element._originalLeft   = left - parseFloat(element.style.left  || 0); 
     2028    element._originalTop    = top  - parseFloat(element.style.top || 0); 
     2029    element._originalWidth  = element.style.width; 
     2030    element._originalHeight = element.style.height; 
     2031 
     2032    element.style.position = 'absolute'; 
     2033    element.style.top    = top + 'px'; 
     2034    element.style.left   = left + 'px'; 
     2035    element.style.width  = width + 'px'; 
     2036    element.style.height = height + 'px'; 
     2037    return element; 
     2038  }, 
     2039 
     2040  relativize: function(element) { 
     2041    element = $(element); 
     2042    if (element.getStyle('position') == 'relative') return; 
     2043    // Position.prepare(); // To be done manually by Scripty when it needs it. 
     2044 
     2045    element.style.position = 'relative'; 
     2046    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0); 
     2047    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 
     2048 
     2049    element.style.top    = top + 'px'; 
     2050    element.style.left   = left + 'px'; 
     2051    element.style.height = element._originalHeight; 
     2052    element.style.width  = element._originalWidth; 
     2053    return element; 
     2054  }, 
     2055 
     2056  cumulativeScrollOffset: function(element) { 
     2057    var valueT = 0, valueL = 0; 
     2058    do { 
     2059      valueT += element.scrollTop  || 0; 
     2060      valueL += element.scrollLeft || 0; 
     2061      element = element.parentNode; 
     2062    } while (element); 
     2063    return Element._returnOffset(valueL, valueT); 
     2064  }, 
     2065 
     2066  getOffsetParent: function(element) { 
     2067    if (element.offsetParent) return $(element.offsetParent); 
     2068    if (element == document.body) return $(element); 
     2069 
     2070    while ((element = element.parentNode) && element != document.body) 
     2071      if (Element.getStyle(element, 'position') != 'static') 
     2072        return $(element); 
     2073 
     2074    return $(document.body); 
     2075  }, 
     2076 
     2077  viewportOffset: function(forElement) { 
     2078    var valueT = 0, valueL = 0; 
     2079 
     2080    var element = forElement; 
     2081    do { 
     2082      valueT += element.offsetTop  || 0; 
     2083      valueL += element.offsetLeft || 0; 
     2084 
     2085      // Safari fix 
     2086      if (element.offsetParent == document.body && 
     2087        Element.getStyle(element, 'position') == 'absolute') break; 
     2088 
     2089    } while (element = element.offsetParent); 
     2090 
     2091    element = forElement; 
     2092    do { 
     2093      if (!Prototype.Browser.Opera || element.tagName == 'BODY') { 
     2094        valueT -= element.scrollTop  || 0; 
     2095        valueL -= element.scrollLeft || 0; 
     2096      } 
     2097    } while (element = element.parentNode); 
     2098 
     2099    return Element._returnOffset(valueL, valueT); 
     2100  }, 
     2101 
     2102  clonePosition: function(element, source) { 
     2103    var options = Object.extend({ 
     2104      setLeft:    true, 
     2105      setTop:     true, 
     2106      setWidth:   true, 
     2107      setHeight:  true, 
     2108      offsetTop:  0, 
     2109      offsetLeft: 0 
     2110    }, arguments[2] || { }); 
     2111 
     2112    // find page position of source 
     2113    source = $(source); 
     2114    var p = source.viewportOffset(); 
     2115 
     2116    // find coordinate system to use 
     2117    element = $(element); 
     2118    var delta = [0, 0]; 
     2119    var parent = null; 
     2120    // delta [0,0] will do fine with position: fixed elements, 
     2121    // position:absolute needs offsetParent deltas 
     2122    if (Element.getStyle(element, 'position') == 'absolute') { 
     2123      parent = element.getOffsetParent(); 
     2124      delta = parent.viewportOffset(); 
     2125    } 
     2126 
     2127    // correct by body offsets (fixes Safari) 
     2128    if (parent == document.body) { 
     2129      delta[0] -= document.body.offsetLeft; 
     2130      delta[1] -= document.body.offsetTop; 
     2131    } 
     2132 
     2133    // set position 
     2134    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px'; 
     2135    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px'; 
     2136    if (options.setWidth)  element.style.width = source.offsetWidth + 'px'; 
     2137    if (options.setHeight) element.style.height = source.offsetHeight + 'px'; 
     2138    return element; 
    14832139  } 
    14842140}; 
    14852141 
    1486 Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf}); 
    1487  
    1488 Element._attributeTranslations = {}; 
    1489  
    1490 Element._attributeTranslations.names = { 
    1491   colspan:   "colSpan", 
    1492   rowspan:   "rowSpan", 
    1493   valign:    "vAlign", 
    1494   datetime:  "dateTime", 
    1495   accesskey: "accessKey", 
    1496   tabindex:  "tabIndex", 
    1497   enctype:   "encType", 
    1498   maxlength: "maxLength", 
    1499   readonly:  "readOnly", 
    1500   longdesc:  "longDesc" 
     2142Element.Methods.identify.counter = 1; 
     2143 
     2144Object.extend(Element.Methods, { 
     2145  getElementsBySelector: Element.Methods.select, 
     2146  childElements: Element.Methods.immediateDescendants 
     2147}); 
     2148 
     2149Element._attributeTranslations = { 
     2150  write: { 
     2151    names: { 
     2152      className: 'class', 
     2153      htmlFor:   'for' 
     2154    }, 
     2155    values: { } 
     2156  } 
    15012157}; 
    15022158 
    1503 Element._attributeTranslations.values = { 
    1504   _getAttr: function(element, attribute) { 
    1505     return element.getAttribute(attribute, 2); 
    1506   }, 
    1507  
    1508   _flag: function(element, attribute) { 
    1509     return $(element).hasAttribute(attribute) ? attribute : null; 
    1510   }, 
    1511  
    1512   style: function(element) { 
    1513     return element.style.cssText.toLowerCase(); 
    1514   }, 
    1515  
    1516   title: function(element) { 
    1517     var node = element.getAttributeNode('title'); 
    1518     return node.specified ? node.nodeValue : null; 
    1519   } 
     2159if (Prototype.Browser.Opera) { 
     2160  Element.Methods.getStyle = Element.Methods.getStyle.wrap( 
     2161    function(proceed, element, style) { 
     2162      switch (style) { 
     2163        case 'left': case 'top': case 'right': case 'bottom': 
     2164          if (proceed(element, 'position') === 'static') return null; 
     2165        case 'height': case 'width': 
     2166          // returns '0px' for hidden elements; we want it to return null 
     2167          if (!Element.visible(element)) return null; 
     2168 
     2169          // returns the border-box dimensions rather than the content-box 
     2170          // dimensions, so we subtract padding and borders from the value 
     2171          var dim = parseInt(proceed(element, style), 10); 
     2172 
     2173          if (dim !== element['offset' + style.capitalize()]) 
     2174            return dim + 'px'; 
     2175 
     2176          var properties; 
     2177          if (style === 'height') { 
     2178            properties = ['border-top-width', 'padding-top', 
     2179             'padding-bottom', 'border-bottom-width']; 
     2180          } 
     2181          else { 
     2182            properties = ['border-left-width', 'padding-left', 
     2183             'padding-right', 'border-right-width']; 
     2184          } 
     2185          return properties.inject(dim, function(memo, property) { 
     2186            var val = proceed(element, property); 
     2187            return val === null ? memo : memo - parseInt(val, 10); 
     2188          }) + 'px'; 
     2189        default: return proceed(element, style); 
     2190      } 
     2191    } 
     2192  ); 
     2193 
     2194  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( 
     2195    function(proceed, element, attribute) { 
     2196      if (attribute === 'title') return element.title; 
     2197      return proceed(element, attribute); 
     2198    } 
     2199  ); 
     2200} 
     2201 
     2202else if (Prototype.Browser.IE) { 
     2203  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) { 
     2204    Element.Methods[method] = Element.Methods[method].wrap( 
     2205      function(proceed, element) { 
     2206        element = $(element); 
     2207        var position = element.getStyle('position'); 
     2208        if (position != 'static') return proceed(element); 
     2209        element.setStyle({ position: 'relative' }); 
     2210        var value = proceed(element); 
     2211        element.setStyle({ position: position }); 
     2212        return value; 
     2213      } 
     2214    ); 
     2215  }); 
     2216 
     2217  Element.Methods.getStyle = function(element, style) { 
     2218    element = $(element); 
     2219    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); 
     2220    var value = element.style[style]; 
     2221    if (!value && element.currentStyle) value = element.currentStyle[style]; 
     2222 
     2223    if (style == 'opacity') { 
     2224      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) 
     2225        if (value[1]) return parseFloat(value[1]) / 100; 
     2226      return 1.0; 
     2227    } 
     2228 
     2229    if (value == 'auto') { 
     2230      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) 
     2231        return element['offset' + style.capitalize()] + 'px'; 
     2232      return null; 
     2233    } 
     2234    return value; 
     2235  }; 
     2236 
     2237  Element.Methods.setOpacity = function(element, value) { 
     2238    function stripAlpha(filter){ 
     2239      return filter.replace(/alpha\([^\)]*\)/gi,''); 
     2240    } 
     2241    element = $(element); 
     2242    var currentStyle = element.currentStyle; 
     2243    if ((currentStyle && !currentStyle.hasLayout) || 
     2244      (!currentStyle && element.style.zoom == 'normal')) 
     2245        element.style.zoom = 1; 
     2246 
     2247    var filter = element.getStyle('filter'), style = element.style; 
     2248    if (value == 1 || value === '') { 
     2249      (filter = stripAlpha(filter)) ? 
     2250        style.filter = filter : style.removeAttribute('filter'); 
     2251      return element; 
     2252    } else if (value < 0.00001) value = 0; 
     2253    style.filter = stripAlpha(filter) + 
     2254      'alpha(opacity=' + (value * 100) + ')'; 
     2255    return element; 
     2256  }; 
     2257 
     2258  Element._attributeTranslations = { 
     2259    read: { 
     2260      names: { 
     2261        'class': 'className', 
     2262        'for':   'htmlFor' 
     2263      }, 
     2264      values: { 
     2265        _getAttr: function(element, attribute) { 
     2266          return element.getAttribute(attribute, 2); 
     2267        }, 
     2268        _getAttrNode: function(element, attribute) { 
     2269          var node = element.getAttributeNode(attribute); 
     2270          return node ? node.value : ""; 
     2271        }, 
     2272        _getEv: function(element, attribute) { 
     2273          attribute = element.getAttribute(attribute); 
     2274          return attribute ? attribute.toString().slice(23, -2) : null; 
     2275        }, 
     2276        _flag: function(element, attribute) { 
     2277          return $(element).hasAttribute(attribute) ? attribute : null; 
     2278        }, 
     2279        style: function(element) { 
     2280          return element.style.cssText.toLowerCase(); 
     2281        }, 
     2282        title: function(element) { 
     2283          return element.title; 
     2284        } 
     2285      } 
     2286    } 
     2287  }; 
     2288 
     2289  Element._attributeTranslations.write = { 
     2290    names: Object.clone(Element._attributeTranslations.read.names), 
     2291    values: { 
     2292      checked: function(element, value) { 
     2293        element.checked = !!value; 
     2294      }, 
     2295 
     2296      style: function(element, value) { 
     2297        element.style.cssText = value ? value : ''; 
     2298      } 
     2299    } 
     2300  }; 
     2301 
     2302  Element._attributeTranslations.has = {}; 
     2303 
     2304  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + 
     2305      'encType maxLength readOnly longDesc').each(function(attr) { 
     2306    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; 
     2307    Element._attributeTranslations.has[attr.toLowerCase()] = attr; 
     2308  }); 
     2309 
     2310  (function(v) { 
     2311    Object.extend(v, { 
     2312      href:        v._getAttr, 
     2313      src:         v._getAttr, 
     2314      type:        v._getAttr, 
     2315      action:      v._getAttrNode, 
     2316      disabled:    v._flag, 
     2317      checked:     v._flag, 
     2318      readonly:    v._flag, 
     2319      multiple:    v._flag, 
     2320      onload:      v._getEv, 
     2321      onunload:    v._getEv, 
     2322      onclick:     v._getEv, 
     2323      ondblclick:  v._getEv, 
     2324      onmousedown: v._getEv, 
     2325      onmouseup:   v._getEv, 
     2326      onmouseover: v._getEv, 
     2327      onmousemove: v._getEv, 
     2328      onmouseout:  v._getEv, 
     2329      onfocus:     v._getEv, 
     2330      onblur:      v._getEv, 
     2331      onkeypress:  v._getEv, 
     2332      onkeydown:   v._getEv, 
     2333      onkeyup:     v._getEv, 
     2334      onsubmit:    v._getEv, 
     2335      onreset:     v._getEv, 
     2336      onselect:    v._getEv, 
     2337      onchange:    v._getEv 
     2338    }); 
     2339  })(Element._attributeTranslations.read.values); 
     2340} 
     2341 
     2342else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { 
     2343  Element.Methods.setOpacity = function(element, value) { 
     2344    element = $(element); 
     2345    element.style.opacity = (value == 1) ? 0.999999 : 
     2346      (value === '') ? '' : (value < 0.00001) ? 0 : value; 
     2347    return element; 
     2348  }; 
     2349} 
     2350 
     2351else if (Prototype.Browser.WebKit) { 
     2352  Element.Methods.setOpacity = function(element, value) { 
     2353    element = $(element); 
     2354    element.style.opacity = (value == 1 || value === '') ? '' : 
     2355      (value < 0.00001) ? 0 : value; 
     2356 
     2357    if (value == 1) 
     2358      if(element.tagName == 'IMG' && element.width) { 
     2359        element.width++; element.width--; 
     2360      } else try { 
     2361        var n = document.createTextNode(' '); 
     2362        element.appendChild(n); 
     2363        element.removeChild(n); 
     2364      } catch (e) { } 
     2365 
     2366    return element; 
     2367  }; 
     2368 
     2369  // Safari returns margins on body which is incorrect if the child is absolutely 
     2370  // positioned.  For performance reasons, redefine Element#cumulativeOffset for 
     2371  // KHTML/WebKit only. 
     2372  Element.Methods.cumulativeOffset = function(element) { 
     2373    var valueT = 0, valueL = 0; 
     2374    do { 
     2375      valueT += element.offsetTop  || 0; 
     2376      valueL += element.offsetLeft || 0; 
     2377      if (element.offsetParent == document.body) 
     2378        if (Element.getStyle(element, 'position') == 'absolute') break; 
     2379 
     2380      element = element.offsetParent; 
     2381    } while (element); 
     2382 
     2383    return Element._returnOffset(valueL, valueT); 
     2384  }; 
     2385} 
     2386 
     2387if (Prototype.Browser.IE || Prototype.Browser.Opera) { 
     2388  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements 
     2389  Element.Methods.update = function(element, content) { 
     2390    element = $(element); 
     2391 
     2392    if (content && content.toElement) content = content.toElement(); 
     2393    if (Object.isElement(content)) return element.update().insert(content); 
     2394 
     2395    content = Object.toHTML(content); 
     2396    var tagName = element.tagName.toUpperCase(); 
     2397 
     2398    if (tagName in Element._insertionTranslations.tags) { 
     2399      $A(element.childNodes).each(function(node) { element.removeChild(node) }); 
     2400      Element._getContentFromAnonymousElement(tagName, content.stripScripts()) 
     2401        .each(function(node) { element.appendChild(node) }); 
     2402    } 
     2403    else element.innerHTML = content.stripScripts(); 
     2404 
     2405    content.evalScripts.bind(content).defer(); 
     2406    return element; 
     2407  }; 
     2408} 
     2409 
     2410if (document.createElement('div').outerHTML) { 
     2411  Element.Methods.replace = function(element, content) { 
     2412    element = $(element); 
     2413 
     2414    if (content && content.toElement) content = content.toElement(); 
     2415    if (Object.isElement(content)) { 
     2416      element.parentNode.replaceChild(content, element); 
     2417      return element; 
     2418    } 
     2419 
     2420    content = Object.toHTML(content); 
     2421    var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); 
     2422 
     2423    if (Element._insertionTranslations.tags[tagName]) { 
     2424      var nextSibling = element.next(); 
     2425      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); 
     2426      parent.removeChild(element); 
     2427      if (nextSibling) 
     2428        fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); 
     2429      else 
     2430        fragments.each(function(node) { parent.appendChild(node) }); 
     2431    } 
     2432    else element.outerHTML = content.stripScripts(); 
     2433 
     2434    content.evalScripts.bind(content).defer(); 
     2435    return element; 
     2436  }; 
     2437} 
     2438 
     2439Element._returnOffset = function(l, t) { 
     2440  var result = [l, t]; 
     2441  result.left = l; 
     2442  result.top = t; 
     2443  return result; 
    15202444}; 
    15212445 
    1522 Object.extend(Element._attributeTranslations.values, { 
    1523   href: Element._attributeTranslations.values._getAttr, 
    1524   src:  Element._attributeTranslations.values._getAttr, 
    1525   disabled: Element._attributeTranslations.values._flag, 
    1526   checked:  Element._attributeTranslations.values._flag, 
    1527   readonly: Element._attributeTranslations.values._flag, 
    1528   multiple: Element._attributeTranslations.values._flag 
    1529 }); 
     2446Element._getContentFromAnonymousElement = function(tagName, html) { 
     2447  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; 
     2448  if (t) { 
     2449    div.innerHTML = t[0] + html + t[1]; 
     2450    t[2].times(function() { div = div.firstChild }); 
     2451  } else div.innerHTML = html; 
     2452  return $A(div.childNodes); 
     2453}; 
     2454 
     2455Element._insertionTranslations = { 
     2456  before: function(element, node) { 
     2457    element.parentNode.insertBefore(node, element); 
     2458  }, 
     2459  top: function(element, node) { 
     2460    element.insertBefore(node, element.firstChild); 
     2461  }, 
     2462  bottom: function(element, node) { 
     2463    element.appendChild(node); 
     2464  }, 
     2465  after: function(element, node) { 
     2466    element.parentNode.insertBefore(node, element.nextSibling); 
     2467  }, 
     2468  tags: { 
     2469    TABLE:  ['<table>',                '</table>',                   1], 
     2470    TBODY:  ['<table><tbody>',         '</tbody></table>',           2], 
     2471    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3], 
     2472    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], 
     2473    SELECT: ['<select>',               '</select>',                  1] 
     2474  } 
     2475}; 
     2476 
     2477(function() { 
     2478  Object.extend(this.tags, { 
     2479    THEAD: this.tags.TBODY, 
     2480    TFOOT: this.tags.TBODY, 
     2481    TH:    this.tags.TD 
     2482  }); 
     2483}).call(Element._insertionTranslations); 
    15302484 
    15312485Element.Methods.Simulated = { 
    15322486  hasAttribute: function(element, attribute) { 
    1533     var t = Element._attributeTranslations; 
    1534     attribute = t.names[attribute] || attribute; 
    1535     return $(element).getAttributeNode(attribute).specified; 
     2487    attribute = Element._attributeTranslations.has[attribute] || attribute; 
     2488    var node = $(element).getAttributeNode(attribute); 
     2489    return node && node.specified; 
    15362490  } 
    15372491}; 
    15382492 
    1539 // IE is missing .innerHTML support for TABLE-related elements 
    1540 if (document.all && !window.opera){ 
    1541   Element.Methods.update = function(element, html) { 
    1542     element = $(element); 
    1543     html = typeof html == 'undefined' ? '' : html.toString(); 
    1544     var tagName = element.tagName.toUpperCase(); 
    1545     if (['THEAD','TBODY','TR','TD'].include(tagName)) { 
    1546       var div = document.createElement('div'); 
    1547       switch (tagName) { 
    1548         case 'THEAD': 
    1549         case 'TBODY': 
    1550           div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>'; 
    1551           depth = 2; 
    1552           break; 
    1553         case 'TR': 
    1554           div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>'; 
    1555           depth = 3; 
    1556           break; 
    1557         case 'TD': 
    1558           div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>'; 
    1559           depth = 4; 
    1560       } 
    1561       $A(element.childNodes).each(function(node){ 
    1562         element.removeChild(node) 
    1563       }); 
    1564       depth.times(function(){ div = div.firstChild }); 
    1565  
    1566       $A(div.childNodes).each( 
    1567         function(node){ element.appendChild(node) }); 
    1568     } else { 
    1569       element.innerHTML = html.stripScripts(); 
    1570     } 
    1571     setTimeout(function() {html.evalScripts()}, 10); 
     2493Element.Methods.ByTag = { }; 
     2494 
     2495Object.extend(Element, Element.Methods); 
     2496 
     2497if (!Prototype.BrowserFeatures.ElementExtensions && 
     2498    document.createElement('div').__proto__) { 
     2499  window.HTMLElement = { }; 
     2500  window.HTMLElement.prototype = document.createElement('div').__proto__; 
     2501  Prototype.BrowserFeatures.ElementExtensions = true; 
     2502} 
     2503 
     2504Element.extend = (function() { 
     2505  if (Prototype.BrowserFeatures.SpecificElementExtensions) 
     2506    return Prototype.K; 
     2507 
     2508  var Methods = { }, ByTag = Element.Methods.ByTag; 
     2509 
     2510  var extend = Object.extend(function(element) { 
     2511    if (!element || element._extendedByPrototype || 
     2512        element.nodeType != 1 || element == window) return element; 
     2513 
     2514    var methods = Object.clone(Methods), 
     2515      tagName = element.tagName, property, value; 
     2516 
     2517    // extend methods for specific tags 
     2518    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); 
     2519 
     2520    for (property in methods) { 
     2521      value = methods[property]; 
     2522      if (Object.isFunction(value) && !(property in element)) 
     2523        element[property] = value.methodize(); 
     2524    } 
     2525 
     2526    element._extendedByPrototype = Prototype.emptyFunction; 
    15722527    return element; 
    1573   } 
     2528 
     2529  }, { 
     2530    refresh: function() { 
     2531      // extend methods for all tags (Safari doesn't need this) 
     2532      if (!Prototype.BrowserFeatures.ElementExtensions) { 
     2533        Object.extend(Methods, Element.Methods); 
     2534        Object.extend(Methods, Element.Methods.Simulated); 
     2535      } 
     2536    } 
     2537  }); 
     2538 
     2539  extend.refresh(); 
     2540  return extend; 
     2541})(); 
     2542 
     2543Element.hasAttribute = function(element, attribute) { 
     2544  if (element.hasAttribute) return element.hasAttribute(attribute); 
     2545  return Element.Methods.Simulated.hasAttribute(element, attribute); 
    15742546}; 
    15752547 
    1576 Object.extend(Element, Element.Methods); 
    1577  
    1578 var _nativeExtensions = false; 
    1579  
    1580 if(/Konqueror|Safari|KHTML/.test(navigator.userAgent)) 
    1581   ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) { 
    1582     var className = 'HTML' + tag + 'Element'; 
    1583     if(window[className]) return; 
    1584     var klass = window[className] = {}; 
    1585     klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__; 
    1586   }); 
    1587  
    15882548Element.addMethods = function(methods) { 
    1589   Object.extend(Element.Methods, methods || {}); 
     2549  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; 
     2550 
     2551  if (!methods) { 
     2552    Object.extend(Form, Form.Methods); 
     2553    Object.extend(Form.Element, Form.Element.Methods); 
     2554    Object.extend(Element.Methods.ByTag, { 
     2555      "FORM":     Object.clone(Form.Methods), 
     2556      "INPUT":    Object.clone(Form.Element.Methods), 
     2557      "SELECT":   Object.clone(Form.Element.Methods), 
     2558      "TEXTAREA": Object.clone(Form.Element.Methods) 
     2559    }); 
     2560  } 
     2561 
     2562  if (arguments.length == 2) { 
     2563    var tagName = methods; 
     2564    methods = arguments[1]; 
     2565  } 
     2566 
     2567  if (!tagName) Object.extend(Element.Methods, methods || { }); 
     2568  else { 
     2569    if (Object.isArray(tagName)) tagName.each(extend); 
     2570    else extend(tagName); 
     2571  } 
     2572 
     2573  function extend(tagName) { 
     2574    tagName = tagName.toUpperCase(); 
     2575    if (!Element.Methods.ByTag[tagName]) 
     2576      Element.Methods.ByTag[tagName] = { }; 
     2577    Object.extend(Element.Methods.ByTag[tagName], methods); 
     2578  } 
    15902579 
    15912580  function copy(methods, destination, onlyIfAbsent) { 
    15922581    onlyIfAbsent = onlyIfAbsent || false; 
    1593     var cache = Element.extend.cache; 
    15942582    for (var property in methods) { 
    15952583      var value = methods[property]; 
     2584      if (!Object.isFunction(value)) continue; 
    15962585      if (!onlyIfAbsent || !(property in destination)) 
    1597         destination[property] = cache.findOrStore(value); 
    1598     } 
    1599   } 
    1600  
    1601   if (typeof HTMLElement != 'undefined') { 
     2586        destination[property] = value.methodize(); 
     2587    } 
     2588  } 
     2589 
     2590  function findDOMClass(tagName) { 
     2591    var klass; 
     2592    var trans = { 
     2593      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", 
     2594      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", 
     2595      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", 
     2596      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", 
     2597      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": 
     2598      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": 
     2599      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": 
     2600      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": 
     2601      "FrameSet", "IFRAME": "IFrame" 
     2602    }; 
     2603    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; 
     2604    if (window[klass]) return window[klass]; 
     2605    klass = 'HTML' + tagName + 'Element'; 
     2606    if (window[klass]) return window[klass]; 
     2607    klass = 'HTML' + tagName.capitalize() + 'Element'; 
     2608    if (window[klass]) return window[klass]; 
     2609 
     2610    window[klass] = { }; 
     2611    window[klass].prototype = document.createElement(tagName).__proto__; 
     2612    return window[klass]; 
     2613  } 
     2614 
     2615  if (F.ElementExtensions) { 
    16022616    copy(Element.Methods, HTMLElement.prototype); 
    16032617    copy(Element.Methods.Simulated, HTMLElement.prototype, true); 
    1604     copy(Form.Methods, HTMLFormElement.prototype); 
    1605     [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) { 
    1606       copy(Form.Element.Methods, klass.prototype); 
     2618  } 
     2619 
     2620  if (F.SpecificElementExtensions) { 
     2621    for (var tag in Element.Methods.ByTag) { 
     2622      var klass = findDOMClass(tag); 
     2623      if (Object.isUndefined(klass)) continue; 
     2624      copy(T[tag], klass.prototype); 
     2625    } 
     2626  } 
     2627 
     2628  Object.extend(Element, Element.Methods); 
     2629  delete Element.ByTag; 
     2630 
     2631  if (Element.extend.refresh) Element.extend.refresh(); 
     2632  Element.cache = { }; 
     2633}; 
     2634 
     2635document.viewport = { 
     2636  getDimensions: function() { 
     2637    var dimensions = { }; 
     2638    var B = Prototype.Browser; 
     2639    $w('width height').each(function(d) { 
     2640      var D = d.capitalize(); 
     2641      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : 
     2642        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; 
    16072643    }); 
    1608     _nativeExtensions = true; 
    1609   } 
    1610 } 
    1611  
    1612 var Toggle = new Object(); 
    1613 Toggle.display = Element.toggle; 
    1614  
    1615 /*--------------------------------------------------------------------------*/ 
    1616  
    1617 Abstract.Insertion = function(adjacency) { 
    1618   this.adjacency = adjacency; 
    1619 } 
    1620  
    1621 Abstract.Insertion.prototype = { 
    1622   initialize: function(element, content) { 
    1623     this.element = $(element); 
    1624     this.content = content.stripScripts(); 
    1625  
    1626     if (this.adjacency && this.element.insertAdjacentHTML) { 
    1627       try { 
    1628         this.element.insertAdjacentHTML(this.adjacency, this.content); 
    1629       } catch (e) { 
    1630         var tagName = this.element.tagName.toUpperCase(); 
    1631         if (['TBODY', 'TR'].include(tagName)) { 
    1632           this.insertContent(this.contentFromAnonymousTable()); 
    1633         } else { 
    1634           throw e; 
     2644    return dimensions; 
     2645  }, 
     2646 
     2647  getWidth: function() { 
     2648    return this.getDimensions().width; 
     2649  }, 
     2650 
     2651  getHeight: function() { 
     2652    return this.getDimensions().height; 
     2653  }, 
     2654 
     2655  getScrollOffsets: function() { 
     2656    return Element._returnOffset( 
     2657      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, 
     2658      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); 
     2659  } 
     2660}; 
     2661/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, 
     2662 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style 
     2663 * license.  Please see http://www.yui-ext.com/ for more information. */ 
     2664 
     2665var Selector = Class.create({ 
     2666  initialize: function(expression) { 
     2667    this.expression = expression.strip(); 
     2668    this.compileMatcher(); 
     2669  }, 
     2670 
     2671  shouldUseXPath: function() { 
     2672    if (!Prototype.BrowserFeatures.XPath) return false; 
     2673 
     2674    var e = this.expression; 
     2675 
     2676    // Safari 3 chokes on :*-of-type and :empty 
     2677    if (Prototype.Browser.WebKit && 
     2678     (e.include("-of-type") || e.include(":empty"))) 
     2679      return false; 
     2680 
     2681    // XPath can't do namespaced attributes, nor can it read 
     2682    // the "checked" property from DOM nodes 
     2683    if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) 
     2684      return false; 
     2685 
     2686    return true; 
     2687  }, 
     2688 
     2689  compileMatcher: function() { 
     2690    if (this.shouldUseXPath()) 
     2691      return this.compileXPathMatcher(); 
     2692 
     2693    var e = this.expression, ps = Selector.patterns, h = Selector.handlers, 
     2694        c = Selector.criteria, le, p, m; 
     2695 
     2696    if (Selector._cache[e]) { 
     2697      this.matcher = Selector._cache[e]; 
     2698      return; 
     2699    } 
     2700 
     2701    this.matcher = ["this.matcher = function(root) {", 
     2702                    "var r = root, h = Selector.handlers, c = false, n;"]; 
     2703 
     2704    while (e && le != e && (/\S/).test(e)) { 
     2705      le = e; 
     2706      for (var i in ps) { 
     2707        p = ps[i]; 
     2708        if (m = e.match(p)) { 
     2709          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : 
     2710              new Template(c[i]).evaluate(m)); 
     2711          e = e.replace(m[0], ''); 
     2712          break; 
    16352713        } 
    16362714      } 
    1637     } else { 
    1638       this.range = this.element.ownerDocument.createRange(); 
    1639       if (this.initializeRange) this.initializeRange(); 
    1640       this.insertContent([this.range.createContextualFragment(this.content)]); 
    1641     } 
    1642  
    1643     setTimeout(function() {content.evalScripts()}, 10); 
    1644   }, 
    1645  
    1646   contentFromAnonymousTable: function() { 
    1647     var div = document.createElement('div'); 
    1648     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; 
    1649     return $A(div.childNodes[0].childNodes[0].childNodes); 
    1650   } 
    1651 } 
    1652  
    1653 var Insertion = new Object(); 
    1654  
    1655 Insertion.Before = Class.create(); 
    1656 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { 
    1657   initializeRange: function() { 
    1658     this.range.setStartBefore(this.element); 
    1659   }, 
    1660  
    1661   insertContent: function(fragments) { 
    1662     fragments.each((function(fragment) { 
    1663       this.element.parentNode.insertBefore(fragment, this.element); 
    1664     }).bind(this)); 
    1665   } 
    1666 }); 
    1667  
    1668 Insertion.Top = Class.create(); 
    1669 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { 
    1670   initializeRange: function() { 
    1671     this.range.selectNodeContents(this.element); 
    1672     this.range.collapse(true); 
    1673   }, 
    1674  
    1675   insertContent: function(fragments) { 
    1676     fragments.reverse(false).each((function(fragment) { 
    1677       this.element.insertBefore(fragment, this.element.firstChild); 
    1678     }).bind(this)); 
    1679   } 
    1680 }); 
    1681  
    1682 Insertion.Bottom = Class.create(); 
    1683 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { 
    1684   initializeRange: function() { 
    1685     this.range.selectNodeContents(this.element); 
    1686     this.range.collapse(this.element); 
    1687   }, 
    1688  
    1689   insertContent: function(fragments) { 
    1690     fragments.each((function(fragment) { 
    1691       this.element.appendChild(fragment); 
    1692     }).bind(this)); 
    1693   } 
    1694 }); 
    1695  
    1696 Insertion.After = Class.create(); 
    1697 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { 
    1698   initializeRange: function() { 
    1699     this.range.setStartAfter(this.element); 
    1700   }, 
    1701  
    1702   insertContent: function(fragments) { 
    1703     fragments.each((function(fragment) { 
    1704       this.element.parentNode.insertBefore(fragment, 
    1705         this.element.nextSibling); 
    1706     }).bind(this)); 
    1707   } 
    1708 }); 
    1709  
    1710 /*--------------------------------------------------------------------------*/ 
    1711  
    1712 Element.ClassNames = Class.create(); 
    1713 Element.ClassNames.prototype = { 
    1714   initialize: function(element) { 
    1715     this.element = $(element); 
    1716   }, 
    1717  
    1718   _each: function(iterator) { 
    1719     this.element.className.split(/\s+/).select(function(name) { 
    1720       return name.length > 0; 
    1721     })._each(iterator); 
    1722   }, 
    1723  
    1724   set: function(className) { 
    1725     this.element.className = className; 
    1726   }, 
    1727  
    1728   add: function(classNameToAdd) { 
    1729     if (this.include(classNameToAdd)) return; 
    1730     this.set($A(this).concat(classNameToAdd).join(' ')); 
    1731   }, 
    1732  
    1733   remove: function(classNameToRemove) { 
    1734     if (!this.include(classNameToRemove)) return; 
    1735     this.set($A(this).without(classNameToRemove).join(' ')); 
    1736   }, 
    1737  
    1738   toString: function() { 
    1739     return $A(this).join(' '); 
    1740   } 
    1741 }; 
    1742  
    1743 Object.extend(Element.ClassNames.prototype, Enumerable); 
    1744 var Selector = Class.create(); 
    1745 Selector.prototype = { 
    1746   initialize: function(expression) { 
    1747     this.params = {classNames: []}; 
    1748     this.expression = expression.toString().strip(); 
    1749     this.parseExpression(); 
    1750     this.compileMatcher(); 
    1751   }, 
    1752  
    1753   parseExpression: function() { 
    1754     function abort(message) { throw 'Parse error in selector: ' + message; } 
    1755  
    1756     if (this.expression == '')  abort('empty expression'); 
    1757  
    1758     var params = this.params, expr = this.expression, match, modifier, clause, rest; 
    1759     while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) { 
    1760       params.attributes = params.attributes || []; 
    1761       params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''}); 
    1762       expr = match[1]; 
    1763     } 
    1764  
    1765     if (expr == '*') return this.params.wildcard = true; 
    1766  
    1767     while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) { 
    1768       modifier = match[1], clause = match[2], rest = match[3]; 
    1769       switch (modifier) { 
    1770         case '#':       params.id = clause; break; 
    1771         case '.':       params.classNames.push(clause); break; 
    1772         case '': 
    1773         case undefined: params.tagName = clause.toUpperCase(); break; 
    1774         default:        abort(expr.inspect()); 
    1775       } 
    1776       expr = rest; 
    1777     } 
    1778  
    1779     if (expr.length > 0) abort(expr.inspect()); 
    1780   }, 
    1781  
    1782   buildMatchExpression: function() { 
    1783     var params = this.params, conditions = [], clause; 
    1784  
    1785     if (params.wildcard) 
    1786       conditions.push('true'); 
    1787     if (clause = params.id) 
    1788       conditions.push('element.readAttribute("id") == ' + clause.inspect()); 
    1789     if (clause = params.tagName) 
    1790       conditions.push('element.tagName.toUpperCase() == ' + clause.inspect()); 
    1791     if ((clause = params.classNames).length > 0) 
    1792       for (var i = 0, length = clause.length; i < length; i++) 
    1793         conditions.push('element.hasClassName(' + clause[i].inspect() + ')'); 
    1794     if (clause = params.attributes) { 
    1795       clause.each(function(attribute) { 
    1796         var value = 'element.readAttribute(' + attribute.name.inspect() + ')'; 
    1797         var splitValueBy = function(delimiter) { 
    1798           return value + ' && ' + value + '.split(' + delimiter.inspect() + ')'; 
     2715    } 
     2716 
     2717    this.matcher.push("return h.unique(n);\n}"); 
     2718    eval(this.matcher.join('\n')); 
     2719    Selector._cache[this.expression] = this.matcher; 
     2720  }, 
     2721 
     2722  compileXPathMatcher: function() { 
     2723    var e = this.expression, ps = Selector.patterns, 
     2724        x = Selector.xpath, le, m; 
     2725 
     2726    if (Selector._cache[e]) { 
     2727      this.xpath = Selector._cache[e]; return; 
     2728    } 
     2729 
     2730    this.matcher = ['.//*']; 
     2731    while (e && le != e && (/\S/).test(e)) { 
     2732      le = e; 
     2733      for (var i in ps) { 
     2734        if (m = e.match(ps[i])) { 
     2735          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : 
     2736            new Template(x[i]).evaluate(m)); 
     2737          e = e.replace(m[0], ''); 
     2738          break; 
    17992739        } 
    1800  
    1801         switch (attribute.operator) { 
    1802           case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break; 
    1803           case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break; 
    1804           case '|=':      conditions.push( 
    1805                             splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect() 
    1806                           ); break; 
    1807           case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break; 
    1808           case '': 
    1809           case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break; 
    1810           default:        throw 'Unknown operator ' + attribute.operator + ' in selector'; 
     2740      } 
     2741    } 
     2742 
     2743    this.xpath = this.matcher.join(''); 
     2744    Selector._cache[this.expression] = this.xpath; 
     2745  }, 
     2746 
     2747  findElements: function(root) { 
     2748    root = root || document; 
     2749    if (this.xpath) return document._getElementsByXPath(this.xpath, root); 
     2750    return this.matcher(root); 
     2751  }, 
     2752 
     2753  match: function(element) { 
     2754    this.tokens = []; 
     2755 
     2756    var e = this.expression, ps = Selector.patterns, as = Selector.assertions; 
     2757    var le, p, m; 
     2758 
     2759    while (e && le !== e && (/\S/).test(e)) { 
     2760      le = e; 
     2761      for (var i in ps) { 
     2762        p = ps[i]; 
     2763        if (m = e.match(p)) { 
     2764          // use the Selector.assertions methods unless the selector 
     2765          // is too complex. 
     2766          if (as[i]) { 
     2767            this.tokens.push([i, Object.clone(m)]); 
     2768            e = e.replace(m[0], ''); 
     2769          } else { 
     2770            // reluctantly do a document-wide search 
     2771            // and look for a match in the array 
     2772            return this.findElements(document).include(element); 
     2773          } 
    18112774        } 
    1812       }); 
    1813     } 
    1814  
    1815     return conditions.join(' && '); 
    1816   }, 
    1817  
    1818   compileMatcher: function() { 
    1819     this.match = new Function('element', 'if (!element.tagName) return false; \ 
    1820       element = $(element); \ 
    1821       return ' + this.buildMatchExpression()); 
    1822   }, 
    1823  
    1824   findElements: function(scope) { 
    1825     var element; 
    1826  
    1827     if (element = $(this.params.id)) 
    1828       if (this.match(element)) 
    1829         if (!scope || Element.childOf(element, scope)) 
    1830           return [element]; 
    1831  
    1832     scope = (scope || document).getElementsByTagName(this.params.tagName || '*'); 
    1833  
    1834     var results = []; 
    1835     for (var i = 0, length = scope.length; i < length; i++) 
    1836       if (this.match(element = scope[i])) 
    1837         results.push(Element.extend(element)); 
    1838  
    1839     return results; 
     2775      } 
     2776    } 
     2777 
     2778    var match = true, name, matches; 
     2779    for (var i = 0, token; token = this.tokens[i]; i++) { 
     2780      name = token[0], matches = token[1]; 
     2781      if (!Selector.assertions[name](element, matches)) { 
     2782        match = false; break; 
     2783      } 
     2784    } 
     2785 
     2786    return match; 
    18402787  }, 
    18412788 
    18422789  toString: function() { 
    18432790    return this.expression; 
    1844   } 
     2791  }, 
     2792 
     2793  inspect: function() { 
     2794    return "#<Selector:" + this.expression.inspect() + ">"; 
     2795  } 
     2796}); 
     2797 
     2798Object.extend(Selector, { 
     2799  _cache: { }, 
     2800 
     2801  xpath: { 
     2802    descendant:   "//*", 
     2803    child:        "/*", 
     2804    adjacent:     "/following-sibling::*[1]", 
     2805    laterSibling: '/following-sibling::*', 
     2806    tagName:      function(m) { 
     2807      if (m[1] == '*') return ''; 
     2808      return "[local-name()='" + m[1].toLowerCase() + 
     2809             "' or local-name()='" + m[1].toUpperCase() + "']"; 
     2810    }, 
     2811    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]", 
     2812    id:           "[@id='#{1}']", 
     2813    attrPresence: function(m) { 
     2814      m[1] = m[1].toLowerCase(); 
     2815      return new Template("[@#{1}]").evaluate(m); 
     2816    }, 
     2817    attr: function(m) { 
     2818      m[1] = m[1].toLowerCase(); 
     2819      m[3] = m[5] || m[6]; 
     2820      return new Template(Selector.xpath.operators[m[2]]).evaluate(m); 
     2821    }, 
     2822    pseudo: function(m) { 
     2823      var h = Selector.xpath.pseudos[m[1]]; 
     2824      if (!h) return ''; 
     2825      if (Object.isFunction(h)) return h(m); 
     2826      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); 
     2827    }, 
     2828    operators: { 
     2829      '=':  "[@#{1}='#{3}']", 
     2830      '!=': "[@#{1}!='#{3}']", 
     2831      '^=': "[starts-with(@#{1}, '#{3}')]", 
     2832      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", 
     2833      '*=': "[contains(@#{1}, '#{3}')]", 
     2834      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", 
     2835      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" 
     2836    }, 
     2837    pseudos: { 
     2838      'first-child': '[not(preceding-sibling::*)]', 
     2839      'last-child':  '[not(following-sibling::*)]', 
     2840      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]', 
     2841      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", 
     2842      'checked':     "[@checked]", 
     2843      'disabled':    "[@disabled]", 
     2844      'enabled':     "[not(@disabled)]", 
     2845      'not': function(m) { 
     2846        var e = m[6], p = Selector.patterns, 
     2847            x = Selector.xpath, le, v; 
     2848 
     2849        var exclusion = []; 
     2850        while (e && le != e && (/\S/).test(e)) { 
     2851          le = e; 
     2852          for (var i in p) { 
     2853            if (m = e.match(p[i])) { 
     2854              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); 
     2855              exclusion.push("(" + v.substring(1, v.length - 1) + ")"); 
     2856              e = e.replace(m[0], ''); 
     2857              break; 
     2858            } 
     2859          } 
     2860        } 
     2861        return "[not(" + exclusion.join(" and ") + ")]"; 
     2862      }, 
     2863      'nth-child':      function(m) { 
     2864        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); 
     2865      }, 
     2866      'nth-last-child': function(m) { 
     2867        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); 
     2868      }, 
     2869      'nth-of-type':    function(m) { 
     2870        return Selector.xpath.pseudos.nth("position() ", m); 
     2871      }, 
     2872      'nth-last-of-type': function(m) { 
     2873        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); 
     2874      }, 
     2875      'first-of-type':  function(m) { 
     2876        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); 
     2877      }, 
     2878      'last-of-type':   function(m) { 
     2879        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); 
     2880      }, 
     2881      'only-of-type':   function(m) { 
     2882        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); 
     2883      }, 
     2884      nth: function(fragment, m) { 
     2885        var mm, formula = m[6], predicate; 
     2886        if (formula == 'even') formula = '2n+0'; 
     2887        if (formula == 'odd')  formula = '2n+1'; 
     2888        if (mm = formula.match(/^(\d+)$/)) // digit only 
     2889          return '[' + fragment + "= " + mm[1] + ']'; 
     2890        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 
     2891          if (mm[1] == "-") mm[1] = -1; 
     2892          var a = mm[1] ? Number(mm[1]) : 1; 
     2893          var b = mm[2] ? Number(mm[2]) : 0; 
     2894          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + 
     2895          "((#{fragment} - #{b}) div #{a} >= 0)]"; 
     2896          return new Template(predicate).evaluate({ 
     2897            fragment: fragment, a: a, b: b }); 
     2898        } 
     2899      } 
     2900    } 
     2901  }, 
     2902 
     2903  criteria: { 
     2904    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;', 
     2905    className:    'n = h.className(n, r, "#{1}", c); c = false;', 
     2906    id:           'n = h.id(n, r, "#{1}", c);        c = false;', 
     2907    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', 
     2908    attr: function(m) { 
     2909      m[3] = (m[5] || m[6]); 
     2910      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); 
     2911    }, 
     2912    pseudo: function(m) { 
     2913      if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); 
     2914      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); 
     2915    }, 
     2916    descendant:   'c = "descendant";', 
     2917    child:        'c = "child";', 
     2918    adjacent:     'c = "adjacent";', 
     2919    laterSibling: 'c = "laterSibling";' 
     2920  }, 
     2921 
     2922  patterns: { 
     2923    // combinators must be listed first 
     2924    // (and descendant needs to be last combinator) 
     2925    laterSibling: /^\s*~\s*/, 
     2926    child:        /^\s*>\s*/, 
     2927    adjacent:     /^\s*\+\s*/, 
     2928    descendant:   /^\s/, 
     2929 
     2930    // selectors follow 
     2931    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/, 
     2932    id:           /^#([\w\-\*]+)(\b|$)/, 
     2933    className:    /^\.([\w\-\*]+)(\b|$)/, 
     2934    pseudo: 
     2935/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, 
     2936    attrPresence: /^\[([\w]+)\]/, 
     2937    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ 
     2938  }, 
     2939 
     2940  // for Selector.match and Element#match 
     2941  assertions: { 
     2942    tagName: function(element, matches) { 
     2943      return matches[1].toUpperCase() == element.tagName.toUpperCase(); 
     2944    }, 
     2945 
     2946    className: function(element, matches) { 
     2947      return Element.hasClassName(element, matches[1]); 
     2948    }, 
     2949 
     2950    id: function(element, matches) { 
     2951      return element.id === matches[1]; 
     2952    }, 
     2953 
     2954    attrPresence: function(element, matches) { 
     2955      return Element.hasAttribute(element, matches[1]); 
     2956    }, 
     2957 
     2958    attr: function(element, matches) { 
     2959      var nodeValue = Element.readAttribute(element, matches[1]); 
     2960      return Selector.operators[matches[2]](nodeValue, matches[3]); 
     2961    } 
     2962  }, 
     2963 
     2964  handlers: { 
     2965    // UTILITY FUNCTIONS 
     2966    // joins two collections 
     2967    concat: function(a, b) { 
     2968      for (var i = 0, node; node = b[i]; i++) 
     2969        a.push(node); 
     2970      return a; 
     2971    }, 
     2972 
     2973    // marks an array of nodes for counting 
     2974    mark: function(nodes) { 
     2975      for (var i = 0, node; node = nodes[i]; i++) 
     2976        node._counted = true; 
     2977      return nodes; 
     2978    }, 
     2979 
     2980    unmark: function(nodes) { 
     2981      for (var i = 0, node; node = nodes[i]; i++) 
     2982        node._counted = undefined; 
     2983      return nodes; 
     2984    }, 
     2985 
     2986    // mark each child node with its position (for nth calls) 
     2987    // "ofType" flag indicates whether we're indexing for nth-of-type 
     2988    // rather than nth-child 
     2989    index: function(parentNode, reverse, ofType) { 
     2990      parentNode._counted = true; 
     2991      if (reverse) { 
     2992        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { 
     2993          var node = nodes[i]; 
     2994          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; 
     2995        } 
     2996      } else { 
     2997        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) 
     2998          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; 
     2999      } 
     3000    }, 
     3001 
     3002    // filters out duplicates and extends all nodes 
     3003    unique: function(nodes) { 
     3004      if (nodes.length == 0) return nodes; 
     3005      var results = [], n; 
     3006      for (var i = 0, l = nodes.length; i < l; i++) 
     3007        if (!(n = nodes[i])._counted) { 
     3008          n._counted = true; 
     3009          results.push(Element.extend(n)); 
     3010        } 
     3011      return Selector.handlers.unmark(results); 
     3012    }, 
     3013 
     3014    // COMBINATOR FUNCTIONS 
     3015    descendant: function(nodes) { 
     3016      var h = Selector.handlers; 
     3017      for (var i = 0, results = [], node; node = nodes[i]; i++) 
     3018        h.concat(results, node.getElementsByTagName('*')); 
     3019      return results; 
     3020    }, 
     3021 
     3022    child: function(nodes) { 
     3023      var h = Selector.handlers; 
     3024      for (var i = 0, results = [], node; node = nodes[i]; i++) { 
     3025        for (var j = 0, child; child = node.childNodes[j]; j++) 
     3026          if (child.nodeType == 1 && child.tagName != '!') results.push(child); 
     3027      } 
     3028      return results; 
     3029    }, 
     3030 
     3031    adjacent: function(nodes) { 
     3032      for (var i = 0, results = [], node; node = nodes[i]; i++) { 
     3033        var next = this.nextElementSibling(node); 
     3034        if (next) results.push(next); 
     3035      } 
     3036      return results; 
     3037    }, 
     3038 
     3039    laterSibling: function(nodes) { 
     3040      var h = Selector.handlers; 
     3041      for (var i = 0, results = [], node; node = nodes[i]; i++) 
     3042        h.concat(results, Element.nextSiblings(node)); 
     3043      return results; 
     3044    }, 
     3045 
     3046    nextElementSibling: function(node) { 
     3047      while (node = node.nextSibling) 
     3048              if (node.nodeType == 1) return node; 
     3049      return null; 
     3050    }, 
     3051 
     3052    previousElementSibling: function(node) { 
     3053      while (node = node.previousSibling) 
     3054        if (node.nodeType == 1) return node; 
     3055      return null; 
     3056    }, 
     3057 
     3058    // TOKEN FUNCTIONS 
     3059    tagName: function(nodes, root, tagName, combinator) { 
     3060      var uTagName = tagName.toUpperCase(); 
     3061      var results = [], h = Selector.handlers; 
     3062      if (nodes) { 
     3063        if (combinator) { 
     3064          // fastlane for ordinary descendant combinators 
     3065          if (combinator == "descendant") { 
     3066            for (var i = 0, node; node = nodes[i]; i++) 
     3067              h.concat(results, node.getElementsByTagName(tagName)); 
     3068            return results; 
     3069          } else nodes = this[combinator](nodes); 
     3070          if (tagName == "*") return nodes; 
     3071        } 
     3072        for (var i = 0, node; node = nodes[i]; i++) 
     3073          if (node.tagName.toUpperCase() === uTagName) results.push(node); 
     3074        return results; 
     3075      } else return root.getElementsByTagName(tagName); 
     3076    }, 
     3077 
     3078    id: function(nodes, root, id, combinator) { 
     3079      var targetNode = $(id), h = Selector.handlers; 
     3080      if (!targetNode) return []; 
     3081      if (!nodes && root == document) return [targetNode]; 
     3082      if (nodes) { 
     3083        if (combinator) { 
     3084          if (combinator == 'child') { 
     3085            for (var i = 0, node; node = nodes[i]; i++) 
     3086              if (targetNode.parentNode == node) return [targetNode]; 
     3087          } else if (combinator == 'descendant') { 
     3088            for (var i = 0, node; node = nodes[i]; i++) 
     3089              if (Element.descendantOf(targetNode, node)) return [targetNode]; 
     3090          } else if (combinator == 'adjacent') { 
     3091            for (var i = 0, node; node = nodes[i]; i++) 
     3092              if (Selector.handlers.previousElementSibling(targetNode) == node) 
     3093                return [targetNode]; 
     3094          } else nodes = h[combinator](nodes); 
     3095        } 
     3096        for (var i = 0, node; node = nodes[i]; i++) 
     3097          if (node == targetNode) return [targetNode]; 
     3098        return []; 
     3099      } 
     3100      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; 
     3101    }, 
     3102 
     3103    className: function(nodes, root, className, combinator) { 
     3104      if (nodes && combinator) nodes = this[combinator](nodes); 
     3105      return Selector.handlers.byClassName(nodes, root, className); 
     3106    }, 
     3107 
     3108    byClassName: function(nodes, root, className) { 
     3109      if (!nodes) nodes = Selector.handlers.descendant([root]); 
     3110      var needle = ' ' + className + ' '; 
     3111      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { 
     3112        nodeClassName = node.className; 
     3113        if (nodeClassName.length == 0) continue; 
     3114        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) 
     3115          results.push(node); 
     3116      } 
     3117      return results; 
     3118    }, 
     3119 
     3120    attrPresence: function(nodes, root, attr) { 
     3121      if (!nodes) nodes = root.getElementsByTagName("*"); 
     3122      var results = []; 
     3123      for (var i = 0, node; node = nodes[i]; i++) 
     3124        if (Element.hasAttribute(node, attr)) results.push(node); 
     3125      return results; 
     3126    }, 
     3127 
     3128    attr: function(nodes, root, attr, value, operator) { 
     3129      if (!nodes) nodes = root.getElementsByTagName("*"); 
     3130      var handler = Selector.operators[operator], results = []; 
     3131      for (var i = 0, node; node = nodes[i]; i++) { 
     3132        var nodeValue = Element.readAttribute(node, attr); 
     3133        if (nodeValue === null) continue; 
     3134        if (handler(nodeValue, value)) results.push(node); 
     3135      } 
     3136      return results; 
     3137    }, 
     3138 
     3139    pseudo: function(nodes, name, value, root, combinator) { 
     3140      if (nodes && combinator) nodes = this[combinator](nodes); 
     3141      if (!nodes) nodes = root.getElementsByTagName("*"); 
     3142      return Selector.pseudos[name](nodes, value, root); 
     3143    } 
     3144  }, 
     3145 
     3146  pseudos: { 
     3147    'first-child': function(nodes, value, root) { 
     3148      for (var i = 0, results = [], node; node = nodes[i]; i++) { 
     3149        if (Selector.handlers.previousElementSibling(node)) continue; 
     3150          results.push(node); 
     3151      } 
     3152      return results; 
     3153    }, 
     3154    'last-child': function(nodes, value, root) { 
     3155      for (var i = 0, results = [], node; node = nodes[i]; i++) { 
     3156        if (Selector.handlers.nextElementSibling(node)) continue; 
     3157          results.push(node); 
     3158      } 
     3159      return results; 
     3160    }, 
     3161    'only-child': function(nodes, value, root) { 
     3162      var h = Selector.handlers; 
     3163      for (var i = 0, results = [], node; node = nodes[i]; i++) 
     3164        if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) 
     3165          results.push(node); 
     3166      return results; 
     3167    }, 
     3168    'nth-child':        function(nodes, formula, root) { 
     3169      return Selector.pseudos.nth(nodes, formula, root); 
     3170    }, 
     3171    'nth-last-child':   function(nodes, formula, root) { 
     3172      return Selector.pseudos.nth(nodes, formula, root, true); 
     3173    }, 
     3174    'nth-of-type':      function(nodes, formula, root) { 
     3175      return Selector.pseudos.nth(nodes, formula, root, false, true); 
     3176    }, 
     3177    'nth-last-of-type': function(nodes, formula, root) { 
     3178      return Selector.pseudos.nth(nodes, formula, root, true, true); 
     3179    }, 
     3180    'first-of-type':    function(nodes, formula, root) { 
     3181      return Selector.pseudos.nth(nodes, "1", root, false, true); 
     3182    }, 
     3183    'last-of-type':     function(nodes, formula, root) { 
     3184      return Selector.pseudos.nth(nodes, "1", root, true, true); 
     3185    }, 
     3186    'only-of-type':     function(nodes, formula, root) { 
     3187      var p = Selector.pseudos; 
     3188      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); 
     3189    }, 
     3190 
     3191    // handles the an+b logic 
     3192    getIndices: function(a, b, total) { 
     3193      if (a == 0) return b > 0 ? [b] : []; 
     3194      return $R(1, total).inject([], function(memo, i) { 
     3195        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); 
     3196        return memo; 
     3197      }); 
     3198    }, 
     3199 
     3200    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type 
     3201    nth: function(nodes, formula, root, reverse, ofType) { 
     3202      if (nodes.length == 0) return []; 
     3203      if (formula == 'even') formula = '2n+0'; 
     3204      if (formula == 'odd')  formula = '2n+1'; 
     3205      var h = Selector.handlers, results = [], indexed = [], m; 
     3206      h.mark(nodes); 
     3207      for (var i = 0, node; node = nodes[i]; i++) { 
     3208        if (!node.parentNode._counted) { 
     3209          h.index(node.parentNode, reverse, ofType); 
     3210          indexed.push(node.parentNode); 
     3211        } 
     3212      } 
     3213      if (formula.match(/^\d+$/)) { // just a number 
     3214        formula = Number(formula); 
     3215        for (var i = 0, node; node = nodes[i]; i++) 
     3216          if (node.nodeIndex == formula) results.push(node); 
     3217      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b 
     3218        if (m[1] == "-") m[1] = -1; 
     3219        var a = m[1] ? Number(m[1]) : 1; 
     3220        var b = m[2] ? Number(m[2]) : 0; 
     3221        var indices = Selector.pseudos.getIndices(a, b, nodes.length); 
     3222        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { 
     3223          for (var j = 0; j < l; j++) 
     3224            if (node.nodeIndex == indices[j]) results.push(node); 
     3225        } 
     3226      } 
     3227      h.unmark(nodes); 
     3228      h.unmark(indexed); 
     3229      return results; 
     3230    }, 
     3231 
     3232    'empty': function(nodes, value, root) { 
     3233      for (var i = 0, results = [], node; node = nodes[i]; i++) { 
     3234        // IE treats comments as element nodes 
     3235        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; 
     3236        results.push(node); 
     3237      } 
     3238      return results; 
     3239    }, 
     3240 
     3241    'not': function(nodes, selector, root) { 
     3242      var h = Selector.handlers, selectorType, m; 
     3243      var exclusions = new Selector(selector).findElements(root); 
     3244      h.mark(exclusions); 
     3245      for (var i = 0, results = [], node; node = nodes[i]; i++) 
     3246        if (!node._counted) results.push(node); 
     3247      h.unmark(exclusions); 
     3248      return results; 
     3249    }, 
     3250 
     3251    'enabled': function(nodes, value, root) { 
     3252      for (var i = 0, results = [], node; node = nodes[i]; i++) 
     3253        if (!node.disabled) results.push(node); 
     3254      return results; 
     3255    }, 
     3256 
     3257    'disabled': function(nodes, value, root) { 
     3258      for (var i = 0, results = [], node; node = nodes[i]; i++) 
     3259        if (node.disabled) results.push(node); 
     3260      return results; 
     3261    }, 
     3262 
     3263    'checked': function(nodes, value, root) { 
     3264      for (var i = 0, results = [], node; node = nodes[i]; i++) 
     3265        if (node.checked) results.push(node); 
     3266      return results; 
     3267    } 
     3268  }, 
     3269 
     3270  operators: { 
     3271    '=':  function(nv, v) { return nv == v; }, 
     3272    '!=': function(nv, v) { return nv != v; }, 
     3273    '^=': function(nv, v) { return nv.startsWith(v); }, 
     3274    '$=': function(nv, v) { return nv.endsWith(v); }, 
     3275    '*=': function(nv, v) { return nv.include(v); }, 
     3276    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, 
     3277    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } 
     3278  }, 
     3279 
     3280  matchElements: function(elements, expression) { 
     3281    var matches = new Selector(expression).findElements(), h = Selector.handlers; 
     3282    h.mark(matches); 
     3283    for (var i = 0, results = [], element; element = elements[i]; i++) 
     3284      if (element._counted) results.push(element); 
     3285    h.unmark(matches); 
     3286    return results; 
     3287  }, 
     3288 
     3289  findElement: function(elements, expression, index) { 
     3290    if (Object.isNumber(expression)) { 
     3291      index = expression; expression = false; 
     3292    } 
     3293    return Selector.matchElements(elements, expression || '*')[index || 0]; 
     3294  }, 
     3295 
     3296  findChildElements: function(element, expressions) { 
     3297    var exprs = expressions.join(','); 
     3298    expressions = []; 
     3299    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { 
     3300      expressions.push(m[1].strip()); 
     3301    }); 
     3302    var results = [], h = Selector.handlers; 
     3303    for (var i = 0, l = expressions.length, selector; i < l; i++) { 
     3304      selector = new Selector(expressions[i].strip()); 
     3305      h.concat(results, selector.findElements(element)); 
     3306    } 
     3307    return (l > 1) ? h.unique(results) : results; 
     3308  } 
     3309}); 
     3310 
     3311if (Prototype.Browser.IE) { 
     3312  // IE returns comment nodes on getElementsByTagName("*"). 
     3313  // Filter them out. 
     3314  Selector.handlers.concat = function(a, b) { 
     3315    for (var i = 0, node; node = b[i]; i++) 
     3316      if (node.tagName !== "!") a.push(node); 
     3317    return a; 
     3318  }; 
    18453319} 
    1846  
    1847 Object.extend(Selector, { 
    1848   matchElements: function(elements, expression) { 
    1849     var selector = new Selector(expression); 
    1850     return elements.select(selector.match.bind(selector)).map(Element.extend); 
    1851   }, 
    1852  
    1853   findElement: function(elements, expression, index) { 
    1854     if (typeof expression == 'number') index = expression, expression = false; 
    1855     return Selector.matchElements(elements, expression || '*')[index || 0]; 
    1856   }, 
    1857  
    1858   findChildElements: function(element, expressions) { 
    1859     return expressions.map(function(expression) { 
    1860       return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) { 
    1861         var selector = new Selector(expr); 
    1862         return results.inject([], function(elements, result) { 
    1863           return elements.concat(selector.findElements(result || element)); 
    1864         }); 
    1865       }); 
    1866     }).flatten(); 
    1867   } 
    1868 }); 
    18693320 
    18703321function $$() { 
     
    18773328  }, 
    18783329 
    1879   serializeElements: function(elements, getHash) { 
    1880     var data = elements.inject({}, function(result, element) { 
     3330  serializeElements: function(elements, options) { 
     3331    if (typeof options != 'object') options = { hash: !!options }; 
     3332    else if (Object.isUndefined(options.hash)) options.hash = true; 
     3333    var key, value, submitted = false, submit = options.submit; 
     3334 
     3335    var data = elements.inject({ }, function(result, element) { 
    18813336      if (!element.disabled && element.name) { 
    1882         var key = element.name, value = $(element).getValue(); 
    1883         if (value != undefined) { 
    1884           if (result[key]) { 
    1885             if (result[key].constructor != Array) result[key] = [result[key]]; 
     3337        key = element.name; value = $(element).getValue(); 
     3338        if (value != null && (element.type != 'submit' || (!submitted && 
     3339            submit !== false && (!submit || key == submit) && (submitted = true)))) { 
     3340          if (key in result) { 
     3341            // a key is already present; construct an array of values 
     3342            if (!Object.isArray(result[key])) result[key] = [result[key]]; 
    18863343            result[key].push(value); 
    18873344          } 
     
    18923349    }); 
    18933350 
    1894     return getHash ? data : Hash.toQueryString(data); 
     3351    return options.hash ? data : Object.toQueryString(data); 
    18953352  } 
    18963353}; 
    18973354 
    18983355Form.Methods = { 
    1899   serialize: function(form, getHash) { 
    1900     return Form.serializeElements(Form.getElements(form), getHash); 
     3356  serialize: function(form, options) { 
     3357    return Form.serializeElements(Form.getElements(form), options); 
    19013358  }, 
    19023359 
     
    19293386  disable: function(form) { 
    19303387    form = $(form); 
    1931     form.getElements().each(function(element) { 
    1932       element.blur(); 
    1933       element.disabled = 'true'; 
    1934     }); 
     3388    Form.getElements(form).invoke('disable'); 
    19353389    return form; 
    19363390  }, 
     
    19383392  enable: function(form) { 
    19393393    form = $(form); 
    1940     form.getElements().each(function(element) { 
    1941       element.disabled = ''; 
     3394    Form.getElements(form).invoke('enable'); 
     3395    return form; 
     3396  }, 
     3397 
     3398  findFirstElement: function(form) { 
     3399    var elements = $(form).getElements().findAll(function(element) { 
     3400      return 'hidden' != element.type && !element.disabled; 
    19423401    }); 
    1943     return form; 
    1944   }, 
    1945  
    1946   findFirstElement: function(form) { 
    1947     return $(form).getElements().find(function(element) { 
    1948       return element.type != 'hidden' && !element.disabled && 
    1949         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); 
     3402    var firstByIndex = elements.findAll(function(element) { 
     3403      return element.hasAttribute('tabIndex') && element.tabIndex >= 0; 
     3404    }).sortBy(function(element) { return element.tabIndex }).first(); 
     3405 
     3406    return firstByIndex ? firstByIndex : elements.find(function(element) { 
     3407      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); 
    19503408    }); 
    19513409  }, 
     
    19553413    form.findFirstElement().activate(); 
    19563414    return form; 
    1957   } 
    1958 } 
    1959  
    1960 Object.extend(Form, Form.Methods); 
     3415  }, 
     3416 
     3417  request: function(form, options) { 
     3418    form = $(form), options = Object.clone(options || { }); 
     3419 
     3420    var params = options.parameters, action = form.readAttribute('action') || ''; 
     3421    if (action.blank()) action = window.location.href; 
     3422    options.parameters = form.serialize(true); 
     3423 
     3424    if (params) { 
     3425      if (Object.isString(params)) params = params.toQueryParams(); 
     3426      Object.extend(options.parameters, params); 
     3427    } 
     3428 
     3429    if (form.hasAttribute('method') && !options.method) 
     3430      options.method = form.method; 
     3431 
     3432    return new Ajax.Request(action, options); 
     3433  } 
     3434}; 
    19613435 
    19623436/*--------------------------------------------------------------------------*/ 
     
    19723446    return element; 
    19733447  } 
    1974 } 
     3448}; 
    19753449 
    19763450Form.Element.Methods = { 
     
    19803454      var value = element.getValue(); 
    19813455      if (value != undefined) { 
    1982         var pair = {}; 
     3456        var pair = { }; 
    19833457        pair[element.name] = value; 
    1984         return Hash.toQueryString(pair); 
     3458        return Object.toQueryString(pair); 
    19853459      } 
    19863460    } 
     
    19943468  }, 
    19953469 
     3470  setValue: function(element, value) { 
     3471    element = $(element); 
     3472    var method = element.tagName.toLowerCase(); 
     3473    Form.Element.Serializers[method](element, value); 
     3474    return element; 
     3475  }, 
     3476 
    19963477  clear: function(element) { 
    19973478    $(element).value = ''; 
     
    20053486  activate: function(element) { 
    20063487    element = $(element); 
    2007     element.focus(); 
    2008     if (element.select && ( element.tagName.toLowerCase() != 'input' || 
    2009       !['button', 'reset', 'submit'].include(element.type) ) ) 
    2010       element.select(); 
     3488    try { 
     3489      element.focus(); 
     3490      if (element.select && (element.tagName.toLowerCase() != 'input' || 
     3491          !['button', 'reset', 'submit'].include(element.type))) 
     3492        element.select(); 
     3493    } catch (e) { } 
    20113494    return element; 
    20123495  }, 
     
    20143497  disable: function(element) { 
    20153498    element = $(element); 
     3499    element.blur(); 
    20163500    element.disabled = true; 
    20173501    return element; 
     
    20203504  enable: function(element) { 
    20213505    element = $(element); 
    2022     element.blur(); 
    20233506    element.disabled = false; 
    20243507    return element; 
    20253508  } 
    2026 } 
    2027  
    2028 Object.extend(Form.Element, Form.Element.Methods); 
     3509}; 
     3510 
     3511/*--------------------------------------------------------------------------*/ 
     3512 
    20293513var Field = Form.Element; 
    2030 var $F = Form.Element.getValue; 
     3514var $F = Form.Element.Methods.getValue; 
    20313515 
    20323516/*--------------------------------------------------------------------------*/ 
    20333517 
    20343518Form.Element.Serializers = { 
    2035   input: function(element) { 
     3519  input: function(element, value) { 
    20363520    switch (element.type.toLowerCase()) { 
    20373521      case 'checkbox': 
    20383522      case 'radio': 
    2039         return Form.Element.Serializers.inputSelector(element); 
     3523        return Form.Element.Serializers.inputSelector(element, value); 
    20403524      default: 
    2041         return Form.Element.Serializers.textarea(element); 
    2042     } 
    2043   }, 
    2044  
    2045   inputSelector: function(element) { 
    2046     return element.checked ? element.value : null; 
    2047   }, 
    2048  
    2049   textarea: function(element) { 
    2050     return element.value; 
    2051   }, 
    2052  
    2053   select: function(element) { 
    2054     return this[element.type == 'select-one' ? 
    2055       'selectOne' : 'selectMany'](element); 
     3525        return Form.Element.Serializers.textarea(element, value); 
     3526    } 
     3527  }, 
     3528 
     3529  inputSelector: function(element, value) { 
     3530    if (Object.isUndefined(value)) return element.checked ? element.value : null; 
     3531    else element.checked = !!value; 
     3532  }, 
     3533 
     3534  textarea: function(element, value) { 
     3535    if (Object.isUndefined(value)) return element.value; 
     3536    else element.value = value; 
     3537  }, 
     3538 
     3539  select: function(element, index) { 
     3540    if (Object.isUndefined(index)) 
     3541      return this[element.type == 'select-one' ? 
     3542        'selectOne' : 'selectMany'](element); 
     3543    else { 
     3544      var opt, value, single = !Object.isArray(index); 
     3545      for (var i = 0, length = element.length; i < length; i++) { 
     3546        opt = element.options[i]; 
     3547        value = this.optionValue(opt); 
     3548        if (single) { 
     3549          if (value == index) { 
     3550            opt.selected = true; 
     3551            return; 
     3552          } 
     3553        } 
     3554        else opt.selected = index.include(value); 
     3555      } 
     3556    } 
    20563557  }, 
    20573558 
     
    20763577    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; 
    20773578  } 
    2078 } 
     3579}; 
    20793580 
    20803581/*--------------------------------------------------------------------------*/ 
    20813582 
    2082 Abstract.TimedObserver = function() {} 
    2083 Abstract.TimedObserver.prototype = { 
    2084   initialize: function(element, frequency, callback) { 
    2085     this.frequency = frequency; 
     3583Abstract.TimedObserver = Class.create(PeriodicalExecuter, { 
     3584  initialize: function($super, element, frequency, callback) { 
     3585    $super(callback, frequency); 
    20863586    this.element   = $(element); 
    2087     this.callback  = callback; 
    2088  
    20893587    this.lastValue = this.getValue(); 
    2090     this.registerCallback(); 
    2091   }, 
    2092  
    2093   registerCallback: function() { 
    2094     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 
    2095   }, 
    2096  
    2097   onTimerEvent: function() { 
     3588  }, 
     3589 
     3590  execute: function() { 
    20983591    var value = this.getValue(); 
    2099     var changed = ('string' == typeof this.lastValue && 'string' == typeof value 
    2100       ? this.lastValue != value : String(this.lastValue) != String(value)); 
    2101     if (changed) { 
     3592    if (Object.isString(this.lastValue) && Object.isString(value) ? 
     3593        this.lastValue != value : String(this.lastValue) != String(value)) { 
    21023594      this.callback(this.element, value); 
    21033595      this.lastValue = value; 
    21043596    } 
    21053597  } 
    2106 } 
    2107  
    2108 Form.Element.Observer = Class.create(); 
    2109 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 
     3598}); 
     3599 
     3600Form.Element.Observer = Class.create(Abstract.TimedObserver, { 
    21103601  getValue: function() { 
    21113602    return Form.Element.getValue(this.element); 
     
    21133604}); 
    21143605 
    2115 Form.Observer = Class.create(); 
    2116 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 
     3606Form.Observer = Class.create(Abstract.TimedObserver, { 
    21173607  getValue: function() { 
    21183608    return Form.serialize(this.element); 
     
    21223612/*--------------------------------------------------------------------------*/ 
    21233613 
    2124 Abstract.EventObserver = function() {} 
    2125 Abstract.EventObserver.prototype = { 
     3614Abstract.EventObserver = Class.create({ 
    21263615  initialize: function(element, callback) { 
    21273616    this.element  = $(element); 
     
    21443633 
    21453634  registerFormCallbacks: function() { 
    2146     Form.getElements(this.element).each(this.registerCallback.bind(this)); 
     3635    Form.getElements(this.element).each(this.registerCallback, this); 
    21473636  }, 
    21483637 
     
    21603649    } 
    21613650  } 
    2162 } 
    2163  
    2164 Form.Element.EventObserver = Class.create(); 
    2165 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 
     3651}); 
     3652 
     3653Form.Element.EventObserver = Class.create(Abstract.EventObserver, { 
    21663654  getValue: function() { 
    21673655    return Form.Element.getValue(this.element); 
     
    21693657}); 
    21703658 
    2171 Form.EventObserver = Class.create(); 
    2172 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 
     3659Form.EventObserver = Class.create(Abstract.EventObserver, { 
    21733660  getValue: function() { 
    21743661    return Form.serialize(this.element); 
    21753662  } 
    21763663}); 
    2177 if (!window.Event) { 
    2178   var Event = new Object(); 
    2179 } 
     3664if (!window.Event) var Event = { }; 
    21803665 
    21813666Object.extend(Event, { 
     
    21933678  KEY_PAGEUP:   33, 
    21943679  KEY_PAGEDOWN: 34, 
    2195  
    2196   element: function(event) { 
    2197     return event.target || event.srcElement; 
    2198   }, 
    2199  
    2200   isLeftClick: function(event) { 
    2201     return (((event.which) && (event.which == 1)) || 
    2202             ((event.button) && (event.button == 1))); 
    2203   }, 
    2204  
    2205   pointerX: function(event) { 
    2206     return event.pageX || (event.clientX + 
    2207       (document.documentElement.scrollLeft || document.body.scrollLeft)); 
    2208   }, 
    2209  
    2210   pointerY: function(event) { 
    2211     return event.pageY || (event.clientY + 
    2212       (document.documentElement.scrollTop || document.body.scrollTop)); 
    2213   }, 
    2214  
    2215   stop: function(event) { 
    2216     if (event.preventDefault) { 
     3680  KEY_INSERT:   45, 
     3681 
     3682  cache: { }, 
     3683 
     3684  relatedTarget: function(event) { 
     3685    var element; 
     3686    switch(event.type) { 
     3687      case 'mouseover': element = event.fromElement; break; 
     3688      case 'mouseout':  element = event.toElement;   break; 
     3689      default: return null; 
     3690    } 
     3691    return Element.extend(element); 
     3692  } 
     3693}); 
     3694 
     3695Event.Methods = (function() { 
     3696  var isButton; 
     3697 
     3698  if (Prototype.Browser.IE) { 
     3699    var buttonMap = { 0: 1, 1: 4, 2: 2 }; 
     3700    isButton = function(event, code) { 
     3701      return event.button == buttonMap[code]; 
     3702    }; 
     3703 
     3704  } else if (Prototype.Browser.WebKit) { 
     3705    isButton = function(event, code) { 
     3706      switch (code) { 
     3707        case 0: return event.which == 1 && !event.metaKey; 
     3708        case 1: return event.which == 1 && event.metaKey; 
     3709        default: return false; 
     3710      } 
     3711    }; 
     3712 
     3713  } else { 
     3714    isButton = function(event, code) { 
     3715      return event.which ? (event.which === code + 1) : (event.button === code); 
     3716    }; 
     3717  } 
     3718 
     3719  return { 
     3720    isLeftClick:   function(event) { return isButton(event, 0) }, 
     3721    isMiddleClick: function(event) { return isButton(event, 1) }, 
     3722    isRightClick:  function(event) { return isButton(event, 2) }, 
     3723 
     3724    element: function(event) { 
     3725      var node = Event.extend(event).target; 
     3726      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); 
     3727    }, 
     3728 
     3729    findElement: function(event, expression) { 
     3730      var element = Event.element(event); 
     3731      if (!expression) return element; 
     3732      var elements = [element].concat(element.ancestors()); 
     3733      return Selector.findElement(elements, expression, 0); 
     3734    }, 
     3735 
     3736    pointer: function(event) { 
     3737      return { 
     3738        x: event.pageX || (event.clientX + 
     3739          (document.documentElement.scrollLeft || document.body.scrollLeft)), 
     3740        y: event.pageY || (event.clientY + 
     3741          (document.documentElement.scrollTop || document.body.scrollTop)) 
     3742      }; 
     3743    }, 
     3744 
     3745    pointerX: function(event) { return Event.pointer(event).x }, 
     3746    pointerY: function(event) { return Event.pointer(event).y }, 
     3747 
     3748    stop: function(event) { 
     3749      Event.extend(event); 
    22173750      event.preventDefault(); 
    22183751      event.stopPropagation(); 
     3752      event.stopped = true; 
     3753    } 
     3754  }; 
     3755})(); 
     3756 
     3757Event.extend = (function() { 
     3758  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { 
     3759    m[name] = Event.Methods[name].methodize(); 
     3760    return m; 
     3761  }); 
     3762 
     3763  if (Prototype.Browser.IE) { 
     3764    Object.extend(methods, { 
     3765      stopPropagation: function() { this.cancelBubble = true }, 
     3766      preventDefault:  function() { this.returnValue = false }, 
     3767      inspect: function() { return "[object Event]" } 
     3768    }); 
     3769 
     3770    return function(event) { 
     3771      if (!event) return false; 
     3772      if (event._extendedByPrototype) return event; 
     3773 
     3774      event._extendedByPrototype = Prototype.emptyFunction; 
     3775      var pointer = Event.pointer(event); 
     3776      Object.extend(event, { 
     3777        target: event.srcElement, 
     3778        relatedTarget: Event.relatedTarget(event), 
     3779        pageX:  pointer.x, 
     3780        pageY:  pointer.y 
     3781      }); 
     3782      return Object.extend(event, methods); 
     3783    }; 
     3784 
     3785  } else { 
     3786    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; 
     3787    Object.extend(Event.prototype, methods); 
     3788    return Prototype.K; 
     3789  } 
     3790})(); 
     3791 
     3792Object.extend(Event, (function() { 
     3793  var cache = Event.cache; 
     3794 
     3795  function getEventID(element) { 
     3796    if (element._eventID) return element._eventID; 
     3797    arguments.callee.id = arguments.callee.id || 1; 
     3798    return element._eventID = ++arguments.callee.id; 
     3799  } 
     3800 
     3801  function getDOMEventName(eventName) { 
     3802    if (eventName && eventName.include(':')) return "dataavailable"; 
     3803    return eventName; 
     3804  } 
     3805 
     3806  function getCacheForID(id) { 
     3807    return cache[id] = cache[id] || { }; 
     3808  } 
     3809 
     3810  function getWrappersForEventName(id, eventName) { 
     3811    var c = getCacheForID(id); 
     3812    return c[eventName] = c[eventName] || []; 
     3813  } 
     3814 
     3815  function createWrapper(element, eventName, handler) { 
     3816    var id = getEventID(element); 
     3817    var c = getWrappersForEventName(id, eventName); 
     3818    if (c.pluck("handler").include(handler)) return false; 
     3819 
     3820    var wrapper = function(event) { 
     3821      if (!Event || !Event.extend || 
     3822        (event.eventName && event.eventName != eventName)) 
     3823          return false; 
     3824 
     3825      Event.extend(event); 
     3826      handler.call(element, event); 
     3827    }; 
     3828 
     3829    wrapper.handler = handler; 
     3830    c.push(wrapper); 
     3831    return wrapper; 
     3832  } 
     3833 
     3834  function findWrapper(id, eventName, handler) { 
     3835    var c = getWrappersForEventName(id, eventName); 
     3836    return c.find(function(wrapper) { return wrapper.handler == handler }); 
     3837  } 
     3838 
     3839  function destroyWrapper(id, eventName, handler) { 
     3840    var c = getCacheForID(id); 
     3841    if (!c[eventName]) return false; 
     3842    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); 
     3843  } 
     3844 
     3845  function destroyCache() { 
     3846    for (var id in cache) 
     3847      for (var eventName in cache[id]) 
     3848        cache[id][eventName] = null; 
     3849  } 
     3850 
     3851  if (window.attachEvent) { 
     3852    window.attachEvent("onunload", destroyCache); 
     3853  } 
     3854 
     3855  return { 
     3856    observe: function(element, eventName, handler) { 
     3857      element = $(element); 
     3858      var name = getDOMEventName(eventName); 
     3859 
     3860      var wrapper = createWrapper(element, eventName, handler); 
     3861      if (!wrapper) return element; 
     3862 
     3863      if (element.addEventListener) { 
     3864        element.addEventListener(name, wrapper, false); 
     3865      } else { 
     3866        element.attachEvent("on" + name, wrapper); 
     3867      } 
     3868 
     3869      return element; 
     3870    }, 
     3871 
     3872    stopObserving: function(element, eventName, handler) { 
     3873      element = $(element); 
     3874      var id = getEventID(element), name = getDOMEventName(eventName); 
     3875 
     3876      if (!handler && eventName) { 
     3877        getWrappersForEventName(id, eventName).each(function(wrapper) { 
     3878          element.stopObserving(eventName, wrapper.handler); 
     3879        }); 
     3880        return element; 
     3881 
     3882      } else if (!eventName) { 
     3883        Object.keys(getCacheForID(id)).each(function(eventName) { 
     3884          element.stopObserving(eventName); 
     3885        }); 
     3886        return element; 
     3887      } 
     3888 
     3889      var wrapper = findWrapper(id, eventName, handler); 
     3890      if (!wrapper) return element; 
     3891 
     3892      if (element.removeEventListener) { 
     3893        element.removeEventListener(name, wrapper, false); 
     3894      } else { 
     3895        element.detachEvent("on" + name, wrapper); 
     3896      } 
     3897 
     3898      destroyWrapper(id, eventName, handler); 
     3899 
     3900      return element; 
     3901    }, 
     3902 
     3903    fire: function(element, eventName, memo) { 
     3904      element = $(element); 
     3905      if (element == document && document.createEvent && !element.dispatchEvent) 
     3906        element = document.documentElement; 
     3907 
     3908      var event; 
     3909      if (document.createEvent) { 
     3910        event = document.createEvent("HTMLEvents"); 
     3911        event.initEvent("dataavailable", true, true); 
     3912      } else { 
     3913        event = document.createEventObject(); 
     3914        event.eventType = "ondataavailable"; 
     3915      } 
     3916 
     3917      event.eventName = eventName; 
     3918      event.memo = memo || { }; 
     3919 
     3920      if (document.createEvent) { 
     3921        element.dispatchEvent(event); 
     3922      } else { 
     3923        element.fireEvent(event.eventType, event); 
     3924      } 
     3925 
     3926      return Event.extend(event); 
     3927    } 
     3928  }; 
     3929})()); 
     3930 
     3931Object.extend(Event, Event.Methods); 
     3932 
     3933Element.addMethods({ 
     3934  fire:          Event.fire, 
     3935  observe:       Event.observe, 
     3936  stopObserving: Event.stopObserving 
     3937}); 
     3938 
     3939Object.extend(document, { 
     3940  fire:          Element.Methods.fire.methodize(), 
     3941  observe:       Element.Methods.observe.methodize(), 
     3942  stopObserving: Element.Methods.stopObserving.methodize(), 
     3943  loaded:        false 
     3944}); 
     3945 
     3946(function() { 
     3947  /* Support for the DOMContentLoaded event is based on work by Dan Webb, 
     3948     Matthias Miller, Dean Edwards and John Resig. */ 
     3949 
     3950  var timer; 
     3951 
     3952  function fireContentLoadedEvent() { 
     3953    if (document.loaded) return; 
     3954    if (timer) window.clearInterval(timer); 
     3955    document.fire("dom:loaded"); 
     3956    document.loaded = true; 
     3957  } 
     3958 
     3959  if (document.addEventListener) { 
     3960    if (Prototype.Browser.WebKit) { 
     3961      timer = window.setInterval(function() { 
     3962        if (/loaded|complete/.test(document.readyState)) 
     3963          fireContentLoadedEvent(); 
     3964      }, 0); 
     3965 
     3966      Event.observe(window, "load", fireContentLoadedEvent); 
     3967 
    22193968    } else { 
    2220       event.returnValue = false; 
    2221       event.cancelBubble = true; 
    2222     } 
    2223   }, 
    2224  
    2225   // find the first node with the given tagName, starting from the 
    2226   // node the event was triggered on; traverses the DOM upwards 
    2227   findElement: function(event, tagName) { 
    2228     var element = Event.element(event); 
    2229     while (element.parentNode && (!element.tagName || 
    2230         (element.tagName.toUpperCase() != tagName.toUpperCase()))) 
    2231       element = element.parentNode; 
    2232     return element; 
    2233   }, 
    2234  
    2235   observers: false, 
    2236  
    2237   _observeAndCache: function(element, name, observer, useCapture) { 
    2238     if (!this.observers) this.observers = []; 
    2239     if (element.addEventListener) { 
    2240       this.observers.push([element, name, observer, useCapture]); 
    2241       element.addEventListener(name, observer, useCapture); 
    2242     } else if (element.attachEvent) { 
    2243       this.observers.push([element, name, observer, useCapture]); 
    2244       element.attachEvent('on' + name, observer); 
    2245     } 
    2246   }, 
    2247  
    2248   unloadCache: function() { 
    2249     if (!Event.observers) return; 
    2250     for (var i = 0, length = Event.observers.length; i < length; i++) { 
    2251       Event.stopObserving.apply(this, Event.observers[i]); 
    2252       Event.observers[i][0] = null; 
    2253     } 
    2254     Event.observers = false; 
    2255   }, 
    2256  
    2257   observe: function(element, name, observer, useCapture) { 
    2258     element = $(element); 
    2259     useCapture = useCapture || false; 
    2260  
    2261     if (name == 'keypress' && 
    2262         (navigator.appVersion.match(/Konqueror|Safari|KHTML/) 
    2263         || element.attachEvent)) 
    2264       name = 'keydown'; 
    2265  
    2266     Event._observeAndCache(element, name, observer, useCapture); 
    2267   }, 
    2268  
    2269   stopObserving: function(element, name, observer, useCapture) { 
    2270     element = $(element); 
    2271     useCapture = useCapture || false; 
    2272  
    2273     if (name == 'keypress' && 
    2274         (navigator.appVersion.match(/Konqueror|Safari|KHTML/) 
    2275         || element.detachEvent)) 
    2276       name = 'keydown'; 
    2277  
    2278     if (element.removeEventListener) { 
    2279       element.removeEventListener(name, observer, useCapture); 
    2280     } else if (element.detachEvent) { 
    2281       try { 
    2282         element.detachEvent('on' + name, observer); 
    2283       } catch (e) {} 
    2284     } 
    2285   } 
    2286 }); 
    2287  
    2288 /* prevent memory leaks in IE */ 
    2289 if (navigator.appVersion.match(/\bMSIE\b/)) 
    2290   Event.observe(window, 'unload', Event.unloadCache, false); 
     3969      document.addEventListener("DOMContentLoaded", 
     3970        fireContentLoadedEvent, false); 
     3971    } 
     3972 
     3973  } else { 
     3974    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>"); 
     3975    $("__onDOMContentLoaded").onreadystatechange = function() { 
     3976      if (this.readyState == "complete") { 
     3977        this.onreadystatechange = null; 
     3978        fireContentLoadedEvent(); 
     3979      } 
     3980    }; 
     3981  } 
     3982})(); 
     3983/*------------------------------- DEPRECATED -------------------------------*/ 
     3984 
     3985Hash.toQueryString = Object.toQueryString; 
     3986 
     3987var Toggle = { display: Element.toggle }; 
     3988 
     3989Element.Methods.childOf = Element.Methods.descendantOf; 
     3990 
     3991var Insertion = { 
     3992  Before: function(element, content) { 
     3993    return Element.insert(element, {before:content}); 
     3994  }, 
     3995 
     3996  Top: function(element, content) { 
     3997    return Element.insert(element, {top:content}); 
     3998  }, 
     3999 
     4000  Bottom: function(element, content) { 
     4001    return Element.insert(element, {bottom:content}); 
     4002  }, 
     4003 
     4004  After: function(element, content) { 
     4005    return Element.insert(element, {after:content}); 
     4006  } 
     4007}; 
     4008 
     4009var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); 
     4010 
     4011// This should be moved to script.aculo.us; notice the deprecated methods 
     4012// further below, that map to the newer Element methods. 
    22914013var Position = { 
    22924014  // set to true if needed, warning: firefox performance problems 
     
    23084030  }, 
    23094031 
    2310   realOffset: function(element) { 
    2311     var valueT = 0, valueL = 0; 
    2312     do { 
    2313       valueT += element.scrollTop  || 0; 
    2314       valueL += element.scrollLeft || 0; 
    2315       element = element.parentNode; 
    2316     } while (element); 
    2317     return [valueL, valueT]; 
    2318   }, 
    2319  
    2320   cumulativeOffset: function(element) { 
    2321     var valueT = 0, valueL = 0; 
    2322     do { 
    2323       valueT += element.offsetTop  || 0; 
    2324       valueL += element.offsetLeft || 0; 
    2325       element = element.offsetParent; 
    2326     } while (element); 
    2327     return [valueL, valueT]; 
    2328   }, 
    2329  
    2330   positionedOffset: function(element) { 
    2331     var valueT = 0, valueL = 0; 
    2332     do { 
    2333       valueT += element.offsetTop  || 0; 
    2334       valueL += element.offsetLeft || 0; 
    2335       element = element.offsetParent; 
    2336       if (element) { 
    2337         if(element.tagName=='BODY') break; 
    2338         var p = Element.getStyle(element, 'position'); 
    2339         if (p == 'relative' || p == 'absolute') break; 
    2340       } 
    2341     } while (element); 
    2342     return [valueL, valueT]; 
    2343   }, 
    2344  
    2345   offsetParent: function(element) { 
    2346     if (element.offsetParent) return element.offsetParent; 
    2347     if (element == document.body) return element; 
    2348  
    2349     while ((element = element.parentNode) && element != document.body) 
    2350       if (Element.getStyle(element, 'position') != 'static') 
    2351         return element; 
    2352  
    2353     return document.body; 
    2354   }, 
    2355  
    23564032  // caches x/y coordinate pair to use with overlap 
    23574033  within: function(element, x, y) { 
     
    23604036    this.xcomp = x; 
    23614037    this.ycomp = y; 
    2362     this.offset = this.cumulativeOffset(element); 
     4038    this.offset = Element.cumulativeOffset(element); 
    23634039 
    23644040    return (y >= this.offset[1] && 
     
    23694045 
    23704046  withinIncludingScrolloffsets: function(element, x, y) { 
    2371     var offsetcache = this.realOffset(element); 
     4047    var offsetcache = Element.cumulativeScrollOffset(element); 
    23724048 
    23734049    this.xcomp = x + offsetcache[0] - this.deltaX; 
    23744050    this.ycomp = y + offsetcache[1] - this.deltaY; 
    2375     this.offset = this.cumulativeOffset(element); 
     4051    this.offset = Element.cumulativeOffset(element); 
    23764052 
    23774053    return (this.ycomp >= this.offset[1] && 
     
    23924068  }, 
    23934069 
    2394   page: function(forElement) { 
    2395     var valueT = 0, valueL = 0; 
    2396  
    2397     var element = forElement; 
    2398     do { 
    2399       valueT += element.offsetTop  || 0; 
    2400       valueL += element.offsetLeft || 0; 
    2401  
    2402       // Safari fix 
    2403       if (element.offsetParent==document.body) 
    2404         if (Element.getStyle(element,'position')=='absolute') break; 
    2405  
    2406     } while (element = element.offsetParent); 
    2407  
    2408     element = forElement; 
    2409     do { 
    2410       if (!window.opera || element.tagName=='BODY') { 
    2411         valueT -= element.scrollTop  || 0; 
    2412         valueL -= element.scrollLeft || 0; 
    2413       } 
    2414     } while (element = element.parentNode); 
    2415  
    2416     return [valueL, valueT]; 
    2417   }, 
    2418  
    2419   clone: function(source, target) { 
    2420     var options = Object.extend({ 
    2421       setLeft:    true, 
    2422       setTop:     true, 
    2423       setWidth:   true, 
    2424       setHeight:  true, 
    2425       offsetTop:  0, 
    2426       offsetLeft: 0 
    2427     }, arguments[2] || {}) 
    2428  
    2429     // find page position of source 
    2430     source = $(source); 
    2431     var p = Position.page(source); 
    2432  
    2433     // find coordinate system to use 
    2434     target = $(target); 
    2435     var delta = [0, 0]; 
    2436     var parent = null; 
    2437     // delta [0,0] will do fine with position: fixed elements, 
    2438     // position:absolute needs offsetParent deltas 
    2439     if (Element.getStyle(target,'position') == 'absolute') { 
    2440       parent = Position.offsetParent(target); 
    2441       delta = Position.page(parent); 
    2442     } 
    2443  
    2444     // correct by body offsets (fixes Safari) 
    2445     if (parent == document.body) { 
    2446       delta[0] -= document.body.offsetLeft; 
    2447       delta[1] -= document.body.offsetTop; 
    2448     } 
    2449  
    2450     // set position 
    2451     if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px'; 
    2452     if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px'; 
    2453     if(options.setWidth)  target.style.width = source.offsetWidth + 'px'; 
    2454     if(options.setHeight) target.style.height = source.offsetHeight + 'px'; 
    2455   }, 
     4070  // Deprecation layer -- use newer Element methods now (1.5.2). 
     4071 
     4072  cumulativeOffset: Element.Methods.cumulativeOffset, 
     4073 
     4074  positionedOffset: Element.Methods.positionedOffset, 
    24564075 
    24574076  absolutize: function(element) { 
    2458     element = $(element); 
    2459     if (element.style.position == 'absolute') return; 
    24604077    Position.prepare(); 
    2461  
    2462     var offsets = Position.positionedOffset(element); 
    2463     var top     = offsets[1]; 
    2464     var left    = offsets[0]; 
    2465     var width   = element.clientWidth; 
    2466     var height  = element.clientHeight; 
    2467  
    2468     element._originalLeft   = left - parseFloat(element.style.left  || 0); 
    2469     element._originalTop    = top  - parseFloat(element.style.top || 0); 
    2470     element._originalWidth  = element.style.width; 
    2471     element._originalHeight = element.style.height; 
    2472  
    2473     element.style.position = 'absolute'; 
    2474     element.style.top    = top + 'px'; 
    2475     element.style.left   = left + 'px'; 
    2476     element.style.width  = width + 'px'; 
    2477     element.style.height = height + 'px'; 
     4078    return Element.absolutize(element); 
    24784079  }, 
    24794080 
    24804081  relativize: function(element) { 
    2481     element = $(element); 
    2482     if (element.style.position == 'relative') return; 
    24834082    Position.prepare(); 
    2484  
    2485     element.style.position = 'relative'; 
    2486     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0); 
    2487     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 
    2488  
    2489     element.style.top    = top + 'px'; 
    2490     element.style.left   = left + 'px'; 
    2491     element.style.height = element._originalHeight; 
    2492     element.style.width  = element._originalWidth; 
    2493   } 
    2494 } 
    2495  
    2496 // Safari returns margins on body which is incorrect if the child is absolutely 
    2497 // positioned.  For performance reasons, redefine Position.cumulativeOffset for 
    2498 // KHTML/WebKit only. 
    2499 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { 
    2500   Position.cumulativeOffset = function(element) { 
    2501     var valueT = 0, valueL = 0; 
    2502     do { 
    2503       valueT += element.offsetTop  || 0; 
    2504       valueL += element.offsetLeft || 0; 
    2505       if (element.offsetParent == document.body) 
    2506         if (Element.getStyle(element, 'position') == 'absolute') break; 
    2507  
    2508       element = element.offsetParent; 
    2509     } while (element); 
    2510  
    2511     return [valueL, valueT]; 
    2512   } 
    2513 } 
     4083    return Element.relativize(element); 
     4084  }, 
     4085 
     4086  realOffset: Element.Methods.cumulativeScrollOffset, 
     4087 
     4088  offsetParent: Element.Methods.getOffsetParent, 
     4089 
     4090  page: Element.Methods.viewportOffset, 
     4091 
     4092  clone: function(source, target, options) { 
     4093    options = options || { }; 
     4094    return Element.clonePosition(target, source, options); 
     4095  } 
     4096}; 
     4097 
     4098/*--------------------------------------------------------------------------*/ 
     4099 
     4100if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ 
     4101  function iter(name) { 
     4102    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; 
     4103  } 
     4104 
     4105  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? 
     4106  function(element, className) { 
     4107    className = className.toString().strip(); 
     4108    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); 
     4109    return cond ? document._getElementsByXPath('.//*' + cond, element) : []; 
     4110  } : function(element, className) { 
     4111    className = className.toString().strip(); 
     4112    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); 
     4113    if (!classNames && !className) return elements; 
     4114 
     4115    var nodes = $(element).getElementsByTagName('*'); 
     4116    className = ' ' + className + ' '; 
     4117 
     4118    for (var i = 0, child, cn; child = nodes[i]; i++) { 
     4119      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || 
     4120          (classNames && classNames.all(function(name) { 
     4121            return !name.toString().blank() && cn.include(' ' + name + ' '); 
     4122          })))) 
     4123        elements.push(Element.extend(child)); 
     4124    } 
     4125    return elements; 
     4126  }; 
     4127 
     4128  return function(className, parentElement) { 
     4129    return $(parentElement || document.body).getElementsByClassName(className); 
     4130  }; 
     4131}(Element.Methods); 
     4132 
     4133/*--------------------------------------------------------------------------*/ 
     4134 
     4135Element.ClassNames = Class.create(); 
     4136Element.ClassNames.prototype = { 
     4137  initialize: function(element) { 
     4138    this.element = $(element); 
     4139  }, 
     4140 
     4141  _each: function(iterator) { 
     4142    this.element.className.split(/\s+/).select(function(name) { 
     4143      return name.length > 0; 
     4144    })._each(iterator); 
     4145  }, 
     4146 
     4147  set: function(className) { 
     4148    this.element.className = className; 
     4149  }, 
     4150 
     4151  add: function(classNameToAdd) { 
     4152    if (this.include(classNameToAdd)) return; 
     4153    this.set($A(this).concat(classNameToAdd).join(' ')); 
     4154  }, 
     4155 
     4156  remove: function(classNameToRemove) { 
     4157    if (!this.include(classNameToRemove)) return; 
     4158    this.set($A(this).without(classNameToRemove).join(' ')); 
     4159  }, 
     4160 
     4161  toString: function() { 
     4162    return $A(this).join(' '); 
     4163  } 
     4164}; 
     4165 
     4166Object.extend(Element.ClassNames.prototype, Enumerable); 
     4167 
     4168/*--------------------------------------------------------------------------*/ 
    25144169 
    25154170Element.addMethods(); 
Note: See TracChangeset for help on using the changeset viewer.