ここの情報は古いです。ご理解頂いた上でお取り扱いください。

Changeset 6352


Ignore:
Timestamp:
Apr 7, 2008, 1:42:29 PM (13 years ago)
Author:
ebihara
Message:

#333:prototype.jsの拡張メソッド(String.gsub())を使用するためにprototype.jsのバージョンを1.4.0から1.6.0にした

File:
1 edited

Legend:

Unmodified
Added
Removed
  • OpenPNE/trunk/public_html/js/prototype.js

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