[ Index ]

PHP Cross Reference of Wordpress 2.9.1

title

Body

[close]

/wp-includes/js/ -> prototype.js (source)

   1  /*  Prototype JavaScript framework, version 1.6.0
   2   *  (c) 2005-2007 Sam Stephenson
   3   *
   4   *  Prototype is freely distributable under the terms of an MIT-style license.
   5   *  For details, see the Prototype web site: http://www.prototypejs.org/
   6   *
   7   *--------------------------------------------------------------------------*/
   8  
   9  var Prototype = {
  10    Version: '1.6.0',
  11  
  12    Browser: {
  13      IE:     !!(window.attachEvent && !window.opera),
  14      Opera:  !!window.opera,
  15      WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
  16      Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
  17      MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  18    },
  19  
  20    BrowserFeatures: {
  21      XPath: !!document.evaluate,
  22      ElementExtensions: !!window.HTMLElement,
  23      SpecificElementExtensions:
  24        document.createElement('div').__proto__ &&
  25        document.createElement('div').__proto__ !==
  26          document.createElement('form').__proto__
  27    },
  28  
  29    ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  30    JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
  31  
  32    emptyFunction: function() { },
  33    K: function(x) { return x }
  34  };
  35  
  36  if (Prototype.Browser.MobileSafari)
  37    Prototype.BrowserFeatures.SpecificElementExtensions = false;
  38  
  39  if (Prototype.Browser.WebKit)
  40    Prototype.BrowserFeatures.XPath = false;
  41  
  42  /* Based on Alex Arnell's inheritance implementation. */
  43  var Class = {
  44    create: function() {
  45      var parent = null, properties = $A(arguments);
  46      if (Object.isFunction(properties[0]))
  47        parent = properties.shift();
  48  
  49      function klass() {
  50        this.initialize.apply(this, arguments);
  51      }
  52  
  53      Object.extend(klass, Class.Methods);
  54      klass.superclass = parent;
  55      klass.subclasses = [];
  56  
  57      if (parent) {
  58        var subclass = function() { };
  59        subclass.prototype = parent.prototype;
  60        klass.prototype = new subclass;
  61        parent.subclasses.push(klass);
  62      }
  63  
  64      for (var i = 0; i < properties.length; i++)
  65        klass.addMethods(properties[i]);
  66  
  67      if (!klass.prototype.initialize)
  68        klass.prototype.initialize = Prototype.emptyFunction;
  69  
  70      klass.prototype.constructor = klass;
  71  
  72      return klass;
  73    }
  74  };
  75  
  76  Class.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  
 102  var Abstract = { };
 103  
 104  Object.extend = function(destination, source) {
 105    for (var property in source)
 106      destination[property] = source[property];
 107    return destination;
 108  };
 109  
 110  Object.extend(Object, {
 111    inspect: function(object) {
 112      try {
 113        if (object === undefined) return 'undefined';
 114        if (object === null) return 'null';
 115        return object.inspect ? object.inspect() : object.toString();
 116      } catch (e) {
 117        if (e instanceof RangeError) return '...';
 118        throw e;
 119      }
 120    },
 121  
 122    toJSON: function(object) {
 123      var type = typeof object;
 124      switch (type) {
 125        case 'undefined':
 126        case 'function':
 127        case 'unknown': return;
 128        case 'boolean': return object.toString();
 129      }
 130  
 131      if (object === null) return 'null';
 132      if (object.toJSON) return object.toJSON();
 133      if (Object.isElement(object)) return;
 134  
 135      var results = [];
 136      for (var property in object) {
 137        var value = Object.toJSON(object[property]);
 138        if (value !== undefined)
 139          results.push(property.toJSON() + ': ' + value);
 140      }
 141  
 142      return '{' + results.join(', ') + '}';
 143    },
 144  
 145    toQueryString: function(object) {
 146      return $H(object).toQueryString();
 147    },
 148  
 149    toHTML: function(object) {
 150      return object && object.toHTML ? object.toHTML() : String.interpret(object);
 151    },
 152  
 153    keys: function(object) {
 154      var keys = [];
 155      for (var property in object)
 156        keys.push(property);
 157      return keys;
 158    },
 159  
 160    values: function(object) {
 161      var values = [];
 162      for (var property in object)
 163        values.push(object[property]);
 164      return values;
 165    },
 166  
 167    clone: function(object) {
 168      return Object.extend({ }, object);
 169    },
 170  
 171    isElement: function(object) {
 172      return object && object.nodeType == 1;
 173    },
 174  
 175    isArray: function(object) {
 176      return object && object.constructor === Array;
 177    },
 178  
 179    isHash: function(object) {
 180      return object instanceof Hash;
 181    },
 182  
 183    isFunction: function(object) {
 184      return typeof object == "function";
 185    },
 186  
 187    isString: function(object) {
 188      return typeof object == "string";
 189    },
 190  
 191    isNumber: function(object) {
 192      return typeof object == "number";
 193    },
 194  
 195    isUndefined: function(object) {
 196      return typeof object == "undefined";
 197    }
 198  });
 199  
 200  Object.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      };
 249    }
 250  });
 251  
 252  Function.prototype.defer = Function.prototype.delay.curry(0.01);
 253  
 254  Date.prototype.toJSON = function() {
 255    return '"' + this.getUTCFullYear() + '-' +
 256      (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
 257      this.getUTCDate().toPaddedString(2) + 'T' +
 258      this.getUTCHours().toPaddedString(2) + ':' +
 259      this.getUTCMinutes().toPaddedString(2) + ':' +
 260      this.getUTCSeconds().toPaddedString(2) + 'Z"';
 261  };
 262  
 263  var Try = {
 264    these: function() {
 265      var returnValue;
 266  
 267      for (var i = 0, length = arguments.length; i < length; i++) {
 268        var lambda = arguments[i];
 269        try {
 270          returnValue = lambda();
 271          break;
 272        } catch (e) { }
 273      }
 274  
 275      return returnValue;
 276    }
 277  };
 278  
 279  RegExp.prototype.match = RegExp.prototype.test;
 280  
 281  RegExp.escape = function(str) {
 282    return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
 283  };
 284  
 285  /*--------------------------------------------------------------------------*/
 286  
 287  var PeriodicalExecuter = Class.create({
 288    initialize: function(callback, frequency) {
 289      this.callback = callback;
 290      this.frequency = frequency;
 291      this.currentlyExecuting = false;
 292  
 293      this.registerCallback();
 294    },
 295  
 296    registerCallback: function() {
 297      this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
 298    },
 299  
 300    execute: function() {
 301      this.callback(this);
 302    },
 303  
 304    stop: function() {
 305      if (!this.timer) return;
 306      clearInterval(this.timer);
 307      this.timer = null;
 308    },
 309  
 310    onTimerEvent: function() {
 311      if (!this.currentlyExecuting) {
 312        try {
 313          this.currentlyExecuting = true;
 314          this.execute();
 315        } finally {
 316          this.currentlyExecuting = false;
 317        }
 318      }
 319    }
 320  });
 321  Object.extend(String, {
 322    interpret: function(value) {
 323      return value == null ? '' : String(value);
 324    },
 325    specialChar: {
 326      '\b': '\\b',
 327      '\t': '\\t',
 328      '\n': '\\n',
 329      '\f': '\\f',
 330      '\r': '\\r',
 331      '\\': '\\\\'
 332    }
 333  });
 334  
 335  Object.extend(String.prototype, {
 336    gsub: function(pattern, replacement) {
 337      var result = '', source = this, match;
 338      replacement = arguments.callee.prepareReplacement(replacement);
 339  
 340      while (source.length > 0) {
 341        if (match = source.match(pattern)) {
 342          result += source.slice(0, match.index);
 343          result += String.interpret(replacement(match));
 344          source  = source.slice(match.index + match[0].length);
 345        } else {
 346          result += source, source = '';
 347        }
 348      }
 349      return result;
 350    },
 351  
 352    sub: function(pattern, replacement, count) {
 353      replacement = this.gsub.prepareReplacement(replacement);
 354      count = count === undefined ? 1 : count;
 355  
 356      return this.gsub(pattern, function(match) {
 357        if (--count < 0) return match[0];
 358        return replacement(match);
 359      });
 360    },
 361  
 362    scan: function(pattern, iterator) {
 363      this.gsub(pattern, iterator);
 364      return String(this);
 365    },
 366  
 367    truncate: function(length, truncation) {
 368      length = length || 30;
 369      truncation = truncation === undefined ? '...' : truncation;
 370      return this.length > length ?
 371        this.slice(0, length - truncation.length) + truncation : String(this);
 372    },
 373  
 374    strip: function() {
 375      return this.replace(/^\s+/, '').replace(/\s+$/, '');
 376    },
 377  
 378    stripTags: function() {
 379      return this.replace(/<\/?[^>]+>/gi, '');
 380    },
 381  
 382    stripScripts: function() {
 383      return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
 384    },
 385  
 386    extractScripts: function() {
 387      var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
 388      var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
 389      return (this.match(matchAll) || []).map(function(scriptTag) {
 390        return (scriptTag.match(matchOne) || ['', ''])[1];
 391      });
 392    },
 393  
 394    evalScripts: function() {
 395      return this.extractScripts().map(function(script) { return eval(script) });
 396    },
 397  
 398    escapeHTML: function() {
 399      var self = arguments.callee;
 400      self.text.data = this;
 401      return self.div.innerHTML;
 402    },
 403  
 404    unescapeHTML: function() {
 405      var div = new Element('div');
 406      div.innerHTML = this.stripTags();
 407      return div.childNodes[0] ? (div.childNodes.length > 1 ?
 408        $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
 409        div.childNodes[0].nodeValue) : '';
 410    },
 411  
 412    toQueryParams: function(separator) {
 413      var match = this.strip().match(/([^?#]*)(#.*)?$/);
 414      if (!match) return { };
 415  
 416      return match[1].split(separator || '&').inject({ }, function(hash, pair) {
 417        if ((pair = pair.split('='))[0]) {
 418          var key = decodeURIComponent(pair.shift());
 419          var value = pair.length > 1 ? pair.join('=') : pair[0];
 420          if (value != undefined) value = decodeURIComponent(value);
 421  
 422          if (key in hash) {
 423            if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
 424            hash[key].push(value);
 425          }
 426          else hash[key] = value;
 427        }
 428        return hash;
 429      });
 430    },
 431  
 432    toArray: function() {
 433      return this.split('');
 434    },
 435  
 436    succ: function() {
 437      return this.slice(0, this.length - 1) +
 438        String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
 439    },
 440  
 441    times: function(count) {
 442      return count < 1 ? '' : new Array(count + 1).join(this);
 443    },
 444  
 445    camelize: function() {
 446      var parts = this.split('-'), len = parts.length;
 447      if (len == 1) return parts[0];
 448  
 449      var camelized = this.charAt(0) == '-'
 450        ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
 451        : parts[0];
 452  
 453      for (var i = 1; i < len; i++)
 454        camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
 455  
 456      return camelized;
 457    },
 458  
 459    capitalize: function() {
 460      return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
 461    },
 462  
 463    underscore: function() {
 464      return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
 465    },
 466  
 467    dasherize: function() {
 468      return this.gsub(/_/,'-');
 469    },
 470  
 471    inspect: function(useDoubleQuotes) {
 472      var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
 473        var character = String.specialChar[match[0]];
 474        return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
 475      });
 476      if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
 477      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
 478    },
 479  
 480    toJSON: function() {
 481      return this.inspect(true);
 482    },
 483  
 484    unfilterJSON: function(filter) {
 485      return this.sub(filter || Prototype.JSONFilter, '#{1}');
 486    },
 487  
 488    isJSON: function() {
 489      var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
 490      return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
 491    },
 492  
 493    evalJSON: function(sanitize) {
 494      var json = this.unfilterJSON();
 495      try {
 496        if (!sanitize || json.isJSON()) return eval('(' + json + ')');
 497      } catch (e) { }
 498      throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
 499    },
 500  
 501    include: function(pattern) {
 502      return this.indexOf(pattern) > -1;
 503    },
 504  
 505    startsWith: function(pattern) {
 506      return this.indexOf(pattern) === 0;
 507    },
 508  
 509    endsWith: function(pattern) {
 510      var d = this.length - pattern.length;
 511      return d >= 0 && this.lastIndexOf(pattern) === d;
 512    },
 513  
 514    empty: function() {
 515      return this == '';
 516    },
 517  
 518    blank: function() {
 519      return /^\s*$/.test(this);
 520    },
 521  
 522    interpolate: function(object, pattern) {
 523      return new Template(this, pattern).evaluate(object);
 524    }
 525  });
 526  
 527  if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
 528    escapeHTML: function() {
 529      return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
 530    },
 531    unescapeHTML: function() {
 532      return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
 533    }
 534  });
 535  
 536  String.prototype.gsub.prepareReplacement = function(replacement) {
 537    if (Object.isFunction(replacement)) return replacement;
 538    var template = new Template(replacement);
 539    return function(match) { return template.evaluate(match) };
 540  };
 541  
 542  String.prototype.parseQuery = String.prototype.toQueryParams;
 543  
 544  Object.extend(String.prototype.escapeHTML, {
 545    div:  document.createElement('div'),
 546    text: document.createTextNode('')
 547  });
 548  
 549  with (String.prototype.escapeHTML) div.appendChild(text);
 550  
 551  var Template = Class.create({
 552    initialize: function(template, pattern) {
 553      this.template = template.toString();
 554      this.pattern = pattern || Template.Pattern;
 555    },
 556  
 557    evaluate: function(object) {
 558      if (Object.isFunction(object.toTemplateReplacements))
 559        object = object.toTemplateReplacements();
 560  
 561      return this.template.gsub(this.pattern, function(match) {
 562        if (object == null) return '';
 563  
 564        var before = match[1] || '';
 565        if (before == '\\') return match[2];
 566  
 567        var ctx = object, expr = match[3];
 568        var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
 569        if (match == null) return before;
 570  
 571        while (match != null) {
 572          var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
 573          ctx = ctx[comp];
 574          if (null == ctx || '' == match[3]) break;
 575          expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
 576          match = pattern.exec(expr);
 577        }
 578  
 579        return before + String.interpret(ctx);
 580      }.bind(this));
 581    }
 582  });
 583  Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
 584  
 585  var $break = { };
 586  
 587  var Enumerable = {
 588    each: function(iterator, context) {
 589      var index = 0;
 590      iterator = iterator.bind(context);
 591      try {
 592        this._each(function(value) {
 593          iterator(value, index++);
 594        });
 595      } catch (e) {
 596        if (e != $break) throw e;
 597      }
 598      return this;
 599    },
 600  
 601    eachSlice: function(number, iterator, context) {
 602      iterator = iterator ? iterator.bind(context) : Prototype.K;
 603      var index = -number, slices = [], array = this.toArray();
 604      while ((index += number) < array.length)
 605        slices.push(array.slice(index, index+number));
 606      return slices.collect(iterator, context);
 607    },
 608  
 609    all: function(iterator, context) {
 610      iterator = iterator ? iterator.bind(context) : Prototype.K;
 611      var result = true;
 612      this.each(function(value, index) {
 613        result = result && !!iterator(value, index);
 614        if (!result) throw $break;
 615      });
 616      return result;
 617    },
 618  
 619    any: function(iterator, context) {
 620      iterator = iterator ? iterator.bind(context) : Prototype.K;
 621      var result = false;
 622      this.each(function(value, index) {
 623        if (result = !!iterator(value, index))
 624          throw $break;
 625      });
 626      return result;
 627    },
 628  
 629    collect: function(iterator, context) {
 630      iterator = iterator ? iterator.bind(context) : Prototype.K;
 631      var results = [];
 632      this.each(function(value, index) {
 633        results.push(iterator(value, index));
 634      });
 635      return results;
 636    },
 637  
 638    detect: function(iterator, context) {
 639      iterator = iterator.bind(context);
 640      var result;
 641      this.each(function(value, index) {
 642        if (iterator(value, index)) {
 643          result = value;
 644          throw $break;
 645        }
 646      });
 647      return result;
 648    },
 649  
 650    findAll: function(iterator, context) {
 651      iterator = iterator.bind(context);
 652      var results = [];
 653      this.each(function(value, index) {
 654        if (iterator(value, index))
 655          results.push(value);
 656      });
 657      return results;
 658    },
 659  
 660    grep: function(filter, iterator, context) {
 661      iterator = iterator ? iterator.bind(context) : Prototype.K;
 662      var results = [];
 663  
 664      if (Object.isString(filter))
 665        filter = new RegExp(filter);
 666  
 667      this.each(function(value, index) {
 668        if (filter.match(value))
 669          results.push(iterator(value, index));
 670      });
 671      return results;
 672    },
 673  
 674    include: function(object) {
 675      if (Object.isFunction(this.indexOf))
 676        if (this.indexOf(object) != -1) return true;
 677  
 678      var found = false;
 679      this.each(function(value) {
 680        if (value == object) {
 681          found = true;
 682          throw $break;
 683        }
 684      });
 685      return found;
 686    },
 687  
 688    inGroupsOf: function(number, fillWith) {
 689      fillWith = fillWith === undefined ? null : fillWith;
 690      return this.eachSlice(number, function(slice) {
 691        while(slice.length < number) slice.push(fillWith);
 692        return slice;
 693      });
 694    },
 695  
 696    inject: function(memo, iterator, context) {
 697      iterator = iterator.bind(context);
 698      this.each(function(value, index) {
 699        memo = iterator(memo, value, index);
 700      });
 701      return memo;
 702    },
 703  
 704    invoke: function(method) {
 705      var args = $A(arguments).slice(1);
 706      return this.map(function(value) {
 707        return value[method].apply(value, args);
 708      });
 709    },
 710  
 711    max: function(iterator, context) {
 712      iterator = iterator ? iterator.bind(context) : Prototype.K;
 713      var result;
 714      this.each(function(value, index) {
 715        value = iterator(value, index);
 716        if (result == undefined || value >= result)
 717          result = value;
 718      });
 719      return result;
 720    },
 721  
 722    min: function(iterator, context) {
 723      iterator = iterator ? iterator.bind(context) : Prototype.K;
 724      var result;
 725      this.each(function(value, index) {
 726        value = iterator(value, index);
 727        if (result == undefined || value < result)
 728          result = value;
 729      });
 730      return result;
 731    },
 732  
 733    partition: function(iterator, context) {
 734      iterator = iterator ? iterator.bind(context) : Prototype.K;
 735      var trues = [], falses = [];
 736      this.each(function(value, index) {
 737        (iterator(value, index) ?
 738          trues : falses).push(value);
 739      });
 740      return [trues, falses];
 741    },
 742  
 743    pluck: function(property) {
 744      var results = [];
 745      this.each(function(value) {
 746        results.push(value[property]);
 747      });
 748      return results;
 749    },
 750  
 751    reject: function(iterator, context) {
 752      iterator = iterator.bind(context);
 753      var results = [];
 754      this.each(function(value, index) {
 755        if (!iterator(value, index))
 756          results.push(value);
 757      });
 758      return results;
 759    },
 760  
 761    sortBy: function(iterator, context) {
 762      iterator = iterator.bind(context);
 763      return this.map(function(value, index) {
 764        return {value: value, criteria: iterator(value, index)};
 765      }).sort(function(left, right) {
 766        var a = left.criteria, b = right.criteria;
 767        return a < b ? -1 : a > b ? 1 : 0;
 768      }).pluck('value');
 769    },
 770  
 771    toArray: function() {
 772      return this.map();
 773    },
 774  
 775    zip: function() {
 776      var iterator = Prototype.K, args = $A(arguments);
 777      if (Object.isFunction(args.last()))
 778        iterator = args.pop();
 779  
 780      var collections = [this].concat(args).map($A);
 781      return this.map(function(value, index) {
 782        return iterator(collections.pluck(index));
 783      });
 784    },
 785  
 786    size: function() {
 787      return this.toArray().length;
 788    },
 789  
 790    inspect: function() {
 791      return '#<Enumerable:' + this.toArray().inspect() + '>';
 792    }
 793  };
 794  
 795  Object.extend(Enumerable, {
 796    map:     Enumerable.collect,
 797    find:    Enumerable.detect,
 798    select:  Enumerable.findAll,
 799    filter:  Enumerable.findAll,
 800    member:  Enumerable.include,
 801    entries: Enumerable.toArray,
 802    every:   Enumerable.all,
 803    some:    Enumerable.any
 804  });
 805  function $A(iterable) {
 806    if (!iterable) return [];
 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  
 813  if (Prototype.Browser.WebKit) {
 814    function $A(iterable) {
 815      if (!iterable) return [];
 816      if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
 817          iterable.toArray) return iterable.toArray();
 818      var length = iterable.length, results = new Array(length);
 819      while (length--) results[length] = iterable[length];
 820      return results;
 821    }
 822  }
 823  
 824  Array.from = $A;
 825  
 826  Object.extend(Array.prototype, Enumerable);
 827  
 828  if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
 829  
 830  Object.extend(Array.prototype, {
 831    _each: function(iterator) {
 832      for (var i = 0, length = this.length; i < length; i++)
 833        iterator(this[i]);
 834    },
 835  
 836    clear: function() {
 837      this.length = 0;
 838      return this;
 839    },
 840  
 841    first: function() {
 842      return this[0];
 843    },
 844  
 845    last: function() {
 846      return this[this.length - 1];
 847    },
 848  
 849    compact: function() {
 850      return this.select(function(value) {
 851        return value != null;
 852      });
 853    },
 854  
 855    flatten: function() {
 856      return this.inject([], function(array, value) {
 857        return array.concat(Object.isArray(value) ?
 858          value.flatten() : [value]);
 859      });
 860    },
 861  
 862    without: function() {
 863      var values = $A(arguments);
 864      return this.select(function(value) {
 865        return !values.include(value);
 866      });
 867    },
 868  
 869    reverse: function(inline) {
 870      return (inline !== false ? this : this.toArray())._reverse();
 871    },
 872  
 873    reduce: function() {
 874      return this.length > 1 ? this : this[0];
 875    },
 876  
 877    uniq: function(sorted) {
 878      return this.inject([], function(array, value, index) {
 879        if (0 == index || (sorted ? array.last() != value : !array.include(value)))
 880          array.push(value);
 881        return array;
 882      });
 883    },
 884  
 885    intersect: function(array) {
 886      return this.uniq().findAll(function(item) {
 887        return array.detect(function(value) { return item === value });
 888      });
 889    },
 890  
 891    clone: function() {
 892      return [].concat(this);
 893    },
 894  
 895    size: function() {
 896      return this.length;
 897    },
 898  
 899    inspect: function() {
 900      return '[' + this.map(Object.inspect).join(', ') + ']';
 901    },
 902  
 903    toJSON: function() {
 904      var results = [];
 905      this.each(function(object) {
 906        var value = Object.toJSON(object);
 907        if (value !== undefined) results.push(value);
 908      });
 909      return '[' + results.join(', ') + ']';
 910    }
 911  });
 912  
 913  // use native browser JS 1.6 implementation if available
 914  if (Object.isFunction(Array.prototype.forEach))
 915    Array.prototype._each = Array.prototype.forEach;
 916  
 917  if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
 918    i || (i = 0);
 919    var length = this.length;
 920    if (i < 0) i = length + i;
 921    for (; i < length; i++)
 922      if (this[i] === item) return i;
 923    return -1;
 924  };
 925  
 926  if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
 927    i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
 928    var n = this.slice(0, i).reverse().indexOf(item);
 929    return (n < 0) ? n : i - n - 1;
 930  };
 931  
 932  Array.prototype.toArray = Array.prototype.clone;
 933  
 934  function $w(string) {
 935    if (!Object.isString(string)) return [];
 936    string = string.strip();
 937    return string ? string.split(/\s+/) : [];
 938  }
 939  
 940  if (Prototype.Browser.Opera){
 941    Array.prototype.concat = function() {
 942      var array = [];
 943      for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
 944      for (var i = 0, length = arguments.length; i < length; i++) {
 945        if (Object.isArray(arguments[i])) {
 946          for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
 947            array.push(arguments[i][j]);
 948        } else {
 949          array.push(arguments[i]);
 950        }
 951      }
 952      return array;
 953    };
 954  }
 955  Object.extend(Number.prototype, {
 956    toColorPart: function() {
 957      return this.toPaddedString(2, 16);
 958    },
 959  
 960    succ: function() {
 961      return this + 1;
 962    },
 963  
 964    times: function(iterator) {
 965      $R(0, this, true).each(iterator);
 966      return this;
 967    },
 968  
 969    toPaddedString: function(length, radix) {
 970      var string = this.toString(radix || 10);
 971      return '0'.times(length - string.length) + string;
 972    },
 973  
 974    toJSON: function() {
 975      return isFinite(this) ? this.toString() : 'null';
 976    }
 977  });
 978  
 979  $w('abs round ceil floor').each(function(method){
 980    Number.prototype[method] = Math[method].methodize();
 981  });
 982  function $H(object) {
 983    return new Hash(object);
 984  };
 985  
 986  var 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  
1100  Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
1101  Hash.from = $H;
1102  var ObjectRange = Class.create(Enumerable, {
1103    initialize: function(start, end, exclusive) {
1104      this.start = start;
1105      this.end = end;
1106      this.exclusive = exclusive;
1107    },
1108  
1109    _each: function(iterator) {
1110      var value = this.start;
1111      while (this.include(value)) {
1112        iterator(value);
1113        value = value.succ();
1114      }
1115    },
1116  
1117    include: function(value) {
1118      if (value < this.start)
1119        return false;
1120      if (this.exclusive)
1121        return value < this.end;
1122      return value <= this.end;
1123    }
1124  });
1125  
1126  var $R = function(start, end, exclusive) {
1127    return new ObjectRange(start, end, exclusive);
1128  };
1129  
1130  var Ajax = {
1131    getTransport: function() {
1132      return Try.these(
1133        function() {return new XMLHttpRequest()},
1134        function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1135        function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1136      ) || false;
1137    },
1138  
1139    activeRequestCount: 0
1140  };
1141  
1142  Ajax.Responders = {
1143    responders: [],
1144  
1145    _each: function(iterator) {
1146      this.responders._each(iterator);
1147    },
1148  
1149    register: function(responder) {
1150      if (!this.include(responder))
1151        this.responders.push(responder);
1152    },
1153  
1154    unregister: function(responder) {
1155      this.responders = this.responders.without(responder);
1156    },
1157  
1158    dispatch: function(callback, request, transport, json) {
1159      this.each(function(responder) {
1160        if (Object.isFunction(responder[callback])) {
1161          try {
1162            responder[callback].apply(responder, [request, transport, json]);
1163          } catch (e) { }
1164        }
1165      });
1166    }
1167  };
1168  
1169  Object.extend(Ajax.Responders, Enumerable);
1170  
1171  Ajax.Responders.register({
1172    onCreate:   function() { Ajax.activeRequestCount++ },
1173    onComplete: function() { Ajax.activeRequestCount-- }
1174  });
1175  
1176  Ajax.Base = Class.create({
1177    initialize: function(options) {
1178      this.options = {
1179        method:       'post',
1180        asynchronous: true,
1181        contentType:  'application/x-www-form-urlencoded',
1182        encoding:     'UTF-8',
1183        parameters:   '',
1184        evalJSON:     true,
1185        evalJS:       true
1186      };
1187      Object.extend(this.options, options || { });
1188  
1189      this.options.method = this.options.method.toLowerCase();
1190      if (Object.isString(this.options.parameters))
1191        this.options.parameters = this.options.parameters.toQueryParams();
1192    }
1193  });
1194  
1195  Ajax.Request = Class.create(Ajax.Base, {
1196    _complete: false,
1197  
1198    initialize: function($super, url, options) {
1199      $super(options);
1200      this.transport = Ajax.getTransport();
1201      this.request(url);
1202    },
1203  
1204    request: function(url) {
1205      this.url = url;
1206      this.method = this.options.method;
1207      var params = Object.clone(this.options.parameters);
1208  
1209      if (!['get', 'post'].include(this.method)) {
1210        // simulate other verbs over post
1211        params['_method'] = this.method;
1212        this.method = 'post';
1213      }
1214  
1215      this.parameters = params;
1216  
1217      if (params = Object.toQueryString(params)) {
1218        // when GET, append parameters to URL
1219        if (this.method == 'get')
1220          this.url += (this.url.include('?') ? '&' : '?') + params;
1221        else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1222          params += '&_=';
1223      }
1224  
1225      try {
1226        var response = new Ajax.Response(this);
1227        if (this.options.onCreate) this.options.onCreate(response);
1228        Ajax.Responders.dispatch('onCreate', this, response);
1229  
1230        this.transport.open(this.method.toUpperCase(), this.url,
1231          this.options.asynchronous);
1232  
1233        if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1234  
1235        this.transport.onreadystatechange = this.onStateChange.bind(this);
1236        this.setRequestHeaders();
1237  
1238        this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1239        this.transport.send(this.body);
1240  
1241        /* Force Firefox to handle ready state 4 for synchronous requests */
1242        if (!this.options.asynchronous && this.transport.overrideMimeType)
1243          this.onStateChange();
1244  
1245      }
1246      catch (e) {
1247        this.dispatchException(e);
1248      }
1249    },
1250  
1251    onStateChange: function() {
1252      var readyState = this.transport.readyState;
1253      if (readyState > 1 && !((readyState == 4) && this._complete))
1254        this.respondToReadyState(this.transport.readyState);
1255    },
1256  
1257    setRequestHeaders: function() {
1258      var headers = {
1259        'X-Requested-With': 'XMLHttpRequest',
1260        'X-Prototype-Version': Prototype.Version,
1261        'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1262      };
1263  
1264      if (this.method == 'post') {
1265        headers['Content-type'] = this.options.contentType +
1266          (this.options.encoding ? '; charset=' + this.options.encoding : '');
1267  
1268        /* Force "Connection: close" for older Mozilla browsers to work
1269         * around a bug where XMLHttpRequest sends an incorrect
1270         * Content-length header. See Mozilla Bugzilla #246651.
1271         */
1272        if (this.transport.overrideMimeType &&
1273            (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1274              headers['Connection'] = 'close';
1275      }
1276  
1277      // user-defined headers
1278      if (typeof this.options.requestHeaders == 'object') {
1279        var extras = this.options.requestHeaders;
1280  
1281        if (Object.isFunction(extras.push))
1282          for (var i = 0, length = extras.length; i < length; i += 2)
1283            headers[extras[i]] = extras[i+1];
1284        else
1285          $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1286      }
1287  
1288      for (var name in headers)
1289        this.transport.setRequestHeader(name, headers[name]);
1290    },
1291  
1292    success: function() {
1293      var status = this.getStatus();
1294      return !status || (status >= 200 && status < 300);
1295    },
1296  
1297    getStatus: function() {
1298      try {
1299        return this.transport.status || 0;
1300      } catch (e) { return 0 }
1301    },
1302  
1303    respondToReadyState: function(readyState) {
1304      var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1305  
1306      if (state == 'Complete') {
1307        try {
1308          this._complete = true;
1309          (this.options['on' + response.status]
1310           || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1311           || Prototype.emptyFunction)(response, response.headerJSON);
1312        } catch (e) {
1313          this.dispatchException(e);
1314        }
1315  
1316        var contentType = response.getHeader('Content-type');
1317        if (this.options.evalJS == 'force'
1318            || (this.options.evalJS && contentType
1319            && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1320          this.evalResponse();
1321      }
1322  
1323      try {
1324        (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1325        Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1326      } catch (e) {
1327        this.dispatchException(e);
1328      }
1329  
1330      if (state == 'Complete') {
1331        // avoid memory leak in MSIE: clean up
1332        this.transport.onreadystatechange = Prototype.emptyFunction;
1333      }
1334    },
1335  
1336    getHeader: function(name) {
1337      try {
1338        return this.transport.getResponseHeader(name);
1339      } catch (e) { return null }
1340    },
1341  
1342    evalResponse: function() {
1343      try {
1344        return eval((this.transport.responseText || '').unfilterJSON());
1345      } catch (e) {
1346        this.dispatchException(e);
1347      }
1348    },
1349  
1350    dispatchException: function(exception) {
1351      (this.options.onException || Prototype.emptyFunction)(this, exception);
1352      Ajax.Responders.dispatch('onException', this, exception);
1353    }
1354  });
1355  
1356  Ajax.Request.Events =
1357    ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1358  
1359  Ajax.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  
1430  Ajax.Updater = Class.create(Ajax.Request, {
1431    initialize: function($super, container, url, options) {
1432      this.container = {
1433        success: (container.success || container),
1434        failure: (container.failure || (container.success ? null : container))
1435      };
1436  
1437      options = options || { };
1438      var onComplete = options.onComplete;
1439      options.onComplete = (function(response, param) {
1440        this.updateContent(response.responseText);
1441        if (Object.isFunction(onComplete)) onComplete(response, param);
1442      }).bind(this);
1443  
1444      $super(url, options);
1445    },
1446  
1447    updateContent: function(responseText) {
1448      var receiver = this.container[this.success() ? 'success' : 'failure'],
1449          options = this.options;
1450  
1451      if (!options.evalScripts) responseText = responseText.stripScripts();
1452  
1453      if (receiver = $(receiver)) {
1454        if (options.insertion) {
1455          if (Object.isString(options.insertion)) {
1456            var insertion = { }; insertion[options.insertion] = responseText;
1457            receiver.insert(insertion);
1458          }
1459          else options.insertion(receiver, responseText);
1460        }
1461        else receiver.update(responseText);
1462      }
1463  
1464      if (this.success()) {
1465        if (this.onComplete) this.onComplete.bind(this).defer();
1466      }
1467    }
1468  });
1469  
1470  Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1471    initialize: function($super, container, url, options) {
1472      $super(options);
1473      this.onComplete = this.options.onComplete;
1474  
1475      this.frequency = (this.options.frequency || 2);
1476      this.decay = (this.options.decay || 1);
1477  
1478      this.updater = { };
1479      this.container = container;
1480      this.url = url;
1481  
1482      this.start();
1483    },
1484  
1485    start: function() {
1486      this.options.onComplete = this.updateComplete.bind(this);
1487      this.onTimerEvent();
1488    },
1489  
1490    stop: function() {
1491      this.updater.options.onComplete = undefined;
1492      clearTimeout(this.timer);
1493      (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1494    },
1495  
1496    updateComplete: function(response) {
1497      if (this.options.decay) {
1498        this.decay = (response.responseText == this.lastText ?
1499          this.decay * this.options.decay : 1);
1500  
1501        this.lastText = response.responseText;
1502      }
1503      this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1504    },
1505  
1506    onTimerEvent: function() {
1507      this.updater = new Ajax.Updater(this.container, this.url, this.options);
1508    }
1509  });
1510  function $(element) {
1511    if (arguments.length > 1) {
1512      for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1513        elements.push($(arguments[i]));
1514      return elements;
1515    }
1516    if (Object.isString(element))
1517      element = document.getElementById(element);
1518    return Element.extend(element);
1519  }
1520  
1521  if (Prototype.BrowserFeatures.XPath) {
1522    document._getElementsByXPath = function(expression, parentElement) {
1523      var results = [];
1524      var query = document.evaluate(expression, $(parentElement) || document,
1525        null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1526      for (var i = 0, length = query.snapshotLength; i < length; i++)
1527        results.push(Element.extend(query.snapshotItem(i)));
1528      return results;
1529    };
1530  }
1531  
1532  /*--------------------------------------------------------------------------*/
1533  
1534  if (!window.Node) var Node = { };
1535  
1536  if (!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  
1571  Element.cache = { };
1572  
1573  Element.Methods = {
1574    visible: function(element) {
1575      return $(element).style.display != 'none';
1576    },
1577  
1578    toggle: function(element) {
1579      element = $(element);
1580      Element[Element.visible(element) ? 'hide' : 'show'](element);
1581      return element;
1582    },
1583  
1584    hide: function(element) {
1585      $(element).style.display = 'none';
1586      return element;
1587    },
1588  
1589    show: function(element) {
1590      $(element).style.display = '';
1591      return element;
1592    },
1593  
1594    remove: function(element) {
1595      element = $(element);
1596      element.parentNode.removeChild(element);
1597      return element;
1598    },
1599  
1600    update: function(element, content) {
1601      element = $(element);
1602      if (content && content.toElement) content = content.toElement();
1603      if (Object.isElement(content)) return element.update().insert(content);
1604      content = Object.toHTML(content);
1605      element.innerHTML = content.stripScripts();
1606      content.evalScripts.bind(content).defer();
1607      return element;
1608    },
1609  
1610    replace: function(element, content) {
1611      element = $(element);
1612      if (content && content.toElement) content = content.toElement();
1613      else if (!Object.isElement(content)) {
1614        content = Object.toHTML(content);
1615        var range = element.ownerDocument.createRange();
1616        range.selectNode(element);
1617        content.evalScripts.bind(content).defer();
1618        content = range.createContextualFragment(content.stripScripts());
1619      }
1620      element.parentNode.replaceChild(content, element);
1621      return element;
1622    },
1623  
1624    insert: function(element, insertions) {
1625      element = $(element);
1626  
1627      if (Object.isString(insertions) || Object.isNumber(insertions) ||
1628          Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1629            insertions = {bottom:insertions};
1630  
1631      var content, t, range;
1632  
1633      for (position in insertions) {
1634        content  = insertions[position];
1635        position = position.toLowerCase();
1636        t = Element._insertionTranslations[position];
1637  
1638        if (content && content.toElement) content = content.toElement();
1639        if (Object.isElement(content)) {
1640          t.insert(element, content);
1641          continue;
1642        }
1643  
1644        content = Object.toHTML(content);
1645  
1646        range = element.ownerDocument.createRange();
1647        t.initializeRange(element, range);
1648        t.insert(element, range.createContextualFragment(content.stripScripts()));
1649  
1650        content.evalScripts.bind(content).defer();
1651      }
1652  
1653      return element;
1654    },
1655  
1656    wrap: function(element, wrapper, attributes) {
1657      element = $(element);
1658      if (Object.isElement(wrapper))
1659        $(wrapper).writeAttribute(attributes || { });
1660      else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1661      else wrapper = new Element('div', wrapper);
1662      if (element.parentNode)
1663        element.parentNode.replaceChild(wrapper, element);
1664      wrapper.appendChild(element);
1665      return wrapper;
1666    },
1667  
1668    inspect: function(element) {
1669      element = $(element);
1670      var result = '<' + element.tagName.toLowerCase();
1671      $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1672        var property = pair.first(), attribute = pair.last();
1673        var value = (element[property] || '').toString();
1674        if (value) result += ' ' + attribute + '=' + value.inspect(true);
1675      });
1676      return result + '>';
1677    },
1678  
1679    recursivelyCollect: function(element, property) {
1680      element = $(element);
1681      var elements = [];
1682      while (element = element[property])
1683        if (element.nodeType == 1)
1684          elements.push(Element.extend(element));
1685      return elements;
1686    },
1687  
1688    ancestors: function(element) {
1689      return $(element).recursivelyCollect('parentNode');
1690    },
1691  
1692    descendants: function(element) {
1693      return $A($(element).getElementsByTagName('*')).each(Element.extend);
1694    },
1695  
1696    firstDescendant: function(element) {
1697      element = $(element).firstChild;
1698      while (element && element.nodeType != 1) element = element.nextSibling;
1699      return $(element);
1700    },
1701  
1702    immediateDescendants: function(element) {
1703      if (!(element = $(element).firstChild)) return [];
1704      while (element && element.nodeType != 1) element = element.nextSibling;
1705      if (element) return [element].concat($(element).nextSiblings());
1706      return [];
1707    },
1708  
1709    previousSiblings: function(element) {
1710      return $(element).recursivelyCollect('previousSibling');
1711    },
1712  
1713    nextSiblings: function(element) {
1714      return $(element).recursivelyCollect('nextSibling');
1715    },
1716  
1717    siblings: function(element) {
1718      element = $(element);
1719      return element.previousSiblings().reverse().concat(element.nextSiblings());
1720    },
1721  
1722    match: function(element, selector) {
1723      if (Object.isString(selector))
1724        selector = new Selector(selector);
1725      return selector.match($(element));
1726    },
1727  
1728    up: function(element, expression, index) {
1729      element = $(element);
1730      if (arguments.length == 1) return $(element.parentNode);
1731      var ancestors = element.ancestors();
1732      return expression ? Selector.findElement(ancestors, expression, index) :
1733        ancestors[index || 0];
1734    },
1735  
1736    down: function(element, expression, index) {
1737      element = $(element);
1738      if (arguments.length == 1) return element.firstDescendant();
1739      var descendants = element.descendants();
1740      return expression ? Selector.findElement(descendants, expression, index) :
1741        descendants[index || 0];
1742    },
1743  
1744    previous: function(element, expression, index) {
1745      element = $(element);
1746      if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1747      var previousSiblings = element.previousSiblings();
1748      return expression ? Selector.findElement(previousSiblings, expression, index) :
1749        previousSiblings[index || 0];
1750    },
1751  
1752    next: function(element, expression, index) {
1753      element = $(element);
1754      if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1755      var nextSiblings = element.nextSiblings();
1756      return expression ? Selector.findElement(nextSiblings, expression, index) :
1757        nextSiblings[index || 0];
1758    },
1759  
1760    select: function() {
1761      var args = $A(arguments), element = $(args.shift());
1762      return Selector.findChildElements(element, args);
1763    },
1764  
1765    adjacent: function() {
1766      var args = $A(arguments), element = $(args.shift());
1767      return Selector.findChildElements(element.parentNode, args).without(element);
1768    },
1769  
1770    identify: function(element) {
1771      element = $(element);
1772      var id = element.readAttribute('id'), self = arguments.callee;
1773      if (id) return id;
1774      do { id = 'anonymous_element_' + self.counter++ } while ($(id));
1775      element.writeAttribute('id', id);
1776      return id;
1777    },
1778  
1779    readAttribute: function(element, name) {
1780      element = $(element);
1781      if (Prototype.Browser.IE) {
1782        var t = Element._attributeTranslations.read;
1783        if (t.values[name]) return t.values[name](element, name);
1784        if (t.names[name]) name = t.names[name];
1785        if (name.include(':')) {
1786          return (!element.attributes || !element.attributes[name]) ? null :
1787           element.attributes[name].value;
1788        }
1789      }
1790      return element.getAttribute(name);
1791    },
1792  
1793    writeAttribute: function(element, name, value) {
1794      element = $(element);
1795      var attributes = { }, t = Element._attributeTranslations.write;
1796  
1797      if (typeof name == 'object') attributes = name;
1798      else attributes[name] = value === undefined ? true : value;
1799  
1800      for (var attr in attributes) {
1801        var name = t.names[attr] || attr, value = attributes[attr];
1802        if (t.values[attr]) name = t.values[attr](element, value);
1803        if (value === false || value === null)
1804          element.removeAttribute(name);
1805        else if (value === true)
1806          element.setAttribute(name, name);
1807        else element.setAttribute(name, value);
1808      }
1809      return element;
1810    },
1811  
1812    getHeight: function(element) {
1813      return $(element).getDimensions().height;
1814    },
1815  
1816    getWidth: function(element) {
1817      return $(element).getDimensions().width;
1818    },
1819  
1820    classNames: function(element) {
1821      return new Element.ClassNames(element);
1822    },
1823  
1824    hasClassName: function(element, className) {
1825      if (!(element = $(element))) return;
1826      var elementClassName = element.className;
1827      return (elementClassName.length > 0 && (elementClassName == className ||
1828        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
1829    },
1830  
1831    addClassName: function(element, className) {
1832      if (!(element = $(element))) return;
1833      if (!element.hasClassName(className))
1834        element.className += (element.className ? ' ' : '') + className;
1835      return element;
1836    },
1837  
1838    removeClassName: function(element, className) {
1839      if (!(element = $(element))) return;
1840      element.className = element.className.replace(
1841        new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
1842      return element;
1843    },
1844  
1845    toggleClassName: function(element, className) {
1846      if (!(element = $(element))) return;
1847      return element[element.hasClassName(className) ?
1848        'removeClassName' : 'addClassName'](className);
1849    },
1850  
1851    // removes whitespace-only text node children
1852    cleanWhitespace: function(element) {
1853      element = $(element);
1854      var node = element.firstChild;
1855      while (node) {
1856        var nextNode = node.nextSibling;
1857        if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1858          element.removeChild(node);
1859        node = nextNode;
1860      }
1861      return element;
1862    },
1863  
1864    empty: function(element) {
1865      return $(element).innerHTML.blank();
1866    },
1867  
1868    descendantOf: function(element, ancestor) {
1869      element = $(element), ancestor = $(ancestor);
1870  
1871      if (element.compareDocumentPosition)
1872        return (element.compareDocumentPosition(ancestor) & 8) === 8;
1873  
1874      if (element.sourceIndex && !Prototype.Browser.Opera) {
1875        var e = element.sourceIndex, a = ancestor.sourceIndex,
1876         nextAncestor = ancestor.nextSibling;
1877        if (!nextAncestor) {
1878          do { ancestor = ancestor.parentNode; }
1879          while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
1880        }
1881        if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
1882      }
1883  
1884      while (element = element.parentNode)
1885        if (element == ancestor) return true;
1886      return false;
1887    },
1888  
1889    scrollTo: function(element) {
1890      element = $(element);
1891      var pos = element.cumulativeOffset();
1892      window.scrollTo(pos[0], pos[1]);
1893      return element;
1894    },
1895  
1896    getStyle: function(element, style) {
1897      element = $(element);
1898      style = style == 'float' ? 'cssFloat' : style.camelize();
1899      var value = element.style[style];
1900      if (!value) {
1901        var css = document.defaultView.getComputedStyle(element, null);
1902        value = css ? css[style] : null;
1903      }
1904      if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1905      return value == 'auto' ? null : value;
1906    },
1907  
1908    getOpacity: function(element) {
1909      return $(element).getStyle('opacity');
1910    },
1911  
1912    setStyle: function(element, styles) {
1913      element = $(element);
1914      var elementStyle = element.style, match;
1915      if (Object.isString(styles)) {
1916        element.style.cssText += ';' + styles;
1917        return styles.include('opacity') ?
1918          element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
1919      }
1920      for (var property in styles)
1921        if (property == 'opacity') element.setOpacity(styles[property]);
1922        else
1923          elementStyle[(property == 'float' || property == 'cssFloat') ?
1924            (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
1925              property] = styles[property];
1926  
1927      return element;
1928    },
1929  
1930    setOpacity: function(element, value) {
1931      element = $(element);
1932      element.style.opacity = (value == 1 || value === '') ? '' :
1933        (value < 0.00001) ? 0 : value;
1934      return element;
1935    },
1936  
1937    getDimensions: function(element) {
1938      element = $(element);
1939      var display = $(element).getStyle('display');
1940      if (display != 'none' && display != null) // Safari bug
1941        return {width: element.offsetWidth, height: element.offsetHeight};
1942  
1943      // All *Width and *Height properties give 0 on elements with display none,
1944      // so enable the element temporarily
1945      var els = element.style;
1946      var originalVisibility = els.visibility;
1947      var originalPosition = els.position;
1948      var originalDisplay = els.display;
1949      els.visibility = 'hidden';
1950      els.position = 'absolute';
1951      els.display = 'block';
1952      var originalWidth = element.clientWidth;
1953      var originalHeight = element.clientHeight;
1954      els.display = originalDisplay;
1955      els.position = originalPosition;
1956      els.visibility = originalVisibility;
1957      return {width: originalWidth, height: originalHeight};
1958    },
1959  
1960    makePositioned: function(element) {
1961      element = $(element);
1962      var pos = Element.getStyle(element, 'position');
1963      if (pos == 'static' || !pos) {
1964        element._madePositioned = true;
1965        element.style.position = 'relative';
1966        // Opera returns the offset relative to the positioning context, when an
1967        // element is position relative but top and left have not been defined
1968        if (window.opera) {
1969          element.style.top = 0;
1970          element.style.left = 0;
1971        }
1972      }
1973      return element;
1974    },
1975  
1976    undoPositioned: function(element) {
1977      element = $(element);
1978      if (element._madePositioned) {
1979        element._madePositioned = undefined;
1980        element.style.position =
1981          element.style.top =
1982          element.style.left =
1983          element.style.bottom =
1984          element.style.right = '';
1985      }
1986      return element;
1987    },
1988  
1989    makeClipping: function(element) {
1990      element = $(element);
1991      if (element._overflow) return element;
1992      element._overflow = Element.getStyle(element, 'overflow') || 'auto';
1993      if (element._overflow !== 'hidden')
1994        element.style.overflow = 'hidden';
1995      return element;
1996    },
1997  
1998    undoClipping: function(element) {
1999      element = $(element);
2000      if (!element._overflow) return element;
2001      element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2002      element._overflow = null;
2003      return element;
2004    },
2005  
2006    cumulativeOffset: function(element) {
2007      var valueT = 0, valueL = 0;
2008      do {
2009        valueT += element.offsetTop  || 0;
2010        valueL += element.offsetLeft || 0;
2011        element = element.offsetParent;
2012      } while (element);
2013      return Element._returnOffset(valueL, valueT);
2014    },
2015  
2016    positionedOffset: function(element) {
2017      var valueT = 0, valueL = 0;
2018      do {
2019        valueT += element.offsetTop  || 0;
2020        valueL += element.offsetLeft || 0;
2021        element = element.offsetParent;
2022        if (element) {
2023          if (element.tagName == 'BODY') break;
2024          var p = Element.getStyle(element, 'position');
2025          if (p == 'relative' || p == 'absolute') break;
2026        }
2027      } while (element);
2028      return Element._returnOffset(valueL, valueT);
2029    },
2030  
2031    absolutize: function(element) {
2032      element = $(element);
2033      if (element.getStyle('position') == 'absolute') return;
2034      // Position.prepare(); // To be done manually by Scripty when it needs it.
2035  
2036      var offsets = element.positionedOffset();
2037      var top     = offsets[1];
2038      var left    = offsets[0];
2039      var width   = element.clientWidth;
2040      var height  = element.clientHeight;
2041  
2042      element._originalLeft   = left - parseFloat(element.style.left  || 0);
2043      element._originalTop    = top  - parseFloat(element.style.top || 0);
2044      element._originalWidth  = element.style.width;
2045      element._originalHeight = element.style.height;
2046  
2047      element.style.position = 'absolute';
2048      element.style.top    = top + 'px';
2049      element.style.left   = left + 'px';
2050      element.style.width  = width + 'px';
2051      element.style.height = height + 'px';
2052      return element;
2053    },
2054  
2055    relativize: function(element) {
2056      element = $(element);
2057      if (element.getStyle('position') == 'relative') return;
2058      // Position.prepare(); // To be done manually by Scripty when it needs it.
2059  
2060      element.style.position = 'relative';
2061      var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
2062      var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2063  
2064      element.style.top    = top + 'px';
2065      element.style.left   = left + 'px';
2066      element.style.height = element._originalHeight;
2067      element.style.width  = element._originalWidth;
2068      return element;
2069    },
2070  
2071    cumulativeScrollOffset: function(element) {
2072      var valueT = 0, valueL = 0;
2073      do {
2074        valueT += element.scrollTop  || 0;
2075        valueL += element.scrollLeft || 0;
2076        element = element.parentNode;
2077      } while (element);
2078      return Element._returnOffset(valueL, valueT);
2079    },
2080  
2081    getOffsetParent: function(element) {
2082      if (element.offsetParent) return $(element.offsetParent);
2083      if (element == document.body) return $(element);
2084  
2085      while ((element = element.parentNode) && element != document.body)
2086        if (Element.getStyle(element, 'position') != 'static')
2087          return $(element);
2088  
2089      return $(document.body);
2090    },
2091  
2092    viewportOffset: function(forElement) {
2093      var valueT = 0, valueL = 0;
2094  
2095      var element = forElement;
2096      do {
2097        valueT += element.offsetTop  || 0;
2098        valueL += element.offsetLeft || 0;
2099  
2100        // Safari fix
2101        if (element.offsetParent == document.body &&
2102          Element.getStyle(element, 'position') == 'absolute') break;
2103  
2104      } while (element = element.offsetParent);
2105  
2106      element = forElement;
2107      do {
2108        if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
2109          valueT -= element.scrollTop  || 0;
2110          valueL -= element.scrollLeft || 0;
2111        }
2112      } while (element = element.parentNode);
2113  
2114      return Element._returnOffset(valueL, valueT);
2115    },
2116  
2117    clonePosition: function(element, source) {
2118      var options = Object.extend({
2119        setLeft:    true,
2120        setTop:     true,
2121        setWidth:   true,
2122        setHeight:  true,
2123        offsetTop:  0,
2124        offsetLeft: 0
2125      }, arguments[2] || { });
2126  
2127      // find page position of source
2128      source = $(source);
2129      var p = source.viewportOffset();
2130  
2131      // find coordinate system to use
2132      element = $(element);
2133      var delta = [0, 0];
2134      var parent = null;
2135      // delta [0,0] will do fine with position: fixed elements,
2136      // position:absolute needs offsetParent deltas
2137      if (Element.getStyle(element, 'position') == 'absolute') {
2138        parent = element.getOffsetParent();
2139        delta = parent.viewportOffset();
2140      }
2141  
2142      // correct by body offsets (fixes Safari)
2143      if (parent == document.body) {
2144        delta[0] -= document.body.offsetLeft;
2145        delta[1] -= document.body.offsetTop;
2146      }
2147  
2148      // set position
2149      if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2150      if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2151      if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
2152      if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2153      return element;
2154    }
2155  };
2156  
2157  Element.Methods.identify.counter = 1;
2158  
2159  Object.extend(Element.Methods, {
2160    getElementsBySelector: Element.Methods.select,
2161    childElements: Element.Methods.immediateDescendants
2162  });
2163  
2164  Element._attributeTranslations = {
2165    write: {
2166      names: {
2167        className: 'class',
2168        htmlFor:   'for'
2169      },
2170      values: { }
2171    }
2172  };
2173  
2174  
2175  if (!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  }
2213  
2214  if (Prototype.Browser.Opera) {
2215    Element.Methods._getStyle = Element.Methods.getStyle;
2216    Element.Methods.getStyle = function(element, style) {
2217      switch(style) {
2218        case 'left':
2219        case 'top':
2220        case 'right':
2221        case 'bottom':
2222          if (Element._getStyle(element, 'position') == 'static') return null;
2223        default: return Element._getStyle(element, style);
2224      }
2225    };
2226    Element.Methods._readAttribute = Element.Methods.readAttribute;
2227    Element.Methods.readAttribute = function(element, attribute) {
2228      if (attribute == 'title') return element.title;
2229      return Element._readAttribute(element, attribute);
2230    };
2231  }
2232  
2233  else if (Prototype.Browser.IE) {
2234    $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
2235      Element.Methods[method] = Element.Methods[method].wrap(
2236        function(proceed, element) {
2237          element = $(element);
2238          var position = element.getStyle('position');
2239          if (position != 'static') return proceed(element);
2240          element.setStyle({ position: 'relative' });
2241          var value = proceed(element);
2242          element.setStyle({ position: position });
2243          return value;
2244        }
2245      );
2246    });
2247  
2248    Element.Methods.getStyle = function(element, style) {
2249      element = $(element);
2250      style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2251      var value = element.style[style];
2252      if (!value && element.currentStyle) value = element.currentStyle[style];
2253  
2254      if (style == 'opacity') {
2255        if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2256          if (value[1]) return parseFloat(value[1]) / 100;
2257        return 1.0;
2258      }
2259  
2260      if (value == 'auto') {
2261        if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2262          return element['offset' + style.capitalize()] + 'px';
2263        return null;
2264      }
2265      return value;
2266    };
2267  
2268    Element.Methods.setOpacity = function(element, value) {
2269      function stripAlpha(filter){
2270        return filter.replace(/alpha\([^\)]*\)/gi,'');
2271      }
2272      element = $(element);
2273      var currentStyle = element.currentStyle;
2274      if ((currentStyle && !currentStyle.hasLayout) ||
2275        (!currentStyle && element.style.zoom == 'normal'))
2276          element.style.zoom = 1;
2277  
2278      var filter = element.getStyle('filter'), style = element.style;
2279      if (value == 1 || value === '') {
2280        (filter = stripAlpha(filter)) ?
2281          style.filter = filter : style.removeAttribute('filter');
2282        return element;
2283      } else if (value < 0.00001) value = 0;
2284      style.filter = stripAlpha(filter) +
2285        'alpha(opacity=' + (value * 100) + ')';
2286      return element;
2287    };
2288  
2289    Element._attributeTranslations = {
2290      read: {
2291        names: {
2292          'class': 'className',
2293          'for':   'htmlFor'
2294        },
2295        values: {
2296          _getAttr: function(element, attribute) {
2297            return element.getAttribute(attribute, 2);
2298          },
2299          _getAttrNode: function(element, attribute) {
2300            var node = element.getAttributeNode(attribute);
2301            return node ? node.value : "";
2302          },
2303          _getEv: function(element, attribute) {
2304            var attribute = element.getAttribute(attribute);
2305            return attribute ? attribute.toString().slice(23, -2) : null;
2306          },
2307          _flag: function(element, attribute) {
2308            return $(element).hasAttribute(attribute) ? attribute : null;
2309          },
2310          style: function(element) {
2311            return element.style.cssText.toLowerCase();
2312          },
2313          title: function(element) {
2314            return element.title;
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);
2371  }
2372  
2373  else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2374    Element.Methods.setOpacity = function(element, value) {
2375      element = $(element);
2376      element.style.opacity = (value == 1) ? 0.999999 :
2377        (value === '') ? '' : (value < 0.00001) ? 0 : value;
2378      return element;
2379    };
2380  }
2381  
2382  else 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  
2418  if (Prototype.Browser.IE || Prototype.Browser.Opera) {
2419    // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
2420    Element.Methods.update = function(element, content) {
2421      element = $(element);
2422  
2423      if (content && content.toElement) content = content.toElement();
2424      if (Object.isElement(content)) return element.update().insert(content);
2425  
2426      content = Object.toHTML(content);
2427      var tagName = element.tagName.toUpperCase();
2428  
2429      if (tagName in Element._insertionTranslations.tags) {
2430        $A(element.childNodes).each(function(node) { element.removeChild(node) });
2431        Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2432          .each(function(node) { element.appendChild(node) });
2433      }
2434      else element.innerHTML = content.stripScripts();
2435  
2436      content.evalScripts.bind(content).defer();
2437      return element;
2438    };
2439  }
2440  
2441  if (document.createElement('div').outerHTML) {
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  
2470  Element._returnOffset = function(l, t) {
2471    var result = [l, t];
2472    result.left = l;
2473    result.top = t;
2474    return result;
2475  };
2476  
2477  Element._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  
2484  Element._insertionTranslations = {
2485    before: {
2486      adjacency: 'beforeBegin',
2487      insert: function(element, node) {
2488        element.parentNode.insertBefore(node, element);
2489      },
2490      initializeRange: function(element, range) {
2491        range.setStartBefore(element);
2492      }
2493    },
2494    top: {
2495      adjacency: 'afterBegin',
2496      insert: function(element, node) {
2497        element.insertBefore(node, element.firstChild);
2498      },
2499      initializeRange: function(element, range) {
2500        range.selectNodeContents(element);
2501        range.collapse(true);
2502      }
2503    },
2504    bottom: {
2505      adjacency: 'beforeEnd',
2506      insert: function(element, node) {
2507        element.appendChild(node);
2508      }
2509    },
2510    after: {
2511      adjacency: 'afterEnd',
2512      insert: function(element, node) {
2513        element.parentNode.insertBefore(node, element.nextSibling);
2514      },
2515      initializeRange: function(element, range) {
2516        range.setStartAfter(element);
2517      }
2518    },
2519    tags: {
2520      TABLE:  ['<table>',                '</table>',                   1],
2521      TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
2522      TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
2523      TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2524      SELECT: ['<select>',               '</select>',                  1]
2525    }
2526  };
2527  
2528  (function() {
2529    this.bottom.initializeRange = this.top.initializeRange;
2530    Object.extend(this.tags, {
2531      THEAD: this.tags.TBODY,
2532      TFOOT: this.tags.TBODY,
2533      TH:    this.tags.TD
2534    });
2535  }).call(Element._insertionTranslations);
2536  
2537  Element.Methods.Simulated = {
2538    hasAttribute: function(element, attribute) {
2539      attribute = Element._attributeTranslations.has[attribute] || attribute;
2540      var node = $(element).getAttributeNode(attribute);
2541      return node && node.specified;
2542    }
2543  };
2544  
2545  Element.Methods.ByTag = { };
2546  
2547  Object.extend(Element, Element.Methods);
2548  
2549  if (!Prototype.BrowserFeatures.ElementExtensions &&
2550      document.createElement('div').__proto__) {
2551    window.HTMLElement = { };
2552    window.HTMLElement.prototype = document.createElement('div').__proto__;
2553    Prototype.BrowserFeatures.ElementExtensions = true;
2554  }
2555  
2556  Element.extend = (function() {
2557    if (Prototype.BrowserFeatures.SpecificElementExtensions)
2558      return Prototype.K;
2559  
2560    var Methods = { }, ByTag = Element.Methods.ByTag;
2561  
2562    var extend = Object.extend(function(element) {
2563      if (!element || element._extendedByPrototype ||
2564          element.nodeType != 1 || element == window) return element;
2565  
2566      var methods = Object.clone(Methods),
2567        tagName = element.tagName, property, value;
2568  
2569      // extend methods for specific tags
2570      if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2571  
2572      for (property in methods) {
2573        value = methods[property];
2574        if (Object.isFunction(value) && !(property in element))
2575          element[property] = value.methodize();
2576      }
2577  
2578      element._extendedByPrototype = Prototype.emptyFunction;
2579      return element;
2580  
2581    }, {
2582      refresh: function() {
2583        // extend methods for all tags (Safari doesn't need this)
2584        if (!Prototype.BrowserFeatures.ElementExtensions) {
2585          Object.extend(Methods, Element.Methods);
2586          Object.extend(Methods, Element.Methods.Simulated);
2587        }
2588      }
2589    });
2590  
2591    extend.refresh();
2592    return extend;
2593  })();
2594  
2595  Element.hasAttribute = function(element, attribute) {
2596    if (element.hasAttribute) return element.hasAttribute(attribute);
2597    return Element.Methods.Simulated.hasAttribute(element, attribute);
2598  };
2599  
2600  Element.addMethods = function(methods) {
2601    var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2602  
2603    if (!methods) {
2604      Object.extend(Form, Form.Methods);
2605      Object.extend(Form.Element, Form.Element.Methods);
2606      Object.extend(Element.Methods.ByTag, {
2607        "FORM":     Object.clone(Form.Methods),
2608        "INPUT":    Object.clone(Form.Element.Methods),
2609        "SELECT":   Object.clone(Form.Element.Methods),
2610        "TEXTAREA": Object.clone(Form.Element.Methods)
2611      });
2612    }
2613  
2614    if (arguments.length == 2) {
2615      var tagName = methods;
2616      methods = arguments[1];
2617    }
2618  
2619    if (!tagName) Object.extend(Element.Methods, methods || { });
2620    else {
2621      if (Object.isArray(tagName)) tagName.each(extend);
2622      else extend(tagName);
2623    }
2624  
2625    function extend(tagName) {
2626      tagName = tagName.toUpperCase();
2627      if (!Element.Methods.ByTag[tagName])
2628        Element.Methods.ByTag[tagName] = { };
2629      Object.extend(Element.Methods.ByTag[tagName], methods);
2630    }
2631  
2632    function copy(methods, destination, onlyIfAbsent) {
2633      onlyIfAbsent = onlyIfAbsent || false;
2634      for (var property in methods) {
2635        var value = methods[property];
2636        if (!Object.isFunction(value)) continue;
2637        if (!onlyIfAbsent || !(property in destination))
2638          destination[property] = value.methodize();
2639      }
2640    }
2641  
2642    function findDOMClass(tagName) {
2643      var klass;
2644      var trans = {
2645        "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
2646        "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
2647        "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
2648        "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
2649        "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
2650        "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
2651        "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
2652        "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
2653        "FrameSet", "IFRAME": "IFrame"
2654      };
2655      if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
2656      if (window[klass]) return window[klass];
2657      klass = 'HTML' + tagName + 'Element';
2658      if (window[klass]) return window[klass];
2659      klass = 'HTML' + tagName.capitalize() + 'Element';
2660      if (window[klass]) return window[klass];
2661  
2662      window[klass] = { };
2663      window[klass].prototype = document.createElement(tagName).__proto__;
2664      return window[klass];
2665    }
2666  
2667    if (F.ElementExtensions) {
2668      copy(Element.Methods, HTMLElement.prototype);
2669      copy(Element.Methods.Simulated, HTMLElement.prototype, true);
2670    }
2671  
2672    if (F.SpecificElementExtensions) {
2673      for (var tag in Element.Methods.ByTag) {
2674        var klass = findDOMClass(tag);
2675        if (Object.isUndefined(klass)) continue;
2676        copy(T[tag], klass.prototype);
2677      }
2678    }
2679  
2680    Object.extend(Element, Element.Methods);
2681    delete Element.ByTag;
2682  
2683    if (Element.extend.refresh) Element.extend.refresh();
2684    Element.cache = { };
2685  };
2686  
2687  document.viewport = {
2688    getDimensions: function() {
2689      var dimensions = { };
2690      $w('width height').each(function(d) {
2691        var D = d.capitalize();
2692        dimensions[d] = self['inner' + D] ||
2693         (document.documentElement['client' + D] || document.body['client' + D]);
2694      });
2695      return dimensions;
2696    },
2697  
2698    getWidth: function() {
2699      return this.getDimensions().width;
2700    },
2701  
2702    getHeight: function() {
2703      return this.getDimensions().height;
2704    },
2705  
2706    getScrollOffsets: function() {
2707      return Element._returnOffset(
2708        window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
2709        window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
2710    }
2711  };
2712  /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2713   * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2714   * license.  Please see http://www.yui-ext.com/ for more information. */
2715  
2716  var Selector = Class.create({
2717    initialize: function(expression) {
2718      this.expression = expression.strip();
2719      this.compileMatcher();
2720    },
2721  
2722    compileMatcher: function() {
2723      // Selectors with namespaced attributes can't use the XPath version
2724      if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
2725        return this.compileXPathMatcher();
2726  
2727      var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2728          c = Selector.criteria, le, p, m;
2729  
2730      if (Selector._cache[e]) {
2731        this.matcher = Selector._cache[e];
2732        return;
2733      }
2734  
2735      this.matcher = ["this.matcher = function(root) {",
2736                      "var r = root, h = Selector.handlers, c = false, n;"];
2737  
2738      while (e && le != e && (/\S/).test(e)) {
2739        le = e;
2740        for (var i in ps) {
2741          p = ps[i];
2742          if (m = e.match(p)) {
2743            this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
2744                new Template(c[i]).evaluate(m));
2745            e = e.replace(m[0], '');
2746            break;
2747          }
2748        }
2749      }
2750  
2751      this.matcher.push("return h.unique(n);\n}");
2752      eval(this.matcher.join('\n'));
2753      Selector._cache[this.expression] = this.matcher;
2754    },
2755  
2756    compileXPathMatcher: function() {
2757      var e = this.expression, ps = Selector.patterns,
2758          x = Selector.xpath, le, m;
2759  
2760      if (Selector._cache[e]) {
2761        this.xpath = Selector._cache[e]; return;
2762      }
2763  
2764      this.matcher = ['.//*'];
2765      while (e && le != e && (/\S/).test(e)) {
2766        le = e;
2767        for (var i in ps) {
2768          if (m = e.match(ps[i])) {
2769            this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
2770              new Template(x[i]).evaluate(m));
2771            e = e.replace(m[0], '');
2772            break;
2773          }
2774        }
2775      }
2776  
2777      this.xpath = this.matcher.join('');
2778      Selector._cache[this.expression] = this.xpath;
2779    },
2780  
2781    findElements: function(root) {
2782      root = root || document;
2783      if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2784      return this.matcher(root);
2785    },
2786  
2787    match: function(element) {
2788      this.tokens = [];
2789  
2790      var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
2791      var le, p, m;
2792  
2793      while (e && le !== e && (/\S/).test(e)) {
2794        le = e;
2795        for (var i in ps) {
2796          p = ps[i];
2797          if (m = e.match(p)) {
2798            // use the Selector.assertions methods unless the selector
2799            // is too complex.
2800            if (as[i]) {
2801              this.tokens.push([i, Object.clone(m)]);
2802              e = e.replace(m[0], '');
2803            } else {
2804              // reluctantly do a document-wide search
2805              // and look for a match in the array
2806              return this.findElements(document).include(element);
2807            }
2808          }
2809        }
2810      }
2811  
2812      var match = true, name, matches;
2813      for (var i = 0, token; token = this.tokens[i]; i++) {
2814        name = token[0], matches = token[1];
2815        if (!Selector.assertions[name](element, matches)) {
2816          match = false; break;
2817        }
2818      }
2819  
2820      return match;
2821    },
2822  
2823    toString: function() {
2824      return this.expression;
2825    },
2826  
2827    inspect: function() {
2828      return "#<Selector:" + this.expression.inspect() + ">";
2829    }
2830  });
2831  
2832  Object.extend(Selector, {
2833    _cache: { },
2834  
2835    xpath: {
2836      descendant:   "//*",
2837      child:        "/*",
2838      adjacent:     "/following-sibling::*[1]",
2839      laterSibling: '/following-sibling::*',
2840      tagName:      function(m) {
2841        if (m[1] == '*') return '';
2842        return "[local-name()='" + m[1].toLowerCase() +
2843               "' or local-name()='" + m[1].toUpperCase() + "']";
2844      },
2845      className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2846      id:           "[@id='#{1}']",
2847      attrPresence: "[@#{1}]",
2848      attr: function(m) {
2849        m[3] = m[5] || m[6];
2850        return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2851      },
2852      pseudo: function(m) {
2853        var h = Selector.xpath.pseudos[m[1]];
2854        if (!h) return '';
2855        if (Object.isFunction(h)) return h(m);
2856        return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2857      },
2858      operators: {
2859        '=':  "[@#{1}='#{3}']",
2860        '!=': "[@#{1}!='#{3}']",
2861        '^=': "[starts-with(@#{1}, '#{3}')]",
2862        '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2863        '*=': "[contains(@#{1}, '#{3}')]",
2864        '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2865        '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2866      },
2867      pseudos: {
2868        'first-child': '[not(preceding-sibling::*)]',
2869        'last-child':  '[not(following-sibling::*)]',
2870        'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
2871        'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2872        'checked':     "[@checked]",
2873        'disabled':    "[@disabled]",
2874        'enabled':     "[not(@disabled)]",
2875        'not': function(m) {
2876          var e = m[6], p = Selector.patterns,
2877              x = Selector.xpath, le, m, v;
2878  
2879          var exclusion = [];
2880          while (e && le != e && (/\S/).test(e)) {
2881            le = e;
2882            for (var i in p) {
2883              if (m = e.match(p[i])) {
2884                v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
2885                exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2886                e = e.replace(m[0], '');
2887                break;
2888              }
2889            }
2890          }
2891          return "[not(" + exclusion.join(" and ") + ")]";
2892        },
2893        'nth-child':      function(m) {
2894          return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2895        },
2896        'nth-last-child': function(m) {
2897          return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2898        },
2899        'nth-of-type':    function(m) {
2900          return Selector.xpath.pseudos.nth("position() ", m);
2901        },
2902        'nth-last-of-type': function(m) {
2903          return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2904        },
2905        'first-of-type':  function(m) {
2906          m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2907        },
2908        'last-of-type':   function(m) {
2909          m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2910        },
2911        'only-of-type':   function(m) {
2912          var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2913        },
2914        nth: function(fragment, m) {
2915          var mm, formula = m[6], predicate;
2916          if (formula == 'even') formula = '2n+0';
2917          if (formula == 'odd')  formula = '2n+1';
2918          if (mm = formula.match(/^(\d+)$/)) // digit only
2919            return '[' + fragment + "= " + mm[1] + ']';
2920          if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2921            if (mm[1] == "-") mm[1] = -1;
2922            var a = mm[1] ? Number(mm[1]) : 1;
2923            var b = mm[2] ? Number(mm[2]) : 0;
2924            predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2925            "((#{fragment} - #{b}) div #{a} >= 0)]";
2926            return new Template(predicate).evaluate({
2927              fragment: fragment, a: a, b: b });
2928          }
2929        }
2930      }
2931    },
2932  
2933    criteria: {
2934      tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
2935      className:    'n = h.className(n, r, "#{1}", c); c = false;',
2936      id:           'n = h.id(n, r, "#{1}", c);        c = false;',
2937      attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2938      attr: function(m) {
2939        m[3] = (m[5] || m[6]);
2940        return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2941      },
2942      pseudo: function(m) {
2943        if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2944        return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2945      },
2946      descendant:   'c = "descendant";',
2947      child:        'c = "child";',
2948      adjacent:     'c = "adjacent";',
2949      laterSibling: 'c = "laterSibling";'
2950    },
2951  
2952    patterns: {
2953      // combinators must be listed first
2954      // (and descendant needs to be last combinator)
2955      laterSibling: /^\s*~\s*/,
2956      child:        /^\s*>\s*/,
2957      adjacent:     /^\s*\+\s*/,
2958      descendant:   /^\s/,
2959  
2960      // selectors follow
2961      tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
2962      id:           /^#([\w\-\*]+)(\b|$)/,
2963      className:    /^\.([\w\-\*]+)(\b|$)/,
2964      pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
2965      attrPresence: /^\[([\w]+)\]/,
2966      attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
2967    },
2968  
2969    // for Selector.match and Element#match
2970    assertions: {
2971      tagName: function(element, matches) {
2972        return matches[1].toUpperCase() == element.tagName.toUpperCase();
2973      },
2974  
2975      className: function(element, matches) {
2976        return Element.hasClassName(element, matches[1]);
2977      },
2978  
2979      id: function(element, matches) {
2980        return element.id === matches[1];
2981      },
2982  
2983      attrPresence: function(element, matches) {
2984        return Element.hasAttribute(element, matches[1]);
2985      },
2986  
2987      attr: function(element, matches) {
2988        var nodeValue = Element.readAttribute(element, matches[1]);
2989        return Selector.operators[matches[2]](nodeValue, matches[3]);
2990      }
2991    },
2992  
2993    handlers: {
2994      // UTILITY FUNCTIONS
2995      // joins two collections
2996      concat: function(a, b) {
2997        for (var i = 0, node; node = b[i]; i++)
2998          a.push(node);
2999        return a;
3000      },
3001  
3002      // marks an array of nodes for counting
3003      mark: function(nodes) {
3004        for (var i = 0, node; node = nodes[i]; i++)
3005          node._counted = true;
3006        return nodes;
3007      },
3008  
3009      unmark: function(nodes) {
3010        for (var i = 0, node; node = nodes[i]; i++)
3011          node._counted = undefined;
3012        return nodes;
3013      },
3014  
3015      // mark each child node with its position (for nth calls)
3016      // "ofType" flag indicates whether we're indexing for nth-of-type
3017      // rather than nth-child
3018      index: function(parentNode, reverse, ofType) {
3019        parentNode._counted = true;
3020        if (reverse) {
3021          for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3022            var node = nodes[i];
3023            if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
3024          }
3025        } else {
3026          for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3027            if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
3028        }
3029      },
3030  
3031      // filters out duplicates and extends all nodes
3032      unique: function(nodes) {
3033        if (nodes.length == 0) return nodes;
3034        var results = [], n;
3035        for (var i = 0, l = nodes.length; i < l; i++)
3036          if (!(n = nodes[i])._counted) {
3037            n._counted = true;
3038            results.push(Element.extend(n));
3039          }
3040        return Selector.handlers.unmark(results);
3041      },
3042  
3043      // COMBINATOR FUNCTIONS
3044      descendant: function(nodes) {
3045        var h = Selector.handlers;
3046        for (var i = 0, results = [], node; node = nodes[i]; i++)
3047          h.concat(results, node.getElementsByTagName('*'));
3048        return results;
3049      },
3050  
3051      child: function(nodes) {
3052        var h = Selector.handlers;
3053        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3054          for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
3055            if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3056        }
3057        return results;
3058      },
3059  
3060      adjacent: function(nodes) {
3061        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3062          var next = this.nextElementSibling(node);
3063          if (next) results.push(next);
3064        }
3065        return results;
3066      },
3067  
3068      laterSibling: function(nodes) {
3069        var h = Selector.handlers;
3070        for (var i = 0, results = [], node; node = nodes[i]; i++)
3071          h.concat(results, Element.nextSiblings(node));
3072        return results;
3073      },
3074  
3075      nextElementSibling: function(node) {
3076        while (node = node.nextSibling)
3077            if (node.nodeType == 1) return node;
3078        return null;
3079      },
3080  
3081      previousElementSibling: function(node) {
3082        while (node = node.previousSibling)
3083          if (node.nodeType == 1) return node;
3084        return null;
3085      },
3086  
3087      // TOKEN FUNCTIONS
3088      tagName: function(nodes, root, tagName, combinator) {
3089        tagName = tagName.toUpperCase();
3090        var results = [], h = Selector.handlers;
3091        if (nodes) {
3092          if (combinator) {
3093            // fastlane for ordinary descendant combinators
3094            if (combinator == "descendant") {
3095              for (var i = 0, node; node = nodes[i]; i++)
3096                h.concat(results, node.getElementsByTagName(tagName));
3097              return results;
3098            } else nodes = this[combinator](nodes);
3099            if (tagName == "*") return nodes;
3100          }
3101          for (var i = 0, node; node = nodes[i]; i++)
3102            if (node.tagName.toUpperCase() == tagName) results.push(node);
3103          return results;
3104        } else return root.getElementsByTagName(tagName);
3105      },
3106  
3107      id: function(nodes, root, id, combinator) {
3108        var targetNode = $(id), h = Selector.handlers;
3109        if (!targetNode) return [];
3110        if (!nodes && root == document) return [targetNode];
3111        if (nodes) {
3112          if (combinator) {
3113            if (combinator == 'child') {
3114              for (var i = 0, node; node = nodes[i]; i++)
3115                if (targetNode.parentNode == node) return [targetNode];
3116            } else if (combinator == 'descendant') {
3117              for (var i = 0, node; node = nodes[i]; i++)
3118                if (Element.descendantOf(targetNode, node)) return [targetNode];
3119            } else if (combinator == 'adjacent') {
3120              for (var i = 0, node; node = nodes[i]; i++)
3121                if (Selector.handlers.previousElementSibling(targetNode) == node)
3122                  return [targetNode];
3123            } else nodes = h[combinator](nodes);
3124          }
3125          for (var i = 0, node; node = nodes[i]; i++)
3126            if (node == targetNode) return [targetNode];
3127          return [];
3128        }
3129        return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3130      },
3131  
3132      className: function(nodes, root, className, combinator) {
3133        if (nodes && combinator) nodes = this[combinator](nodes);
3134        return Selector.handlers.byClassName(nodes, root, className);
3135      },
3136  
3137      byClassName: function(nodes, root, className) {
3138        if (!nodes) nodes = Selector.handlers.descendant([root]);
3139        var needle = ' ' + className + ' ';
3140        for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
3141          nodeClassName = node.className;
3142          if (nodeClassName.length == 0) continue;
3143          if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
3144            results.push(node);
3145        }
3146        return results;
3147      },
3148  
3149      attrPresence: function(nodes, root, attr) {
3150        if (!nodes) nodes = root.getElementsByTagName("*");
3151        var results = [];
3152        for (var i = 0, node; node = nodes[i]; i++)
3153          if (Element.hasAttribute(node, attr)) results.push(node);
3154        return results;
3155      },
3156  
3157      attr: function(nodes, root, attr, value, operator) {
3158        if (!nodes) nodes = root.getElementsByTagName("*");
3159        var handler = Selector.operators[operator], results = [];
3160        for (var i = 0, node; node = nodes[i]; i++) {
3161          var nodeValue = Element.readAttribute(node, attr);
3162          if (nodeValue === null) continue;
3163          if (handler(nodeValue, value)) results.push(node);
3164        }
3165        return results;
3166      },
3167  
3168      pseudo: function(nodes, name, value, root, combinator) {
3169        if (nodes && combinator) nodes = this[combinator](nodes);
3170        if (!nodes) nodes = root.getElementsByTagName("*");
3171        return Selector.pseudos[name](nodes, value, root);
3172      }
3173    },
3174  
3175    pseudos: {
3176      'first-child': function(nodes, value, root) {
3177        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3178          if (Selector.handlers.previousElementSibling(node)) continue;
3179            results.push(node);
3180        }
3181        return results;
3182      },
3183      'last-child': function(nodes, value, root) {
3184        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3185          if (Selector.handlers.nextElementSibling(node)) continue;
3186            results.push(node);
3187        }
3188        return results;
3189      },
3190      'only-child': function(nodes, value, root) {
3191        var h = Selector.handlers;
3192        for (var i = 0, results = [], node; node = nodes[i]; i++)
3193          if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3194            results.push(node);
3195        return results;
3196      },
3197      'nth-child':        function(nodes, formula, root) {
3198        return Selector.pseudos.nth(nodes, formula, root);
3199      },
3200      'nth-last-child':   function(nodes, formula, root) {
3201        return Selector.pseudos.nth(nodes, formula, root, true);
3202      },
3203      'nth-of-type':      function(nodes, formula, root) {
3204        return Selector.pseudos.nth(nodes, formula, root, false, true);
3205      },
3206      'nth-last-of-type': function(nodes, formula, root) {
3207        return Selector.pseudos.nth(nodes, formula, root, true, true);
3208      },
3209      'first-of-type':    function(nodes, formula, root) {
3210        return Selector.pseudos.nth(nodes, "1", root, false, true);
3211      },
3212      'last-of-type':     function(nodes, formula, root) {
3213        return Selector.pseudos.nth(nodes, "1", root, true, true);
3214      },
3215      'only-of-type':     function(nodes, formula, root) {
3216        var p = Selector.pseudos;
3217        return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
3218      },
3219  
3220      // handles the an+b logic
3221      getIndices: function(a, b, total) {
3222        if (a == 0) return b > 0 ? [b] : [];
3223        return $R(1, total).inject([], function(memo, i) {
3224          if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3225          return memo;
3226        });
3227      },
3228  
3229      // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
3230      nth: function(nodes, formula, root, reverse, ofType) {
3231        if (nodes.length == 0) return [];
3232        if (formula == 'even') formula = '2n+0';
3233        if (formula == 'odd')  formula = '2n+1';
3234        var h = Selector.handlers, results = [], indexed = [], m;
3235        h.mark(nodes);
3236        for (var i = 0, node; node = nodes[i]; i++) {
3237          if (!node.parentNode._counted) {
3238            h.index(node.parentNode, reverse, ofType);
3239            indexed.push(node.parentNode);
3240          }
3241        }
3242        if (formula.match(/^\d+$/)) { // just a number
3243          formula = Number(formula);
3244          for (var i = 0, node; node = nodes[i]; i++)
3245            if (node.nodeIndex == formula) results.push(node);
3246        } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3247          if (m[1] == "-") m[1] = -1;
3248          var a = m[1] ? Number(m[1]) : 1;
3249          var b = m[2] ? Number(m[2]) : 0;
3250          var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3251          for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3252            for (var j = 0; j < l; j++)
3253              if (node.nodeIndex == indices[j]) results.push(node);
3254          }
3255        }
3256        h.unmark(nodes);
3257        h.unmark(indexed);
3258        return results;
3259      },
3260  
3261      'empty': function(nodes, value, root) {
3262        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3263          // IE treats comments as element nodes
3264          if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
3265          results.push(node);
3266        }
3267        return results;
3268      },
3269  
3270      'not': function(nodes, selector, root) {
3271        var h = Selector.handlers, selectorType, m;
3272        var exclusions = new Selector(selector).findElements(root);
3273        h.mark(exclusions);
3274        for (var i = 0, results = [], node; node = nodes[i]; i++)
3275          if (!node._counted) results.push(node);
3276        h.unmark(exclusions);
3277        return results;
3278      },
3279  
3280      'enabled': function(nodes, value, root) {
3281        for (var i = 0, results = [], node; node = nodes[i]; i++)
3282          if (!node.disabled) results.push(node);
3283        return results;
3284      },
3285  
3286      'disabled': function(nodes, value, root) {
3287        for (var i = 0, results = [], node; node = nodes[i]; i++)
3288          if (node.disabled) results.push(node);
3289        return results;
3290      },
3291  
3292      'checked': function(nodes, value, root) {
3293        for (var i = 0, results = [], node; node = nodes[i]; i++)
3294          if (node.checked) results.push(node);
3295        return results;
3296      }
3297    },
3298  
3299    operators: {
3300      '=':  function(nv, v) { return nv == v; },
3301      '!=': function(nv, v) { return nv != v; },
3302      '^=': function(nv, v) { return nv.startsWith(v); },
3303      '$=': function(nv, v) { return nv.endsWith(v); },
3304      '*=': function(nv, v) { return nv.include(v); },
3305      '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3306      '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
3307    },
3308  
3309    matchElements: function(elements, expression) {
3310      var matches = new Selector(expression).findElements(), h = Selector.handlers;
3311      h.mark(matches);
3312      for (var i = 0, results = [], element; element = elements[i]; i++)
3313        if (element._counted) results.push(element);
3314      h.unmark(matches);
3315      return results;
3316    },
3317  
3318    findElement: function(elements, expression, index) {
3319      if (Object.isNumber(expression)) {
3320        index = expression; expression = false;
3321      }
3322      return Selector.matchElements(elements, expression || '*')[index || 0];
3323    },
3324  
3325    findChildElements: function(element, expressions) {
3326      var exprs = expressions.join(','), expressions = [];
3327      exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
3328        expressions.push(m[1].strip());
3329      });
3330      var results = [], h = Selector.handlers;
3331      for (var i = 0, l = expressions.length, selector; i < l; i++) {
3332        selector = new Selector(expressions[i].strip());
3333        h.concat(results, selector.findElements(element));
3334      }
3335      return (l > 1) ? h.unique(results) : results;
3336    }
3337  });
3338  
3339  function $$() {
3340    return Selector.findChildElements(document, $A(arguments));
3341  }
3342  var Form = {
3343    reset: function(form) {
3344      $(form).reset();
3345      return form;
3346    },
3347  
3348    serializeElements: function(elements, options) {
3349      if (typeof options != 'object') options = { hash: !!options };
3350      else if (options.hash === undefined) options.hash = true;
3351      var key, value, submitted = false, submit = options.submit;
3352  
3353      var data = elements.inject({ }, function(result, element) {
3354        if (!element.disabled && element.name) {
3355          key = element.name; value = $(element).getValue();
3356          if (value != null && (element.type != 'submit' || (!submitted &&
3357              submit !== false && (!submit || key == submit) && (submitted = true)))) {
3358            if (key in result) {
3359              // a key is already present; construct an array of values
3360              if (!Object.isArray(result[key])) result[key] = [result[key]];
3361              result[key].push(value);
3362            }
3363            else result[key] = value;
3364          }
3365        }
3366        return result;
3367      });
3368  
3369      return options.hash ? data : Object.toQueryString(data);
3370    }
3371  };
3372  
3373  Form.Methods = {
3374    serialize: function(form, options) {
3375      return Form.serializeElements(Form.getElements(form), options);
3376    },
3377  
3378    getElements: function(form) {
3379      return $A($(form).getElementsByTagName('*')).inject([],
3380        function(elements, child) {
3381          if (Form.Element.Serializers[child.tagName.toLowerCase()])
3382            elements.push(Element.extend(child));
3383          return elements;
3384        }
3385      );
3386    },
3387  
3388    getInputs: function(form, typeName, name) {
3389      form = $(form);
3390      var inputs = form.getElementsByTagName('input');
3391  
3392      if (!typeName && !name) return $A(inputs).map(Element.extend);
3393  
3394      for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
3395        var input = inputs[i];
3396        if ((typeName && input.type != typeName) || (name && input.name != name))
3397          continue;
3398        matchingInputs.push(Element.extend(input));
3399      }
3400  
3401      return matchingInputs;
3402    },
3403  
3404    disable: function(form) {
3405      form = $(form);
3406      Form.getElements(form).invoke('disable');
3407      return form;
3408    },
3409  
3410    enable: function(form) {
3411      form = $(form);
3412      Form.getElements(form).invoke('enable');
3413      return form;
3414    },
3415  
3416    findFirstElement: function(form) {
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());
3426      });
3427    },
3428  
3429    focusFirstElement: function(form) {
3430      form = $(form);
3431      form.findFirstElement().activate();
3432      return form;
3433    },
3434  
3435    request: function(form, options) {
3436      form = $(form), options = Object.clone(options || { });
3437  
3438      var params = options.parameters, action = form.readAttribute('action') || '';
3439      if (action.blank()) action = window.location.href;
3440      options.parameters = form.serialize(true);
3441  
3442      if (params) {
3443        if (Object.isString(params)) params = params.toQueryParams();
3444        Object.extend(options.parameters, params);
3445      }
3446  
3447      if (form.hasAttribute('method') && !options.method)
3448        options.method = form.method;
3449  
3450      return new Ajax.Request(action, options);
3451    }
3452  };
3453  
3454  /*--------------------------------------------------------------------------*/
3455  
3456  Form.Element = {
3457    focus: function(element) {
3458      $(element).focus();
3459      return element;
3460    },
3461  
3462    select: function(element) {
3463      $(element).select();
3464      return element;
3465    }
3466  };
3467  
3468  Form.Element.Methods = {
3469    serialize: function(element) {
3470      element = $(element);
3471      if (!element.disabled && element.name) {
3472        var value = element.getValue();
3473        if (value != undefined) {
3474          var pair = { };
3475          pair[element.name] = value;
3476          return Object.toQueryString(pair);
3477        }
3478      }
3479      return '';
3480    },
3481  
3482    getValue: function(element) {
3483      element = $(element);
3484      var method = element.tagName.toLowerCase();
3485      return Form.Element.Serializers[method](element);
3486    },
3487  
3488    setValue: function(element, value) {
3489      element = $(element);
3490      var method = element.tagName.toLowerCase();
3491      Form.Element.Serializers[method](element, value);
3492      return element;
3493    },
3494  
3495    clear: function(element) {
3496      $(element).value = '';
3497      return element;
3498    },
3499  
3500    present: function(element) {
3501      return $(element).value != '';
3502    },
3503  
3504    activate: function(element) {
3505      element = $(element);
3506      try {
3507        element.focus();
3508        if (element.select && (element.tagName.toLowerCase() != 'input' ||
3509            !['button', 'reset', 'submit'].include(element.type)))
3510          element.select();
3511      } catch (e) { }
3512      return element;
3513    },
3514  
3515    disable: function(element) {
3516      element = $(element);
3517      element.blur();
3518      element.disabled = true;
3519      return element;
3520    },
3521  
3522    enable: function(element) {
3523      element = $(element);
3524      element.disabled = false;
3525      return element;
3526    }
3527  };
3528  
3529  /*--------------------------------------------------------------------------*/
3530  
3531  var Field = Form.Element;
3532  var $F = Form.Element.Methods.getValue;
3533  
3534  /*--------------------------------------------------------------------------*/
3535  
3536  Form.Element.Serializers = {
3537    input: function(element, value) {
3538      switch (element.type.toLowerCase()) {
3539        case 'checkbox':
3540        case 'radio':
3541          return Form.Element.Serializers.inputSelector(element, value);
3542        default:
3543          return Form.Element.Serializers.textarea(element, value);
3544      }
3545    },
3546  
3547    inputSelector: function(element, value) {
3548      if (value === undefined) return element.checked ? element.value : null;
3549      else element.checked = !!value;
3550    },
3551  
3552    textarea: function(element, value) {
3553      if (value === undefined) return element.value;
3554      else element.value = value;
3555    },
3556  
3557    select: function(element, index) {
3558      if (index === undefined)
3559        return this[element.type == 'select-one' ?
3560          'selectOne' : 'selectMany'](element);
3561      else {
3562        var opt, value, single = !Object.isArray(index);
3563        for (var i = 0, length = element.length; i < length; i++) {
3564          opt = element.options[i];
3565          value = this.optionValue(opt);
3566          if (single) {
3567            if (value == index) {
3568              opt.selected = true;
3569              return;
3570            }
3571          }
3572          else opt.selected = index.include(value);
3573        }
3574      }
3575    },
3576  
3577    selectOne: function(element) {
3578      var index = element.selectedIndex;
3579      return index >= 0 ? this.optionValue(element.options[index]) : null;
3580    },
3581  
3582    selectMany: function(element) {
3583      var values, length = element.length;
3584      if (!length) return null;
3585  
3586      for (var i = 0, values = []; i < length; i++) {
3587        var opt = element.options[i];
3588        if (opt.selected) values.push(this.optionValue(opt));
3589      }
3590      return values;
3591    },
3592  
3593    optionValue: function(opt) {
3594      // extend element because hasAttribute may not be native
3595      return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
3596    }
3597  };
3598  
3599  /*--------------------------------------------------------------------------*/
3600  
3601  Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
3602    initialize: function($super, element, frequency, callback) {
3603      $super(callback, frequency);
3604      this.element   = $(element);
3605      this.lastValue = this.getValue();
3606    },
3607  
3608    execute: function() {
3609      var value = this.getValue();
3610      if (Object.isString(this.lastValue) && Object.isString(value) ?
3611          this.lastValue != value : String(this.lastValue) != String(value)) {
3612        this.callback(this.element, value);
3613        this.lastValue = value;
3614      }
3615    }
3616  });
3617  
3618  Form.Element.Observer = Class.create(Abstract.TimedObserver, {
3619    getValue: function() {
3620      return Form.Element.getValue(this.element);
3621    }
3622  });
3623  
3624  Form.Observer = Class.create(Abstract.TimedObserver, {
3625    getValue: function() {
3626      return Form.serialize(this.element);
3627    }
3628  });
3629  
3630  /*--------------------------------------------------------------------------*/
3631  
3632  Abstract.EventObserver = Class.create({
3633    initialize: function(element, callback) {
3634      this.element  = $(element);
3635      this.callback = callback;
3636  
3637      this.lastValue = this.getValue();
3638      if (this.element.tagName.toLowerCase() == 'form')
3639        this.registerFormCallbacks();
3640      else
3641        this.registerCallback(this.element);
3642    },
3643  
3644    onElementEvent: function() {
3645      var value = this.getValue();
3646      if (this.lastValue != value) {
3647        this.callback(this.element, value);
3648        this.lastValue = value;
3649      }
3650    },
3651  
3652    registerFormCallbacks: function() {
3653      Form.getElements(this.element).each(this.registerCallback, this);
3654    },
3655  
3656    registerCallback: function(element) {
3657      if (element.type) {
3658        switch (element.type.toLowerCase()) {
3659          case 'checkbox':
3660          case 'radio':
3661            Event.observe(element, 'click', this.onElementEvent.bind(this));
3662            break;
3663          default:
3664            Event.observe(element, 'change', this.onElementEvent.bind(this));
3665            break;
3666        }
3667      }
3668    }
3669  });
3670  
3671  Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
3672    getValue: function() {
3673      return Form.Element.getValue(this.element);
3674    }
3675  });
3676  
3677  Form.EventObserver = Class.create(Abstract.EventObserver, {
3678    getValue: function() {
3679      return Form.serialize(this.element);
3680    }
3681  });
3682  if (!window.Event) var Event = { };
3683  
3684  Object.extend(Event, {
3685    KEY_BACKSPACE: 8,
3686    KEY_TAB:       9,
3687    KEY_RETURN:   13,
3688    KEY_ESC:      27,
3689    KEY_LEFT:     37,
3690    KEY_UP:       38,
3691    KEY_RIGHT:    39,
3692    KEY_DOWN:     40,
3693    KEY_DELETE:   46,
3694    KEY_HOME:     36,
3695    KEY_END:      35,
3696    KEY_PAGEUP:   33,
3697    KEY_PAGEDOWN: 34,
3698    KEY_INSERT:   45,
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  
3713  Event.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);
3766        event.preventDefault();
3767        event.stopPropagation();
3768        event.stopped = true;
3769      }
3770    };
3771  })();
3772  
3773  Event.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  
3808  Object.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  
3946  Object.extend(Event, Event.Methods);
3947  
3948  Element.addMethods({
3949    fire:          Event.fire,
3950    observe:       Event.observe,
3951    stopObserving: Event.stopObserving
3952  });
3953  
3954  Object.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  
3982      } else {
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  
3999  Hash.toQueryString = Object.toQueryString;
4000  
4001  var Toggle = { display: Element.toggle };
4002  
4003  Element.Methods.childOf = Element.Methods.descendantOf;
4004  
4005  var 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  
4023  var $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.
4027  var Position = {
4028    // set to true if needed, warning: firefox performance problems
4029    // NOT neeeded for page scrolling, only if draggable contained in
4030    // scrollable elements
4031    includeScrollOffsets: false,
4032  
4033    // must be called before calling withinIncludingScrolloffset, every time the
4034    // page is scrolled
4035    prepare: function() {
4036      this.deltaX =  window.pageXOffset
4037                  || document.documentElement.scrollLeft
4038                  || document.body.scrollLeft
4039                  || 0;
4040      this.deltaY =  window.pageYOffset
4041                  || document.documentElement.scrollTop
4042                  || document.body.scrollTop
4043                  || 0;
4044    },
4045  
4046    // caches x/y coordinate pair to use with overlap
4047    within: function(element, x, y) {
4048      if (this.includeScrollOffsets)
4049        return this.withinIncludingScrolloffsets(element, x, y);
4050      this.xcomp = x;
4051      this.ycomp = y;
4052      this.offset = Element.cumulativeOffset(element);
4053  
4054      return (y >= this.offset[1] &&
4055              y <  this.offset[1] + element.offsetHeight &&
4056              x >= this.offset[0] &&
4057              x <  this.offset[0] + element.offsetWidth);
4058    },
4059  
4060    withinIncludingScrolloffsets: function(element, x, y) {
4061      var offsetcache = Element.cumulativeScrollOffset(element);
4062  
4063      this.xcomp = x + offsetcache[0] - this.deltaX;
4064      this.ycomp = y + offsetcache[1] - this.deltaY;
4065      this.offset = Element.cumulativeOffset(element);
4066  
4067      return (this.ycomp >= this.offset[1] &&
4068              this.ycomp <  this.offset[1] + element.offsetHeight &&
4069              this.xcomp >= this.offset[0] &&
4070              this.xcomp <  this.offset[0] + element.offsetWidth);
4071    },
4072  
4073    // within must be called directly before
4074    overlap: function(mode, element) {
4075      if (!mode) return 0;
4076      if (mode == 'vertical')
4077        return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
4078          element.offsetHeight;
4079      if (mode == 'horizontal')
4080        return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
4081          element.offsetWidth;
4082    },
4083  
4084    // Deprecation layer -- use newer Element methods now (1.5.2).
4085  
4086    cumulativeOffset: Element.Methods.cumulativeOffset,
4087  
4088    positionedOffset: Element.Methods.positionedOffset,
4089  
4090    absolutize: function(element) {
4091      Position.prepare();
4092      return Element.absolutize(element);
4093    },
4094  
4095    relativize: function(element) {
4096      Position.prepare();
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  
4114  if (!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  
4149  Element.ClassNames = Class.create();
4150  Element.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  
4180  Object.extend(Element.ClassNames.prototype, Enumerable);
4181  
4182  /*--------------------------------------------------------------------------*/
4183  
4184  Element.addMethods();


Generated: Fri Jan 8 00:19:48 2010 Cross-referenced by PHPXref 0.7