WordPress.org

Make WordPress Core

Changeset 6512


Ignore:
Timestamp:
12/28/2007 07:17:21 PM (14 years ago)
Author:
ryan
Message:

Prototype 1.6.0 and script.aculo.us 1.8.0. fixes #5543

Location:
trunk/wp-includes
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/wp-includes/js/prototype.js

    r5743 r6512  
    1 /*  Prototype JavaScript framework, version 1.5.1.1
     1/*  Prototype JavaScript framework, version 1.6.0
    22 *  (c) 2005-2007 Sam Stephenson
    33 *
     
    55 *  For details, see the Prototype web site: http://www.prototypejs.org/
    66 *
    7 /*--------------------------------------------------------------------------*/
     7 *--------------------------------------------------------------------------*/
    88
    99var Prototype = {
    10   Version: '1.5.1.1',
     10  Version: '1.6.0',
    1111
    1212  Browser: {
     
    1414    Opera:  !!window.opera,
    1515    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    16     Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
     16    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
     17    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
    1718  },
    1819
     
    2122    ElementExtensions: !!window.HTMLElement,
    2223    SpecificElementExtensions:
    23       (document.createElement('div').__proto__ !==
    24        document.createElement('form').__proto__)
     24      document.createElement('div').__proto__ &&
     25      document.createElement('div').__proto__ !==
     26        document.createElement('form').__proto__
    2527  },
    2628
     
    3032  emptyFunction: function() { },
    3133  K: function(x) { return x }
    32 }
    33 
     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. */
    3443var Class = {
    3544  create: function() {
    36     return function() {
     45    var parent = null, properties = $A(arguments);
     46    if (Object.isFunction(properties[0]))
     47      parent = properties.shift();
     48
     49    function klass() {
    3750      this.initialize.apply(this, arguments);
    3851    }
    39   }
    40 }
    41 
    42 var Abstract = new Object();
     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 = { };
    43103
    44104Object.extend = function(destination, source) {
    45   for (var property in source) {
     105  for (var property in source)
    46106    destination[property] = source[property];
    47   }
    48107  return destination;
    49 }
     108};
    50109
    51110Object.extend(Object, {
     
    63122  toJSON: function(object) {
    64123    var type = typeof object;
    65     switch(type) {
     124    switch (type) {
    66125      case 'undefined':
    67126      case 'function':
     
    69128      case 'boolean': return object.toString();
    70129    }
     130
    71131    if (object === null) return 'null';
    72132    if (object.toJSON) return object.toJSON();
    73     if (object.ownerDocument === document) return;
     133    if (Object.isElement(object)) return;
     134
    74135    var results = [];
    75136    for (var property in object) {
     
    78139        results.push(property.toJSON() + ': ' + value);
    79140    }
     141
    80142    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);
    81151  },
    82152
     
    96166
    97167  clone: function(object) {
    98     return Object.extend({}, 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";
    99197  }
    100198});
    101199
    102 Function.prototype.bind = function() {
    103   var __method = this, args = $A(arguments), object = args.shift();
    104   return function() {
    105     return __method.apply(object, args.concat($A(arguments)));
    106   }
    107 }
    108 
    109 Function.prototype.bindAsEventListener = function(object) {
    110   var __method = this, args = $A(arguments), object = args.shift();
    111   return function(event) {
    112     return __method.apply(object, [event || window.event].concat(args));
    113   }
    114 }
    115 
    116 Object.extend(Number.prototype, {
    117   toColorPart: function() {
    118     return this.toPaddedString(2, 16);
    119   },
    120 
    121   succ: function() {
    122     return this + 1;
    123   },
    124 
    125   times: function(iterator) {
    126     $R(0, this, true).each(iterator);
    127     return this;
    128   },
    129 
    130   toPaddedString: function(length, radix) {
    131     var string = this.toString(radix || 10);
    132     return '0'.times(length - string.length) + string;
    133   },
    134 
    135   toJSON: function() {
    136     return isFinite(this) ? this.toString() : 'null';
     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();
     209    return function() {
     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    };
    137249  }
    138250});
    139251
     252Function.prototype.defer = Function.prototype.delay.curry(0.01);
     253
    140254Date.prototype.toJSON = function() {
    141   return '"' + this.getFullYear() + '-' +
    142     (this.getMonth() + 1).toPaddedString(2) + '-' +
    143     this.getDate().toPaddedString(2) + 'T' +
    144     this.getHours().toPaddedString(2) + ':' +
    145     this.getMinutes().toPaddedString(2) + ':' +
    146     this.getSeconds().toPaddedString(2) + '"';
     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"';
    147261};
    148262
     
    156270        returnValue = lambda();
    157271        break;
    158       } catch (e) {}
     272      } catch (e) { }
    159273    }
    160274
    161275    return returnValue;
    162276  }
    163 }
     277};
     278
     279RegExp.prototype.match = RegExp.prototype.test;
     280
     281RegExp.escape = function(str) {
     282  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
     283};
    164284
    165285/*--------------------------------------------------------------------------*/
    166286
    167 var PeriodicalExecuter = Class.create();
    168 PeriodicalExecuter.prototype = {
     287var PeriodicalExecuter = Class.create({
    169288  initialize: function(callback, frequency) {
    170289    this.callback = callback;
     
    179298  },
    180299
     300  execute: function() {
     301    this.callback(this);
     302  },
     303
    181304  stop: function() {
    182305    if (!this.timer) return;
     
    189312      try {
    190313        this.currentlyExecuting = true;
    191         this.callback(this);
     314        this.execute();
    192315      } finally {
    193316        this.currentlyExecuting = false;
     
    195318    }
    196319  }
    197 }
     320});
    198321Object.extend(String, {
    199322  interpret: function(value) {
     
    239362  scan: function(pattern, iterator) {
    240363    this.gsub(pattern, iterator);
    241     return this;
     364    return String(this);
    242365  },
    243366
     
    246369    truncation = truncation === undefined ? '...' : truncation;
    247370    return this.length > length ?
    248       this.slice(0, length - truncation.length) + truncation : this;
     371      this.slice(0, length - truncation.length) + truncation : String(this);
    249372  },
    250373
     
    280403
    281404  unescapeHTML: function() {
    282     var div = document.createElement('div');
     405    var div = new Element('div');
    283406    div.innerHTML = this.stripTags();
    284407    return div.childNodes[0] ? (div.childNodes.length > 1 ?
     
    289412  toQueryParams: function(separator) {
    290413    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    291     if (!match) return {};
    292 
    293     return match[1].split(separator || '&').inject({}, function(hash, pair) {
     414    if (!match) return { };
     415
     416    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
    294417      if ((pair = pair.split('='))[0]) {
    295418        var key = decodeURIComponent(pair.shift());
     
    298421
    299422        if (key in hash) {
    300           if (hash[key].constructor != Array) hash[key] = [hash[key]];
     423          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
    301424          hash[key].push(value);
    302425        }
     
    317440
    318441  times: function(count) {
    319     var result = '';
    320     for (var i = 0; i < count; i++) result += this;
    321     return result;
     442    return count < 1 ? '' : new Array(count + 1).join(this);
    322443  },
    323444
     
    397518  blank: function() {
    398519    return /^\s*$/.test(this);
     520  },
     521
     522  interpolate: function(object, pattern) {
     523    return new Template(this, pattern).evaluate(object);
    399524  }
    400525});
     
    410535
    411536String.prototype.gsub.prepareReplacement = function(replacement) {
    412   if (typeof replacement == 'function') return replacement;
     537  if (Object.isFunction(replacement)) return replacement;
    413538  var template = new Template(replacement);
    414539  return function(match) { return template.evaluate(match) };
    415 }
     540};
    416541
    417542String.prototype.parseQuery = String.prototype.toQueryParams;
     
    424549with (String.prototype.escapeHTML) div.appendChild(text);
    425550
    426 var Template = Class.create();
    427 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
    428 Template.prototype = {
     551var Template = Class.create({
    429552  initialize: function(template, pattern) {
    430553    this.template = template.toString();
    431     this.pattern  = pattern || Template.Pattern;
     554    this.pattern = pattern || Template.Pattern;
    432555  },
    433556
    434557  evaluate: function(object) {
     558    if (Object.isFunction(object.toTemplateReplacements))
     559      object = object.toTemplateReplacements();
     560
    435561    return this.template.gsub(this.pattern, function(match) {
    436       var before = match[1];
     562      if (object == null) return '';
     563
     564      var before = match[1] || '';
    437565      if (before == '\\') return match[2];
    438       return before + String.interpret(object[match[3]]);
    439     });
    440   }
    441 }
    442 
    443 var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
     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 = { };
    444586
    445587var Enumerable = {
    446   each: function(iterator) {
     588  each: function(iterator, context) {
    447589    var index = 0;
     590    iterator = iterator.bind(context);
    448591    try {
    449592      this._each(function(value) {
     
    456599  },
    457600
    458   eachSlice: function(number, iterator) {
     601  eachSlice: function(number, iterator, context) {
     602    iterator = iterator ? iterator.bind(context) : Prototype.K;
    459603    var index = -number, slices = [], array = this.toArray();
    460604    while ((index += number) < array.length)
    461605      slices.push(array.slice(index, index+number));
    462     return slices.map(iterator);
    463   },
    464 
    465   all: function(iterator) {
     606    return slices.collect(iterator, context);
     607  },
     608
     609  all: function(iterator, context) {
     610    iterator = iterator ? iterator.bind(context) : Prototype.K;
    466611    var result = true;
    467612    this.each(function(value, index) {
    468       result = result && !!(iterator || Prototype.K)(value, index);
     613      result = result && !!iterator(value, index);
    469614      if (!result) throw $break;
    470615    });
     
    472617  },
    473618
    474   any: function(iterator) {
     619  any: function(iterator, context) {
     620    iterator = iterator ? iterator.bind(context) : Prototype.K;
    475621    var result = false;
    476622    this.each(function(value, index) {
    477       if (result = !!(iterator || Prototype.K)(value, index))
     623      if (result = !!iterator(value, index))
    478624        throw $break;
    479625    });
     
    481627  },
    482628
    483   collect: function(iterator) {
     629  collect: function(iterator, context) {
     630    iterator = iterator ? iterator.bind(context) : Prototype.K;
    484631    var results = [];
    485632    this.each(function(value, index) {
    486       results.push((iterator || Prototype.K)(value, index));
     633      results.push(iterator(value, index));
    487634    });
    488635    return results;
    489636  },
    490637
    491   detect: function(iterator) {
     638  detect: function(iterator, context) {
     639    iterator = iterator.bind(context);
    492640    var result;
    493641    this.each(function(value, index) {
     
    500648  },
    501649
    502   findAll: function(iterator) {
     650  findAll: function(iterator, context) {
     651    iterator = iterator.bind(context);
    503652    var results = [];
    504653    this.each(function(value, index) {
     
    509658  },
    510659
    511   grep: function(pattern, iterator) {
     660  grep: function(filter, iterator, context) {
     661    iterator = iterator ? iterator.bind(context) : Prototype.K;
    512662    var results = [];
     663
     664    if (Object.isString(filter))
     665      filter = new RegExp(filter);
     666
    513667    this.each(function(value, index) {
    514       var stringValue = value.toString();
    515       if (stringValue.match(pattern))
    516         results.push((iterator || Prototype.K)(value, index));
    517     })
     668      if (filter.match(value))
     669        results.push(iterator(value, index));
     670    });
    518671    return results;
    519672  },
    520673
    521674  include: function(object) {
     675    if (Object.isFunction(this.indexOf))
     676      if (this.indexOf(object) != -1) return true;
     677
    522678    var found = false;
    523679    this.each(function(value) {
     
    538694  },
    539695
    540   inject: function(memo, iterator) {
     696  inject: function(memo, iterator, context) {
     697    iterator = iterator.bind(context);
    541698    this.each(function(value, index) {
    542699      memo = iterator(memo, value, index);
     
    552709  },
    553710
    554   max: function(iterator) {
     711  max: function(iterator, context) {
     712    iterator = iterator ? iterator.bind(context) : Prototype.K;
    555713    var result;
    556714    this.each(function(value, index) {
    557       value = (iterator || Prototype.K)(value, index);
     715      value = iterator(value, index);
    558716      if (result == undefined || value >= result)
    559717        result = value;
     
    562720  },
    563721
    564   min: function(iterator) {
     722  min: function(iterator, context) {
     723    iterator = iterator ? iterator.bind(context) : Prototype.K;
    565724    var result;
    566725    this.each(function(value, index) {
    567       value = (iterator || Prototype.K)(value, index);
     726      value = iterator(value, index);
    568727      if (result == undefined || value < result)
    569728        result = value;
     
    572731  },
    573732
    574   partition: function(iterator) {
     733  partition: function(iterator, context) {
     734    iterator = iterator ? iterator.bind(context) : Prototype.K;
    575735    var trues = [], falses = [];
    576736    this.each(function(value, index) {
    577       ((iterator || Prototype.K)(value, index) ?
     737      (iterator(value, index) ?
    578738        trues : falses).push(value);
    579739    });
     
    583743  pluck: function(property) {
    584744    var results = [];
    585     this.each(function(value, index) {
     745    this.each(function(value) {
    586746      results.push(value[property]);
    587747    });
     
    589749  },
    590750
    591   reject: function(iterator) {
     751  reject: function(iterator, context) {
     752    iterator = iterator.bind(context);
    592753    var results = [];
    593754    this.each(function(value, index) {
     
    598759  },
    599760
    600   sortBy: function(iterator) {
     761  sortBy: function(iterator, context) {
     762    iterator = iterator.bind(context);
    601763    return this.map(function(value, index) {
    602764      return {value: value, criteria: iterator(value, index)};
     
    613775  zip: function() {
    614776    var iterator = Prototype.K, args = $A(arguments);
    615     if (typeof args.last() == 'function')
     777    if (Object.isFunction(args.last()))
    616778      iterator = args.pop();
    617779
     
    629791    return '#<Enumerable:' + this.toArray().inspect() + '>';
    630792  }
    631 }
     793};
    632794
    633795Object.extend(Enumerable, {
     
    635797  find:    Enumerable.detect,
    636798  select:  Enumerable.findAll,
     799  filter:  Enumerable.findAll,
    637800  member:  Enumerable.include,
    638   entries: Enumerable.toArray
     801  entries: Enumerable.toArray,
     802  every:   Enumerable.all,
     803  some:    Enumerable.any
    639804});
    640 var $A = Array.from = function(iterable) {
     805function $A(iterable) {
    641806  if (!iterable) return [];
    642   if (iterable.toArray) {
    643     return iterable.toArray();
    644   } else {
    645     var results = [];
    646     for (var i = 0, length = iterable.length; i < length; i++)
    647       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];
    648820    return results;
    649821  }
    650822}
    651823
    652 if (Prototype.Browser.WebKit) {
    653   $A = Array.from = function(iterable) {
    654     if (!iterable) return [];
    655     if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
    656       iterable.toArray) {
    657       return iterable.toArray();
    658     } else {
    659       var results = [];
    660       for (var i = 0, length = iterable.length; i < length; i++)
    661         results.push(iterable[i]);
    662       return results;
    663     }
    664   }
    665 }
     824Array.from = $A;
    666825
    667826Object.extend(Array.prototype, Enumerable);
    668827
    669 if (!Array.prototype._reverse)
    670   Array.prototype._reverse = Array.prototype.reverse;
     828if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
    671829
    672830Object.extend(Array.prototype, {
     
    697855  flatten: function() {
    698856    return this.inject([], function(array, value) {
    699       return array.concat(value && value.constructor == Array ?
     857      return array.concat(Object.isArray(value) ?
    700858        value.flatten() : [value]);
    701859    });
     
    709867  },
    710868
    711   indexOf: function(object) {
    712     for (var i = 0, length = this.length; i < length; i++)
    713       if (this[i] == object) return i;
    714     return -1;
    715   },
    716 
    717869  reverse: function(inline) {
    718870    return (inline !== false ? this : this.toArray())._reverse();
     
    728880        array.push(value);
    729881      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 });
    730888    });
    731889  },
     
    753911});
    754912
     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
    755932Array.prototype.toArray = Array.prototype.clone;
    756933
    757934function $w(string) {
     935  if (!Object.isString(string)) return [];
    758936  string = string.strip();
    759937  return string ? string.split(/\s+/) : [];
     
    765943    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    766944    for (var i = 0, length = arguments.length; i < length; i++) {
    767       if (arguments[i].constructor == Array) {
     945      if (Object.isArray(arguments[i])) {
    768946        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
    769947          array.push(arguments[i][j]);
     
    773951    }
    774952    return array;
    775   }
     953  };
    776954}
    777 var Hash = function(object) {
    778   if (object instanceof Hash) this.merge(object);
    779   else Object.extend(this, object || {});
    780 };
    781 
    782 Object.extend(Hash, {
    783   toQueryString: function(obj) {
    784     var parts = [];
    785     parts.add = arguments.callee.addPair;
    786 
    787     this.prototype._each.call(obj, function(pair) {
    788       if (!pair.key) return;
    789       var value = pair.value;
    790 
    791       if (value && typeof value == 'object') {
    792         if (value.constructor == Array) value.each(function(value) {
    793           parts.add(pair.key, value);
    794         });
    795         return;
    796       }
    797       parts.add(pair.key, value);
    798     });
    799 
    800     return parts.join('&');
    801   },
    802 
    803   toJSON: function(object) {
    804     var results = [];
    805     this.prototype._each.call(object, function(pair) {
    806       var value = Object.toJSON(pair.value);
    807       if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
    808     });
    809     return '{' + results.join(', ') + '}';
     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';
    810976  }
    811977});
    812978
    813 Hash.toQueryString.addPair = function(key, value, prefix) {
    814   key = encodeURIComponent(key);
    815   if (value === undefined) this.push(key);
    816   else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
    817 }
    818 
    819 Object.extend(Hash.prototype, Enumerable);
    820 Object.extend(Hash.prototype, {
    821   _each: function(iterator) {
    822     for (var key in this) {
    823       var value = this[key];
    824       if (value && value == Hash.prototype[key]) continue;
    825 
    826       var pair = [key, value];
    827       pair.key = key;
    828       pair.value = value;
    829       iterator(pair);
    830     }
    831   },
    832 
    833   keys: function() {
    834     return this.pluck('key');
    835   },
    836 
    837   values: function() {
    838     return this.pluck('value');
    839   },
    840 
    841   merge: function(hash) {
    842     return $H(hash).inject(this, function(mergedHash, pair) {
    843       mergedHash[pair.key] = pair.value;
    844       return mergedHash;
    845     });
    846   },
    847 
    848   remove: function() {
    849     var result;
    850     for(var i = 0, length = arguments.length; i < length; i++) {
    851       var value = this[arguments[i]];
    852       if (value !== undefined){
    853         if (result === undefined) result = value;
    854         else {
    855           if (result.constructor != Array) result = [result];
    856           result.push(value)
    857         }
    858       }
    859       delete this[arguments[i]];
    860     }
    861     return result;
    862   },
    863 
    864   toQueryString: function() {
    865     return Hash.toQueryString(this);
    866   },
    867 
    868   inspect: function() {
    869     return '#<Hash:{' + this.map(function(pair) {
    870       return pair.map(Object.inspect).join(': ');
    871     }).join(', ') + '}>';
    872   },
    873 
    874   toJSON: function() {
    875     return Hash.toJSON(this);
    876   }
     979$w('abs round ceil floor').each(function(method){
     980  Number.prototype[method] = Math[method].methodize();
    877981});
    878 
    879982function $H(object) {
    880   if (object instanceof Hash) return object;
    881983  return new Hash(object);
    882984};
    883985
    884 // Safari iterates over shadowed properties
    885 if (function() {
    886   var i = 0, Test = function(value) { this.key = value };
    887   Test.prototype.key = 'foo';
    888   for (var property in new Test('bar')) i++;
    889   return i > 1;
    890 }()) Hash.prototype._each = function(iterator) {
    891   var cache = [];
    892   for (var key in this) {
    893     var value = this[key];
    894     if ((value && value == Hash.prototype[key]) || cache.include(key)) continue;
    895     cache.push(key);
    896     var pair = [key, value];
    897     pair.key = key;
    898     pair.value = value;
    899     iterator(pair);
    900   }
    901 };
    902 ObjectRange = Class.create();
    903 Object.extend(ObjectRange.prototype, Enumerable);
    904 Object.extend(ObjectRange.prototype, {
     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, {
    9051103  initialize: function(start, end, exclusive) {
    9061104    this.start = start;
     
    9281126var $R = function(start, end, exclusive) {
    9291127  return new ObjectRange(start, end, exclusive);
    930 }
     1128};
    9311129
    9321130var Ajax = {
     
    9401138
    9411139  activeRequestCount: 0
    942 }
     1140};
    9431141
    9441142Ajax.Responders = {
     
    9601158  dispatch: function(callback, request, transport, json) {
    9611159    this.each(function(responder) {
    962       if (typeof responder[callback] == 'function') {
     1160      if (Object.isFunction(responder[callback])) {
    9631161        try {
    9641162          responder[callback].apply(responder, [request, transport, json]);
    965         } catch (e) {}
     1163        } catch (e) { }
    9661164      }
    9671165    });
     
    9721170
    9731171Ajax.Responders.register({
    974   onCreate: function() {
    975     Ajax.activeRequestCount++;
    976   },
    977   onComplete: function() {
    978     Ajax.activeRequestCount--;
    979   }
     1172  onCreate:   function() { Ajax.activeRequestCount++ },
     1173  onComplete: function() { Ajax.activeRequestCount-- }
    9801174});
    9811175
    982 Ajax.Base = function() {};
    983 Ajax.Base.prototype = {
    984   setOptions: function(options) {
     1176Ajax.Base = Class.create({
     1177  initialize: function(options) {
    9851178    this.options = {
    9861179      method:       'post',
     
    9881181      contentType:  'application/x-www-form-urlencoded',
    9891182      encoding:     'UTF-8',
    990       parameters:   ''
    991     }
    992     Object.extend(this.options, options || {});
     1183      parameters:   '',
     1184      evalJSON:     true,
     1185      evalJS:       true
     1186    };
     1187    Object.extend(this.options, options || { });
    9931188
    9941189    this.options.method = this.options.method.toLowerCase();
    995     if (typeof this.options.parameters == 'string')
     1190    if (Object.isString(this.options.parameters))
    9961191      this.options.parameters = this.options.parameters.toQueryParams();
    9971192  }
    998 }
    999 
    1000 Ajax.Request = Class.create();
    1001 Ajax.Request.Events =
    1002   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
    1003 
    1004 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
     1193});
     1194
     1195Ajax.Request = Class.create(Ajax.Base, {
    10051196  _complete: false,
    10061197
    1007   initialize: function(url, options) {
     1198  initialize: function($super, url, options) {
     1199    $super(options);
    10081200    this.transport = Ajax.getTransport();
    1009     this.setOptions(options);
    10101201    this.request(url);
    10111202  },
     
    10241215    this.parameters = params;
    10251216
    1026     if (params = Hash.toQueryString(params)) {
     1217    if (params = Object.toQueryString(params)) {
    10271218      // when GET, append parameters to URL
    10281219      if (this.method == 'get')
     
    10331224
    10341225    try {
    1035       if (this.options.onCreate) this.options.onCreate(this.transport);
    1036       Ajax.Responders.dispatch('onCreate', this, this.transport);
     1226      var response = new Ajax.Response(this);
     1227      if (this.options.onCreate) this.options.onCreate(response);
     1228      Ajax.Responders.dispatch('onCreate', this, response);
    10371229
    10381230      this.transport.open(this.method.toUpperCase(), this.url,
    10391231        this.options.asynchronous);
    10401232
    1041       if (this.options.asynchronous)
    1042         setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
     1233      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
    10431234
    10441235      this.transport.onreadystatechange = this.onStateChange.bind(this);
     
    10881279      var extras = this.options.requestHeaders;
    10891280
    1090       if (typeof extras.push == 'function')
     1281      if (Object.isFunction(extras.push))
    10911282        for (var i = 0, length = extras.length; i < length; i += 2)
    10921283          headers[extras[i]] = extras[i+1];
     
    11001291
    11011292  success: function() {
    1102     return !this.transport.status
    1103         || (this.transport.status >= 200 && this.transport.status < 300);
     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 }
    11041301  },
    11051302
    11061303  respondToReadyState: function(readyState) {
    1107     var state = Ajax.Request.Events[readyState];
    1108     var transport = this.transport, json = this.evalJSON();
     1304    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
    11091305
    11101306    if (state == 'Complete') {
    11111307      try {
    11121308        this._complete = true;
    1113         (this.options['on' + this.transport.status]
     1309        (this.options['on' + response.status]
    11141310         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
    1115          || Prototype.emptyFunction)(transport, json);
     1311         || Prototype.emptyFunction)(response, response.headerJSON);
    11161312      } catch (e) {
    11171313        this.dispatchException(e);
    11181314      }
    11191315
    1120       var contentType = this.getHeader('Content-type');
    1121       if (contentType && contentType.strip().
    1122         match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
    1123           this.evalResponse();
     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();
    11241321    }
    11251322
    11261323    try {
    1127       (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
    1128       Ajax.Responders.dispatch('on' + state, this, transport, json);
     1324      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
     1325      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
    11291326    } catch (e) {
    11301327      this.dispatchException(e);
     
    11401337    try {
    11411338      return this.transport.getResponseHeader(name);
    1142     } catch (e) { return null }
    1143   },
    1144 
    1145   evalJSON: function() {
    1146     try {
    1147       var json = this.getHeader('X-JSON');
    1148       return json ? json.evalJSON() : null;
    11491339    } catch (e) { return null }
    11501340  },
     
    11641354});
    11651355
    1166 Ajax.Updater = Class.create();
    1167 
    1168 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
    1169   initialize: function(container, url, options) {
     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) {
    11701432    this.container = {
    11711433      success: (container.success || container),
    11721434      failure: (container.failure || (container.success ? null : container))
    1173     }
    1174 
    1175     this.transport = Ajax.getTransport();
    1176     this.setOptions(options);
    1177 
    1178     var onComplete = this.options.onComplete || Prototype.emptyFunction;
    1179     this.options.onComplete = (function(transport, param) {
    1180       this.updateContent();
    1181       onComplete(transport, param);
     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);
    11821442    }).bind(this);
    11831443
    1184     this.request(url);
    1185   },
    1186 
    1187   updateContent: function() {
    1188     var receiver = this.container[this.success() ? 'success' : 'failure'];
    1189     var response = this.transport.responseText;
    1190 
    1191     if (!this.options.evalScripts) response = response.stripScripts();
     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();
    11921452
    11931453    if (receiver = $(receiver)) {
    1194       if (this.options.insertion)
    1195         new this.options.insertion(receiver, response);
    1196       else
    1197         receiver.update(response);
     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);
    11981462    }
    11991463
    12001464    if (this.success()) {
    1201       if (this.onComplete)
    1202         setTimeout(this.onComplete.bind(this), 10);
     1465      if (this.onComplete) this.onComplete.bind(this).defer();
    12031466    }
    12041467  }
    12051468});
    12061469
    1207 Ajax.PeriodicalUpdater = Class.create();
    1208 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
    1209   initialize: function(container, url, options) {
    1210     this.setOptions(options);
     1470Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
     1471  initialize: function($super, container, url, options) {
     1472    $super(options);
    12111473    this.onComplete = this.options.onComplete;
    12121474
     
    12141476    this.decay = (this.options.decay || 1);
    12151477
    1216     this.updater = {};
     1478    this.updater = { };
    12171479    this.container = container;
    12181480    this.url = url;
     
    12321494  },
    12331495
    1234   updateComplete: function(request) {
     1496  updateComplete: function(response) {
    12351497    if (this.options.decay) {
    1236       this.decay = (request.responseText == this.lastText ?
     1498      this.decay = (response.responseText == this.lastText ?
    12371499        this.decay * this.options.decay : 1);
    12381500
    1239       this.lastText = request.responseText;
    1240     }
    1241     this.timer = setTimeout(this.onTimerEvent.bind(this),
    1242       this.decay * this.frequency * 1000);
     1501      this.lastText = response.responseText;
     1502    }
     1503    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
    12431504  },
    12441505
     
    12531514    return elements;
    12541515  }
    1255   if (typeof element == 'string')
     1516  if (Object.isString(element))
    12561517    element = document.getElementById(element);
    12571518  return Element.extend(element);
     
    12641525      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    12651526    for (var i = 0, length = query.snapshotLength; i < length; i++)
    1266       results.push(query.snapshotItem(i));
     1527      results.push(Element.extend(query.snapshotItem(i)));
    12671528    return results;
    12681529  };
    1269 
    1270   document.getElementsByClassName = function(className, parentElement) {
    1271     var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
    1272     return document._getElementsByXPath(q, parentElement);
    1273   }
    1274 
    1275 } else document.getElementsByClassName = function(className, parentElement) {
    1276   var children = ($(parentElement) || document.body).getElementsByTagName('*');
    1277   var elements = [], child, pattern = new RegExp("(^|\\s)" + className + "(\\s|$)");
    1278   for (var i = 0, length = children.length; i < length; i++) {
    1279     child = children[i];
    1280     var elementClassName = child.className;
    1281     if (elementClassName.length == 0) continue;
    1282     if (elementClassName == className || elementClassName.match(pattern))
    1283       elements.push(Element.extend(child));
    1284   }
    1285   return elements;
    1286 };
     1530}
    12871531
    12881532/*--------------------------------------------------------------------------*/
    12891533
    1290 if (!window.Element) var Element = {};
    1291 
    1292 Element.extend = function(element) {
    1293   var F = Prototype.BrowserFeatures;
    1294   if (!element || !element.tagName || element.nodeType == 3 ||
    1295    element._extended || F.SpecificElementExtensions || element == window)
    1296     return element;
    1297 
    1298   var methods = {}, tagName = element.tagName, cache = Element.extend.cache,
    1299    T = Element.Methods.ByTag;
    1300 
    1301   // extend methods for all tags (Safari doesn't need this)
    1302   if (!F.ElementExtensions) {
    1303     Object.extend(methods, Element.Methods),
    1304     Object.extend(methods, Element.Methods.Simulated);
    1305   }
    1306 
    1307   // extend methods for specific tags
    1308   if (T[tagName]) Object.extend(methods, T[tagName]);
    1309 
    1310   for (var property in methods) {
    1311     var value = methods[property];
    1312     if (typeof value == 'function' && !(property in element))
    1313       element[property] = cache.findOrStore(value);
    1314   }
    1315 
    1316   element._extended = Prototype.emptyFunction;
    1317   return element;
    1318 };
    1319 
    1320 Element.extend.cache = {
    1321   findOrStore: function(value) {
    1322     return this[value] = this[value] || function() {
    1323       return value.apply(null, [this].concat($A(arguments)));
    1324     }
    1325   }
    1326 };
     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
     1551  });
     1552}
     1553
     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 = { };
    13271572
    13281573Element.Methods = {
     
    13531598  },
    13541599
    1355   update: function(element, html) {
    1356     html = typeof html == 'undefined' ? '' : html.toString();
    1357     $(element).innerHTML = html.stripScripts();
    1358     setTimeout(function() {html.evalScripts()}, 10);
     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();
    13591607    return element;
    13601608  },
    13611609
    1362   replace: function(element, html) {
     1610  replace: function(element, content) {
    13631611    element = $(element);
    1364     html = typeof html == 'undefined' ? '' : html.toString();
    1365     if (element.outerHTML) {
    1366       element.outerHTML = html.stripScripts();
    1367     } else {
     1612    if (content && content.toElement) content = content.toElement();
     1613    else if (!Object.isElement(content)) {
     1614      content = Object.toHTML(content);
    13681615      var range = element.ownerDocument.createRange();
    1369       range.selectNodeContents(element);
    1370       element.parentNode.replaceChild(
    1371         range.createContextualFragment(html.stripScripts()), element);
    1372     }
    1373     setTimeout(function() {html.evalScripts()}, 10);
     1616      range.selectNode(element);
     1617      content.evalScripts.bind(content).defer();
     1618      content = range.createContextualFragment(content.stripScripts());
     1619    }
     1620    element.parentNode.replaceChild(content, element);
    13741621    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;
    13751666  },
    13761667
     
    14301721
    14311722  match: function(element, selector) {
    1432     if (typeof selector == 'string')
     1723    if (Object.isString(selector))
    14331724      selector = new Selector(selector);
    14341725    return selector.match($(element));
     
    14671758  },
    14681759
    1469   getElementsBySelector: function() {
     1760  select: function() {
    14701761    var args = $A(arguments), element = $(args.shift());
    14711762    return Selector.findChildElements(element, args);
    14721763  },
    14731764
    1474   getElementsByClassName: function(element, className) {
    1475     return document.getElementsByClassName(className, element);
     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;
    14761777  },
    14771778
     
    14791780    element = $(element);
    14801781    if (Prototype.Browser.IE) {
    1481       if (!element.attributes) return null;
    1482       var t = Element._attributeTranslations;
     1782      var t = Element._attributeTranslations.read;
    14831783      if (t.values[name]) return t.values[name](element, name);
    1484       if (t.names[name])  name = t.names[name];
    1485       var attribute = element.attributes[name];
    1486       return attribute ? attribute.nodeValue : null;
     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      }
    14871789    }
    14881790    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;
    14891810  },
    14901811
     
    15041825    if (!(element = $(element))) return;
    15051826    var elementClassName = element.className;
    1506     if (elementClassName.length == 0) return false;
    1507     if (elementClassName == className ||
    1508         elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
    1509       return true;
    1510     return false;
     1827    return (elementClassName.length > 0 && (elementClassName == className ||
     1828      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
    15111829  },
    15121830
    15131831  addClassName: function(element, className) {
    15141832    if (!(element = $(element))) return;
    1515     Element.classNames(element).add(className);
     1833    if (!element.hasClassName(className))
     1834      element.className += (element.className ? ' ' : '') + className;
    15161835    return element;
    15171836  },
     
    15191838  removeClassName: function(element, className) {
    15201839    if (!(element = $(element))) return;
    1521     Element.classNames(element).remove(className);
     1840    element.className = element.className.replace(
     1841      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    15221842    return element;
    15231843  },
     
    15251845  toggleClassName: function(element, className) {
    15261846    if (!(element = $(element))) return;
    1527     Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
    1528     return element;
    1529   },
    1530 
    1531   observe: function() {
    1532     Event.observe.apply(Event, arguments);
    1533     return $A(arguments).first();
    1534   },
    1535 
    1536   stopObserving: function() {
    1537     Event.stopObserving.apply(Event, arguments);
    1538     return $A(arguments).first();
     1847    return element[element.hasClassName(className) ?
     1848      'removeClassName' : 'addClassName'](className);
    15391849  },
    15401850
     
    15581868  descendantOf: function(element, ancestor) {
    15591869    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
    15601884    while (element = element.parentNode)
    15611885      if (element == ancestor) return true;
     
    15651889  scrollTo: function(element) {
    15661890    element = $(element);
    1567     var pos = Position.cumulativeOffset(element);
     1891    var pos = element.cumulativeOffset();
    15681892    window.scrollTo(pos[0], pos[1]);
    15691893    return element;
     
    15861910  },
    15871911
    1588   setStyle: function(element, styles, camelized) {
     1912  setStyle: function(element, styles) {
    15891913    element = $(element);
    1590     var elementStyle = element.style;
    1591 
     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    }
    15921920    for (var property in styles)
    1593       if (property == 'opacity') element.setOpacity(styles[property])
     1921      if (property == 'opacity') element.setOpacity(styles[property]);
    15941922      else
    15951923        elementStyle[(property == 'float' || property == 'cssFloat') ?
    15961924          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
    1597           (camelized ? property : property.camelize())] = styles[property];
     1925            property] = styles[property];
    15981926
    15991927    return element;
     
    16621990    element = $(element);
    16631991    if (element._overflow) return element;
    1664     element._overflow = element.style.overflow || 'auto';
    1665     if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
     1992    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
     1993    if (element._overflow !== 'hidden')
    16661994      element.style.overflow = 'hidden';
    16671995    return element;
     
    16742002    element._overflow = null;
    16752003    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;
    16762154  }
    16772155};
    16782156
     2157Element.Methods.identify.counter = 1;
     2158
    16792159Object.extend(Element.Methods, {
    1680   childOf: Element.Methods.descendantOf,
     2160  getElementsBySelector: Element.Methods.select,
    16812161  childElements: Element.Methods.immediateDescendants
    16822162});
     2163
     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  };
     2212}
    16832213
    16842214if (Prototype.Browser.Opera) {
     
    16942224    }
    16952225  };
     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  };
    16962231}
     2232
    16972233else 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
    16982248  Element.Methods.getStyle = function(element, style) {
    16992249    element = $(element);
     
    17102260    if (value == 'auto') {
    17112261      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
    1712         return element['offset'+style.capitalize()] + 'px';
     2262        return element['offset' + style.capitalize()] + 'px';
    17132263      return null;
    17142264    }
     
    17172267
    17182268  Element.Methods.setOpacity = function(element, value) {
     2269    function stripAlpha(filter){
     2270      return filter.replace(/alpha\([^\)]*\)/gi,'');
     2271    }
    17192272    element = $(element);
     2273    var currentStyle = element.currentStyle;
     2274    if ((currentStyle && !currentStyle.hasLayout) ||
     2275      (!currentStyle && element.style.zoom == 'normal'))
     2276        element.style.zoom = 1;
     2277
    17202278    var filter = element.getStyle('filter'), style = element.style;
    17212279    if (value == 1 || value === '') {
    1722       style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
     2280      (filter = stripAlpha(filter)) ?
     2281        style.filter = filter : style.removeAttribute('filter');
    17232282      return element;
    17242283    } else if (value < 0.00001) value = 0;
    1725     style.filter = filter.replace(/alpha\([^\)]*\)/gi, '') +
     2284    style.filter = stripAlpha(filter) +
    17262285      'alpha(opacity=' + (value * 100) + ')';
    17272286    return element;
    17282287  };
    17292288
    1730   // IE is missing .innerHTML support for TABLE-related elements
    1731   Element.Methods.update = function(element, html) {
    1732     element = $(element);
    1733     html = typeof html == 'undefined' ? '' : html.toString();
    1734     var tagName = element.tagName.toUpperCase();
    1735     if (['THEAD','TBODY','TR','TD'].include(tagName)) {
    1736       var div = document.createElement('div');
    1737       switch (tagName) {
    1738         case 'THEAD':
    1739         case 'TBODY':
    1740           div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
    1741           depth = 2;
    1742           break;
    1743         case 'TR':
    1744           div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
    1745           depth = 3;
    1746           break;
    1747         case 'TD':
    1748           div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
    1749           depth = 4;
    1750       }
    1751       $A(element.childNodes).each(function(node) { element.removeChild(node) });
    1752       depth.times(function() { div = div.firstChild });
    1753       $A(div.childNodes).each(function(node) { element.appendChild(node) });
    1754     } else {
    1755       element.innerHTML = html.stripScripts();
    1756     }
    1757     setTimeout(function() { html.evalScripts() }, 10);
    1758     return element;
    1759   }
     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;
     2315        }
     2316      }
     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);
    17602371}
    1761 else if (Prototype.Browser.Gecko) {
     2372
     2373else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
    17622374  Element.Methods.setOpacity = function(element, value) {
    17632375    element = $(element);
     
    17682380}
    17692381
    1770 Element._attributeTranslations = {
    1771   names: {
    1772     colspan:   "colSpan",
    1773     rowspan:   "rowSpan",
    1774     valign:    "vAlign",
    1775     datetime:  "dateTime",
    1776     accesskey: "accessKey",
    1777     tabindex:  "tabIndex",
    1778     enctype:   "encType",
    1779     maxlength: "maxLength",
    1780     readonly:  "readOnly",
    1781     longdesc:  "longDesc"
    1782   },
    1783   values: {
    1784     _getAttr: function(element, attribute) {
    1785       return element.getAttribute(attribute, 2);
    1786     },
    1787     _flag: function(element, attribute) {
    1788       return $(element).hasAttribute(attribute) ? attribute : null;
    1789     },
    1790     style: function(element) {
    1791       return element.style.cssText.toLowerCase();
    1792     },
    1793     title: function(element) {
    1794       var node = element.getAttributeNode('title');
    1795       return node.specified ? node.nodeValue : null;
    1796     }
    1797   }
     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;
    17982475};
    17992476
     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
    18002528(function() {
    1801   Object.extend(this, {
    1802     href: this._getAttr,
    1803     src:  this._getAttr,
    1804     type: this._getAttr,
    1805     disabled: this._flag,
    1806     checked:  this._flag,
    1807     readonly: this._flag,
    1808     multiple: this._flag
     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
    18092534  });
    1810 }).call(Element._attributeTranslations.values);
     2535}).call(Element._insertionTranslations);
    18112536
    18122537Element.Methods.Simulated = {
    18132538  hasAttribute: function(element, attribute) {
    1814     var t = Element._attributeTranslations, node;
    1815     attribute = t.names[attribute] || attribute;
    1816     node = $(element).getAttributeNode(attribute);
     2539    attribute = Element._attributeTranslations.has[attribute] || attribute;
     2540    var node = $(element).getAttributeNode(attribute);
    18172541    return node && node.specified;
    18182542  }
    18192543};
    18202544
    1821 Element.Methods.ByTag = {};
     2545Element.Methods.ByTag = { };
    18222546
    18232547Object.extend(Element, Element.Methods);
    18242548
    18252549if (!Prototype.BrowserFeatures.ElementExtensions &&
    1826  document.createElement('div').__proto__) {
    1827   window.HTMLElement = {};
     2550    document.createElement('div').__proto__) {
     2551  window.HTMLElement = { };
    18282552  window.HTMLElement.prototype = document.createElement('div').__proto__;
    18292553  Prototype.BrowserFeatures.ElementExtensions = true;
    18302554}
     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})();
    18312594
    18322595Element.hasAttribute = function(element, attribute) {
     
    18542617  }
    18552618
    1856   if (!tagName) Object.extend(Element.Methods, methods || {});
     2619  if (!tagName) Object.extend(Element.Methods, methods || { });
    18572620  else {
    1858     if (tagName.constructor == Array) tagName.each(extend);
     2621    if (Object.isArray(tagName)) tagName.each(extend);
    18592622    else extend(tagName);
    18602623  }
     
    18632626    tagName = tagName.toUpperCase();
    18642627    if (!Element.Methods.ByTag[tagName])
    1865       Element.Methods.ByTag[tagName] = {};
     2628      Element.Methods.ByTag[tagName] = { };
    18662629    Object.extend(Element.Methods.ByTag[tagName], methods);
    18672630  }
     
    18692632  function copy(methods, destination, onlyIfAbsent) {
    18702633    onlyIfAbsent = onlyIfAbsent || false;
    1871     var cache = Element.extend.cache;
    18722634    for (var property in methods) {
    18732635      var value = methods[property];
     2636      if (!Object.isFunction(value)) continue;
    18742637      if (!onlyIfAbsent || !(property in destination))
    1875         destination[property] = cache.findOrStore(value);
     2638        destination[property] = value.methodize();
    18762639    }
    18772640  }
     
    18972660    if (window[klass]) return window[klass];
    18982661
    1899     window[klass] = {};
     2662    window[klass] = { };
    19002663    window[klass].prototype = document.createElement(tagName).__proto__;
    19012664    return window[klass];
     
    19102673    for (var tag in Element.Methods.ByTag) {
    19112674      var klass = findDOMClass(tag);
    1912       if (typeof klass == "undefined") continue;
     2675      if (Object.isUndefined(klass)) continue;
    19132676      copy(T[tag], klass.prototype);
    19142677    }
     
    19172680  Object.extend(Element, Element.Methods);
    19182681  delete Element.ByTag;
     2682
     2683  if (Element.extend.refresh) Element.extend.refresh();
     2684  Element.cache = { };
    19192685};
    19202686
    1921 var Toggle = { display: Element.toggle };
    1922 
    1923 /*--------------------------------------------------------------------------*/
    1924 
    1925 Abstract.Insertion = function(adjacency) {
    1926   this.adjacency = adjacency;
    1927 }
    1928 
    1929 Abstract.Insertion.prototype = {
    1930   initialize: function(element, content) {
    1931     this.element = $(element);
    1932     this.content = content.stripScripts();
    1933 
    1934     if (this.adjacency && this.element.insertAdjacentHTML) {
    1935       try {
    1936         this.element.insertAdjacentHTML(this.adjacency, this.content);
    1937       } catch (e) {
    1938         var tagName = this.element.tagName.toUpperCase();
    1939         if (['TBODY', 'TR'].include(tagName)) {
    1940           this.insertContent(this.contentFromAnonymousTable());
    1941         } else {
    1942           throw e;
    1943         }
    1944       }
    1945     } else {
    1946       this.range = this.element.ownerDocument.createRange();
    1947       if (this.initializeRange) this.initializeRange();
    1948       this.insertContent([this.range.createContextualFragment(this.content)]);
    1949     }
    1950 
    1951     setTimeout(function() {content.evalScripts()}, 10);
    1952   },
    1953 
    1954   contentFromAnonymousTable: function() {
    1955     var div = document.createElement('div');
    1956     div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
    1957     return $A(div.childNodes[0].childNodes[0].childNodes);
    1958   }
    1959 }
    1960 
    1961 var Insertion = new Object();
    1962 
    1963 Insertion.Before = Class.create();
    1964 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
    1965   initializeRange: function() {
    1966     this.range.setStartBefore(this.element);
    1967   },
    1968 
    1969   insertContent: function(fragments) {
    1970     fragments.each((function(fragment) {
    1971       this.element.parentNode.insertBefore(fragment, this.element);
    1972     }).bind(this));
    1973   }
    1974 });
    1975 
    1976 Insertion.Top = Class.create();
    1977 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
    1978   initializeRange: function() {
    1979     this.range.selectNodeContents(this.element);
    1980     this.range.collapse(true);
    1981   },
    1982 
    1983   insertContent: function(fragments) {
    1984     fragments.reverse(false).each((function(fragment) {
    1985       this.element.insertBefore(fragment, this.element.firstChild);
    1986     }).bind(this));
    1987   }
    1988 });
    1989 
    1990 Insertion.Bottom = Class.create();
    1991 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
    1992   initializeRange: function() {
    1993     this.range.selectNodeContents(this.element);
    1994     this.range.collapse(this.element);
    1995   },
    1996 
    1997   insertContent: function(fragments) {
    1998     fragments.each((function(fragment) {
    1999       this.element.appendChild(fragment);
    2000     }).bind(this));
    2001   }
    2002 });
    2003 
    2004 Insertion.After = Class.create();
    2005 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
    2006   initializeRange: function() {
    2007     this.range.setStartAfter(this.element);
    2008   },
    2009 
    2010   insertContent: function(fragments) {
    2011     fragments.each((function(fragment) {
    2012       this.element.parentNode.insertBefore(fragment,
    2013         this.element.nextSibling);
    2014     }).bind(this));
    2015   }
    2016 });
    2017 
    2018 /*--------------------------------------------------------------------------*/
    2019 
    2020 Element.ClassNames = Class.create();
    2021 Element.ClassNames.prototype = {
    2022   initialize: function(element) {
    2023     this.element = $(element);
    2024   },
    2025 
    2026   _each: function(iterator) {
    2027     this.element.className.split(/\s+/).select(function(name) {
    2028       return name.length > 0;
    2029     })._each(iterator);
    2030   },
    2031 
    2032   set: function(className) {
    2033     this.element.className = className;
    2034   },
    2035 
    2036   add: function(classNameToAdd) {
    2037     if (this.include(classNameToAdd)) return;
    2038     this.set($A(this).concat(classNameToAdd).join(' '));
    2039   },
    2040 
    2041   remove: function(classNameToRemove) {
    2042     if (!this.include(classNameToRemove)) return;
    2043     this.set($A(this).without(classNameToRemove).join(' '));
    2044   },
    2045 
    2046   toString: function() {
    2047     return $A(this).join(' ');
     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);
    20482710  }
    20492711};
    2050 
    2051 Object.extend(Element.ClassNames.prototype, Enumerable);
    20522712/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
    20532713 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
    20542714 * license.  Please see http://www.yui-ext.com/ for more information. */
    20552715
    2056 var Selector = Class.create();
    2057 
    2058 Selector.prototype = {
     2716var Selector = Class.create({
    20592717  initialize: function(expression) {
    20602718    this.expression = expression.strip();
     
    20642722  compileMatcher: function() {
    20652723    // Selectors with namespaced attributes can't use the XPath version
    2066     if (Prototype.BrowserFeatures.XPath && !(/\[[\w-]*?:/).test(this.expression))
     2724    if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
    20672725      return this.compileXPathMatcher();
    20682726
     
    20712729
    20722730    if (Selector._cache[e]) {
    2073       this.matcher = Selector._cache[e]; return;
    2074     }
     2731      this.matcher = Selector._cache[e];
     2732      return;
     2733    }
     2734
    20752735    this.matcher = ["this.matcher = function(root) {",
    20762736                    "var r = root, h = Selector.handlers, c = false, n;"];
     
    20812741        p = ps[i];
    20822742        if (m = e.match(p)) {
    2083           this.matcher.push(typeof c[i] == 'function' ? c[i](m) :
     2743          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
    20842744              new Template(c[i]).evaluate(m));
    20852745          e = e.replace(m[0], '');
     
    20962756  compileXPathMatcher: function() {
    20972757    var e = this.expression, ps = Selector.patterns,
    2098         x = Selector.xpath, le,  m;
     2758        x = Selector.xpath, le, m;
    20992759
    21002760    if (Selector._cache[e]) {
     
    21072767      for (var i in ps) {
    21082768        if (m = e.match(ps[i])) {
    2109           this.matcher.push(typeof x[i] == 'function' ? x[i](m) :
     2769          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
    21102770            new Template(x[i]).evaluate(m));
    21112771          e = e.replace(m[0], '');
     
    21262786
    21272787  match: function(element) {
    2128     return this.findElements(document).include(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;
    21292821  },
    21302822
     
    21362828    return "#<Selector:" + this.expression.inspect() + ">";
    21372829  }
    2138 };
     2830});
    21392831
    21402832Object.extend(Selector, {
    2141   _cache: {},
     2833  _cache: { },
    21422834
    21432835  xpath: {
     
    21612853      var h = Selector.xpath.pseudos[m[1]];
    21622854      if (!h) return '';
    2163       if (typeof h === 'function') return h(m);
     2855      if (Object.isFunction(h)) return h(m);
    21642856      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    21652857    },
     
    21902882          for (var i in p) {
    21912883            if (m = e.match(p[i])) {
    2192               v = typeof x[i] == 'function' ? x[i](m) : new Template(x[i]).evaluate(m);
     2884              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
    21932885              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
    21942886              e = e.replace(m[0], '');
     
    22482940      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
    22492941    },
    2250     pseudo:       function(m) {
     2942    pseudo: function(m) {
    22512943      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
    22522944      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
     
    22702962    id:           /^#([\w\-\*]+)(\b|$)/,
    22712963    className:    /^\.([\w\-\*]+)(\b|$)/,
    2272     pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|\s|(?=:))/,
     2964    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
    22732965    attrPresence: /^\[([\w]+)\]/,
    2274     attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\]]*?)\4|([^'"][^\]]*?)))?\]/
     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    }
    22752991  },
    22762992
     
    23043020      if (reverse) {
    23053021        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
    2306           node = nodes[i];
     3022          var node = nodes[i];
    23073023          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
    23083024        }
     
    23913107    id: function(nodes, root, id, combinator) {
    23923108      var targetNode = $(id), h = Selector.handlers;
    2393       if (!nodes && root == document) return targetNode ? [targetNode] : [];
     3109      if (!targetNode) return [];
     3110      if (!nodes && root == document) return [targetNode];
    23943111      if (nodes) {
    23953112        if (combinator) {
     
    24313148
    24323149    attrPresence: function(nodes, root, attr) {
     3150      if (!nodes) nodes = root.getElementsByTagName("*");
    24333151      var results = [];
    24343152      for (var i = 0, node; node = nodes[i]; i++)
     
    25993317
    26003318  findElement: function(elements, expression, index) {
    2601     if (typeof expression == 'number') {
     3319    if (Object.isNumber(expression)) {
    26023320      index = expression; expression = false;
    26033321    }
     
    26283346  },
    26293347
    2630   serializeElements: function(elements, getHash) {
    2631     var data = elements.inject({}, function(result, element) {
     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) {
    26323354      if (!element.disabled && element.name) {
    2633         var key = element.name, value = $(element).getValue();
    2634         if (value != null) {
    2635             if (key in result) {
    2636             if (result[key].constructor != Array) result[key] = [result[key]];
     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]];
    26373361            result[key].push(value);
    26383362          }
     
    26433367    });
    26443368
    2645     return getHash ? data : Hash.toQueryString(data);
     3369    return options.hash ? data : Object.toQueryString(data);
    26463370  }
    26473371};
    26483372
    26493373Form.Methods = {
    2650   serialize: function(form, getHash) {
    2651     return Form.serializeElements(Form.getElements(form), getHash);
     3374  serialize: function(form, options) {
     3375    return Form.serializeElements(Form.getElements(form), options);
    26523376  },
    26533377
     
    26913415
    26923416  findFirstElement: function(form) {
    2693     return $(form).getElements().find(function(element) {
    2694       return element.type != 'hidden' && !element.disabled &&
    2695         ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
     3417    var elements = $(form).getElements().findAll(function(element) {
     3418      return 'hidden' != element.type && !element.disabled;
     3419    });
     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());
    26963426    });
    26973427  },
     
    27043434
    27053435  request: function(form, options) {
    2706     form = $(form), options = Object.clone(options || {});
    2707 
    2708     var params = options.parameters;
     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;
    27093440    options.parameters = form.serialize(true);
    27103441
    27113442    if (params) {
    2712       if (typeof params == 'string') params = params.toQueryParams();
     3443      if (Object.isString(params)) params = params.toQueryParams();
    27133444      Object.extend(options.parameters, params);
    27143445    }
     
    27173448      options.method = form.method;
    27183449
    2719     return new Ajax.Request(form.readAttribute('action'), options);
    2720   }
    2721 }
     3450    return new Ajax.Request(action, options);
     3451  }
     3452};
    27223453
    27233454/*--------------------------------------------------------------------------*/
     
    27333464    return element;
    27343465  }
    2735 }
     3466};
    27363467
    27373468Form.Element.Methods = {
     
    27413472      var value = element.getValue();
    27423473      if (value != undefined) {
    2743         var pair = {};
     3474        var pair = { };
    27443475        pair[element.name] = value;
    2745         return Hash.toQueryString(pair);
     3476        return Object.toQueryString(pair);
    27463477      }
    27473478    }
     
    27533484    var method = element.tagName.toLowerCase();
    27543485    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;
    27553493  },
    27563494
     
    27693507      element.focus();
    27703508      if (element.select && (element.tagName.toLowerCase() != 'input' ||
    2771         !['button', 'reset', 'submit'].include(element.type)))
     3509          !['button', 'reset', 'submit'].include(element.type)))
    27723510        element.select();
    2773     } catch (e) {}
     3511    } catch (e) { }
    27743512    return element;
    27753513  },
     
    27873525    return element;
    27883526  }
    2789 }
     3527};
    27903528
    27913529/*--------------------------------------------------------------------------*/
     
    27973535
    27983536Form.Element.Serializers = {
    2799   input: function(element) {
     3537  input: function(element, value) {
    28003538    switch (element.type.toLowerCase()) {
    28013539      case 'checkbox':
    28023540      case 'radio':
    2803         return Form.Element.Serializers.inputSelector(element);
     3541        return Form.Element.Serializers.inputSelector(element, value);
    28043542      default:
    2805         return Form.Element.Serializers.textarea(element);
    2806     }
    2807   },
    2808 
    2809   inputSelector: function(element) {
    2810     return element.checked ? element.value : null;
    2811   },
    2812 
    2813   textarea: function(element) {
    2814     return element.value;
    2815   },
    2816 
    2817   select: function(element) {
    2818     return this[element.type == 'select-one' ?
    2819       'selectOne' : 'selectMany'](element);
     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    }
    28203575  },
    28213576
     
    28403595    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
    28413596  }
    2842 }
     3597};
    28433598
    28443599/*--------------------------------------------------------------------------*/
    28453600
    2846 Abstract.TimedObserver = function() {}
    2847 Abstract.TimedObserver.prototype = {
    2848   initialize: function(element, frequency, callback) {
    2849     this.frequency = frequency;
     3601Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
     3602  initialize: function($super, element, frequency, callback) {
     3603    $super(callback, frequency);
    28503604    this.element   = $(element);
    2851     this.callback  = callback;
    2852 
    28533605    this.lastValue = this.getValue();
    2854     this.registerCallback();
    2855   },
    2856 
    2857   registerCallback: function() {
    2858     setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
    2859   },
    2860 
    2861   onTimerEvent: function() {
     3606  },
     3607
     3608  execute: function() {
    28623609    var value = this.getValue();
    2863     var changed = ('string' == typeof this.lastValue && 'string' == typeof value
    2864       ? this.lastValue != value : String(this.lastValue) != String(value));
    2865     if (changed) {
     3610    if (Object.isString(this.lastValue) && Object.isString(value) ?
     3611        this.lastValue != value : String(this.lastValue) != String(value)) {
    28663612      this.callback(this.element, value);
    28673613      this.lastValue = value;
    28683614    }
    28693615  }
    2870 }
    2871 
    2872 Form.Element.Observer = Class.create();
    2873 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
     3616});
     3617
     3618Form.Element.Observer = Class.create(Abstract.TimedObserver, {
    28743619  getValue: function() {
    28753620    return Form.Element.getValue(this.element);
     
    28773622});
    28783623
    2879 Form.Observer = Class.create();
    2880 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
     3624Form.Observer = Class.create(Abstract.TimedObserver, {
    28813625  getValue: function() {
    28823626    return Form.serialize(this.element);
     
    28863630/*--------------------------------------------------------------------------*/
    28873631
    2888 Abstract.EventObserver = function() {}
    2889 Abstract.EventObserver.prototype = {
     3632Abstract.EventObserver = Class.create({
    28903633  initialize: function(element, callback) {
    28913634    this.element  = $(element);
     
    29083651
    29093652  registerFormCallbacks: function() {
    2910     Form.getElements(this.element).each(this.registerCallback.bind(this));
     3653    Form.getElements(this.element).each(this.registerCallback, this);
    29113654  },
    29123655
     
    29243667    }
    29253668  }
    2926 }
    2927 
    2928 Form.Element.EventObserver = Class.create();
    2929 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
     3669});
     3670
     3671Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
    29303672  getValue: function() {
    29313673    return Form.Element.getValue(this.element);
     
    29333675});
    29343676
    2935 Form.EventObserver = Class.create();
    2936 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
     3677Form.EventObserver = Class.create(Abstract.EventObserver, {
    29373678  getValue: function() {
    29383679    return Form.serialize(this.element);
    29393680  }
    29403681});
    2941 if (!window.Event) {
    2942   var Event = new Object();
    2943 }
     3682if (!window.Event) var Event = { };
    29443683
    29453684Object.extend(Event, {
     
    29573696  KEY_PAGEUP:   33,
    29583697  KEY_PAGEDOWN: 34,
    2959 
    2960   element: function(event) {
    2961     return $(event.target || event.srcElement);
    2962   },
    2963 
    2964   isLeftClick: function(event) {
    2965     return (((event.which) && (event.which == 1)) ||
    2966             ((event.button) && (event.button == 1)));
    2967   },
    2968 
    2969   pointerX: function(event) {
    2970     return event.pageX || (event.clientX +
    2971       (document.documentElement.scrollLeft || document.body.scrollLeft));
    2972   },
    2973 
    2974   pointerY: function(event) {
    2975     return event.pageY || (event.clientY +
    2976       (document.documentElement.scrollTop || document.body.scrollTop));
    2977   },
    2978 
    2979   stop: function(event) {
    2980     if (event.preventDefault) {
     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);
    29813766      event.preventDefault();
    29823767      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
    29833982    } else {
    2984       event.returnValue = false;
    2985       event.cancelBubble = true;
    2986     }
    2987   },
    2988 
    2989   // find the first node with the given tagName, starting from the
    2990   // node the event was triggered on; traverses the DOM upwards
    2991   findElement: function(event, tagName) {
    2992     var element = Event.element(event);
    2993     while (element.parentNode && (!element.tagName ||
    2994         (element.tagName.toUpperCase() != tagName.toUpperCase())))
    2995       element = element.parentNode;
    2996     return element;
    2997   },
    2998 
    2999   observers: false,
    3000 
    3001   _observeAndCache: function(element, name, observer, useCapture) {
    3002     if (!this.observers) this.observers = [];
    3003     if (element.addEventListener) {
    3004       this.observers.push([element, name, observer, useCapture]);
    3005       element.addEventListener(name, observer, useCapture);
    3006     } else if (element.attachEvent) {
    3007       this.observers.push([element, name, observer, useCapture]);
    3008       element.attachEvent('on' + name, observer);
    3009     }
    3010   },
    3011 
    3012   unloadCache: function() {
    3013     if (!Event.observers) return;
    3014     for (var i = 0, length = Event.observers.length; i < length; i++) {
    3015       Event.stopObserving.apply(this, Event.observers[i]);
    3016       Event.observers[i][0] = null;
    3017     }
    3018     Event.observers = false;
    3019   },
    3020 
    3021   observe: function(element, name, observer, useCapture) {
    3022     element = $(element);
    3023     useCapture = useCapture || false;
    3024 
    3025     if (name == 'keypress' &&
    3026       (Prototype.Browser.WebKit || element.attachEvent))
    3027       name = 'keydown';
    3028 
    3029     Event._observeAndCache(element, name, observer, useCapture);
    3030   },
    3031 
    3032   stopObserving: function(element, name, observer, useCapture) {
    3033     element = $(element);
    3034     useCapture = useCapture || false;
    3035 
    3036     if (name == 'keypress' &&
    3037         (Prototype.Browser.WebKit || element.attachEvent))
    3038       name = 'keydown';
    3039 
    3040     if (element.removeEventListener) {
    3041       element.removeEventListener(name, observer, useCapture);
    3042     } else if (element.detachEvent) {
    3043       try {
    3044         element.detachEvent('on' + name, observer);
    3045       } catch (e) {}
    3046     }
    3047   }
    3048 });
    3049 
    3050 /* prevent memory leaks in IE */
    3051 if (Prototype.Browser.IE)
    3052   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.
    30534027var Position = {
    30544028  // set to true if needed, warning: firefox performance problems
     
    30704044  },
    30714045
    3072   realOffset: function(element) {
    3073     var valueT = 0, valueL = 0;
    3074     do {
    3075       valueT += element.scrollTop  || 0;
    3076       valueL += element.scrollLeft || 0;
    3077       element = element.parentNode;
    3078     } while (element);
    3079     return [valueL, valueT];
    3080   },
    3081 
    3082   cumulativeOffset: function(element) {
    3083     var valueT = 0, valueL = 0;
    3084     do {
    3085       valueT += element.offsetTop  || 0;
    3086       valueL += element.offsetLeft || 0;
    3087       element = element.offsetParent;
    3088     } while (element);
    3089     return [valueL, valueT];
    3090   },
    3091 
    3092   positionedOffset: function(element) {
    3093     var valueT = 0, valueL = 0;
    3094     do {
    3095       valueT += element.offsetTop  || 0;
    3096       valueL += element.offsetLeft || 0;
    3097       element = element.offsetParent;
    3098       if (element) {
    3099         if(element.tagName=='BODY') break;
    3100         var p = Element.getStyle(element, 'position');
    3101         if (p == 'relative' || p == 'absolute') break;
    3102       }
    3103     } while (element);
    3104     return [valueL, valueT];
    3105   },
    3106 
    3107   offsetParent: function(element) {
    3108     if (element.offsetParent) return element.offsetParent;
    3109     if (element == document.body) return element;
    3110 
    3111     while ((element = element.parentNode) && element != document.body)
    3112       if (Element.getStyle(element, 'position') != 'static')
    3113         return element;
    3114 
    3115     return document.body;
    3116   },
    3117 
    31184046  // caches x/y coordinate pair to use with overlap
    31194047  within: function(element, x, y) {
     
    31224050    this.xcomp = x;
    31234051    this.ycomp = y;
    3124     this.offset = this.cumulativeOffset(element);
     4052    this.offset = Element.cumulativeOffset(element);
    31254053
    31264054    return (y >= this.offset[1] &&
     
    31314059
    31324060  withinIncludingScrolloffsets: function(element, x, y) {
    3133     var offsetcache = this.realOffset(element);
     4061    var offsetcache = Element.cumulativeScrollOffset(element);
    31344062
    31354063    this.xcomp = x + offsetcache[0] - this.deltaX;
    31364064    this.ycomp = y + offsetcache[1] - this.deltaY;
    3137     this.offset = this.cumulativeOffset(element);
     4065    this.offset = Element.cumulativeOffset(element);
    31384066
    31394067    return (this.ycomp >= this.offset[1] &&
     
    31544082  },
    31554083
    3156   page: function(forElement) {
    3157     var valueT = 0, valueL = 0;
    3158 
    3159     var element = forElement;
    3160     do {
    3161       valueT += element.offsetTop  || 0;
    3162       valueL += element.offsetLeft || 0;
    3163 
    3164       // Safari fix
    3165       if (element.offsetParent == document.body)
    3166         if (Element.getStyle(element,'position')=='absolute') break;
    3167 
    3168     } while (element = element.offsetParent);
    3169 
    3170     element = forElement;
    3171     do {
    3172       if (!window.opera || element.tagName=='BODY') {
    3173         valueT -= element.scrollTop  || 0;
    3174         valueL -= element.scrollLeft || 0;
    3175       }
    3176     } while (element = element.parentNode);
    3177 
    3178     return [valueL, valueT];
    3179   },
    3180 
    3181   clone: function(source, target) {
    3182     var options = Object.extend({
    3183       setLeft:    true,
    3184       setTop:     true,
    3185       setWidth:   true,
    3186       setHeight:  true,
    3187       offsetTop:  0,
    3188       offsetLeft: 0
    3189     }, arguments[2] || {})
    3190 
    3191     // find page position of source
    3192     source = $(source);
    3193     var p = Position.page(source);
    3194 
    3195     // find coordinate system to use
    3196     target = $(target);
    3197     var delta = [0, 0];
    3198     var parent = null;
    3199     // delta [0,0] will do fine with position: fixed elements,
    3200     // position:absolute needs offsetParent deltas
    3201     if (Element.getStyle(target,'position') == 'absolute') {
    3202       parent = Position.offsetParent(target);
    3203       delta = Position.page(parent);
    3204     }
    3205 
    3206     // correct by body offsets (fixes Safari)
    3207     if (parent == document.body) {
    3208       delta[0] -= document.body.offsetLeft;
    3209       delta[1] -= document.body.offsetTop;
    3210     }
    3211 
    3212     // set position
    3213     if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    3214     if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    3215     if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
    3216     if(options.setHeight) target.style.height = source.offsetHeight + 'px';
    3217   },
     4084  // Deprecation layer -- use newer Element methods now (1.5.2).
     4085
     4086  cumulativeOffset: Element.Methods.cumulativeOffset,
     4087
     4088  positionedOffset: Element.Methods.positionedOffset,
    32184089
    32194090  absolutize: function(element) {
    3220     element = $(element);
    3221     if (element.style.position == 'absolute') return;
    32224091    Position.prepare();
    3223 
    3224     var offsets = Position.positionedOffset(element);
    3225     var top     = offsets[1];
    3226     var left    = offsets[0];
    3227     var width   = element.clientWidth;
    3228     var height  = element.clientHeight;
    3229 
    3230     element._originalLeft   = left - parseFloat(element.style.left  || 0);
    3231     element._originalTop    = top  - parseFloat(element.style.top || 0);
    3232     element._originalWidth  = element.style.width;
    3233     element._originalHeight = element.style.height;
    3234 
    3235     element.style.position = 'absolute';
    3236     element.style.top    = top + 'px';
    3237     element.style.left   = left + 'px';
    3238     element.style.width  = width + 'px';
    3239     element.style.height = height + 'px';
     4092    return Element.absolutize(element);
    32404093  },
    32414094
    32424095  relativize: function(element) {
    3243     element = $(element);
    3244     if (element.style.position == 'relative') return;
    32454096    Position.prepare();
    3246 
    3247     element.style.position = 'relative';
    3248     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    3249     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
    3250 
    3251     element.style.top    = top + 'px';
    3252     element.style.left   = left + 'px';
    3253     element.style.height = element._originalHeight;
    3254     element.style.width  = element._originalWidth;
    3255   }
    3256 }
    3257 
    3258 // Safari returns margins on body which is incorrect if the child is absolutely
    3259 // positioned.  For performance reasons, redefine Position.cumulativeOffset for
    3260 // KHTML/WebKit only.
    3261 if (Prototype.Browser.WebKit) {
    3262   Position.cumulativeOffset = function(element) {
    3263     var valueT = 0, valueL = 0;
    3264     do {
    3265       valueT += element.offsetTop  || 0;
    3266       valueL += element.offsetLeft || 0;
    3267       if (element.offsetParent == document.body)
    3268         if (Element.getStyle(element, 'position') == 'absolute') break;
    3269 
    3270       element = element.offsetParent;
    3271     } while (element);
    3272 
    3273     return [valueL, valueT];
    3274   }
    3275 }
     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/*--------------------------------------------------------------------------*/
    32764183
    32774184Element.addMethods();
  • trunk/wp-includes/js/scriptaculous/builder.js

    r5792 r6512  
    1 // script.aculo.us builder.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
     1// script.aculo.us builder.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
    22
    33// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
  • trunk/wp-includes/js/scriptaculous/controls.js

    r5792 r6512  
    1 // script.aculo.us controls.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
     1// script.aculo.us controls.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
    22
    33// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
     
    4040  throw("controls.js requires including script.aculo.us' effects.js library");
    4141
    42 var Autocompleter = {}
    43 Autocompleter.Base = function() {};
    44 Autocompleter.Base.prototype = {
     42var Autocompleter = { }
     43Autocompleter.Base = Class.create({
    4544  baseInitialize: function(element, update, options) {
    4645    element          = $(element)
     
    5251    this.index       = 0;     
    5352    this.entryCount  = 0;
     53    this.oldElementValue = this.element.value;
    5454
    5555    if(this.setOptions)
    5656      this.setOptions(options);
    5757    else
    58       this.options = options || {};
     58      this.options = options || { };
    5959
    6060    this.options.paramName    = this.options.paramName || this.element.name;
     
    7878    if(typeof(this.options.tokens) == 'string')
    7979      this.options.tokens = new Array(this.options.tokens);
     80    // Force carriage returns as token delimiters anyway
     81    if (!this.options.tokens.include('\n'))
     82      this.options.tokens.push('\n');
    8083
    8184    this.observer = null;
     
    8790    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
    8891    Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
    89 
    90     // Turn autocomplete back on when the user leaves the page, so that the
    91     // field's value will be remembered on Mozilla-based browsers.
    92     Event.observe(window, 'beforeunload', function(){
    93       element.setAttribute('autocomplete', 'on');
    94     });
    9592  },
    9693
     
    246243    var value = '';
    247244    if (this.options.select) {
    248       var nodes = document.getElementsByClassName(this.options.select, selectedElement) || [];
     245      var nodes = $(selectedElement).select('.' + this.options.select) || [];
    249246      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
    250247    } else
    251248      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
    252249   
    253     var lastTokenPos = this.findLastToken();
    254     if (lastTokenPos != -1) {
    255       var newValue = this.element.value.substr(0, lastTokenPos + 1);
    256       var whitespace = this.element.value.substr(lastTokenPos + 1).match(/^\s+/);
     250    var bounds = this.getTokenBounds();
     251    if (bounds[0] != -1) {
     252      var newValue = this.element.value.substr(0, bounds[0]);
     253      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
    257254      if (whitespace)
    258255        newValue += whitespace[0];
    259       this.element.value = newValue + value;
     256      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
    260257    } else {
    261258      this.element.value = value;
    262259    }
     260    this.oldElementValue = this.element.value;
    263261    this.element.focus();
    264262   
     
    304302  onObserverEvent: function() {
    305303    this.changed = false;   
     304    this.tokenBounds = null;
    306305    if(this.getToken().length>=this.options.minChars) {
    307306      this.getUpdatedChoices();
     
    310309      this.hide();
    311310    }
     311    this.oldElementValue = this.element.value;
    312312  },
    313313
    314314  getToken: function() {
    315     var tokenPos = this.findLastToken();
    316     if (tokenPos != -1)
    317       var ret = this.element.value.substr(tokenPos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
    318     else
    319       var ret = this.element.value;
    320 
    321     return /\n/.test(ret) ? '' : ret;
    322   },
    323 
    324   findLastToken: function() {
    325     var lastTokenPos = -1;
    326 
    327     for (var i=0; i<this.options.tokens.length; i++) {
    328       var thisTokenPos = this.element.value.lastIndexOf(this.options.tokens[i]);
    329       if (thisTokenPos > lastTokenPos)
    330         lastTokenPos = thisTokenPos;
    331     }
    332     return lastTokenPos;
     315    var bounds = this.getTokenBounds();
     316    return this.element.value.substring(bounds[0], bounds[1]).strip();
     317  },
     318
     319  getTokenBounds: function() {
     320    if (null != this.tokenBounds) return this.tokenBounds;
     321    var value = this.element.value;
     322    if (value.strip().empty()) return [-1, 0];
     323    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
     324    var offset = (diff == this.oldElementValue.length ? 1 : 0);
     325    var prevTokenPos = -1, nextTokenPos = value.length;
     326    var tp;
     327    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
     328      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
     329      if (tp > prevTokenPos) prevTokenPos = tp;
     330      tp = value.indexOf(this.options.tokens[index], diff + offset);
     331      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
     332    }
     333    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
    333334  }
    334 }
    335 
    336 Ajax.Autocompleter = Class.create();
    337 Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), {
     335});
     336
     337Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
     338  var boundary = Math.min(newS.length, oldS.length);
     339  for (var index = 0; index < boundary; ++index)
     340    if (newS[index] != oldS[index])
     341      return index;
     342  return boundary;
     343};
     344
     345Ajax.Autocompleter = Class.create(Autocompleter.Base, {
    338346  initialize: function(element, update, url, options) {
    339347    this.baseInitialize(element, update, options);
     
    362370    this.updateChoices(request.responseText);
    363371  }
    364 
    365372});
    366373
     
    400407// you support them.
    401408
    402 Autocompleter.Local = Class.create();
    403 Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
     409Autocompleter.Local = Class.create(Autocompleter.Base, {
    404410  initialize: function(element, update, array, options) {
    405411    this.baseInitialize(element, update, options);
     
    457463        return "<ul>" + ret.join('') + "</ul>";
    458464      }
    459     }, options || {});
     465    }, options || { });
    460466  }
    461467});
    462468
    463 // AJAX in-place editor
    464 //
    465 // see documentation on http://wiki.script.aculo.us/scriptaculous/show/Ajax.InPlaceEditor
     469// AJAX in-place editor and collection editor
     470// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
    466471
    467472// Use this if you notice weird scrolling problems on some browsers,
     
    474479}
    475480
    476 Ajax.InPlaceEditor = Class.create();
    477 Ajax.InPlaceEditor.defaultHighlightColor = "#FFFF99";
    478 Ajax.InPlaceEditor.prototype = {
     481Ajax.InPlaceEditor = Class.create({
    479482  initialize: function(element, url, options) {
    480483    this.url = url;
    481     this.element = $(element);
    482 
    483     this.options = Object.extend({
    484       paramName: "value",
    485       okButton: true,
    486       okLink: false,
    487       okText: "ok",
    488       cancelButton: false,
    489       cancelLink: true,
    490       cancelText: "cancel",
    491       textBeforeControls: '',
    492       textBetweenControls: '',
    493       textAfterControls: '',
    494       savingText: "Saving...",
    495       clickToEditText: "Click to edit",
    496       okText: "ok",
    497       rows: 1,
    498       onComplete: function(transport, element) {
    499         new Effect.Highlight(element, {startcolor: this.options.highlightcolor});
    500       },
    501       onFailure: function(transport) {
    502         alert("Error communicating with the server: " + transport.responseText.stripTags());
    503       },
    504       callback: function(form) {
    505         return Form.serialize(form);
    506       },
    507       handleLineBreaks: true,
    508       loadingText: 'Loading...',
    509       savingClassName: 'inplaceeditor-saving',
    510       loadingClassName: 'inplaceeditor-loading',
    511       formClassName: 'inplaceeditor-form',
    512       highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
    513       highlightendcolor: "#FFFFFF",
    514       externalControl: null,
    515       submitOnBlur: false,
    516       ajaxOptions: {},
    517       evalScripts: false
    518     }, options || {});
    519 
    520     if(!this.options.formId && this.element.id) {
    521       this.options.formId = this.element.id + "-inplaceeditor";
    522       if ($(this.options.formId)) {
    523         // there's already a form with that name, don't specify an id
    524         this.options.formId = null;
    525       }
    526     }
    527    
    528     if (this.options.externalControl) {
     484    this.element = element = $(element);
     485    this.prepareOptions();
     486    this._controls = { };
     487    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
     488    Object.extend(this.options, options || { });
     489    if (!this.options.formId && this.element.id) {
     490      this.options.formId = this.element.id + '-inplaceeditor';
     491      if ($(this.options.formId))
     492        this.options.formId = '';
     493    }
     494    if (this.options.externalControl)
    529495      this.options.externalControl = $(this.options.externalControl);
    530     }
    531    
    532     this.originalBackground = Element.getStyle(this.element, 'background-color');
    533     if (!this.originalBackground) {
    534       this.originalBackground = "transparent";
    535     }
    536    
     496    if (!this.options.externalControl)
     497      this.options.externalControlOnly = false;
     498    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
    537499    this.element.title = this.options.clickToEditText;
    538    
    539     this.onclickListener = this.enterEditMode.bindAsEventListener(this);
    540     this.mouseoverListener = this.enterHover.bindAsEventListener(this);
    541     this.mouseoutListener = this.leaveHover.bindAsEventListener(this);
    542     Event.observe(this.element, 'click', this.onclickListener);
    543     Event.observe(this.element, 'mouseover', this.mouseoverListener);
    544     Event.observe(this.element, 'mouseout', this.mouseoutListener);
    545     if (this.options.externalControl) {
    546       Event.observe(this.options.externalControl, 'click', this.onclickListener);
    547       Event.observe(this.options.externalControl, 'mouseover', this.mouseoverListener);
    548       Event.observe(this.options.externalControl, 'mouseout', this.mouseoutListener);
    549     }
    550   },
    551   enterEditMode: function(evt) {
    552     if (this.saving) return;
    553     if (this.editing) return;
    554     this.editing = true;
    555     this.onEnterEditMode();
    556     if (this.options.externalControl) {
    557       Element.hide(this.options.externalControl);
    558     }
    559     Element.hide(this.element);
     500    this._boundCancelHandler = this.handleFormCancellation.bind(this);
     501    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
     502    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
     503    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
     504    this._boundWrapperHandler = this.wrapUp.bind(this);
     505    this.registerListeners();
     506  },
     507  checkForEscapeOrReturn: function(e) {
     508    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
     509    if (Event.KEY_ESC == e.keyCode)
     510      this.handleFormCancellation(e);
     511    else if (Event.KEY_RETURN == e.keyCode)
     512      this.handleFormSubmission(e);
     513  },
     514  createControl: function(mode, handler, extraClasses) {
     515    var control = this.options[mode + 'Control'];
     516    var text = this.options[mode + 'Text'];
     517    if ('button' == control) {
     518      var btn = document.createElement('input');
     519      btn.type = 'submit';
     520      btn.value = text;
     521      btn.className = 'editor_' + mode + '_button';
     522      if ('cancel' == mode)
     523        btn.onclick = this._boundCancelHandler;
     524      this._form.appendChild(btn);
     525      this._controls[mode] = btn;
     526    } else if ('link' == control) {
     527      var link = document.createElement('a');
     528      link.href = '#';
     529      link.appendChild(document.createTextNode(text));
     530      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
     531      link.className = 'editor_' + mode + '_link';
     532      if (extraClasses)
     533        link.className += ' ' + extraClasses;
     534      this._form.appendChild(link);
     535      this._controls[mode] = link;
     536    }
     537  },
     538  createEditField: function() {
     539    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
     540    var fld;
     541    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
     542      fld = document.createElement('input');
     543      fld.type = 'text';
     544      var size = this.options.size || this.options.cols || 0;
     545      if (0 < size) fld.size = size;
     546    } else {
     547      fld = document.createElement('textarea');
     548      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
     549      fld.cols = this.options.cols || 40;
     550    }
     551    fld.name = this.options.paramName;
     552    fld.value = text; // No HTML breaks conversion anymore
     553    fld.className = 'editor_field';
     554    if (this.options.submitOnBlur)
     555      fld.onblur = this._boundSubmitHandler;
     556    this._controls.editor = fld;
     557    if (this.options.loadTextURL)
     558      this.loadExternalText();
     559    this._form.appendChild(this._controls.editor);
     560  },
     561  createForm: function() {
     562    var ipe = this;
     563    function addText(mode, condition) {
     564      var text = ipe.options['text' + mode + 'Controls'];
     565      if (!text || condition === false) return;
     566      ipe._form.appendChild(document.createTextNode(text));
     567    };
     568    this._form = $(document.createElement('form'));
     569    this._form.id = this.options.formId;
     570    this._form.addClassName(this.options.formClassName);
     571    this._form.onsubmit = this._boundSubmitHandler;
     572    this.createEditField();
     573    if ('textarea' == this._controls.editor.tagName.toLowerCase())
     574      this._form.appendChild(document.createElement('br'));
     575    if (this.options.onFormCustomization)
     576      this.options.onFormCustomization(this, this._form);
     577    addText('Before', this.options.okControl || this.options.cancelControl);
     578    this.createControl('ok', this._boundSubmitHandler);
     579    addText('Between', this.options.okControl && this.options.cancelControl);
     580    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
     581    addText('After', this.options.okControl || this.options.cancelControl);
     582  },
     583  destroy: function() {
     584    if (this._oldInnerHTML)
     585      this.element.innerHTML = this._oldInnerHTML;
     586    this.leaveEditMode();
     587    this.unregisterListeners();
     588  },
     589  enterEditMode: function(e) {
     590    if (this._saving || this._editing) return;
     591    this._editing = true;
     592    this.triggerCallback('onEnterEditMode');
     593    if (this.options.externalControl)
     594      this.options.externalControl.hide();
     595    this.element.hide();
    560596    this.createForm();
    561     this.element.parentNode.insertBefore(this.form, this.element);
    562     if (!this.options.loadTextURL) Field.scrollFreeActivate(this.editField);
    563     // stop the event to avoid a page refresh in Safari
    564     if (evt) {
    565       Event.stop(evt);
    566     }
    567     return false;
    568   },
    569   createForm: function() {
    570     this.form = document.createElement("form");
    571     this.form.id = this.options.formId;
    572     Element.addClassName(this.form, this.options.formClassName)
    573     this.form.onsubmit = this.onSubmit.bind(this);
    574 
    575     this.createEditField();
    576 
    577     if (this.options.textarea) {
    578       var br = document.createElement("br");
    579       this.form.appendChild(br);
    580     }
    581    
    582     if (this.options.textBeforeControls)
    583       this.form.appendChild(document.createTextNode(this.options.textBeforeControls));
    584 
    585     if (this.options.okButton) {
    586       var okButton = document.createElement("input");
    587       okButton.type = "submit";
    588       okButton.value = this.options.okText;
    589       okButton.className = 'editor_ok_button';
    590       this.form.appendChild(okButton);
    591     }
    592    
    593     if (this.options.okLink) {
    594       var okLink = document.createElement("a");
    595       okLink.href = "#";
    596       okLink.appendChild(document.createTextNode(this.options.okText));
    597       okLink.onclick = this.onSubmit.bind(this);
    598       okLink.className = 'editor_ok_link';
    599       this.form.appendChild(okLink);
    600     }
    601    
    602     if (this.options.textBetweenControls &&
    603       (this.options.okLink || this.options.okButton) &&
    604       (this.options.cancelLink || this.options.cancelButton))
    605       this.form.appendChild(document.createTextNode(this.options.textBetweenControls));
    606      
    607     if (this.options.cancelButton) {
    608       var cancelButton = document.createElement("input");
    609       cancelButton.type = "submit";
    610       cancelButton.value = this.options.cancelText;
    611       cancelButton.onclick = this.onclickCancel.bind(this);
    612       cancelButton.className = 'editor_cancel_button';
    613       this.form.appendChild(cancelButton);
    614     }
    615 
    616     if (this.options.cancelLink) {
    617       var cancelLink = document.createElement("a");
    618       cancelLink.href = "#";
    619       cancelLink.appendChild(document.createTextNode(this.options.cancelText));
    620       cancelLink.onclick = this.onclickCancel.bind(this);
    621       cancelLink.className = 'editor_cancel editor_cancel_link';     
    622       this.form.appendChild(cancelLink);
    623     }
    624    
    625     if (this.options.textAfterControls)
    626       this.form.appendChild(document.createTextNode(this.options.textAfterControls));
    627   },
    628   hasHTMLLineBreaks: function(string) {
    629     if (!this.options.handleLineBreaks) return false;
    630     return string.match(/<br/i) || string.match(/<p>/i);
    631   },
    632   convertHTMLLineBreaks: function(string) {
    633     return string.replace(/<br>/gi, "\n").replace(/<br\/>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<p>/gi, "");
    634   },
    635   createEditField: function() {
    636     var text;
    637     if(this.options.loadTextURL) {
    638       text = this.options.loadingText;
    639     } else {
    640       text = this.getText();
    641     }
    642 
    643     var obj = this;
    644    
    645     if (this.options.rows == 1 && !this.hasHTMLLineBreaks(text)) {
    646       this.options.textarea = false;
    647       var textField = document.createElement("input");
    648       textField.obj = this;
    649       textField.type = "text";
    650       textField.name = this.options.paramName;
    651       textField.value = text;
    652       textField.style.backgroundColor = this.options.highlightcolor;
    653       textField.className = 'editor_field';
    654       var size = this.options.size || this.options.cols || 0;
    655       if (size != 0) textField.size = size;
    656       if (this.options.submitOnBlur)
    657         textField.onblur = this.onSubmit.bind(this);
    658       this.editField = textField;
    659     } else {
    660       this.options.textarea = true;
    661       var textArea = document.createElement("textarea");
    662       textArea.obj = this;
    663       textArea.name = this.options.paramName;
    664       textArea.value = this.convertHTMLLineBreaks(text);
    665       textArea.rows = this.options.rows;
    666       textArea.cols = this.options.cols || 40;
    667       textArea.className = 'editor_field';     
    668       if (this.options.submitOnBlur)
    669         textArea.onblur = this.onSubmit.bind(this);
    670       this.editField = textArea;
    671     }
    672    
    673     if(this.options.loadTextURL) {
    674       this.loadExternalText();
    675     }
    676     this.form.appendChild(this.editField);
     597    this.element.parentNode.insertBefore(this._form, this.element);
     598    if (!this.options.loadTextURL)
     599      this.postProcessEditField();
     600    if (e) Event.stop(e);
     601  },
     602  enterHover: function(e) {
     603    if (this.options.hoverClassName)
     604      this.element.addClassName(this.options.hoverClassName);
     605    if (this._saving) return;
     606    this.triggerCallback('onEnterHover');
    677607  },
    678608  getText: function() {
    679609    return this.element.innerHTML;
    680610  },
     611  handleAJAXFailure: function(transport) {
     612    this.triggerCallback('onFailure', transport);
     613    if (this._oldInnerHTML) {
     614      this.element.innerHTML = this._oldInnerHTML;
     615      this._oldInnerHTML = null;
     616    }
     617  },
     618  handleFormCancellation: function(e) {
     619    this.wrapUp();
     620    if (e) Event.stop(e);
     621  },
     622  handleFormSubmission: function(e) {
     623    var form = this._form;
     624    var value = $F(this._controls.editor);
     625    this.prepareSubmission();
     626    var params = this.options.callback(form, value) || '';
     627    if (Object.isString(params))
     628      params = params.toQueryParams();
     629    params.editorId = this.element.id;
     630    if (this.options.htmlResponse) {
     631      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
     632      Object.extend(options, {
     633        parameters: params,
     634        onComplete: this._boundWrapperHandler,
     635        onFailure: this._boundFailureHandler
     636      });
     637      new Ajax.Updater({ success: this.element }, this.url, options);
     638    } else {
     639      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
     640      Object.extend(options, {
     641        parameters: params,
     642        onComplete: this._boundWrapperHandler,
     643        onFailure: this._boundFailureHandler
     644      });
     645      new Ajax.Request(this.url, options);
     646    }
     647    if (e) Event.stop(e);
     648  },
     649  leaveEditMode: function() {
     650    this.element.removeClassName(this.options.savingClassName);
     651    this.removeForm();
     652    this.leaveHover();
     653    this.element.style.backgroundColor = this._originalBackground;
     654    this.element.show();
     655    if (this.options.externalControl)
     656      this.options.externalControl.show();
     657    this._saving = false;
     658    this._editing = false;
     659    this._oldInnerHTML = null;
     660    this.triggerCallback('onLeaveEditMode');
     661  },
     662  leaveHover: function(e) {
     663    if (this.options.hoverClassName)
     664      this.element.removeClassName(this.options.hoverClassName);
     665    if (this._saving) return;
     666    this.triggerCallback('onLeaveHover');
     667  },
    681668  loadExternalText: function() {
    682     Element.addClassName(this.form, this.options.loadingClassName);
    683     this.editField.disabled = true;
    684     new Ajax.Request(
    685       this.options.loadTextURL,
    686       Object.extend({
    687         asynchronous: true,
    688         onComplete: this.onLoadedExternalText.bind(this)
    689       }, this.options.ajaxOptions)
    690     );
    691   },
    692   onLoadedExternalText: function(transport) {
    693     Element.removeClassName(this.form, this.options.loadingClassName);
    694     this.editField.disabled = false;
    695     this.editField.value = transport.responseText.stripTags();
    696     Field.scrollFreeActivate(this.editField);
    697   },
    698   onclickCancel: function() {
    699     this.onComplete();
    700     this.leaveEditMode();
    701     return false;
    702   },
    703   onFailure: function(transport) {
    704     this.options.onFailure(transport);
    705     if (this.oldInnerHTML) {
    706       this.element.innerHTML = this.oldInnerHTML;
    707       this.oldInnerHTML = null;
    708     }
    709     return false;
    710   },
    711   onSubmit: function() {
    712     // onLoading resets these so we need to save them away for the Ajax call
    713     var form = this.form;
    714     var value = this.editField.value;
    715    
    716     // do this first, sometimes the ajax call returns before we get a chance to switch on Saving...
    717     // which means this will actually switch on Saving... *after* we've left edit mode causing Saving...
    718     // to be displayed indefinitely
    719     this.onLoading();
    720    
    721     if (this.options.evalScripts) {
    722       new Ajax.Request(
    723         this.url, Object.extend({
    724           parameters: this.options.callback(form, value),
    725           onComplete: this.onComplete.bind(this),
    726           onFailure: this.onFailure.bind(this),
    727           asynchronous:true,
    728           evalScripts:true
    729         }, this.options.ajaxOptions));
    730     } else  {
    731       new Ajax.Updater(
    732         { success: this.element,
    733           // don't update on failure (this could be an option)
    734           failure: null },
    735         this.url, Object.extend({
    736           parameters: this.options.callback(form, value),
    737           onComplete: this.onComplete.bind(this),
    738           onFailure: this.onFailure.bind(this)
    739         }, this.options.ajaxOptions));
    740     }
    741     // stop the event to avoid a page refresh in Safari
    742     if (arguments.length > 1) {
    743       Event.stop(arguments[0]);
    744     }
    745     return false;
    746   },
    747   onLoading: function() {
    748     this.saving = true;
     669    this._form.addClassName(this.options.loadingClassName);
     670    this._controls.editor.disabled = true;
     671    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
     672    Object.extend(options, {
     673      parameters: 'editorId=' + encodeURIComponent(this.element.id),
     674      onComplete: Prototype.emptyFunction,
     675      onSuccess: function(transport) {
     676        this._form.removeClassName(this.options.loadingClassName);
     677        var text = transport.responseText;
     678        if (this.options.stripLoadedTextTags)
     679          text = text.stripTags();
     680        this._controls.editor.value = text;
     681        this._controls.editor.disabled = false;
     682        this.postProcessEditField();
     683      }.bind(this),
     684      onFailure: this._boundFailureHandler
     685    });
     686    new Ajax.Request(this.options.loadTextURL, options);
     687  },
     688  postProcessEditField: function() {
     689    var fpc = this.options.fieldPostCreation;
     690    if (fpc)
     691      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
     692  },
     693  prepareOptions: function() {
     694    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
     695    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
     696    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
     697      Object.extend(this.options, defs);
     698    }.bind(this));
     699  },
     700  prepareSubmission: function() {
     701    this._saving = true;
    749702    this.removeForm();
    750703    this.leaveHover();
    751704    this.showSaving();
    752705  },
     706  registerListeners: function() {
     707    this._listeners = { };
     708    var listener;
     709    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
     710      listener = this[pair.value].bind(this);
     711      this._listeners[pair.key] = listener;
     712      if (!this.options.externalControlOnly)
     713        this.element.observe(pair.key, listener);
     714      if (this.options.externalControl)
     715        this.options.externalControl.observe(pair.key, listener);
     716    }.bind(this));
     717  },
     718  removeForm: function() {
     719    if (!this._form) return;
     720    this._form.remove();
     721    this._form = null;
     722    this._controls = { };
     723  },
    753724  showSaving: function() {
    754     this.oldInnerHTML = this.element.innerHTML;
     725    this._oldInnerHTML = this.element.innerHTML;
    755726    this.element.innerHTML = this.options.savingText;
    756     Element.addClassName(this.element, this.options.savingClassName);
    757     this.element.style.backgroundColor = this.originalBackground;
    758     Element.show(this.element);
    759   },
    760   removeForm: function() {
    761     if(this.form) {
    762       if (this.form.parentNode) Element.remove(this.form);
    763       this.form = null;
    764     }
    765   },
    766   enterHover: function() {
    767     if (this.saving) return;
    768     this.element.style.backgroundColor = this.options.highlightcolor;
    769     if (this.effect) {
    770       this.effect.cancel();
    771     }
    772     Element.addClassName(this.element, this.options.hoverClassName)
    773   },
    774   leaveHover: function() {
    775     if (this.options.backgroundColor) {
    776       this.element.style.backgroundColor = this.oldBackground;
    777     }
    778     Element.removeClassName(this.element, this.options.hoverClassName)
    779     if (this.saving) return;
    780     this.effect = new Effect.Highlight(this.element, {
    781       startcolor: this.options.highlightcolor,
    782       endcolor: this.options.highlightendcolor,
    783       restorecolor: this.originalBackground
    784     });
    785   },
    786   leaveEditMode: function() {
    787     Element.removeClassName(this.element, this.options.savingClassName);
    788     this.removeForm();
    789     this.leaveHover();
    790     this.element.style.backgroundColor = this.originalBackground;
    791     Element.show(this.element);
    792     if (this.options.externalControl) {
    793       Element.show(this.options.externalControl);
    794     }
    795     this.editing = false;
    796     this.saving = false;
    797     this.oldInnerHTML = null;
    798     this.onLeaveEditMode();
    799   },
    800   onComplete: function(transport) {
     727    this.element.addClassName(this.options.savingClassName);
     728    this.element.style.backgroundColor = this._originalBackground;
     729    this.element.show();
     730  },
     731  triggerCallback: function(cbName, arg) {
     732    if ('function' == typeof this.options[cbName]) {
     733      this.options[cbName](this, arg);
     734    }
     735  },
     736  unregisterListeners: function() {
     737    $H(this._listeners).each(function(pair) {
     738      if (!this.options.externalControlOnly)
     739        this.element.stopObserving(pair.key, pair.value);
     740      if (this.options.externalControl)
     741        this.options.externalControl.stopObserving(pair.key, pair.value);
     742    }.bind(this));
     743  },
     744  wrapUp: function(transport) {
    801745    this.leaveEditMode();
    802     this.options.onComplete.bind(this)(transport, this.element);
    803   },
    804   onEnterEditMode: function() {},
    805   onLeaveEditMode: function() {},
    806   dispose: function() {
    807     if (this.oldInnerHTML) {
    808       this.element.innerHTML = this.oldInnerHTML;
    809     }
    810     this.leaveEditMode();
    811     Event.stopObserving(this.element, 'click', this.onclickListener);
    812     Event.stopObserving(this.element, 'mouseover', this.mouseoverListener);
    813     Event.stopObserving(this.element, 'mouseout', this.mouseoutListener);
    814     if (this.options.externalControl) {
    815       Event.stopObserving(this.options.externalControl, 'click', this.onclickListener);
    816       Event.stopObserving(this.options.externalControl, 'mouseover', this.mouseoverListener);
    817       Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
    818     }
    819   }
    820 };
    821 
    822 Ajax.InPlaceCollectionEditor = Class.create();
    823 Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
    824 Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
    825   createEditField: function() {
    826     if (!this.cached_selectTag) {
    827       var selectTag = document.createElement("select");
    828       var collection = this.options.collection || [];
    829       var optionTag;
    830       collection.each(function(e,i) {
    831         optionTag = document.createElement("option");
    832         optionTag.value = (e instanceof Array) ? e[0] : e;
    833         if((typeof this.options.value == 'undefined') &&
    834           ((e instanceof Array) ? this.element.innerHTML == e[1] : e == optionTag.value)) optionTag.selected = true;
    835         if(this.options.value==optionTag.value) optionTag.selected = true;
    836         optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
    837         selectTag.appendChild(optionTag);
    838       }.bind(this));
    839       this.cached_selectTag = selectTag;
    840     }
    841 
    842     this.editField = this.cached_selectTag;
    843     if(this.options.loadTextURL) this.loadExternalText();
    844     this.form.appendChild(this.editField);
    845     this.options.callback = function(form, value) {
    846       return "value=" + encodeURIComponent(value);
    847     }
     746    // Can't use triggerCallback due to backward compatibility: requires
     747    // binding + direct element
     748    this._boundComplete(transport, this.element);
    848749  }
    849750});
     751
     752Object.extend(Ajax.InPlaceEditor.prototype, {
     753  dispose: Ajax.InPlaceEditor.prototype.destroy
     754});
     755
     756Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
     757  initialize: function($super, element, url, options) {
     758    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
     759    $super(element, url, options);
     760  },
     761
     762  createEditField: function() {
     763    var list = document.createElement('select');
     764    list.name = this.options.paramName;
     765    list.size = 1;
     766    this._controls.editor = list;
     767    this._collection = this.options.collection || [];
     768    if (this.options.loadCollectionURL)
     769      this.loadCollection();
     770    else
     771      this.checkForExternalText();
     772    this._form.appendChild(this._controls.editor);
     773  },
     774
     775  loadCollection: function() {
     776    this._form.addClassName(this.options.loadingClassName);
     777    this.showLoadingText(this.options.loadingCollectionText);
     778    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
     779    Object.extend(options, {
     780      parameters: 'editorId=' + encodeURIComponent(this.element.id),
     781      onComplete: Prototype.emptyFunction,
     782      onSuccess: function(transport) {
     783        var js = transport.responseText.strip();
     784        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
     785          throw 'Server returned an invalid collection representation.';
     786        this._collection = eval(js);
     787        this.checkForExternalText();
     788      }.bind(this),
     789      onFailure: this.onFailure
     790    });
     791    new Ajax.Request(this.options.loadCollectionURL, options);
     792  },
     793
     794  showLoadingText: function(text) {
     795    this._controls.editor.disabled = true;
     796    var tempOption = this._controls.editor.firstChild;
     797    if (!tempOption) {
     798      tempOption = document.createElement('option');
     799      tempOption.value = '';
     800      this._controls.editor.appendChild(tempOption);
     801      tempOption.selected = true;
     802    }
     803    tempOption.update((text || '').stripScripts().stripTags());
     804  },
     805
     806  checkForExternalText: function() {
     807    this._text = this.getText();
     808    if (this.options.loadTextURL)
     809      this.loadExternalText();
     810    else
     811      this.buildOptionList();
     812  },
     813
     814  loadExternalText: function() {
     815    this.showLoadingText(this.options.loadingText);
     816    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
     817    Object.extend(options, {
     818      parameters: 'editorId=' + encodeURIComponent(this.element.id),
     819      onComplete: Prototype.emptyFunction,
     820      onSuccess: function(transport) {
     821        this._text = transport.responseText.strip();
     822        this.buildOptionList();
     823      }.bind(this),
     824      onFailure: this.onFailure
     825    });
     826    new Ajax.Request(this.options.loadTextURL, options);
     827  },
     828
     829  buildOptionList: function() {
     830    this._form.removeClassName(this.options.loadingClassName);
     831    this._collection = this._collection.map(function(entry) {
     832      return 2 === entry.length ? entry : [entry, entry].flatten();
     833    });
     834    var marker = ('value' in this.options) ? this.options.value : this._text;
     835    var textFound = this._collection.any(function(entry) {
     836      return entry[0] == marker;
     837    }.bind(this));
     838    this._controls.editor.update('');
     839    var option;
     840    this._collection.each(function(entry, index) {
     841      option = document.createElement('option');
     842      option.value = entry[0];
     843      option.selected = textFound ? entry[0] == marker : 0 == index;
     844      option.appendChild(document.createTextNode(entry[1]));
     845      this._controls.editor.appendChild(option);
     846    }.bind(this));
     847    this._controls.editor.disabled = false;
     848    Field.scrollFreeActivate(this._controls.editor);
     849  }
     850});
     851
     852//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
     853//**** This only  exists for a while,  in order to  let ****
     854//**** users adapt to  the new API.  Read up on the new ****
     855//**** API and convert your code to it ASAP!            ****
     856
     857Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
     858  if (!options) return;
     859  function fallback(name, expr) {
     860    if (name in options || expr === undefined) return;
     861    options[name] = expr;
     862  };
     863  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
     864    options.cancelLink == options.cancelButton == false ? false : undefined)));
     865  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
     866    options.okLink == options.okButton == false ? false : undefined)));
     867  fallback('highlightColor', options.highlightcolor);
     868  fallback('highlightEndColor', options.highlightendcolor);
     869};
     870
     871Object.extend(Ajax.InPlaceEditor, {
     872  DefaultOptions: {
     873    ajaxOptions: { },
     874    autoRows: 3,                                // Use when multi-line w/ rows == 1
     875    cancelControl: 'link',                      // 'link'|'button'|false
     876    cancelText: 'cancel',
     877    clickToEditText: 'Click to edit',
     878    externalControl: null,                      // id|elt
     879    externalControlOnly: false,
     880    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
     881    formClassName: 'inplaceeditor-form',
     882    formId: null,                               // id|elt
     883    highlightColor: '#ffff99',
     884    highlightEndColor: '#ffffff',
     885    hoverClassName: '',
     886    htmlResponse: true,
     887    loadingClassName: 'inplaceeditor-loading',
     888    loadingText: 'Loading...',
     889    okControl: 'button',                        // 'link'|'button'|false
     890    okText: 'ok',
     891    paramName: 'value',
     892    rows: 1,                                    // If 1 and multi-line, uses autoRows
     893    savingClassName: 'inplaceeditor-saving',
     894    savingText: 'Saving...',
     895    size: 0,
     896    stripLoadedTextTags: false,
     897    submitOnBlur: false,
     898    textAfterControls: '',
     899    textBeforeControls: '',
     900    textBetweenControls: ''
     901  },
     902  DefaultCallbacks: {
     903    callback: function(form) {
     904      return Form.serialize(form);
     905    },
     906    onComplete: function(transport, element) {
     907      // For backward compatibility, this one is bound to the IPE, and passes
     908      // the element directly.  It was too often customized, so we don't break it.
     909      new Effect.Highlight(element, {
     910        startcolor: this.options.highlightColor, keepBackgroundImage: true });
     911    },
     912    onEnterEditMode: null,
     913    onEnterHover: function(ipe) {
     914      ipe.element.style.backgroundColor = ipe.options.highlightColor;
     915      if (ipe._effect)
     916        ipe._effect.cancel();
     917    },
     918    onFailure: function(transport, ipe) {
     919      alert('Error communication with the server: ' + transport.responseText.stripTags());
     920    },
     921    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
     922    onLeaveEditMode: null,
     923    onLeaveHover: function(ipe) {
     924      ipe._effect = new Effect.Highlight(ipe.element, {
     925        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
     926        restorecolor: ipe._originalBackground, keepBackgroundImage: true
     927      });
     928    }
     929  },
     930  Listeners: {
     931    click: 'enterEditMode',
     932    keydown: 'checkForEscapeOrReturn',
     933    mouseover: 'enterHover',
     934    mouseout: 'leaveHover'
     935  }
     936});
     937
     938Ajax.InPlaceCollectionEditor.DefaultOptions = {
     939  loadingCollectionText: 'Loading options...'
     940};
    850941
    851942// Delayed observer, like Form.Element.Observer,
     
    853944// Ideal for live-search fields
    854945
    855 Form.Element.DelayedObserver = Class.create();
    856 Form.Element.DelayedObserver.prototype = {
     946Form.Element.DelayedObserver = Class.create({
    857947  initialize: function(element, delay, callback) {
    858948    this.delay     = delay || 0.5;
     
    873963    this.callback(this.element, $F(this.element));
    874964  }
    875 };
     965});
  • trunk/wp-includes/js/scriptaculous/dragdrop.js

    r5792 r6512  
    1 // script.aculo.us dragdrop.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
     1// script.aculo.us dragdrop.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
    22
    33// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
     
    77// For details, see the script.aculo.us web site: http://script.aculo.us/
    88
    9 if(typeof Effect == 'undefined')
     9if(Object.isUndefined(Effect))
    1010  throw("dragdrop.js requires including script.aculo.us' effects.js library");
    1111
     
    2323      hoverclass: null,
    2424      tree:       false
    25     }, arguments[1] || {});
     25    }, arguments[1] || { });
    2626
    2727    // cache containers
     
    2929      options._containers = [];
    3030      var containment = options.containment;
    31       if((typeof containment == 'object') &&
    32         (containment.constructor == Array)) {
     31      if(Object.isArray(containment)) {
    3332        containment.each( function(c) { options._containers.push($(c)) });
    3433      } else {
     
    9089  show: function(point, element) {
    9190    if(!this.drops.length) return;
    92     var affected = [];
    93    
    94     if(this.last_active) this.deactivate(this.last_active);
     91    var drop, affected = [];
     92   
    9593    this.drops.each( function(drop) {
    9694      if(Droppables.isAffected(point, element, drop))
     
    9896    });
    9997       
    100     if(affected.length>0) {
     98    if(affected.length>0)
    10199      drop = Droppables.findDeepestChild(affected);
     100
     101    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
     102    if (drop) {
    102103      Position.within(drop.element, point[0], point[1]);
    103104      if(drop.onHover)
    104105        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
    105106     
    106       Droppables.activate(drop);
     107      if (drop != this.last_active) Droppables.activate(drop);
    107108    }
    108109  },
     
    224225/*--------------------------------------------------------------------------*/
    225226
    226 var Draggable = Class.create();
    227 Draggable._dragging    = {};
    228 
    229 Draggable.prototype = {
     227var Draggable = Class.create({
    230228  initialize: function(element) {
    231229    var defaults = {
     
    238236      },
    239237      endeffect: function(element) {
    240         var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0;
     238        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
    241239        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
    242240          queue: {scope:'_draggable', position:'end'},
     
    256254    };
    257255   
    258     if(!arguments[1] || typeof arguments[1].endeffect == 'undefined')
     256    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
    259257      Object.extend(defaults, {
    260258        starteffect: function(element) {
     
    265263      });
    266264   
    267     var options = Object.extend(defaults, arguments[1] || {});
     265    var options = Object.extend(defaults, arguments[1] || { });
    268266
    269267    this.element = $(element);
    270268   
    271     if(options.handle && (typeof options.handle == 'string'))
     269    if(options.handle && Object.isString(options.handle))
    272270      this.handle = this.element.down('.'+options.handle, 0);
    273271   
     
    282280    Element.makePositioned(this.element); // fix IE   
    283281
    284     this.delta    = this.currentDelta();
    285282    this.options  = options;
    286283    this.dragging = false;   
     
    304301 
    305302  initDrag: function(event) {
    306     if(typeof Draggable._dragging[this.element] != 'undefined' &&
     303    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
    307304      Draggable._dragging[this.element]) return;
    308305    if(Event.isLeftClick(event)) {   
     
    327324  startDrag: function(event) {
    328325    this.dragging = true;
     326    if(!this.delta)
     327      this.delta = this.currentDelta();
    329328   
    330329    if(this.options.zindex) {
     
    335334    if(this.options.ghosting) {
    336335      this._clone = this.element.cloneNode(true);
    337       Position.absolutize(this.element);
     336      this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
     337      if (!this.element._originallyAbsolute)
     338        Position.absolutize(this.element);
    338339      this.element.parentNode.insertBefore(this._clone, this.element);
    339340    }
     
    405406
    406407    if(this.options.ghosting) {
    407       Position.relativize(this.element);
     408      if (!this.element._originallyAbsolute)
     409        Position.relativize(this.element);
     410      delete this.element._originallyAbsolute;
    408411      Element.remove(this._clone);
    409412      this._clone = null;
     
    419422
    420423    var revert = this.options.revert;
    421     if(revert && typeof revert == 'function') revert = revert(this.element);
     424    if(revert && Object.isFunction(revert)) revert = revert(this.element);
    422425   
    423426    var d = this.currentDelta();
     
    473476   
    474477    if(this.options.snap) {
    475       if(typeof this.options.snap == 'function') {
     478      if(Object.isFunction(this.options.snap)) {
    476479        p = this.options.snap(p[0],p[1],this);
    477480      } else {
    478       if(this.options.snap instanceof Array) {
     481      if(Object.isArray(this.options.snap)) {
    479482        p = p.map( function(v, i) {
    480           return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
     483          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
    481484      } else {
    482485        p = p.map( function(v) {
    483           return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
     486          return (v/this.options.snap).round()*this.options.snap }.bind(this))
    484487      }
    485488    }}
     
    565568    return { top: T, left: L, width: W, height: H };
    566569  }
    567 }
     570});
     571
     572Draggable._dragging = { };
    568573
    569574/*--------------------------------------------------------------------------*/
    570575
    571 var SortableObserver = Class.create();
    572 SortableObserver.prototype = {
     576var SortableObserver = Class.create({
    573577  initialize: function(element, observer) {
    574578    this.element   = $(element);
     
    586590      this.observer(this.element)
    587591  }
    588 }
     592});
    589593
    590594var Sortable = {
    591595  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
    592596 
    593   sortables: {},
     597  sortables: { },
    594598 
    595599  _findRootElement: function(element) {
     
    644648      elements:    false,
    645649      handles:     false,
    646 
     650     
    647651      onChange:    Prototype.emptyFunction,
    648652      onUpdate:    Prototype.emptyFunction
    649     }, arguments[1] || {});
     653    }, arguments[1] || { });
    650654
    651655    // clear any old sortable with same element
     
    711715    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
    712716      var handle = options.handles ? $(options.handles[i]) :
    713         (options.handle ? $(e).getElementsByClassName(options.handle)[0] : e);
     717        (options.handle ? $(e).select('.' + options.handle)[0] : e);
    714718      options.draggables.push(
    715719        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
     
    871875      name: element.id,
    872876      format: sortableOptions.format
    873     }, arguments[1] || {});
     877    }, arguments[1] || { });
    874878   
    875879    var root = {
     
    895899  sequence: function(element) {
    896900    element = $(element);
    897     var options = Object.extend(this.options(element), arguments[1] || {});
     901    var options = Object.extend(this.options(element), arguments[1] || { });
    898902   
    899903    return $(this.findElements(element, options) || []).map( function(item) {
     
    904908  setSequence: function(element, new_sequence) {
    905909    element = $(element);
    906     var options = Object.extend(this.options(element), arguments[2] || {});
    907    
    908     var nodeMap = {};
     910    var options = Object.extend(this.options(element), arguments[2] || { });
     911   
     912    var nodeMap = { };
    909913    this.findElements(element, options).each( function(n) {
    910914        if (n.id.match(options.format))
     
    924928  serialize: function(element) {
    925929    element = $(element);
    926     var options = Object.extend(Sortable.options(element), arguments[1] || {});
     930    var options = Object.extend(Sortable.options(element), arguments[1] || { });
    927931    var name = encodeURIComponent(
    928932      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
     
    948952}
    949953
    950 Element.findChildren = function(element, only, recursive, tagName) {    
     954Element.findChildren = function(element, only, recursive, tagName) {   
    951955  if(!element.hasChildNodes()) return null;
    952956  tagName = tagName.toUpperCase();
  • trunk/wp-includes/js/scriptaculous/effects.js

    r5792 r6512  
    1 // script.aculo.us effects.js v1.7.1_beta3, Fri May 25 17:19:41 +0200 2007
     1// script.aculo.us effects.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
    22
    33// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
     
    1414String.prototype.parseColor = function() { 
    1515  var color = '#';
    16   if(this.slice(0,4) == 'rgb(') { 
     16  if (this.slice(0,4) == 'rgb(') { 
    1717    var cols = this.slice(4,this.length-1).split(','); 
    1818    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3); 
    1919  } else { 
    20     if(this.slice(0,1) == '#') { 
    21       if(this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); 
    22       if(this.length==7) color = this.toLowerCase(); 
     20    if (this.slice(0,1) == '#') { 
     21      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase(); 
     22      if (this.length==7) color = this.toLowerCase(); 
    2323    } 
    2424  } 
    25   return(color.length==7 ? color : (arguments[0] || this)); 
    26 }
     25  return (color.length==7 ? color : (arguments[0] || this)); 
     26};
    2727
    2828/*--------------------------------------------------------------------------*/
     
    3333      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
    3434  }).flatten().join('');
    35 }
     35};
    3636
    3737Element.collectTextNodesIgnoreClass = function(element, className) { 
     
    4141        Element.collectTextNodesIgnoreClass(node, className) : ''));
    4242  }).flatten().join('');
    43 }
     43};
    4444
    4545Element.setContentZoom = function(element, percent) {
    4646  element = $(element); 
    4747  element.setStyle({fontSize: (percent/100) + 'em'});   
    48   if(Prototype.Browser.WebKit) window.scrollBy(0,0);
     48  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
    4949  return element;
    50 }
     50};
    5151
    5252Element.getInlineOpacity = function(element){
    5353  return $(element).style.opacity || '';
    54 }
     54};
    5555
    5656Element.forceRerendering = function(element) {
     
    6565/*--------------------------------------------------------------------------*/
    6666
    67 Array.prototype.call = function() {
    68   var args = arguments;
    69   this.each(function(f){ f.apply(this, args) });
    70 }
    71 
    72 /*--------------------------------------------------------------------------*/
    73 
    7467var Effect = {
    7568  _elementDoesNotExistError: {
     
    7770    message: 'The specified DOM element does not exist, but is required for this effect to operate'
    7871  },
     72  Transitions: {
     73    linear: Prototype.K,
     74    sinoidal: function(pos) {
     75      return (-Math.cos(pos*Math.PI)/2) + 0.5;
     76    },
     77    reverse: function(pos) {
     78      return 1-pos;
     79    },
     80    flicker: function(pos) {
     81      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
     82      return pos > 1 ? 1 : pos;
     83    },
     84    wobble: function(pos) {
     85      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
     86    },
     87    pulse: function(pos, pulses) {
     88      pulses = pulses || 5;
     89      return (
     90        ((pos % (1/pulses)) * pulses).round() == 0 ?
     91              ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
     92          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
     93        );
     94    },
     95    spring: function(pos) {
     96      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
     97    },
     98    none: function(pos) {
     99      return 0;
     100    },
     101    full: function(pos) {
     102      return 1;
     103    }
     104  },
     105  DefaultOptions: {
     106    duration:   1.0,   // seconds
     107    fps:        100,   // 100= assume 66fps max.
     108    sync:       false, // true for combining
     109    from:       0.0,
     110    to:         1.0,
     111    delay:      0.0,
     112    queue:      'parallel'
     113  },
    79114  tagifyText: function(element) {
    80     if(typeof Builder == 'undefined')
    81       throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
    82      
    83115    var tagifyStyle = 'position:relative';
    84     if(Prototype.Browser.IE) tagifyStyle += ';zoom:1';
     116    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
    85117   
    86118    element = $(element);
    87119    $A(element.childNodes).each( function(child) {
    88       if(child.nodeType==3) {
     120      if (child.nodeType==3) {
    89121        child.nodeValue.toArray().each( function(character) {
    90122          element.insertBefore(
    91             Builder.node('span',{style: tagifyStyle},
     123            new Element('span', {style: tagifyStyle}).update(
    92124              character == ' ' ? String.fromCharCode(160) : character),
    93125              child);
     
    99131  multiple: function(element, effect) {
    100132    var elements;
    101     if(((typeof element == 'object') ||
    102         (typeof element == 'function')) &&
     133    if (((typeof element == 'object') ||
     134        Object.isFunction(element)) &&
    103135       (element.length))
    104136      elements = element;
     
    109141      speed: 0.1,
    110142      delay: 0.0
    111     }, arguments[2] || {});
     143    }, arguments[2] || { });
    112144    var masterDelay = options.delay;
    113145
     
    126158    var options = Object.extend({
    127159      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    128     }, arguments[2] || {});
     160    }, arguments[2] || { });
    129161    Effect[element.visible() ?
    130162      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
     
    132164};
    133165
    134 var Effect2 = Effect; // deprecated
    135 
    136 /* ------------- transitions ------------- */
    137 
    138 Effect.Transitions = {
    139   linear: Prototype.K,
    140   sinoidal: function(pos) {
    141     return (-Math.cos(pos*Math.PI)/2) + 0.5;
    142   },
    143   reverse: function(pos) {
    144     return 1-pos;
    145   },
    146   flicker: function(pos) {
    147     var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
    148     return (pos > 1 ? 1 : pos);
    149   },
    150   wobble: function(pos) {
    151     return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
    152   },
    153   pulse: function(pos, pulses) {
    154     pulses = pulses || 5;
    155     return (
    156       Math.round((pos % (1/pulses)) * pulses) == 0 ?
    157             ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
    158         1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
    159       );
    160   },
    161   none: function(pos) {
    162     return 0;
    163   },
    164   full: function(pos) {
    165     return 1;
    166   }
    167 };
     166Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
    168167
    169168/* ------------- core effects ------------- */
    170169
    171 Effect.ScopedQueue = Class.create();
    172 Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
     170Effect.ScopedQueue = Class.create(Enumerable, {
    173171  initialize: function() {
    174172    this.effects  = [];
     
    181179    var timestamp = new Date().getTime();
    182180   
    183     var position = (typeof effect.options.queue == 'string') ?
     181    var position = Object.isString(effect.options.queue) ?
    184182      effect.options.queue : effect.options.queue.position;
    185183   
     
    204202    effect.finishOn += timestamp;
    205203
    206     if(!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
     204    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
    207205      this.effects.push(effect);
    208206   
    209     if(!this.interval)
     207    if (!this.interval)
    210208      this.interval = setInterval(this.loop.bind(this), 15);
    211209  },
    212210  remove: function(effect) {
    213211    this.effects = this.effects.reject(function(e) { return e==effect });
    214     if(this.effects.length == 0) {
     212    if (this.effects.length == 0) {
    215213      clearInterval(this.interval);
    216214      this.interval = null;
     
    227225  instances: $H(),
    228226  get: function(queueName) {
    229     if(typeof queueName != 'string') return queueName;
    230    
    231     if(!this.instances[queueName])
    232       this.instances[queueName] = new Effect.ScopedQueue();
    233      
    234     return this.instances[queueName];
    235   }
    236 }
     227    if (!Object.isString(queueName)) return queueName;
     228   
     229    return this.instances.get(queueName) ||
     230      this.instances.set(queueName, new Effect.ScopedQueue());
     231  }
     232};
    237233Effect.Queue = Effect.Queues.get('global');
    238234
    239 Effect.DefaultOptions = {
    240   transition: Effect.Transitions.sinoidal,
    241   duration:   1.0,   // seconds
    242   fps:        100,   // 100= assume 66fps max.
    243   sync:       false, // true for combining
    244   from:       0.0,
    245   to:         1.0,
    246   delay:      0.0,
    247   queue:      'parallel'
    248 }
    249 
    250 Effect.Base = function() {};
    251 Effect.Base.prototype = {
     235Effect.Base = Class.create({
    252236  position: null,
    253237  start: function(options) {
     
    258242      );
    259243    }
    260     if(options.transition === false) options.transition = Effect.Transitions.linear;
    261     this.options      = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
     244    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
     245    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
    262246    this.currentFrame = 0;
    263247    this.state        = 'idle';
     
    269253   
    270254    eval('this.render = function(pos){ '+
    271       'if(this.state=="idle"){this.state="running";'+
    272       codeForEvent(options,'beforeSetup')+
     255      'if (this.state=="idle"){this.state="running";'+
     256      codeForEvent(this.options,'beforeSetup')+
    273257      (this.setup ? 'this.setup();':'')+
    274       codeForEvent(options,'afterSetup')+
    275       '};if(this.state=="running"){'+
     258      codeForEvent(this.options,'afterSetup')+
     259      '};if (this.state=="running"){'+
    276260      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
    277261      'this.position=pos;'+
    278       codeForEvent(options,'beforeUpdate')+
     262      codeForEvent(this.options,'beforeUpdate')+
    279263      (this.update ? 'this.update(pos);':'')+
    280       codeForEvent(options,'afterUpdate')+
     264      codeForEvent(this.options,'afterUpdate')+
    281265      '}}');
    282266   
    283267    this.event('beforeStart');
    284     if(!this.options.sync)
    285       Effect.Queues.get(typeof this.options.queue == 'string' ?
     268    if (!this.options.sync)
     269      Effect.Queues.get(Object.isString(this.options.queue) ?
    286270        'global' : this.options.queue.scope).add(this);
    287271  },
    288272  loop: function(timePos) {
    289     if(timePos >= this.startOn) {
    290       if(timePos >= this.finishOn) {
     273    if (timePos >= this.startOn) {
     274      if (timePos >= this.finishOn) {
    291275        this.render(1.0);
    292276        this.cancel();
    293277        this.event('beforeFinish');
    294         if(this.finish) this.finish();
     278        if (this.finish) this.finish();
    295279        this.event('afterFinish');
    296280        return; 
    297281      }
    298282      var pos   = (timePos - this.startOn) / this.totalTime,
    299           frame = Math.round(pos * this.totalFrames);
    300       if(frame > this.currentFrame) {
     283          frame = (pos * this.totalFrames).round();
     284      if (frame > this.currentFrame) {
    301285        this.render(pos);
    302286        this.currentFrame = frame;
     
    305289  },
    306290  cancel: function() {
    307     if(!this.options.sync)
    308       Effect.Queues.get(typeof this.options.queue == 'string' ?
     291    if (!this.options.sync)
     292      Effect.Queues.get(Object.isString(this.options.queue) ?
    309293        'global' : this.options.queue.scope).remove(this);
    310294    this.state = 'finished';
    311295  },
    312296  event: function(eventName) {
    313     if(this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    314     if(this.options[eventName]) this.options[eventName](this);
     297    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
     298    if (this.options[eventName]) this.options[eventName](this);
    315299  },
    316300  inspect: function() {
    317301    var data = $H();
    318302    for(property in this)
    319       if(typeof this[property] != 'function') data[property] = this[property];
     303      if (!Object.isFunction(this[property])) data.set(property, this[property]);
    320304    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
    321305  }
    322 }
    323 
    324 Effect.Parallel = Class.create();
    325 Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
     306});
     307
     308Effect.Parallel = Class.create(Effect.Base, {
    326309  initialize: function(effects) {
    327310    this.effects = effects || [];
     
    336319      effect.cancel();
    337320      effect.event('beforeFinish');
    338       if(effect.finish) effect.finish(position);
     321      if (effect.finish) effect.finish(position);
    339322      effect.event('afterFinish');
    340323    });
     
    342325});
    343326
    344 Effect.Event = Class.create();
    345 Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
     327Effect.Tween = Class.create(Effect.Base, {
     328  initialize: function(object, from, to) {
     329    object = Object.isString(object) ? $(object) : object;
     330    var args = $A(arguments), method = args.last(),
     331      options = args.length == 5 ? args[3] : null;
     332    this.method = Object.isFunction(method) ? method.bind(object) :
     333      Object.isFunction(object[method]) ? object[method].bind(object) :
     334      function(value) { object[method] = value };
     335    this.start(Object.extend({ from: from, to: to }, options || { }));
     336  },
     337  update: function(position) {
     338    this.method(position);
     339  }
     340});
     341
     342Effect.Event = Class.create(Effect.Base, {
    346343  initialize: function() {
    347     var options = Object.extend({
    348       duration: 0
    349     }, arguments[0] || {});
    350     this.start(options);
     344    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
    351345  },
    352346  update: Prototype.emptyFunction
    353347});
    354348
    355 Effect.Opacity = Class.create();
    356 Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
     349Effect.Opacity = Class.create(Effect.Base, {
    357350  initialize: function(element) {
    358351    this.element = $(element);
    359     if(!this.element) throw(Effect._elementDoesNotExistError);
     352    if (!this.element) throw(Effect._elementDoesNotExistError);
    360353    // make this work on IE on elements without 'layout'
    361     if(Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
     354    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
    362355      this.element.setStyle({zoom: 1});
    363356    var options = Object.extend({
    364357      from: this.element.getOpacity() || 0.0,
    365358      to:   1.0
    366     }, arguments[1] || {});
     359    }, arguments[1] || { });
    367360    this.start(options);
    368361  },
     
    372365});
    373366
    374 Effect.Move = Class.create();
    375 Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
     367Effect.Move = Class.create(Effect.Base, {
    376368  initialize: function(element) {
    377369    this.element = $(element);
    378     if(!this.element) throw(Effect._elementDoesNotExistError);
     370    if (!this.element) throw(Effect._elementDoesNotExistError);
    379371    var options = Object.extend({
    380372      x:    0,
    381373      y:    0,
    382374      mode: 'relative'
    383     }, arguments[1] || {});
     375    }, arguments[1] || { });
    384376    this.start(options);
    385377  },
    386378  setup: function() {
    387     // Bug in Opera: Opera returns the "real" position of a static element or
    388     // relative element that does not have top/left explicitly set.
    389     // ==> Always set top and left for position relative elements in your stylesheets
    390     // (to 0 if you do not need them)
    391379    this.element.makePositioned();
    392380    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    393381    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    394     if(this.options.mode == 'absolute') {
    395       // absolute movement, so we need to calc deltaX and deltaY
     382    if (this.options.mode == 'absolute') {
    396383      this.options.x = this.options.x - this.originalLeft;
    397384      this.options.y = this.options.y - this.originalTop;
     
    400387  update: function(position) {
    401388    this.element.setStyle({
    402       left: Math.round(this.options.x  * position + this.originalLeft) + 'px',
    403       top:  Math.round(this.options.y  * position + this.originalTop)  + 'px'
     389      left: (this.options.x  * position + this.originalLeft).round() + 'px',
     390      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
    404391    });
    405392  }
     
    409396Effect.MoveBy = function(element, toTop, toLeft) {
    410397  return new Effect.Move(element,
    411     Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
    412 };
    413 
    414 Effect.Scale = Class.create();
    415 Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
     398    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
     399};
     400
     401Effect.Scale = Class.create(Effect.Base, {
    416402  initialize: function(element, percent) {
    417403    this.element = $(element);
    418     if(!this.element) throw(Effect._elementDoesNotExistError);
     404    if (!this.element) throw(Effect._elementDoesNotExistError);
    419405    var options = Object.extend({
    420406      scaleX: true,
     
    422408      scaleContent: true,
    423409      scaleFromCenter: false,
    424       scaleMode: 'box',        // 'box' or 'contents' or {} with provided values
     410      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
    425411      scaleFrom: 100.0,
    426412      scaleTo:   percent
    427     }, arguments[2] || {});
     413    }, arguments[2] || { });
    428414    this.start(options);
    429415  },
     
    432418    this.elementPositioning = this.element.getStyle('position');
    433419   
    434     this.originalStyle = {};
     420    this.originalStyle = { };
    435421    ['top','left','width','height','fontSize'].each( function(k) {
    436422      this.originalStyle[k] = this.element.style[k];
     
    442428    var fontSize = this.element.getStyle('font-size') || '100%';
    443429    ['em','px','%','pt'].each( function(fontSizeType) {
    444       if(fontSize.indexOf(fontSizeType)>0) {
     430      if (fontSize.indexOf(fontSizeType)>0) {
    445431        this.fontSize     = parseFloat(fontSize);
    446432        this.fontSizeType = fontSizeType;
     
    451437   
    452438    this.dims = null;
    453     if(this.options.scaleMode=='box')
     439    if (this.options.scaleMode=='box')
    454440      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    455     if(/^content/.test(this.options.scaleMode))
     441    if (/^content/.test(this.options.scaleMode))
    456442      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    457     if(!this.dims)
     443    if (!this.dims)
    458444      this.dims = [this.options.scaleMode.originalHeight,
    459445                   this.options.scaleMode.originalWidth];
     
    461447  update: function(position) {
    462448    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    463     if(this.options.scaleContent && this.fontSize)
     449    if (this.options.scaleContent && this.fontSize)
    464450      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    465451    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
    466452  },
    467453  finish: function(position) {
    468     if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
     454    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
    469455  },
    470456  setDimensions: function(height, width) {
    471     var d = {};
    472     if(this.options.scaleX) d.width = Math.round(width) + 'px';
    473     if(this.options.scaleY) d.height = Math.round(height) + 'px';
    474     if(this.options.scaleFromCenter) {
     457    var d = { };
     458    if (this.options.scaleX) d.width = width.round() + 'px';
     459    if (this.options.scaleY) d.height = height.round() + 'px';
     460    if (this.options.scaleFromCenter) {
    475461      var topd  = (height - this.dims[0])/2;
    476462      var leftd = (width  - this.dims[1])/2;
    477       if(this.elementPositioning == 'absolute') {
    478         if(this.options.scaleY) d.top = this.originalTop-topd + 'px';
    479         if(this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
     463      if (this.elementPositioning == 'absolute') {
     464        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
     465        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
    480466      } else {
    481         if(this.options.scaleY) d.top = -topd + 'px';
    482         if(this.options.scaleX) d.left = -leftd + 'px';
     467        if (this.options.scaleY) d.top = -topd + 'px';
     468        if (this.options.scaleX) d.left = -leftd + 'px';
    483469      }
    484470    }
     
    487473});
    488474
    489 Effect.Highlight = Class.create();
    490 Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
     475Effect.Highlight = Class.create(Effect.Base, {
    491476  initialize: function(element) {
    492477    this.element = $(element);
    493     if(!this.element) throw(Effect._elementDoesNotExistError);
    494     var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
     478    if (!this.element) throw(Effect._elementDoesNotExistError);
     479    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
    495480    this.start(options);
    496481  },
    497482  setup: function() {
    498483    // Prevent executing on elements not in the layout flow
    499     if(this.element.getStyle('display')=='none') { this.cancel(); return; }
     484    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
    500485    // Disable background image during the effect
    501     this.oldStyle = {};
     486    this.oldStyle = { };
    502487    if (!this.options.keepBackgroundImage) {
    503488      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
    504489      this.element.setStyle({backgroundImage: 'none'});
    505490    }
    506     if(!this.options.endcolor)
     491    if (!this.options.endcolor)
    507492      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    508     if(!this.options.restorecolor)
     493    if (!this.options.restorecolor)
    509494      this.options.restorecolor = this.element.getStyle('background-color');
    510495    // init color calculations
     
    514499  update: function(position) {
    515500    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
    516       return m+(Math.round(this._base[i]+(this._delta[i]*position)).toColorPart()); }.bind(this)) });
     501      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
    517502  },
    518503  finish: function() {
     
    523508});
    524509
    525 Effect.ScrollTo = Class.create();
    526 Object.extend(Object.extend(Effect.ScrollTo.prototype, Effect.Base.prototype), {
    527   initialize: function(element) {
    528     this.element = $(element);
    529     this.start(arguments[1] || {});
    530   },
    531   setup: function() {
    532     Position.prepare();
    533     var offsets = Position.cumulativeOffset(this.element);
    534     if(this.options.offset) offsets[1] += this.options.offset;
    535     var max = window.innerHeight ?
    536       window.height - window.innerHeight :
    537       document.body.scrollHeight -
    538         (document.documentElement.clientHeight ?
    539           document.documentElement.clientHeight : document.body.clientHeight);
    540     this.scrollStart = Position.deltaY;
    541     this.delta = (offsets[1] > max ? max : offsets[1]) - this.scrollStart;
    542   },
    543   update: function(position) {
    544     Position.prepare();
    545     window.scrollTo(Position.deltaX,
    546       this.scrollStart + (position*this.delta));
    547   }
    548 });
     510Effect.ScrollTo = function(element) {
     511  var options = arguments[1] || { },
     512    scrollOffsets = document.viewport.getScrollOffsets(),
     513    elementOffsets = $(element).cumulativeOffset(),
     514    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight(); 
     515
     516  if (options.offset) elementOffsets[1] += options.offset;
     517
     518  return new Effect.Tween(null,
     519    scrollOffsets.top,
     520    elementOffsets[1] > max ? max : elementOffsets[1],
     521    options,
     522    function(p){ scrollTo(scrollOffsets.left, p.round()) }
     523  );
     524};
    549525
    550526/* ------------- combination effects ------------- */
     
    554530  var oldOpacity = element.getInlineOpacity();
    555531  var options = Object.extend({
    556   from: element.getOpacity() || 1.0,
    557   to:   0.0,
    558   afterFinishInternal: function(effect) {
    559     if(effect.options.to!=0) return;
    560     effect.element.hide().setStyle({opacity: oldOpacity});
    561   }}, arguments[1] || {});
     532    from: element.getOpacity() || 1.0,
     533    to:   0.0,
     534    afterFinishInternal: function(effect) {
     535      if (effect.options.to!=0) return;
     536      effect.element.hide().setStyle({opacity: oldOpacity});
     537    }
     538  }, arguments[1] || { });
    562539  return new Effect.Opacity(element,options);
    563 }
     540};
    564541
    565542Effect.Appear = function(element) {
     
    574551  beforeSetup: function(effect) {
    575552    effect.element.setOpacity(effect.options.from).show();
    576   }}, arguments[1] || {});
     553  }}, arguments[1] || { });
    577554  return new Effect.Opacity(element,options);
    578 }
     555};
    579556
    580557Effect.Puff = function(element) {
     
    598575      afterFinishInternal: function(effect) {
    599576         effect.effects[0].element.hide().setStyle(oldStyle); }
    600      }, arguments[1] || {})
     577     }, arguments[1] || { })
    601578   );
    602 }
     579};
    603580
    604581Effect.BlindUp = function(element) {
     
    612589        effect.element.hide().undoClipping();
    613590      }
    614     }, arguments[1] || {})
     591    }, arguments[1] || { })
    615592  );
    616 }
     593};
    617594
    618595Effect.BlindDown = function(element) {
     
    631608      effect.element.undoClipping();
    632609    }
    633   }, arguments[1] || {}));
    634 }
     610  }, arguments[1] || { }));
     611};
    635612
    636613Effect.SwitchOff = function(element) {
     
    653630      })
    654631    }
    655   }, arguments[1] || {}));
    656 }
     632  }, arguments[1] || { }));
     633};
    657634
    658635Effect.DropOut = function(element) {
     
    673650          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
    674651        }
    675       }, arguments[1] || {}));
    676 }
     652      }, arguments[1] || { }));
     653};
    677654
    678655Effect.Shake = function(element) {
    679656  element = $(element);
     657  var options = Object.extend({
     658    distance: 20,
     659    duration: 0.5
     660  }, arguments[1] || {});
     661  var distance = parseFloat(options.distance);
     662  var split = parseFloat(options.duration) / 10.0;
    680663  var oldStyle = {
    681664    top: element.getStyle('top'),
    682665    left: element.getStyle('left') };
    683     return new Effect.Move(element, 
    684       { x:  20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
     666    return new Effect.Move(element,
     667      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    685668    new Effect.Move(effect.element,
    686       { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
     669      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    687670    new Effect.Move(effect.element,
    688       { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
     671      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    689672    new Effect.Move(effect.element,
    690       { x: -40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
     673      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    691674    new Effect.Move(effect.element,
    692       { x:  40, y: 0, duration: 0.1,  afterFinishInternal: function(effect) {
     675      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    693676    new Effect.Move(effect.element,
    694       { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
     677      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    695678        effect.element.undoPositioned().setStyle(oldStyle);
    696679  }}) }}) }}) }}) }}) }});
    697 }
     680};
    698681
    699682Effect.SlideDown = function(element) {
     
    711694      effect.element.makePositioned();
    712695      effect.element.down().makePositioned();
    713       if(window.opera) effect.element.setStyle({top: ''});
     696      if (window.opera) effect.element.setStyle({top: ''});
    714697      effect.element.makeClipping().setStyle({height: '0px'}).show();
    715698    },
     
    721704      effect.element.undoClipping().undoPositioned();
    722705      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    723     }, arguments[1] || {})
     706    }, arguments[1] || { })
    724707  );
    725 }
     708};
    726709
    727710Effect.SlideUp = function(element) {
    728711  element = $(element).cleanWhitespace();
    729712  var oldInnerBottom = element.down().getStyle('bottom');
     713  var elementDimensions = element.getDimensions();
    730714  return new Effect.Scale(element, window.opera ? 0 : 1,
    731715   Object.extend({ scaleContent: false,
     
    733717    scaleMode: 'box',
    734718    scaleFrom: 100,
     719    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    735720    restoreAfterFinish: true,
    736     beforeStartInternal: function(effect) {
     721    afterSetup: function(effect) {
    737722      effect.element.makePositioned();
    738723      effect.element.down().makePositioned();
    739       if(window.opera) effect.element.setStyle({top: ''});
     724      if (window.opera) effect.element.setStyle({top: ''});
    740725      effect.element.makeClipping().show();
    741726    }, 
     
    745730    },
    746731    afterFinishInternal: function(effect) {
    747       effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
    748       effect.element.down().undoPositioned();
    749     }
    750    }, arguments[1] || {})
     732      effect.element.hide().undoClipping().undoPositioned();
     733      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
     734    }
     735   }, arguments[1] || { })
    751736  );
    752 }
     737};
    753738
    754739// Bug in opera makes the TD containing this element expand for a instance after finish
     
    763748    }
    764749  });
    765 }
     750};
    766751
    767752Effect.Grow = function(element) {
     
    772757    scaleTransition: Effect.Transitions.sinoidal,
    773758    opacityTransition: Effect.Transitions.full
    774   }, arguments[1] || {});
     759  }, arguments[1] || { });
    775760  var oldStyle = {
    776761    top: element.style.top,
     
    837822    }
    838823  });
    839 }
     824};
    840825
    841826Effect.Shrink = function(element) {
     
    846831    scaleTransiti