[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

   1  /*  Prototype JavaScript framework, version 1.6.1
   2   *  (c) 2005-2009 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.1',
  11  
  12    Browser: (function(){
  13      var ua = navigator.userAgent;
  14      var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
  15      return {
  16        IE:             !!window.attachEvent && !isOpera,
  17        Opera:          isOpera,
  18        WebKit:         ua.indexOf('AppleWebKit/') > -1,
  19        Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
  20        MobileSafari:   /Apple.*Mobile.*Safari/.test(ua)
  21      }
  22    })(),
  23  
  24    BrowserFeatures: {
  25      XPath: !!document.evaluate,
  26      SelectorsAPI: !!document.querySelector,
  27      ElementExtensions: (function() {
  28        var constructor = window.Element || window.HTMLElement;
  29        return !!(constructor && constructor.prototype);
  30      })(),
  31      SpecificElementExtensions: (function() {
  32        if (typeof window.HTMLDivElement !== 'undefined')
  33          return true;
  34  
  35        var div = document.createElement('div');
  36        var form = document.createElement('form');
  37        var isSupported = false;
  38  
  39        if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
  40          isSupported = true;
  41        }
  42  
  43        div = form = null;
  44  
  45        return isSupported;
  46      })()
  47    },
  48  
  49    ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  50    JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
  51  
  52    emptyFunction: function() { },
  53    K: function(x) { return x }
  54  };
  55  
  56  if (Prototype.Browser.MobileSafari)
  57    Prototype.BrowserFeatures.SpecificElementExtensions = false;
  58  
  59  
  60  var Abstract = { };
  61  
  62  
  63  var Try = {
  64    these: function() {
  65      var returnValue;
  66  
  67      for (var i = 0, length = arguments.length; i < length; i++) {
  68        var lambda = arguments[i];
  69        try {
  70          returnValue = lambda();
  71          break;
  72        } catch (e) { }
  73      }
  74  
  75      return returnValue;
  76    }
  77  };
  78  
  79  /* Based on Alex Arnell's inheritance implementation. */
  80  
  81  var Class = (function() {
  82    function subclass() {};
  83    function create() {
  84      var parent = null, properties = $A(arguments);
  85      if (Object.isFunction(properties[0]))
  86        parent = properties.shift();
  87  
  88      function klass() {
  89        this.initialize.apply(this, arguments);
  90      }
  91  
  92      Object.extend(klass, Class.Methods);
  93      klass.superclass = parent;
  94      klass.subclasses = [];
  95  
  96      if (parent) {
  97        subclass.prototype = parent.prototype;
  98        klass.prototype = new subclass;
  99        parent.subclasses.push(klass);
 100      }
 101  
 102      for (var i = 0; i < properties.length; i++)
 103        klass.addMethods(properties[i]);
 104  
 105      if (!klass.prototype.initialize)
 106        klass.prototype.initialize = Prototype.emptyFunction;
 107  
 108      klass.prototype.constructor = klass;
 109      return klass;
 110    }
 111  
 112    function addMethods(source) {
 113      var ancestor   = this.superclass && this.superclass.prototype;
 114      var properties = Object.keys(source);
 115  
 116      if (!Object.keys({ toString: true }).length) {
 117        if (source.toString != Object.prototype.toString)
 118          properties.push("toString");
 119        if (source.valueOf != Object.prototype.valueOf)
 120          properties.push("valueOf");
 121      }
 122  
 123      for (var i = 0, length = properties.length; i < length; i++) {
 124        var property = properties[i], value = source[property];
 125        if (ancestor && Object.isFunction(value) &&
 126            value.argumentNames().first() == "$super") {
 127          var method = value;
 128          value = (function(m) {
 129            return function() { return ancestor[m].apply(this, arguments); };
 130          })(property).wrap(method);
 131  
 132          value.valueOf = method.valueOf.bind(method);
 133          value.toString = method.toString.bind(method);
 134        }
 135        this.prototype[property] = value;
 136      }
 137  
 138      return this;
 139    }
 140  
 141    return {
 142      create: create,
 143      Methods: {
 144        addMethods: addMethods
 145      }
 146    };
 147  })();
 148  (function() {
 149  
 150    var _toString = Object.prototype.toString;
 151  
 152    function extend(destination, source) {
 153      for (var property in source)
 154        destination[property] = source[property];
 155      return destination;
 156    }
 157  
 158    function inspect(object) {
 159      try {
 160        if (isUndefined(object)) return 'undefined';
 161        if (object === null) return 'null';
 162        return object.inspect ? object.inspect() : String(object);
 163      } catch (e) {
 164        if (e instanceof RangeError) return '...';
 165        throw e;
 166      }
 167    }
 168  
 169    function toJSON(object) {
 170      var type = typeof object;
 171      switch (type) {
 172        case 'undefined':
 173        case 'function':
 174        case 'unknown': return;
 175        case 'boolean': return object.toString();
 176      }
 177  
 178      if (object === null) return 'null';
 179      if (object.toJSON) return object.toJSON();
 180      if (isElement(object)) return;
 181  
 182      var results = [];
 183      for (var property in object) {
 184        var value = toJSON(object[property]);
 185        if (!isUndefined(value))
 186          results.push(property.toJSON() + ': ' + value);
 187      }
 188  
 189      return '{' + results.join(', ') + '}';
 190    }
 191  
 192    function toQueryString(object) {
 193      return $H(object).toQueryString();
 194    }
 195  
 196    function toHTML(object) {
 197      return object && object.toHTML ? object.toHTML() : String.interpret(object);
 198    }
 199  
 200    function keys(object) {
 201      var results = [];
 202      for (var property in object)
 203        results.push(property);
 204      return results;
 205    }
 206  
 207    function values(object) {
 208      var results = [];
 209      for (var property in object)
 210        results.push(object[property]);
 211      return results;
 212    }
 213  
 214    function clone(object) {
 215      return extend({ }, object);
 216    }
 217  
 218    function isElement(object) {
 219      return !!(object && object.nodeType == 1);
 220    }
 221  
 222    function isArray(object) {
 223      return _toString.call(object) == "[object Array]";
 224    }
 225  
 226  
 227    function isHash(object) {
 228      return object instanceof Hash;
 229    }
 230  
 231    function isFunction(object) {
 232      return typeof object === "function";
 233    }
 234  
 235    function isString(object) {
 236      return _toString.call(object) == "[object String]";
 237    }
 238  
 239    function isNumber(object) {
 240      return _toString.call(object) == "[object Number]";
 241    }
 242  
 243    function isUndefined(object) {
 244      return typeof object === "undefined";
 245    }
 246  
 247    extend(Object, {
 248      extend:        extend,
 249      inspect:       inspect,
 250      toJSON:        toJSON,
 251      toQueryString: toQueryString,
 252      toHTML:        toHTML,
 253      keys:          keys,
 254      values:        values,
 255      clone:         clone,
 256      isElement:     isElement,
 257      isArray:       isArray,
 258      isHash:        isHash,
 259      isFunction:    isFunction,
 260      isString:      isString,
 261      isNumber:      isNumber,
 262      isUndefined:   isUndefined
 263    });
 264  })();
 265  Object.extend(Function.prototype, (function() {
 266    var slice = Array.prototype.slice;
 267  
 268    function update(array, args) {
 269      var arrayLength = array.length, length = args.length;
 270      while (length--) array[arrayLength + length] = args[length];
 271      return array;
 272    }
 273  
 274    function merge(array, args) {
 275      array = slice.call(array, 0);
 276      return update(array, args);
 277    }
 278  
 279    function argumentNames() {
 280      var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
 281        .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
 282        .replace(/\s+/g, '').split(',');
 283      return names.length == 1 && !names[0] ? [] : names;
 284    }
 285  
 286    function bind(context) {
 287      if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
 288      var __method = this, args = slice.call(arguments, 1);
 289      return function() {
 290        var a = merge(args, arguments);
 291        return __method.apply(context, a);
 292      }
 293    }
 294  
 295    function bindAsEventListener(context) {
 296      var __method = this, args = slice.call(arguments, 1);
 297      return function(event) {
 298        var a = update([event || window.event], args);
 299        return __method.apply(context, a);
 300      }
 301    }
 302  
 303    function curry() {
 304      if (!arguments.length) return this;
 305      var __method = this, args = slice.call(arguments, 0);
 306      return function() {
 307        var a = merge(args, arguments);
 308        return __method.apply(this, a);
 309      }
 310    }
 311  
 312    function delay(timeout) {
 313      var __method = this, args = slice.call(arguments, 1);
 314      timeout = timeout * 1000
 315      return window.setTimeout(function() {
 316        return __method.apply(__method, args);
 317      }, timeout);
 318    }
 319  
 320    function defer() {
 321      var args = update([0.01], arguments);
 322      return this.delay.apply(this, args);
 323    }
 324  
 325    function wrap(wrapper) {
 326      var __method = this;
 327      return function() {
 328        var a = update([__method.bind(this)], arguments);
 329        return wrapper.apply(this, a);
 330      }
 331    }
 332  
 333    function methodize() {
 334      if (this._methodized) return this._methodized;
 335      var __method = this;
 336      return this._methodized = function() {
 337        var a = update([this], arguments);
 338        return __method.apply(null, a);
 339      };
 340    }
 341  
 342    return {
 343      argumentNames:       argumentNames,
 344      bind:                bind,
 345      bindAsEventListener: bindAsEventListener,
 346      curry:               curry,
 347      delay:               delay,
 348      defer:               defer,
 349      wrap:                wrap,
 350      methodize:           methodize
 351    }
 352  })());
 353  
 354  
 355  Date.prototype.toJSON = function() {
 356    return '"' + this.getUTCFullYear() + '-' +
 357      (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
 358      this.getUTCDate().toPaddedString(2) + 'T' +
 359      this.getUTCHours().toPaddedString(2) + ':' +
 360      this.getUTCMinutes().toPaddedString(2) + ':' +
 361      this.getUTCSeconds().toPaddedString(2) + 'Z"';
 362  };
 363  
 364  
 365  RegExp.prototype.match = RegExp.prototype.test;
 366  
 367  RegExp.escape = function(str) {
 368    return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
 369  };
 370  var PeriodicalExecuter = Class.create({
 371    initialize: function(callback, frequency) {
 372      this.callback = callback;
 373      this.frequency = frequency;
 374      this.currentlyExecuting = false;
 375  
 376      this.registerCallback();
 377    },
 378  
 379    registerCallback: function() {
 380      this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
 381    },
 382  
 383    execute: function() {
 384      this.callback(this);
 385    },
 386  
 387    stop: function() {
 388      if (!this.timer) return;
 389      clearInterval(this.timer);
 390      this.timer = null;
 391    },
 392  
 393    onTimerEvent: function() {
 394      if (!this.currentlyExecuting) {
 395        try {
 396          this.currentlyExecuting = true;
 397          this.execute();
 398          this.currentlyExecuting = false;
 399        } catch(e) {
 400          this.currentlyExecuting = false;
 401          throw e;
 402        }
 403      }
 404    }
 405  });
 406  Object.extend(String, {
 407    interpret: function(value) {
 408      return value == null ? '' : String(value);
 409    },
 410    specialChar: {
 411      '\b': '\\b',
 412      '\t': '\\t',
 413      '\n': '\\n',
 414      '\f': '\\f',
 415      '\r': '\\r',
 416      '\\': '\\\\'
 417    }
 418  });
 419  
 420  Object.extend(String.prototype, (function() {
 421  
 422    function prepareReplacement(replacement) {
 423      if (Object.isFunction(replacement)) return replacement;
 424      var template = new Template(replacement);
 425      return function(match) { return template.evaluate(match) };
 426    }
 427  
 428    function gsub(pattern, replacement) {
 429      var result = '', source = this, match;
 430      replacement = prepareReplacement(replacement);
 431  
 432      if (Object.isString(pattern))
 433        pattern = RegExp.escape(pattern);
 434  
 435      if (!(pattern.length || pattern.source)) {
 436        replacement = replacement('');
 437        return replacement + source.split('').join(replacement) + replacement;
 438      }
 439  
 440      while (source.length > 0) {
 441        if (match = source.match(pattern)) {
 442          result += source.slice(0, match.index);
 443          result += String.interpret(replacement(match));
 444          source  = source.slice(match.index + match[0].length);
 445        } else {
 446          result += source, source = '';
 447        }
 448      }
 449      return result;
 450    }
 451  
 452    function sub(pattern, replacement, count) {
 453      replacement = prepareReplacement(replacement);
 454      count = Object.isUndefined(count) ? 1 : count;
 455  
 456      return this.gsub(pattern, function(match) {
 457        if (--count < 0) return match[0];
 458        return replacement(match);
 459      });
 460    }
 461  
 462    function scan(pattern, iterator) {
 463      this.gsub(pattern, iterator);
 464      return String(this);
 465    }
 466  
 467    function truncate(length, truncation) {
 468      length = length || 30;
 469      truncation = Object.isUndefined(truncation) ? '...' : truncation;
 470      return this.length > length ?
 471        this.slice(0, length - truncation.length) + truncation : String(this);
 472    }
 473  
 474    function strip() {
 475      return this.replace(/^\s+/, '').replace(/\s+$/, '');
 476    }
 477  
 478    function stripTags() {
 479      return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
 480    }
 481  
 482    function stripScripts() {
 483      return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
 484    }
 485  
 486    function extractScripts() {
 487      var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
 488      var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
 489      return (this.match(matchAll) || []).map(function(scriptTag) {
 490        return (scriptTag.match(matchOne) || ['', ''])[1];
 491      });
 492    }
 493  
 494    function evalScripts() {
 495      return this.extractScripts().map(function(script) { return eval(script) });
 496    }
 497  
 498    function escapeHTML() {
 499      return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
 500    }
 501  
 502    function unescapeHTML() {
 503      return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
 504    }
 505  
 506  
 507    function toQueryParams(separator) {
 508      var match = this.strip().match(/([^?#]*)(#.*)?$/);
 509      if (!match) return { };
 510  
 511      return match[1].split(separator || '&').inject({ }, function(hash, pair) {
 512        if ((pair = pair.split('='))[0]) {
 513          var key = decodeURIComponent(pair.shift());
 514          var value = pair.length > 1 ? pair.join('=') : pair[0];
 515          if (value != undefined) value = decodeURIComponent(value);
 516  
 517          if (key in hash) {
 518            if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
 519            hash[key].push(value);
 520          }
 521          else hash[key] = value;
 522        }
 523        return hash;
 524      });
 525    }
 526  
 527    function toArray() {
 528      return this.split('');
 529    }
 530  
 531    function succ() {
 532      return this.slice(0, this.length - 1) +
 533        String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
 534    }
 535  
 536    function times(count) {
 537      return count < 1 ? '' : new Array(count + 1).join(this);
 538    }
 539  
 540    function camelize() {
 541      var parts = this.split('-'), len = parts.length;
 542      if (len == 1) return parts[0];
 543  
 544      var camelized = this.charAt(0) == '-'
 545        ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
 546        : parts[0];
 547  
 548      for (var i = 1; i < len; i++)
 549        camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
 550  
 551      return camelized;
 552    }
 553  
 554    function capitalize() {
 555      return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
 556    }
 557  
 558    function underscore() {
 559      return this.replace(/::/g, '/')
 560                 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
 561                 .replace(/([a-z\d])([A-Z])/g, '$1_$2')
 562                 .replace(/-/g, '_')
 563                 .toLowerCase();
 564    }
 565  
 566    function dasherize() {
 567      return this.replace(/_/g, '-');
 568    }
 569  
 570    function inspect(useDoubleQuotes) {
 571      var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
 572        if (character in String.specialChar) {
 573          return String.specialChar[character];
 574        }
 575        return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
 576      });
 577      if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
 578      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
 579    }
 580  
 581    function toJSON() {
 582      return this.inspect(true);
 583    }
 584  
 585    function unfilterJSON(filter) {
 586      return this.replace(filter || Prototype.JSONFilter, '$1');
 587    }
 588  
 589    function isJSON() {
 590      var str = this;
 591      if (str.blank()) return false;
 592      str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
 593      return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
 594    }
 595  
 596    function evalJSON(sanitize) {
 597      var json = this.unfilterJSON();
 598      try {
 599        if (!sanitize || json.isJSON()) return eval('(' + json + ')');
 600      } catch (e) { }
 601      throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
 602    }
 603  
 604    function include(pattern) {
 605      return this.indexOf(pattern) > -1;
 606    }
 607  
 608    function startsWith(pattern) {
 609      return this.indexOf(pattern) === 0;
 610    }
 611  
 612    function endsWith(pattern) {
 613      var d = this.length - pattern.length;
 614      return d >= 0 && this.lastIndexOf(pattern) === d;
 615    }
 616  
 617    function empty() {
 618      return this == '';
 619    }
 620  
 621    function blank() {
 622      return /^\s*$/.test(this);
 623    }
 624  
 625    function interpolate(object, pattern) {
 626      return new Template(this, pattern).evaluate(object);
 627    }
 628  
 629    return {
 630      gsub:           gsub,
 631      sub:            sub,
 632      scan:           scan,
 633      truncate:       truncate,
 634      strip:          String.prototype.trim ? String.prototype.trim : strip,
 635      stripTags:      stripTags,
 636      stripScripts:   stripScripts,
 637      extractScripts: extractScripts,
 638      evalScripts:    evalScripts,
 639      escapeHTML:     escapeHTML,
 640      unescapeHTML:   unescapeHTML,
 641      toQueryParams:  toQueryParams,
 642      parseQuery:     toQueryParams,
 643      toArray:        toArray,
 644      succ:           succ,
 645      times:          times,
 646      camelize:       camelize,
 647      capitalize:     capitalize,
 648      underscore:     underscore,
 649      dasherize:      dasherize,
 650      inspect:        inspect,
 651      toJSON:         toJSON,
 652      unfilterJSON:   unfilterJSON,
 653      isJSON:         isJSON,
 654      evalJSON:       evalJSON,
 655      include:        include,
 656      startsWith:     startsWith,
 657      endsWith:       endsWith,
 658      empty:          empty,
 659      blank:          blank,
 660      interpolate:    interpolate
 661    };
 662  })());
 663  
 664  var Template = Class.create({
 665    initialize: function(template, pattern) {
 666      this.template = template.toString();
 667      this.pattern = pattern || Template.Pattern;
 668    },
 669  
 670    evaluate: function(object) {
 671      if (object && Object.isFunction(object.toTemplateReplacements))
 672        object = object.toTemplateReplacements();
 673  
 674      return this.template.gsub(this.pattern, function(match) {
 675        if (object == null) return (match[1] + '');
 676  
 677        var before = match[1] || '';
 678        if (before == '\\') return match[2];
 679  
 680        var ctx = object, expr = match[3];
 681        var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
 682        match = pattern.exec(expr);
 683        if (match == null) return before;
 684  
 685        while (match != null) {
 686          var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
 687          ctx = ctx[comp];
 688          if (null == ctx || '' == match[3]) break;
 689          expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
 690          match = pattern.exec(expr);
 691        }
 692  
 693        return before + String.interpret(ctx);
 694      });
 695    }
 696  });
 697  Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
 698  
 699  var $break = { };
 700  
 701  var Enumerable = (function() {
 702    function each(iterator, context) {
 703      var index = 0;
 704      try {
 705        this._each(function(value) {
 706          iterator.call(context, value, index++);
 707        });
 708      } catch (e) {
 709        if (e != $break) throw e;
 710      }
 711      return this;
 712    }
 713  
 714    function eachSlice(number, iterator, context) {
 715      var index = -number, slices = [], array = this.toArray();
 716      if (number < 1) return array;
 717      while ((index += number) < array.length)
 718        slices.push(array.slice(index, index+number));
 719      return slices.collect(iterator, context);
 720    }
 721  
 722    function all(iterator, context) {
 723      iterator = iterator || Prototype.K;
 724      var result = true;
 725      this.each(function(value, index) {
 726        result = result && !!iterator.call(context, value, index);
 727        if (!result) throw $break;
 728      });
 729      return result;
 730    }
 731  
 732    function any(iterator, context) {
 733      iterator = iterator || Prototype.K;
 734      var result = false;
 735      this.each(function(value, index) {
 736        if (result = !!iterator.call(context, value, index))
 737          throw $break;
 738      });
 739      return result;
 740    }
 741  
 742    function collect(iterator, context) {
 743      iterator = iterator || Prototype.K;
 744      var results = [];
 745      this.each(function(value, index) {
 746        results.push(iterator.call(context, value, index));
 747      });
 748      return results;
 749    }
 750  
 751    function detect(iterator, context) {
 752      var result;
 753      this.each(function(value, index) {
 754        if (iterator.call(context, value, index)) {
 755          result = value;
 756          throw $break;
 757        }
 758      });
 759      return result;
 760    }
 761  
 762    function findAll(iterator, context) {
 763      var results = [];
 764      this.each(function(value, index) {
 765        if (iterator.call(context, value, index))
 766          results.push(value);
 767      });
 768      return results;
 769    }
 770  
 771    function grep(filter, iterator, context) {
 772      iterator = iterator || Prototype.K;
 773      var results = [];
 774  
 775      if (Object.isString(filter))
 776        filter = new RegExp(RegExp.escape(filter));
 777  
 778      this.each(function(value, index) {
 779        if (filter.match(value))
 780          results.push(iterator.call(context, value, index));
 781      });
 782      return results;
 783    }
 784  
 785    function include(object) {
 786      if (Object.isFunction(this.indexOf))
 787        if (this.indexOf(object) != -1) return true;
 788  
 789      var found = false;
 790      this.each(function(value) {
 791        if (value == object) {
 792          found = true;
 793          throw $break;
 794        }
 795      });
 796      return found;
 797    }
 798  
 799    function inGroupsOf(number, fillWith) {
 800      fillWith = Object.isUndefined(fillWith) ? null : fillWith;
 801      return this.eachSlice(number, function(slice) {
 802        while(slice.length < number) slice.push(fillWith);
 803        return slice;
 804      });
 805    }
 806  
 807    function inject(memo, iterator, context) {
 808      this.each(function(value, index) {
 809        memo = iterator.call(context, memo, value, index);
 810      });
 811      return memo;
 812    }
 813  
 814    function invoke(method) {
 815      var args = $A(arguments).slice(1);
 816      return this.map(function(value) {
 817        return value[method].apply(value, args);
 818      });
 819    }
 820  
 821    function max(iterator, context) {
 822      iterator = iterator || Prototype.K;
 823      var result;
 824      this.each(function(value, index) {
 825        value = iterator.call(context, value, index);
 826        if (result == null || value >= result)
 827          result = value;
 828      });
 829      return result;
 830    }
 831  
 832    function min(iterator, context) {
 833      iterator = iterator || Prototype.K;
 834      var result;
 835      this.each(function(value, index) {
 836        value = iterator.call(context, value, index);
 837        if (result == null || value < result)
 838          result = value;
 839      });
 840      return result;
 841    }
 842  
 843    function partition(iterator, context) {
 844      iterator = iterator || Prototype.K;
 845      var trues = [], falses = [];
 846      this.each(function(value, index) {
 847        (iterator.call(context, value, index) ?
 848          trues : falses).push(value);
 849      });
 850      return [trues, falses];
 851    }
 852  
 853    function pluck(property) {
 854      var results = [];
 855      this.each(function(value) {
 856        results.push(value[property]);
 857      });
 858      return results;
 859    }
 860  
 861    function reject(iterator, context) {
 862      var results = [];
 863      this.each(function(value, index) {
 864        if (!iterator.call(context, value, index))
 865          results.push(value);
 866      });
 867      return results;
 868    }
 869  
 870    function sortBy(iterator, context) {
 871      return this.map(function(value, index) {
 872        return {
 873          value: value,
 874          criteria: iterator.call(context, value, index)
 875        };
 876      }).sort(function(left, right) {
 877        var a = left.criteria, b = right.criteria;
 878        return a < b ? -1 : a > b ? 1 : 0;
 879      }).pluck('value');
 880    }
 881  
 882    function toArray() {
 883      return this.map();
 884    }
 885  
 886    function zip() {
 887      var iterator = Prototype.K, args = $A(arguments);
 888      if (Object.isFunction(args.last()))
 889        iterator = args.pop();
 890  
 891      var collections = [this].concat(args).map($A);
 892      return this.map(function(value, index) {
 893        return iterator(collections.pluck(index));
 894      });
 895    }
 896  
 897    function size() {
 898      return this.toArray().length;
 899    }
 900  
 901    function inspect() {
 902      return '#<Enumerable:' + this.toArray().inspect() + '>';
 903    }
 904  
 905  
 906  
 907  
 908  
 909  
 910  
 911  
 912  
 913    return {
 914      each:       each,
 915      eachSlice:  eachSlice,
 916      all:        all,
 917      every:      all,
 918      any:        any,
 919      some:       any,
 920      collect:    collect,
 921      map:        collect,
 922      detect:     detect,
 923      findAll:    findAll,
 924      select:     findAll,
 925      filter:     findAll,
 926      grep:       grep,
 927      include:    include,
 928      member:     include,
 929      inGroupsOf: inGroupsOf,
 930      inject:     inject,
 931      invoke:     invoke,
 932      max:        max,
 933      min:        min,
 934      partition:  partition,
 935      pluck:      pluck,
 936      reject:     reject,
 937      sortBy:     sortBy,
 938      toArray:    toArray,
 939      entries:    toArray,
 940      zip:        zip,
 941      size:       size,
 942      inspect:    inspect,
 943      find:       detect
 944    };
 945  })();
 946  function $A(iterable) {
 947    if (!iterable) return [];
 948    if ('toArray' in Object(iterable)) return iterable.toArray();
 949    var length = iterable.length || 0, results = new Array(length);
 950    while (length--) results[length] = iterable[length];
 951    return results;
 952  }
 953  
 954  function $w(string) {
 955    if (!Object.isString(string)) return [];
 956    string = string.strip();
 957    return string ? string.split(/\s+/) : [];
 958  }
 959  
 960  Array.from = $A;
 961  
 962  
 963  (function() {
 964    var arrayProto = Array.prototype,
 965        slice = arrayProto.slice,
 966        _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
 967  
 968    function each(iterator) {
 969      for (var i = 0, length = this.length; i < length; i++)
 970        iterator(this[i]);
 971    }
 972    if (!_each) _each = each;
 973  
 974    function clear() {
 975      this.length = 0;
 976      return this;
 977    }
 978  
 979    function first() {
 980      return this[0];
 981    }
 982  
 983    function last() {
 984      return this[this.length - 1];
 985    }
 986  
 987    function compact() {
 988      return this.select(function(value) {
 989        return value != null;
 990      });
 991    }
 992  
 993    function flatten() {
 994      return this.inject([], function(array, value) {
 995        if (Object.isArray(value))
 996          return array.concat(value.flatten());
 997        array.push(value);
 998        return array;
 999      });
1000    }
1001  
1002    function without() {
1003      var values = slice.call(arguments, 0);
1004      return this.select(function(value) {
1005        return !values.include(value);
1006      });
1007    }
1008  
1009    function reverse(inline) {
1010      return (inline !== false ? this : this.toArray())._reverse();
1011    }
1012  
1013    function uniq(sorted) {
1014      return this.inject([], function(array, value, index) {
1015        if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1016          array.push(value);
1017        return array;
1018      });
1019    }
1020  
1021    function intersect(array) {
1022      return this.uniq().findAll(function(item) {
1023        return array.detect(function(value) { return item === value });
1024      });
1025    }
1026  
1027  
1028    function clone() {
1029      return slice.call(this, 0);
1030    }
1031  
1032    function size() {
1033      return this.length;
1034    }
1035  
1036    function inspect() {
1037      return '[' + this.map(Object.inspect).join(', ') + ']';
1038    }
1039  
1040    function toJSON() {
1041      var results = [];
1042      this.each(function(object) {
1043        var value = Object.toJSON(object);
1044        if (!Object.isUndefined(value)) results.push(value);
1045      });
1046      return '[' + results.join(', ') + ']';
1047    }
1048  
1049    function indexOf(item, i) {
1050      i || (i = 0);
1051      var length = this.length;
1052      if (i < 0) i = length + i;
1053      for (; i < length; i++)
1054        if (this[i] === item) return i;
1055      return -1;
1056    }
1057  
1058    function lastIndexOf(item, i) {
1059      i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1060      var n = this.slice(0, i).reverse().indexOf(item);
1061      return (n < 0) ? n : i - n - 1;
1062    }
1063  
1064    function concat() {
1065      var array = slice.call(this, 0), item;
1066      for (var i = 0, length = arguments.length; i < length; i++) {
1067        item = arguments[i];
1068        if (Object.isArray(item) && !('callee' in item)) {
1069          for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1070            array.push(item[j]);
1071        } else {
1072          array.push(item);
1073        }
1074      }
1075      return array;
1076    }
1077  
1078    Object.extend(arrayProto, Enumerable);
1079  
1080    if (!arrayProto._reverse)
1081      arrayProto._reverse = arrayProto.reverse;
1082  
1083    Object.extend(arrayProto, {
1084      _each:     _each,
1085      clear:     clear,
1086      first:     first,
1087      last:      last,
1088      compact:   compact,
1089      flatten:   flatten,
1090      without:   without,
1091      reverse:   reverse,
1092      uniq:      uniq,
1093      intersect: intersect,
1094      clone:     clone,
1095      toArray:   clone,
1096      size:      size,
1097      inspect:   inspect,
1098      toJSON:    toJSON
1099    });
1100  
1101    var CONCAT_ARGUMENTS_BUGGY = (function() {
1102      return [].concat(arguments)[0][0] !== 1;
1103    })(1,2)
1104  
1105    if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1106  
1107    if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1108    if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1109  })();
1110  function $H(object) {
1111    return new Hash(object);
1112  };
1113  
1114  var Hash = Class.create(Enumerable, (function() {
1115    function initialize(object) {
1116      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1117    }
1118  
1119    function _each(iterator) {
1120      for (var key in this._object) {
1121        var value = this._object[key], pair = [key, value];
1122        pair.key = key;
1123        pair.value = value;
1124        iterator(pair);
1125      }
1126    }
1127  
1128    function set(key, value) {
1129      return this._object[key] = value;
1130    }
1131  
1132    function get(key) {
1133      if (this._object[key] !== Object.prototype[key])
1134        return this._object[key];
1135    }
1136  
1137    function unset(key) {
1138      var value = this._object[key];
1139      delete this._object[key];
1140      return value;
1141    }
1142  
1143    function toObject() {
1144      return Object.clone(this._object);
1145    }
1146  
1147    function keys() {
1148      return this.pluck('key');
1149    }
1150  
1151    function values() {
1152      return this.pluck('value');
1153    }
1154  
1155    function index(value) {
1156      var match = this.detect(function(pair) {
1157        return pair.value === value;
1158      });
1159      return match && match.key;
1160    }
1161  
1162    function merge(object) {
1163      return this.clone().update(object);
1164    }
1165  
1166    function update(object) {
1167      return new Hash(object).inject(this, function(result, pair) {
1168        result.set(pair.key, pair.value);
1169        return result;
1170      });
1171    }
1172  
1173    function toQueryPair(key, value) {
1174      if (Object.isUndefined(value)) return key;
1175      return key + '=' + encodeURIComponent(String.interpret(value));
1176    }
1177  
1178    function toQueryString() {
1179      return this.inject([], function(results, pair) {
1180        var key = encodeURIComponent(pair.key), values = pair.value;
1181  
1182        if (values && typeof values == 'object') {
1183          if (Object.isArray(values))
1184            return results.concat(values.map(toQueryPair.curry(key)));
1185        } else results.push(toQueryPair(key, values));
1186        return results;
1187      }).join('&');
1188    }
1189  
1190    function inspect() {
1191      return '#<Hash:{' + this.map(function(pair) {
1192        return pair.map(Object.inspect).join(': ');
1193      }).join(', ') + '}>';
1194    }
1195  
1196    function toJSON() {
1197      return Object.toJSON(this.toObject());
1198    }
1199  
1200    function clone() {
1201      return new Hash(this);
1202    }
1203  
1204    return {
1205      initialize:             initialize,
1206      _each:                  _each,
1207      set:                    set,
1208      get:                    get,
1209      unset:                  unset,
1210      toObject:               toObject,
1211      toTemplateReplacements: toObject,
1212      keys:                   keys,
1213      values:                 values,
1214      index:                  index,
1215      merge:                  merge,
1216      update:                 update,
1217      toQueryString:          toQueryString,
1218      inspect:                inspect,
1219      toJSON:                 toJSON,
1220      clone:                  clone
1221    };
1222  })());
1223  
1224  Hash.from = $H;
1225  Object.extend(Number.prototype, (function() {
1226    function toColorPart() {
1227      return this.toPaddedString(2, 16);
1228    }
1229  
1230    function succ() {
1231      return this + 1;
1232    }
1233  
1234    function times(iterator, context) {
1235      $R(0, this, true).each(iterator, context);
1236      return this;
1237    }
1238  
1239    function toPaddedString(length, radix) {
1240      var string = this.toString(radix || 10);
1241      return '0'.times(length - string.length) + string;
1242    }
1243  
1244    function toJSON() {
1245      return isFinite(this) ? this.toString() : 'null';
1246    }
1247  
1248    function abs() {
1249      return Math.abs(this);
1250    }
1251  
1252    function round() {
1253      return Math.round(this);
1254    }
1255  
1256    function ceil() {
1257      return Math.ceil(this);
1258    }
1259  
1260    function floor() {
1261      return Math.floor(this);
1262    }
1263  
1264    return {
1265      toColorPart:    toColorPart,
1266      succ:           succ,
1267      times:          times,
1268      toPaddedString: toPaddedString,
1269      toJSON:         toJSON,
1270      abs:            abs,
1271      round:          round,
1272      ceil:           ceil,
1273      floor:          floor
1274    };
1275  })());
1276  
1277  function $R(start, end, exclusive) {
1278    return new ObjectRange(start, end, exclusive);
1279  }
1280  
1281  var ObjectRange = Class.create(Enumerable, (function() {
1282    function initialize(start, end, exclusive) {
1283      this.start = start;
1284      this.end = end;
1285      this.exclusive = exclusive;
1286    }
1287  
1288    function _each(iterator) {
1289      var value = this.start;
1290      while (this.include(value)) {
1291        iterator(value);
1292        value = value.succ();
1293      }
1294    }
1295  
1296    function include(value) {
1297      if (value < this.start)
1298        return false;
1299      if (this.exclusive)
1300        return value < this.end;
1301      return value <= this.end;
1302    }
1303  
1304    return {
1305      initialize: initialize,
1306      _each:      _each,
1307      include:    include
1308    };
1309  })());
1310  
1311  
1312  
1313  var Ajax = {
1314    getTransport: function() {
1315      return Try.these(
1316        function() {return new XMLHttpRequest()},
1317        function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1318        function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1319      ) || false;
1320    },
1321  
1322    activeRequestCount: 0
1323  };
1324  
1325  Ajax.Responders = {
1326    responders: [],
1327  
1328    _each: function(iterator) {
1329      this.responders._each(iterator);
1330    },
1331  
1332    register: function(responder) {
1333      if (!this.include(responder))
1334        this.responders.push(responder);
1335    },
1336  
1337    unregister: function(responder) {
1338      this.responders = this.responders.without(responder);
1339    },
1340  
1341    dispatch: function(callback, request, transport, json) {
1342      this.each(function(responder) {
1343        if (Object.isFunction(responder[callback])) {
1344          try {
1345            responder[callback].apply(responder, [request, transport, json]);
1346          } catch (e) { }
1347        }
1348      });
1349    }
1350  };
1351  
1352  Object.extend(Ajax.Responders, Enumerable);
1353  
1354  Ajax.Responders.register({
1355    onCreate:   function() { Ajax.activeRequestCount++ },
1356    onComplete: function() { Ajax.activeRequestCount-- }
1357  });
1358  Ajax.Base = Class.create({
1359    initialize: function(options) {
1360      this.options = {
1361        method:       'post',
1362        asynchronous: true,
1363        contentType:  'application/x-www-form-urlencoded',
1364        encoding:     'UTF-8',
1365        parameters:   '',
1366        evalJSON:     true,
1367        evalJS:       true
1368      };
1369      Object.extend(this.options, options || { });
1370  
1371      this.options.method = this.options.method.toLowerCase();
1372  
1373      if (Object.isString(this.options.parameters))
1374        this.options.parameters = this.options.parameters.toQueryParams();
1375      else if (Object.isHash(this.options.parameters))
1376        this.options.parameters = this.options.parameters.toObject();
1377    }
1378  });
1379  Ajax.Request = Class.create(Ajax.Base, {
1380    _complete: false,
1381  
1382    initialize: function($super, url, options) {
1383      $super(options);
1384      this.transport = Ajax.getTransport();
1385      this.request(url);
1386    },
1387  
1388    request: function(url) {
1389      this.url = url;
1390      this.method = this.options.method;
1391      var params = Object.clone(this.options.parameters);
1392  
1393      if (!['get', 'post'].include(this.method)) {
1394        params['_method'] = this.method;
1395        this.method = 'post';
1396      }
1397  
1398      this.parameters = params;
1399  
1400      if (params = Object.toQueryString(params)) {
1401        if (this.method == 'get')
1402          this.url += (this.url.include('?') ? '&' : '?') + params;
1403        else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1404          params += '&_=';
1405      }
1406  
1407      try {
1408        var response = new Ajax.Response(this);
1409        if (this.options.onCreate) this.options.onCreate(response);
1410        Ajax.Responders.dispatch('onCreate', this, response);
1411  
1412        this.transport.open(this.method.toUpperCase(), this.url,
1413          this.options.asynchronous);
1414  
1415        if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1416  
1417        this.transport.onreadystatechange = this.onStateChange.bind(this);
1418        this.setRequestHeaders();
1419  
1420        this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1421        this.transport.send(this.body);
1422  
1423        /* Force Firefox to handle ready state 4 for synchronous requests */
1424        if (!this.options.asynchronous && this.transport.overrideMimeType)
1425          this.onStateChange();
1426  
1427      }
1428      catch (e) {
1429        this.dispatchException(e);
1430      }
1431    },
1432  
1433    onStateChange: function() {
1434      var readyState = this.transport.readyState;
1435      if (readyState > 1 && !((readyState == 4) && this._complete))
1436        this.respondToReadyState(this.transport.readyState);
1437    },
1438  
1439    setRequestHeaders: function() {
1440      var headers = {
1441        'X-Requested-With': 'XMLHttpRequest',
1442        'X-Prototype-Version': Prototype.Version,
1443        'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1444      };
1445  
1446      if (this.method == 'post') {
1447        headers['Content-type'] = this.options.contentType +
1448          (this.options.encoding ? '; charset=' + this.options.encoding : '');
1449  
1450        /* Force "Connection: close" for older Mozilla browsers to work
1451         * around a bug where XMLHttpRequest sends an incorrect
1452         * Content-length header. See Mozilla Bugzilla #246651.
1453         */
1454        if (this.transport.overrideMimeType &&
1455            (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1456              headers['Connection'] = 'close';
1457      }
1458  
1459      if (typeof this.options.requestHeaders == 'object') {
1460        var extras = this.options.requestHeaders;
1461  
1462        if (Object.isFunction(extras.push))
1463          for (var i = 0, length = extras.length; i < length; i += 2)
1464            headers[extras[i]] = extras[i+1];
1465        else
1466          $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1467      }
1468  
1469      for (var name in headers)
1470        this.transport.setRequestHeader(name, headers[name]);
1471    },
1472  
1473    success: function() {
1474      var status = this.getStatus();
1475      return !status || (status >= 200 && status < 300);
1476    },
1477  
1478    getStatus: function() {
1479      try {
1480        return this.transport.status || 0;
1481      } catch (e) { return 0 }
1482    },
1483  
1484    respondToReadyState: function(readyState) {
1485      var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1486  
1487      if (state == 'Complete') {
1488        try {
1489          this._complete = true;
1490          (this.options['on' + response.status]
1491           || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1492           || Prototype.emptyFunction)(response, response.headerJSON);
1493        } catch (e) {
1494          this.dispatchException(e);
1495        }
1496  
1497        var contentType = response.getHeader('Content-type');
1498        if (this.options.evalJS == 'force'
1499            || (this.options.evalJS && this.isSameOrigin() && contentType
1500            && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1501          this.evalResponse();
1502      }
1503  
1504      try {
1505        (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1506        Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1507      } catch (e) {
1508        this.dispatchException(e);
1509      }
1510  
1511      if (state == 'Complete') {
1512        this.transport.onreadystatechange = Prototype.emptyFunction;
1513      }
1514    },
1515  
1516    isSameOrigin: function() {
1517      var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1518      return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1519        protocol: location.protocol,
1520        domain: document.domain,
1521        port: location.port ? ':' + location.port : ''
1522      }));
1523    },
1524  
1525    getHeader: function(name) {
1526      try {
1527        return this.transport.getResponseHeader(name) || null;
1528      } catch (e) { return null; }
1529    },
1530  
1531    evalResponse: function() {
1532      try {
1533        return eval((this.transport.responseText || '').unfilterJSON());
1534      } catch (e) {
1535        this.dispatchException(e);
1536      }
1537    },
1538  
1539    dispatchException: function(exception) {
1540      (this.options.onException || Prototype.emptyFunction)(this, exception);
1541      Ajax.Responders.dispatch('onException', this, exception);
1542    }
1543  });
1544  
1545  Ajax.Request.Events =
1546    ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1547  
1548  
1549  
1550  
1551  
1552  
1553  
1554  
1555  Ajax.Response = Class.create({
1556    initialize: function(request){
1557      this.request = request;
1558      var transport  = this.transport  = request.transport,
1559          readyState = this.readyState = transport.readyState;
1560  
1561      if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1562        this.status       = this.getStatus();
1563        this.statusText   = this.getStatusText();
1564        this.responseText = String.interpret(transport.responseText);
1565        this.headerJSON   = this._getHeaderJSON();
1566      }
1567  
1568      if(readyState == 4) {
1569        var xml = transport.responseXML;
1570        this.responseXML  = Object.isUndefined(xml) ? null : xml;
1571        this.responseJSON = this._getResponseJSON();
1572      }
1573    },
1574  
1575    status:      0,
1576  
1577    statusText: '',
1578  
1579    getStatus: Ajax.Request.prototype.getStatus,
1580  
1581    getStatusText: function() {
1582      try {
1583        return this.transport.statusText || '';
1584      } catch (e) { return '' }
1585    },
1586  
1587    getHeader: Ajax.Request.prototype.getHeader,
1588  
1589    getAllHeaders: function() {
1590      try {
1591        return this.getAllResponseHeaders();
1592      } catch (e) { return null }
1593    },
1594  
1595    getResponseHeader: function(name) {
1596      return this.transport.getResponseHeader(name);
1597    },
1598  
1599    getAllResponseHeaders: function() {
1600      return this.transport.getAllResponseHeaders();
1601    },
1602  
1603    _getHeaderJSON: function() {
1604      var json = this.getHeader('X-JSON');
1605      if (!json) return null;
1606      json = decodeURIComponent(escape(json));
1607      try {
1608        return json.evalJSON(this.request.options.sanitizeJSON ||
1609          !this.request.isSameOrigin());
1610      } catch (e) {
1611        this.request.dispatchException(e);
1612      }
1613    },
1614  
1615    _getResponseJSON: function() {
1616      var options = this.request.options;
1617      if (!options.evalJSON || (options.evalJSON != 'force' &&
1618        !(this.getHeader('Content-type') || '').include('application/json')) ||
1619          this.responseText.blank())
1620            return null;
1621      try {
1622        return this.responseText.evalJSON(options.sanitizeJSON ||
1623          !this.request.isSameOrigin());
1624      } catch (e) {
1625        this.request.dispatchException(e);
1626      }
1627    }
1628  });
1629  
1630  Ajax.Updater = Class.create(Ajax.Request, {
1631    initialize: function($super, container, url, options) {
1632      this.container = {
1633        success: (container.success || container),
1634        failure: (container.failure || (container.success ? null : container))
1635      };
1636  
1637      options = Object.clone(options);
1638      var onComplete = options.onComplete;
1639      options.onComplete = (function(response, json) {
1640        this.updateContent(response.responseText);
1641        if (Object.isFunction(onComplete)) onComplete(response, json);
1642      }).bind(this);
1643  
1644      $super(url, options);
1645    },
1646  
1647    updateContent: function(responseText) {
1648      var receiver = this.container[this.success() ? 'success' : 'failure'],
1649          options = this.options;
1650  
1651      if (!options.evalScripts) responseText = responseText.stripScripts();
1652  
1653      if (receiver = $(receiver)) {
1654        if (options.insertion) {
1655          if (Object.isString(options.insertion)) {
1656            var insertion = { }; insertion[options.insertion] = responseText;
1657            receiver.insert(insertion);
1658          }
1659          else options.insertion(receiver, responseText);
1660        }
1661        else receiver.update(responseText);
1662      }
1663    }
1664  });
1665  
1666  Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1667    initialize: function($super, container, url, options) {
1668      $super(options);
1669      this.onComplete = this.options.onComplete;
1670  
1671      this.frequency = (this.options.frequency || 2);
1672      this.decay = (this.options.decay || 1);
1673  
1674      this.updater = { };
1675      this.container = container;
1676      this.url = url;
1677  
1678      this.start();
1679    },
1680  
1681    start: function() {
1682      this.options.onComplete = this.updateComplete.bind(this);
1683      this.onTimerEvent();
1684    },
1685  
1686    stop: function() {
1687      this.updater.options.onComplete = undefined;
1688      clearTimeout(this.timer);
1689      (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1690    },
1691  
1692    updateComplete: function(response) {
1693      if (this.options.decay) {
1694        this.decay = (response.responseText == this.lastText ?
1695          this.decay * this.options.decay : 1);
1696  
1697        this.lastText = response.responseText;
1698      }
1699      this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1700    },
1701  
1702    onTimerEvent: function() {
1703      this.updater = new Ajax.Updater(this.container, this.url, this.options);
1704    }
1705  });
1706  
1707  
1708  
1709  function $(element) {
1710    if (arguments.length > 1) {
1711      for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1712        elements.push($(arguments[i]));
1713      return elements;
1714    }
1715    if (Object.isString(element))
1716      element = document.getElementById(element);
1717    return Element.extend(element);
1718  }
1719  
1720  if (Prototype.BrowserFeatures.XPath) {
1721    document._getElementsByXPath = function(expression, parentElement) {
1722      var results = [];
1723      var query = document.evaluate(expression, $(parentElement) || document,
1724        null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1725      for (var i = 0, length = query.snapshotLength; i < length; i++)
1726        results.push(Element.extend(query.snapshotItem(i)));
1727      return results;
1728    };
1729  }
1730  
1731  /*--------------------------------------------------------------------------*/
1732  
1733  if (!window.Node) var Node = { };
1734  
1735  if (!Node.ELEMENT_NODE) {
1736    Object.extend(Node, {
1737      ELEMENT_NODE: 1,
1738      ATTRIBUTE_NODE: 2,
1739      TEXT_NODE: 3,
1740      CDATA_SECTION_NODE: 4,
1741      ENTITY_REFERENCE_NODE: 5,
1742      ENTITY_NODE: 6,
1743      PROCESSING_INSTRUCTION_NODE: 7,
1744      COMMENT_NODE: 8,
1745      DOCUMENT_NODE: 9,
1746      DOCUMENT_TYPE_NODE: 10,
1747      DOCUMENT_FRAGMENT_NODE: 11,
1748      NOTATION_NODE: 12
1749    });
1750  }
1751  
1752  
1753  (function(global) {
1754  
1755    var SETATTRIBUTE_IGNORES_NAME = (function(){
1756      var elForm = document.createElement("form");
1757      var elInput = document.createElement("input");
1758      var root = document.documentElement;
1759      elInput.setAttribute("name", "test");
1760      elForm.appendChild(elInput);
1761      root.appendChild(elForm);
1762      var isBuggy = elForm.elements
1763        ? (typeof elForm.elements.test == "undefined")
1764        : null;
1765      root.removeChild(elForm);
1766      elForm = elInput = null;
1767      return isBuggy;
1768    })();
1769  
1770    var element = global.Element;
1771    global.Element = function(tagName, attributes) {
1772      attributes = attributes || { };
1773      tagName = tagName.toLowerCase();
1774      var cache = Element.cache;
1775      if (SETATTRIBUTE_IGNORES_NAME && attributes.name) {
1776        tagName = '<' + tagName + ' name="' + attributes.name + '">';
1777        delete attributes.name;
1778        return Element.writeAttribute(document.createElement(tagName), attributes);
1779      }
1780      if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1781      return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1782    };
1783    Object.extend(global.Element, element || { });
1784    if (element) global.Element.prototype = element.prototype;
1785  })(this);
1786  
1787  Element.cache = { };
1788  Element.idCounter = 1;
1789  
1790  Element.Methods = {
1791    visible: function(element) {
1792      return $(element).style.display != 'none';
1793    },
1794  
1795    toggle: function(element) {
1796      element = $(element);
1797      Element[Element.visible(element) ? 'hide' : 'show'](element);
1798      return element;
1799    },
1800  
1801  
1802    hide: function(element) {
1803      element = $(element);
1804      element.style.display = 'none';
1805      return element;
1806    },
1807  
1808    show: function(element) {
1809      element = $(element);
1810      element.style.display = '';
1811      return element;
1812    },
1813  
1814    remove: function(element) {
1815      element = $(element);
1816      element.parentNode.removeChild(element);
1817      return element;
1818    },
1819  
1820    update: (function(){
1821  
1822      var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1823        var el = document.createElement("select"),
1824            isBuggy = true;
1825        el.innerHTML = "<option value=\"test\">test</option>";
1826        if (el.options && el.options[0]) {
1827          isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1828        }
1829        el = null;
1830        return isBuggy;
1831      })();
1832  
1833      var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1834        try {
1835          var el = document.createElement("table");
1836          if (el && el.tBodies) {
1837            el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1838            var isBuggy = typeof el.tBodies[0] == "undefined";
1839            el = null;
1840            return isBuggy;
1841          }
1842        } catch (e) {
1843          return true;
1844        }
1845      })();
1846  
1847      var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1848        var s = document.createElement("script"),
1849            isBuggy = false;
1850        try {
1851          s.appendChild(document.createTextNode(""));
1852          isBuggy = !s.firstChild ||
1853            s.firstChild && s.firstChild.nodeType !== 3;
1854        } catch (e) {
1855          isBuggy = true;
1856        }
1857        s = null;
1858        return isBuggy;
1859      })();
1860  
1861      function update(element, content) {
1862        element = $(element);
1863  
1864        if (content && content.toElement)
1865          content = content.toElement();
1866  
1867        if (Object.isElement(content))
1868          return element.update().insert(content);
1869  
1870        content = Object.toHTML(content);
1871  
1872        var tagName = element.tagName.toUpperCase();
1873  
1874        if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
1875          element.text = content;
1876          return element;
1877        }
1878  
1879        if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
1880          if (tagName in Element._insertionTranslations.tags) {
1881            while (element.firstChild) {
1882              element.removeChild(element.firstChild);
1883            }
1884            Element._getContentFromAnonymousElement(tagName, content.stripScripts())
1885              .each(function(node) {
1886                element.appendChild(node)
1887              });
1888          }
1889          else {
1890            element.innerHTML = content.stripScripts();
1891          }
1892        }
1893        else {
1894          element.innerHTML = content.stripScripts();
1895        }
1896  
1897        content.evalScripts.bind(content).defer();
1898        return element;
1899      }
1900  
1901      return update;
1902    })(),
1903  
1904    replace: function(element, content) {
1905      element = $(element);
1906      if (content && content.toElement) content = content.toElement();
1907      else if (!Object.isElement(content)) {
1908        content = Object.toHTML(content);
1909        var range = element.ownerDocument.createRange();
1910        range.selectNode(element);
1911        content.evalScripts.bind(content).defer();
1912        content = range.createContextualFragment(content.stripScripts());
1913      }
1914      element.parentNode.replaceChild(content, element);
1915      return element;
1916    },
1917  
1918    insert: function(element, insertions) {
1919      element = $(element);
1920  
1921      if (Object.isString(insertions) || Object.isNumber(insertions) ||
1922          Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1923            insertions = {bottom:insertions};
1924  
1925      var content, insert, tagName, childNodes;
1926  
1927      for (var position in insertions) {
1928        content  = insertions[position];
1929        position = position.toLowerCase();
1930        insert = Element._insertionTranslations[position];
1931  
1932        if (content && content.toElement) content = content.toElement();
1933        if (Object.isElement(content)) {
1934          insert(element, content);
1935          continue;
1936        }
1937  
1938        content = Object.toHTML(content);
1939  
1940        tagName = ((position == 'before' || position == 'after')
1941          ? element.parentNode : element).tagName.toUpperCase();
1942  
1943        childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
1944  
1945        if (position == 'top' || position == 'after') childNodes.reverse();
1946        childNodes.each(insert.curry(element));
1947  
1948        content.evalScripts.bind(content).defer();
1949      }
1950  
1951      return element;
1952    },
1953  
1954    wrap: function(element, wrapper, attributes) {
1955      element = $(element);
1956      if (Object.isElement(wrapper))
1957        $(wrapper).writeAttribute(attributes || { });
1958      else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1959      else wrapper = new Element('div', wrapper);
1960      if (element.parentNode)
1961        element.parentNode.replaceChild(wrapper, element);
1962      wrapper.appendChild(element);
1963      return wrapper;
1964    },
1965  
1966    inspect: function(element) {
1967      element = $(element);
1968      var result = '<' + element.tagName.toLowerCase();
1969      $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1970        var property = pair.first(), attribute = pair.last();
1971        var value = (element[property] || '').toString();
1972        if (value) result += ' ' + attribute + '=' + value.inspect(true);
1973      });
1974      return result + '>';
1975    },
1976  
1977    recursivelyCollect: function(element, property) {
1978      element = $(element);
1979      var elements = [];
1980      while (element = element[property])
1981        if (element.nodeType == 1)
1982          elements.push(Element.extend(element));
1983      return elements;
1984    },
1985  
1986    ancestors: function(element) {
1987      return Element.recursivelyCollect(element, 'parentNode');
1988    },
1989  
1990    descendants: function(element) {
1991      return Element.select(element, "*");
1992    },
1993  
1994    firstDescendant: function(element) {
1995      element = $(element).firstChild;
1996      while (element && element.nodeType != 1) element = element.nextSibling;
1997      return $(element);
1998    },
1999  
2000    immediateDescendants: function(element) {
2001      if (!(element = $(element).firstChild)) return [];
2002      while (element && element.nodeType != 1) element = element.nextSibling;
2003      if (element) return [element].concat($(element).nextSiblings());
2004      return [];
2005    },
2006  
2007    previousSiblings: function(element) {
2008      return Element.recursivelyCollect(element, 'previousSibling');
2009    },
2010  
2011    nextSiblings: function(element) {
2012      return Element.recursivelyCollect(element, 'nextSibling');
2013    },
2014  
2015    siblings: function(element) {
2016      element = $(element);
2017      return Element.previousSiblings(element).reverse()
2018        .concat(Element.nextSiblings(element));
2019    },
2020  
2021    match: function(element, selector) {
2022      if (Object.isString(selector))
2023        selector = new Selector(selector);
2024      return selector.match($(element));
2025    },
2026  
2027    up: function(element, expression, index) {
2028      element = $(element);
2029      if (arguments.length == 1) return $(element.parentNode);
2030      var ancestors = Element.ancestors(element);
2031      return Object.isNumber(expression) ? ancestors[expression] :
2032        Selector.findElement(ancestors, expression, index);
2033    },
2034  
2035    down: function(element, expression, index) {
2036      element = $(element);
2037      if (arguments.length == 1) return Element.firstDescendant(element);
2038      return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2039        Element.select(element, expression)[index || 0];
2040    },
2041  
2042    previous: function(element, expression, index) {
2043      element = $(element);
2044      if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
2045      var previousSiblings = Element.previousSiblings(element);
2046      return Object.isNumber(expression) ? previousSiblings[expression] :
2047        Selector.findElement(previousSiblings, expression, index);
2048    },
2049  
2050    next: function(element, expression, index) {
2051      element = $(element);
2052      if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
2053      var nextSiblings = Element.nextSiblings(element);
2054      return Object.isNumber(expression) ? nextSiblings[expression] :
2055        Selector.findElement(nextSiblings, expression, index);
2056    },
2057  
2058  
2059    select: function(element) {
2060      var args = Array.prototype.slice.call(arguments, 1);
2061      return Selector.findChildElements(element, args);
2062    },
2063  
2064    adjacent: function(element) {
2065      var args = Array.prototype.slice.call(arguments, 1);
2066      return Selector.findChildElements(element.parentNode, args).without(element);
2067    },
2068  
2069    identify: function(element) {
2070      element = $(element);
2071      var id = Element.readAttribute(element, 'id');
2072      if (id) return id;
2073      do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2074      Element.writeAttribute(element, 'id', id);
2075      return id;
2076    },
2077  
2078    readAttribute: function(element, name) {
2079      element = $(element);
2080      if (Prototype.Browser.IE) {
2081        var t = Element._attributeTranslations.read;
2082        if (t.values[name]) return t.values[name](element, name);
2083        if (t.names[name]) name = t.names[name];
2084        if (name.include(':')) {
2085          return (!element.attributes || !element.attributes[name]) ? null :
2086           element.attributes[name].value;
2087        }
2088      }
2089      return element.getAttribute(name);
2090    },
2091  
2092    writeAttribute: function(element, name, value) {
2093      element = $(element);
2094      var attributes = { }, t = Element._attributeTranslations.write;
2095  
2096      if (typeof name == 'object') attributes = name;
2097      else attributes[name] = Object.isUndefined(value) ? true : value;
2098  
2099      for (var attr in attributes) {
2100        name = t.names[attr] || attr;
2101        value = attributes[attr];
2102        if (t.values[attr]) name = t.values[attr](element, value);
2103        if (value === false || value === null)
2104          element.removeAttribute(name);
2105        else if (value === true)
2106          element.setAttribute(name, name);
2107        else element.setAttribute(name, value);
2108      }
2109      return element;
2110    },
2111  
2112    getHeight: function(element) {
2113      return Element.getDimensions(element).height;
2114    },
2115  
2116    getWidth: function(element) {
2117      return Element.getDimensions(element).width;
2118    },
2119  
2120    classNames: function(element) {
2121      return new Element.ClassNames(element);
2122    },
2123  
2124    hasClassName: function(element, className) {
2125      if (!(element = $(element))) return;
2126      var elementClassName = element.className;
2127      return (elementClassName.length > 0 && (elementClassName == className ||
2128        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2129    },
2130  
2131    addClassName: function(element, className) {
2132      if (!(element = $(element))) return;
2133      if (!Element.hasClassName(element, className))
2134        element.className += (element.className ? ' ' : '') + className;
2135      return element;
2136    },
2137  
2138    removeClassName: function(element, className) {
2139      if (!(element = $(element))) return;
2140      element.className = element.className.replace(
2141        new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2142      return element;
2143    },
2144  
2145    toggleClassName: function(element, className) {
2146      if (!(element = $(element))) return;
2147      return Element[Element.hasClassName(element, className) ?
2148        'removeClassName' : 'addClassName'](element, className);
2149    },
2150  
2151    cleanWhitespace: function(element) {
2152      element = $(element);
2153      var node = element.firstChild;
2154      while (node) {
2155        var nextNode = node.nextSibling;
2156        if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2157          element.removeChild(node);
2158        node = nextNode;
2159      }
2160      return element;
2161    },
2162  
2163    empty: function(element) {
2164      return $(element).innerHTML.blank();
2165    },
2166  
2167    descendantOf: function(element, ancestor) {
2168      element = $(element), ancestor = $(ancestor);
2169  
2170      if (element.compareDocumentPosition)
2171        return (element.compareDocumentPosition(ancestor) & 8) === 8;
2172  
2173      if (ancestor.contains)
2174        return ancestor.contains(element) && ancestor !== element;
2175  
2176      while (element = element.parentNode)
2177        if (element == ancestor) return true;
2178  
2179      return false;
2180    },
2181  
2182    scrollTo: function(element) {
2183      element = $(element);
2184      var pos = Element.cumulativeOffset(element);
2185      window.scrollTo(pos[0], pos[1]);
2186      return element;
2187    },
2188  
2189    getStyle: function(element, style) {
2190      element = $(element);
2191      style = style == 'float' ? 'cssFloat' : style.camelize();
2192      var value = element.style[style];
2193      if (!value || value == 'auto') {
2194        var css = document.defaultView.getComputedStyle(element, null);
2195        value = css ? css[style] : null;
2196      }
2197      if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2198      return value == 'auto' ? null : value;
2199    },
2200  
2201    getOpacity: function(element) {
2202      return $(element).getStyle('opacity');
2203    },
2204  
2205    setStyle: function(element, styles) {
2206      element = $(element);
2207      var elementStyle = element.style, match;
2208      if (Object.isString(styles)) {
2209        element.style.cssText += ';' + styles;
2210        return styles.include('opacity') ?
2211          element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2212      }
2213      for (var property in styles)
2214        if (property == 'opacity') element.setOpacity(styles[property]);
2215        else
2216          elementStyle[(property == 'float' || property == 'cssFloat') ?
2217            (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2218              property] = styles[property];
2219  
2220      return element;
2221    },
2222  
2223    setOpacity: function(element, value) {
2224      element = $(element);
2225      element.style.opacity = (value == 1 || value === '') ? '' :
2226        (value < 0.00001) ? 0 : value;
2227      return element;
2228    },
2229  
2230    getDimensions: function(element) {
2231      element = $(element);
2232      var display = Element.getStyle(element, 'display');
2233      if (display != 'none' && display != null) // Safari bug
2234        return {width: element.offsetWidth, height: element.offsetHeight};
2235  
2236      var els = element.style;
2237      var originalVisibility = els.visibility;
2238      var originalPosition = els.position;
2239      var originalDisplay = els.display;
2240      els.visibility = 'hidden';
2241      if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
2242        els.position = 'absolute';
2243      els.display = 'block';
2244      var originalWidth = element.clientWidth;
2245      var originalHeight = element.clientHeight;
2246      els.display = originalDisplay;
2247      els.position = originalPosition;
2248      els.visibility = originalVisibility;
2249      return {width: originalWidth, height: originalHeight};
2250    },
2251  
2252    makePositioned: function(element) {
2253      element = $(element);
2254      var pos = Element.getStyle(element, 'position');
2255      if (pos == 'static' || !pos) {
2256        element._madePositioned = true;
2257        element.style.position = 'relative';
2258        if (Prototype.Browser.Opera) {
2259          element.style.top = 0;
2260          element.style.left = 0;
2261        }
2262      }
2263      return element;
2264    },
2265  
2266    undoPositioned: function(element) {
2267      element = $(element);
2268      if (element._madePositioned) {
2269        element._madePositioned = undefined;
2270        element.style.position =
2271          element.style.top =
2272          element.style.left =
2273          element.style.bottom =
2274          element.style.right = '';
2275      }
2276      return element;
2277    },
2278  
2279    makeClipping: function(element) {
2280      element = $(element);
2281      if (element._overflow) return element;
2282      element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2283      if (element._overflow !== 'hidden')
2284        element.style.overflow = 'hidden';
2285      return element;
2286    },
2287  
2288    undoClipping: function(element) {
2289      element = $(element);
2290      if (!element._overflow) return element;
2291      element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2292      element._overflow = null;
2293      return element;
2294    },
2295  
2296    cumulativeOffset: function(element) {
2297      var valueT = 0, valueL = 0;
2298      do {
2299        valueT += element.offsetTop  || 0;
2300        valueL += element.offsetLeft || 0;
2301        element = element.offsetParent;
2302      } while (element);
2303      return Element._returnOffset(valueL, valueT);
2304    },
2305  
2306    positionedOffset: function(element) {
2307      var valueT = 0, valueL = 0;
2308      do {
2309        valueT += element.offsetTop  || 0;
2310        valueL += element.offsetLeft || 0;
2311        element = element.offsetParent;
2312        if (element) {
2313          if (element.tagName.toUpperCase() == 'BODY') break;
2314          var p = Element.getStyle(element, 'position');
2315          if (p !== 'static') break;
2316        }
2317      } while (element);
2318      return Element._returnOffset(valueL, valueT);
2319    },
2320  
2321    absolutize: function(element) {
2322      element = $(element);
2323      if (Element.getStyle(element, 'position') == 'absolute') return element;
2324  
2325      var offsets = Element.positionedOffset(element);
2326      var top     = offsets[1];
2327      var left    = offsets[0];
2328      var width   = element.clientWidth;
2329      var height  = element.clientHeight;
2330  
2331      element._originalLeft   = left - parseFloat(element.style.left  || 0);
2332      element._originalTop    = top  - parseFloat(element.style.top || 0);
2333      element._originalWidth  = element.style.width;
2334      element._originalHeight = element.style.height;
2335  
2336      element.style.position = 'absolute';
2337      element.style.top    = top + 'px';
2338      element.style.left   = left + 'px';
2339      element.style.width  = width + 'px';
2340      element.style.height = height + 'px';
2341      return element;
2342    },
2343  
2344    relativize: function(element) {
2345      element = $(element);
2346      if (Element.getStyle(element, 'position') == 'relative') return element;
2347  
2348      element.style.position = 'relative';
2349      var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
2350      var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2351  
2352      element.style.top    = top + 'px';
2353      element.style.left   = left + 'px';
2354      element.style.height = element._originalHeight;
2355      element.style.width  = element._originalWidth;
2356      return element;
2357    },
2358  
2359    cumulativeScrollOffset: function(element) {
2360      var valueT = 0, valueL = 0;
2361      do {
2362        valueT += element.scrollTop  || 0;
2363        valueL += element.scrollLeft || 0;
2364        element = element.parentNode;
2365      } while (element);
2366      return Element._returnOffset(valueL, valueT);
2367    },
2368  
2369    getOffsetParent: function(element) {
2370      if (element.offsetParent) return $(element.offsetParent);
2371      if (element == document.body) return $(element);
2372  
2373      while ((element = element.parentNode) && element != document.body)
2374        if (Element.getStyle(element, 'position') != 'static')
2375          return $(element);
2376  
2377      return $(document.body);
2378    },
2379  
2380    viewportOffset: function(forElement) {
2381      var valueT = 0, valueL = 0;
2382  
2383      var element = forElement;
2384      do {
2385        valueT += element.offsetTop  || 0;
2386        valueL += element.offsetLeft || 0;
2387  
2388        if (element.offsetParent == document.body &&
2389          Element.getStyle(element, 'position') == 'absolute') break;
2390  
2391      } while (element = element.offsetParent);
2392  
2393      element = forElement;
2394      do {
2395        if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
2396          valueT -= element.scrollTop  || 0;
2397          valueL -= element.scrollLeft || 0;
2398        }
2399      } while (element = element.parentNode);
2400  
2401      return Element._returnOffset(valueL, valueT);
2402    },
2403  
2404    clonePosition: function(element, source) {
2405      var options = Object.extend({
2406        setLeft:    true,
2407        setTop:     true,
2408        setWidth:   true,
2409        setHeight:  true,
2410        offsetTop:  0,
2411        offsetLeft: 0
2412      }, arguments[2] || { });
2413  
2414      source = $(source);
2415      var p = Element.viewportOffset(source);
2416  
2417      element = $(element);
2418      var delta = [0, 0];
2419      var parent = null;
2420      if (Element.getStyle(element, 'position') == 'absolute') {
2421        parent = Element.getOffsetParent(element);
2422        delta = Element.viewportOffset(parent);
2423      }
2424  
2425      if (parent == document.body) {
2426        delta[0] -= document.body.offsetLeft;
2427        delta[1] -= document.body.offsetTop;
2428      }
2429  
2430      if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2431      if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2432      if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
2433      if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2434      return element;
2435    }
2436  };
2437  
2438  Object.extend(Element.Methods, {
2439    getElementsBySelector: Element.Methods.select,
2440  
2441    childElements: Element.Methods.immediateDescendants
2442  });
2443  
2444  Element._attributeTranslations = {
2445    write: {
2446      names: {
2447        className: 'class',
2448        htmlFor:   'for'
2449      },
2450      values: { }
2451    }
2452  };
2453  
2454  if (Prototype.Browser.Opera) {
2455    Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2456      function(proceed, element, style) {
2457        switch (style) {
2458          case 'left': case 'top': case 'right': case 'bottom':
2459            if (proceed(element, 'position') === 'static') return null;
2460          case 'height': case 'width':
2461            if (!Element.visible(element)) return null;
2462  
2463            var dim = parseInt(proceed(element, style), 10);
2464  
2465            if (dim !== element['offset' + style.capitalize()])
2466              return dim + 'px';
2467  
2468            var properties;
2469            if (style === 'height') {
2470              properties = ['border-top-width', 'padding-top',
2471               'padding-bottom', 'border-bottom-width'];
2472            }
2473            else {
2474              properties = ['border-left-width', 'padding-left',
2475               'padding-right', 'border-right-width'];
2476            }
2477            return properties.inject(dim, function(memo, property) {
2478              var val = proceed(element, property);
2479              return val === null ? memo : memo - parseInt(val, 10);
2480            }) + 'px';
2481          default: return proceed(element, style);
2482        }
2483      }
2484    );
2485  
2486    Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2487      function(proceed, element, attribute) {
2488        if (attribute === 'title') return element.title;
2489        return proceed(element, attribute);
2490      }
2491    );
2492  }
2493  
2494  else if (Prototype.Browser.IE) {
2495    Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
2496      function(proceed, element) {
2497        element = $(element);
2498        try { element.offsetParent }
2499        catch(e) { return $(document.body) }
2500        var position = element.getStyle('position');
2501        if (position !== 'static') return proceed(element);
2502        element.setStyle({ position: 'relative' });
2503        var value = proceed(element);
2504        element.setStyle({ position: position });
2505        return value;
2506      }
2507    );
2508  
2509    $w('positionedOffset viewportOffset').each(function(method) {
2510      Element.Methods[method] = Element.Methods[method].wrap(
2511        function(proceed, element) {
2512          element = $(element);
2513          try { element.offsetParent }
2514          catch(e) { return Element._returnOffset(0,0) }
2515          var position = element.getStyle('position');
2516          if (position !== 'static') return proceed(element);
2517          var offsetParent = element.getOffsetParent();
2518          if (offsetParent && offsetParent.getStyle('position') === 'fixed')
2519            offsetParent.setStyle({ zoom: 1 });
2520          element.setStyle({ position: 'relative' });
2521          var value = proceed(element);
2522          element.setStyle({ position: position });
2523          return value;
2524        }
2525      );
2526    });
2527  
2528    Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
2529      function(proceed, element) {
2530        try { element.offsetParent }
2531        catch(e) { return Element._returnOffset(0,0) }
2532        return proceed(element);
2533      }
2534    );
2535  
2536    Element.Methods.getStyle = function(element, style) {
2537      element = $(element);
2538      style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2539      var value = element.style[style];
2540      if (!value && element.currentStyle) value = element.currentStyle[style];
2541  
2542      if (style == 'opacity') {
2543        if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2544          if (value[1]) return parseFloat(value[1]) / 100;
2545        return 1.0;
2546      }
2547  
2548      if (value == 'auto') {
2549        if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2550          return element['offset' + style.capitalize()] + 'px';
2551        return null;
2552      }
2553      return value;
2554    };
2555  
2556    Element.Methods.setOpacity = function(element, value) {
2557      function stripAlpha(filter){
2558        return filter.replace(/alpha\([^\)]*\)/gi,'');
2559      }
2560      element = $(element);
2561      var currentStyle = element.currentStyle;
2562      if ((currentStyle && !currentStyle.hasLayout) ||
2563        (!currentStyle && element.style.zoom == 'normal'))
2564          element.style.zoom = 1;
2565  
2566      var filter = element.getStyle('filter'), style = element.style;
2567      if (value == 1 || value === '') {
2568        (filter = stripAlpha(filter)) ?
2569          style.filter = filter : style.removeAttribute('filter');
2570        return element;
2571      } else if (value < 0.00001) value = 0;
2572      style.filter = stripAlpha(filter) +
2573        'alpha(opacity=' + (value * 100) + ')';
2574      return element;
2575    };
2576  
2577    Element._attributeTranslations = (function(){
2578  
2579      var classProp = 'className';
2580      var forProp = 'for';
2581  
2582      var el = document.createElement('div');
2583  
2584      el.setAttribute(classProp, 'x');
2585  
2586      if (el.className !== 'x') {
2587        el.setAttribute('class', 'x');
2588        if (el.className === 'x') {
2589          classProp = 'class';
2590        }
2591      }
2592      el = null;
2593  
2594      el = document.createElement('label');
2595      el.setAttribute(forProp, 'x');
2596      if (el.htmlFor !== 'x') {
2597        el.setAttribute('htmlFor', 'x');
2598        if (el.htmlFor === 'x') {
2599          forProp = 'htmlFor';
2600        }
2601      }
2602      el = null;
2603  
2604      return {
2605        read: {
2606          names: {
2607            'class':      classProp,
2608            'className':  classProp,
2609            'for':        forProp,
2610            'htmlFor':    forProp
2611          },
2612          values: {
2613            _getAttr: function(element, attribute) {
2614              return element.getAttribute(attribute);
2615            },
2616            _getAttr2: function(element, attribute) {
2617              return element.getAttribute(attribute, 2);
2618            },
2619            _getAttrNode: function(element, attribute) {
2620              var node = element.getAttributeNode(attribute);
2621              return node ? node.value : "";
2622            },
2623            _getEv: (function(){
2624  
2625              var el = document.createElement('div');
2626              el.onclick = Prototype.emptyFunction;
2627              var value = el.getAttribute('onclick');
2628              var f;
2629  
2630              if (String(value).indexOf('{') > -1) {
2631                f = function(element, attribute) {
2632                  attribute = element.getAttribute(attribute);
2633                  if (!attribute) return null;
2634                  attribute = attribute.toString();
2635                  attribute = attribute.split('{')[1];
2636                  attribute = attribute.split('}')[0];
2637                  return attribute.strip();
2638                };
2639              }
2640              else if (value === '') {
2641                f = function(element, attribute) {
2642                  attribute = element.getAttribute(attribute);
2643                  if (!attribute) return null;
2644                  return attribute.strip();
2645                };
2646              }
2647              el = null;
2648              return f;
2649            })(),
2650            _flag: function(element, attribute) {
2651              return $(element).hasAttribute(attribute) ? attribute : null;
2652            },
2653            style: function(element) {
2654              return element.style.cssText.toLowerCase();
2655            },
2656            title: function(element) {
2657              return element.title;
2658            }
2659          }
2660        }
2661      }
2662    })();
2663  
2664    Element._attributeTranslations.write = {
2665      names: Object.extend({
2666        cellpadding: 'cellPadding',
2667        cellspacing: 'cellSpacing'
2668      }, Element._attributeTranslations.read.names),
2669      values: {
2670        checked: function(element, value) {
2671          element.checked = !!value;
2672        },
2673  
2674        style: function(element, value) {
2675          element.style.cssText = value ? value : '';
2676        }
2677      }
2678    };
2679  
2680    Element._attributeTranslations.has = {};
2681  
2682    $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2683        'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2684      Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2685      Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2686    });
2687  
2688    (function(v) {
2689      Object.extend(v, {
2690        href:        v._getAttr2,
2691        src:         v._getAttr2,
2692        type:        v._getAttr,
2693        action:      v._getAttrNode,
2694        disabled:    v._flag,
2695        checked:     v._flag,
2696        readonly:    v._flag,
2697        multiple:    v._flag,
2698        onload:      v._getEv,
2699        onunload:    v._getEv,
2700        onclick:     v._getEv,
2701        ondblclick:  v._getEv,
2702        onmousedown: v._getEv,
2703        onmouseup:   v._getEv,
2704        onmouseover: v._getEv,
2705        onmousemove: v._getEv,
2706        onmouseout:  v._getEv,
2707        onfocus:     v._getEv,
2708        onblur:      v._getEv,
2709        onkeypress:  v._getEv,
2710        onkeydown:   v._getEv,
2711        onkeyup:     v._getEv,
2712        onsubmit:    v._getEv,
2713        onreset:     v._getEv,
2714        onselect:    v._getEv,
2715        onchange:    v._getEv
2716      });
2717    })(Element._attributeTranslations.read.values);
2718  
2719    if (Prototype.BrowserFeatures.ElementExtensions) {
2720      (function() {
2721        function _descendants(element) {
2722          var nodes = element.getElementsByTagName('*'), results = [];
2723          for (var i = 0, node; node = nodes[i]; i++)
2724            if (node.tagName !== "!") // Filter out comment nodes.
2725              results.push(node);
2726          return results;
2727        }
2728  
2729        Element.Methods.down = function(element, expression, index) {
2730          element = $(element);
2731          if (arguments.length == 1) return element.firstDescendant();
2732          return Object.isNumber(expression) ? _descendants(element)[expression] :
2733            Element.select(element, expression)[index || 0];
2734        }
2735      })();
2736    }
2737  
2738  }
2739  
2740  else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2741    Element.Methods.setOpacity = function(element, value) {
2742      element = $(element);
2743      element.style.opacity = (value == 1) ? 0.999999 :
2744        (value === '') ? '' : (value < 0.00001) ? 0 : value;
2745      return element;
2746    };
2747  }
2748  
2749  else if (Prototype.Browser.WebKit) {
2750    Element.Methods.setOpacity = function(element, value) {
2751      element = $(element);
2752      element.style.opacity = (value == 1 || value === '') ? '' :
2753        (value < 0.00001) ? 0 : value;
2754  
2755      if (value == 1)
2756        if(element.tagName.toUpperCase() == 'IMG' && element.width) {
2757          element.width++; element.width--;
2758        } else try {
2759          var n = document.createTextNode(' ');
2760          element.appendChild(n);
2761          element.removeChild(n);
2762        } catch (e) { }
2763  
2764      return element;
2765    };
2766  
2767    Element.Methods.cumulativeOffset = function(element) {
2768      var valueT = 0, valueL = 0;
2769      do {
2770        valueT += element.offsetTop  || 0;
2771        valueL += element.offsetLeft || 0;
2772        if (element.offsetParent == document.body)
2773          if (Element.getStyle(element, 'position') == 'absolute') break;
2774  
2775        element = element.offsetParent;
2776      } while (element);
2777  
2778      return Element._returnOffset(valueL, valueT);
2779    };
2780  }
2781  
2782  if ('outerHTML' in document.documentElement) {
2783    Element.Methods.replace = function(element, content) {
2784      element = $(element);
2785  
2786      if (content && content.toElement) content = content.toElement();
2787      if (Object.isElement(content)) {
2788        element.parentNode.replaceChild(content, element);
2789        return element;
2790      }
2791  
2792      content = Object.toHTML(content);
2793      var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2794  
2795      if (Element._insertionTranslations.tags[tagName]) {
2796        var nextSibling = element.next();
2797        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2798        parent.removeChild(element);
2799        if (nextSibling)
2800          fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2801        else
2802          fragments.each(function(node) { parent.appendChild(node) });
2803      }
2804      else element.outerHTML = content.stripScripts();
2805  
2806      content.evalScripts.bind(content).defer();
2807      return element;
2808    };
2809  }
2810  
2811  Element._returnOffset = function(l, t) {
2812    var result = [l, t];
2813    result.left = l;
2814    result.top = t;
2815    return result;
2816  };
2817  
2818  Element._getContentFromAnonymousElement = function(tagName, html) {
2819    var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
2820    if (t) {
2821      div.innerHTML = t[0] + html + t[1];
2822      t[2].times(function() { div = div.firstChild });
2823    } else div.innerHTML = html;
2824    return $A(div.childNodes);
2825  };
2826  
2827  Element._insertionTranslations = {
2828    before: function(element, node) {
2829      element.parentNode.insertBefore(node, element);
2830    },
2831    top: function(element, node) {
2832      element.insertBefore(node, element.firstChild);
2833    },
2834    bottom: function(element, node) {
2835      element.appendChild(node);
2836    },
2837    after: function(element, node) {
2838      element.parentNode.insertBefore(node, element.nextSibling);
2839    },
2840    tags: {
2841      TABLE:  ['<table>',                '</table>',                   1],
2842      TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
2843      TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
2844      TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2845      SELECT: ['<select>',               '</select>',                  1]
2846    }
2847  };
2848  
2849  (function() {
2850    var tags = Element._insertionTranslations.tags;
2851    Object.extend(tags, {
2852      THEAD: tags.TBODY,
2853      TFOOT: tags.TBODY,
2854      TH:    tags.TD
2855    });
2856  })();
2857  
2858  Element.Methods.Simulated = {
2859    hasAttribute: function(element, attribute) {
2860      attribute = Element._attributeTranslations.has[attribute] || attribute;
2861      var node = $(element).getAttributeNode(attribute);
2862      return !!(node && node.specified);
2863    }
2864  };
2865  
2866  Element.Methods.ByTag = { };
2867  
2868  Object.extend(Element, Element.Methods);
2869  
2870  (function(div) {
2871  
2872    if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
2873      window.HTMLElement = { };
2874      window.HTMLElement.prototype = div['__proto__'];
2875      Prototype.BrowserFeatures.ElementExtensions = true;
2876    }
2877  
2878    div = null;
2879  
2880  })(document.createElement('div'))
2881  
2882  Element.extend = (function() {
2883  
2884    function checkDeficiency(tagName) {
2885      if (typeof window.Element != 'undefined') {
2886        var proto = window.Element.prototype;
2887        if (proto) {
2888          var id = '_' + (Math.random()+'').slice(2);
2889          var el = document.createElement(tagName);
2890          proto[id] = 'x';
2891          var isBuggy = (el[id] !== 'x');
2892          delete proto[id];
2893          el = null;
2894          return isBuggy;
2895        }
2896      }
2897      return false;
2898    }
2899  
2900    function extendElementWith(element, methods) {
2901      for (var property in methods) {
2902        var value = methods[property];
2903        if (Object.isFunction(value) && !(property in element))
2904          element[property] = value.methodize();
2905      }
2906    }
2907  
2908    var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
2909  
2910    if (Prototype.BrowserFeatures.SpecificElementExtensions) {
2911      if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
2912        return function(element) {
2913          if (element && typeof element._extendedByPrototype == 'undefined') {
2914            var t = element.tagName;
2915            if (t && (/^(?:object|applet|embed)$/i.test(t))) {
2916              extendElementWith(element, Element.Methods);
2917              extendElementWith(element, Element.Methods.Simulated);
2918              extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
2919            }
2920          }
2921          return element;
2922        }
2923      }
2924      return Prototype.K;
2925    }
2926  
2927    var Methods = { }, ByTag = Element.Methods.ByTag;
2928  
2929    var extend = Object.extend(function(element) {
2930      if (!element || typeof element._extendedByPrototype != 'undefined' ||
2931          element.nodeType != 1 || element == window) return element;
2932  
2933      var methods = Object.clone(Methods),
2934          tagName = element.tagName.toUpperCase();
2935  
2936      if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2937  
2938      extendElementWith(element, methods);
2939  
2940      element._extendedByPrototype = Prototype.emptyFunction;
2941      return element;
2942  
2943    }, {
2944      refresh: function() {
2945        if (!Prototype.BrowserFeatures.ElementExtensions) {
2946          Object.extend(Methods, Element.Methods);
2947          Object.extend(Methods, Element.Methods.Simulated);
2948        }
2949      }
2950    });
2951  
2952    extend.refresh();
2953    return extend;
2954  })();
2955  
2956  Element.hasAttribute = function(element, attribute) {
2957    if (element.hasAttribute) return element.hasAttribute(attribute);
2958    return Element.Methods.Simulated.hasAttribute(element, attribute);
2959  };
2960  
2961  Element.addMethods = function(methods) {
2962    var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2963  
2964    if (!methods) {
2965      Object.extend(Form, Form.Methods);
2966      Object.extend(Form.Element, Form.Element.Methods);
2967      Object.extend(Element.Methods.ByTag, {
2968        "FORM":     Object.clone(Form.Methods),
2969        "INPUT":    Object.clone(Form.Element.Methods),
2970        "SELECT":   Object.clone(Form.Element.Methods),
2971        "TEXTAREA": Object.clone(Form.Element.Methods)
2972      });
2973    }
2974  
2975    if (arguments.length == 2) {
2976      var tagName = methods;
2977      methods = arguments[1];
2978    }
2979  
2980    if (!tagName) Object.extend(Element.Methods, methods || { });
2981    else {
2982      if (Object.isArray(tagName)) tagName.each(extend);
2983      else extend(tagName);
2984    }
2985  
2986    function extend(tagName) {
2987      tagName = tagName.toUpperCase();
2988      if (!Element.Methods.ByTag[tagName])
2989        Element.Methods.ByTag[tagName] = { };
2990      Object.extend(Element.Methods.ByTag[tagName], methods);
2991    }
2992  
2993    function copy(methods, destination, onlyIfAbsent) {
2994      onlyIfAbsent = onlyIfAbsent || false;
2995      for (var property in methods) {
2996        var value = methods[property];
2997        if (!Object.isFunction(value)) continue;
2998        if (!onlyIfAbsent || !(property in destination))
2999          destination[property] = value.methodize();
3000      }
3001    }
3002  
3003    function findDOMClass(tagName) {
3004      var klass;
3005      var trans = {
3006        "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3007        "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3008        "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3009        "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3010        "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3011        "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3012        "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3013        "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3014        "FrameSet", "IFRAME": "IFrame"
3015      };
3016      if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3017      if (window[klass]) return window[klass];
3018      klass = 'HTML' + tagName + 'Element';
3019      if (window[klass]) return window[klass];
3020      klass = 'HTML' + tagName.capitalize() + 'Element';
3021      if (window[klass]) return window[klass];
3022  
3023      var element = document.createElement(tagName);
3024      var proto = element['__proto__'] || element.constructor.prototype;
3025      element = null;
3026      return proto;
3027    }
3028  
3029    var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3030     Element.prototype;
3031  
3032    if (F.ElementExtensions) {
3033      copy(Element.Methods, elementPrototype);
3034      copy(Element.Methods.Simulated, elementPrototype, true);
3035    }
3036  
3037    if (F.SpecificElementExtensions) {
3038      for (var tag in Element.Methods.ByTag) {
3039        var klass = findDOMClass(tag);
3040        if (Object.isUndefined(klass)) continue;
3041        copy(T[tag], klass.prototype);
3042      }
3043    }
3044  
3045    Object.extend(Element, Element.Methods);
3046    delete Element.ByTag;
3047  
3048    if (Element.extend.refresh) Element.extend.refresh();
3049    Element.cache = { };
3050  };
3051  
3052  
3053  document.viewport = {
3054  
3055    getDimensions: function() {
3056      return { width: this.getWidth(), height: this.getHeight() };
3057    },
3058  
3059    getScrollOffsets: function() {
3060      return Element._returnOffset(
3061        window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3062        window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
3063    }
3064  };
3065  
3066  (function(viewport) {
3067    var B = Prototype.Browser, doc = document, element, property = {};
3068  
3069    function getRootElement() {
3070      if (B.WebKit && !doc.evaluate)
3071        return document;
3072  
3073      if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3074        return document.body;
3075  
3076      return document.documentElement;
3077    }
3078  
3079    function define(D) {
3080      if (!element) element = getRootElement();
3081  
3082      property[D] = 'client' + D;
3083  
3084      viewport['get' + D] = function() { return element[property[D]] };
3085      return viewport['get' + D]();
3086    }
3087  
3088    viewport.getWidth  = define.curry('Width');
3089  
3090    viewport.getHeight = define.curry('Height');
3091  })(document.viewport);
3092  
3093  
3094  Element.Storage = {
3095    UID: 1
3096  };
3097  
3098  Element.addMethods({
3099    getStorage: function(element) {
3100      if (!(element = $(element))) return;
3101  
3102      var uid;
3103      if (element === window) {
3104        uid = 0;
3105      } else {
3106        if (typeof element._prototypeUID === "undefined")
3107          element._prototypeUID = [Element.Storage.UID++];
3108        uid = element._prototypeUID[0];
3109      }
3110  
3111      if (!Element.Storage[uid])
3112        Element.Storage[uid] = $H();
3113  
3114      return Element.Storage[uid];
3115    },
3116  
3117    store: function(element, key, value) {
3118      if (!(element = $(element))) return;
3119  
3120      if (arguments.length === 2) {
3121        Element.getStorage(element).update(key);
3122      } else {
3123        Element.getStorage(element).set(key, value);
3124      }
3125  
3126      return element;
3127    },
3128  
3129    retrieve: function(element, key, defaultValue) {
3130      if (!(element = $(element))) return;
3131      var hash = Element.getStorage(element), value = hash.get(key);
3132  
3133      if (Object.isUndefined(value)) {
3134        hash.set(key, defaultValue);
3135        value = defaultValue;
3136      }
3137  
3138      return value;
3139    },
3140  
3141    clone: function(element, deep) {
3142      if (!(element = $(element))) return;
3143      var clone = element.cloneNode(deep);
3144      clone._prototypeUID = void 0;
3145      if (deep) {
3146        var descendants = Element.select(clone, '*'),
3147            i = descendants.length;
3148        while (i--) {
3149          descendants[i]._prototypeUID = void 0;
3150        }
3151      }
3152      return Element.extend(clone);
3153    }
3154  });
3155  /* Portions of the Selector class are derived from Jack Slocum's DomQuery,
3156   * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
3157   * license.  Please see http://www.yui-ext.com/ for more information. */
3158  
3159  var Selector = Class.create({
3160    initialize: function(expression) {
3161      this.expression = expression.strip();
3162  
3163      if (this.shouldUseSelectorsAPI()) {
3164        this.mode = 'selectorsAPI';
3165      } else if (this.shouldUseXPath()) {
3166        this.mode = 'xpath';
3167        this.compileXPathMatcher();
3168      } else {
3169        this.mode = "normal";
3170        this.compileMatcher();
3171      }
3172  
3173    },
3174  
3175    shouldUseXPath: (function() {
3176  
3177      var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
3178        var isBuggy = false;
3179        if (document.evaluate && window.XPathResult) {
3180          var el = document.createElement('div');
3181          el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';
3182  
3183          var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
3184            "//*[local-name()='li' or local-name()='LI']";
3185  
3186          var result = document.evaluate(xpath, el, null,
3187            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
3188  
3189          isBuggy = (result.snapshotLength !== 2);
3190          el = null;
3191        }
3192        return isBuggy;
3193      })();
3194  
3195      return function() {
3196        if (!Prototype.BrowserFeatures.XPath) return false;
3197  
3198        var e = this.expression;
3199  
3200        if (Prototype.Browser.WebKit &&
3201         (e.include("-of-type") || e.include(":empty")))
3202          return false;
3203  
3204        if ((/(\[[\w-]*?:|:checked)/).test(e))
3205          return false;
3206  
3207        if (IS_DESCENDANT_SELECTOR_BUGGY) return false;
3208  
3209        return true;
3210      }
3211  
3212    })(),
3213  
3214    shouldUseSelectorsAPI: function() {
3215      if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
3216  
3217      if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;
3218  
3219      if (!Selector._div) Selector._div = new Element('div');
3220  
3221      try {
3222        Selector._div.querySelector(this.expression);
3223      } catch(e) {
3224        return false;
3225      }
3226  
3227      return true;
3228    },
3229  
3230    compileMatcher: function() {
3231      var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
3232          c = Selector.criteria, le, p, m, len = ps.length, name;
3233  
3234      if (Selector._cache[e]) {
3235        this.matcher = Selector._cache[e];
3236        return;
3237      }
3238  
3239      this.matcher = ["this.matcher = function(root) {",
3240                      "var r = root, h = Selector.handlers, c = false, n;"];
3241  
3242      while (e && le != e && (/\S/).test(e)) {
3243        le = e;
3244        for (var i = 0; i<len; i++) {
3245          p = ps[i].re;
3246          name = ps[i].name;
3247          if (m = e.match(p)) {
3248            this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :
3249              new Template(c[name]).evaluate(m));
3250            e = e.replace(m[0], '');
3251            break;
3252          }
3253        }
3254      }
3255  
3256      this.matcher.push("return h.unique(n);\n}");
3257      eval(this.matcher.join('\n'));
3258      Selector._cache[this.expression] = this.matcher;
3259    },
3260  
3261    compileXPathMatcher: function() {
3262      var e = this.expression, ps = Selector.patterns,
3263          x = Selector.xpath, le, m, len = ps.length, name;
3264  
3265      if (Selector._cache[e]) {
3266        this.xpath = Selector._cache[e]; return;
3267      }
3268  
3269      this.matcher = ['.//*'];
3270      while (e && le != e && (/\S/).test(e)) {
3271        le = e;
3272        for (var i = 0; i<len; i++) {
3273          name = ps[i].name;
3274          if (m = e.match(ps[i].re)) {
3275            this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
3276              new Template(x[name]).evaluate(m));
3277            e = e.replace(m[0], '');
3278            break;
3279          }
3280        }
3281      }
3282  
3283      this.xpath = this.matcher.join('');
3284      Selector._cache[this.expression] = this.xpath;
3285    },
3286  
3287    findElements: function(root) {
3288      root = root || document;
3289      var e = this.expression, results;
3290  
3291      switch (this.mode) {
3292        case 'selectorsAPI':
3293          if (root !== document) {
3294            var oldId = root.id, id = $(root).identify();
3295            id = id.replace(/([\.:])/g, "\\$1");
3296            e = "#" + id + " " + e;
3297          }
3298  
3299          results = $A(root.querySelectorAll(e)).map(Element.extend);
3300          root.id = oldId;
3301  
3302          return results;
3303        case 'xpath':
3304          return document._getElementsByXPath(this.xpath, root);
3305        default:
3306         return this.matcher(root);
3307      }
3308    },
3309  
3310    match: function(element) {
3311      this.tokens = [];
3312  
3313      var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
3314      var le, p, m, len = ps.length, name;
3315  
3316      while (e && le !== e && (/\S/).test(e)) {
3317        le = e;
3318        for (var i = 0; i<len; i++) {
3319          p = ps[i].re;
3320          name = ps[i].name;
3321          if (m = e.match(p)) {
3322            if (as[name]) {
3323              this.tokens.push([name, Object.clone(m)]);
3324              e = e.replace(m[0], '');
3325            } else {
3326              return this.findElements(document).include(element);
3327            }
3328          }
3329        }
3330      }
3331  
3332      var match = true, name, matches;
3333      for (var i = 0, token; token = this.tokens[i]; i++) {
3334        name = token[0], matches = token[1];
3335        if (!Selector.assertions[name](element, matches)) {
3336          match = false; break;
3337        }
3338      }
3339  
3340      return match;
3341    },
3342  
3343    toString: function() {
3344      return this.expression;
3345    },
3346  
3347    inspect: function() {
3348      return "#<Selector:" + this.expression.inspect() + ">";
3349    }
3350  });
3351  
3352  if (Prototype.BrowserFeatures.SelectorsAPI &&
3353   document.compatMode === 'BackCompat') {
3354    Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
3355      var div = document.createElement('div'),
3356       span = document.createElement('span');
3357  
3358      div.id = "prototype_test_id";
3359      span.className = 'Test';
3360      div.appendChild(span);
3361      var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
3362      div = span = null;
3363      return isIgnored;
3364    })();
3365  }
3366  
3367  Object.extend(Selector, {
3368    _cache: { },
3369  
3370    xpath: {
3371      descendant:   "//*",
3372      child:        "/*",
3373      adjacent:     "/following-sibling::*[1]",
3374      laterSibling: '/following-sibling::*',
3375      tagName:      function(m) {
3376        if (m[1] == '*') return '';
3377        return "[local-name()='" + m[1].toLowerCase() +
3378               "' or local-name()='" + m[1].toUpperCase() + "']";
3379      },
3380      className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
3381      id:           "[@id='#{1}']",
3382      attrPresence: function(m) {
3383        m[1] = m[1].toLowerCase();
3384        return new Template("[@#{1}]").evaluate(m);
3385      },
3386      attr: function(m) {
3387        m[1] = m[1].toLowerCase();
3388        m[3] = m[5] || m[6];
3389        return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
3390      },
3391      pseudo: function(m) {
3392        var h = Selector.xpath.pseudos[m[1]];
3393        if (!h) return '';
3394        if (Object.isFunction(h)) return h(m);
3395        return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
3396      },
3397      operators: {
3398        '=':  "[@#{1}='#{3}']",
3399        '!=': "[@#{1}!='#{3}']",
3400        '^=': "[starts-with(@#{1}, '#{3}')]",
3401        '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
3402        '*=': "[contains(@#{1}, '#{3}')]",
3403        '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
3404        '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
3405      },
3406      pseudos: {
3407        'first-child': '[not(preceding-sibling::*)]',
3408        'last-child':  '[not(following-sibling::*)]',
3409        'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
3410        'empty':       "[count(*) = 0 and (count(text()) = 0)]",
3411        'checked':     "[@checked]",
3412        'disabled':    "[(@disabled) and (@type!='hidden')]",
3413        'enabled':     "[not(@disabled) and (@type!='hidden')]",
3414        'not': function(m) {
3415          var e = m[6], p = Selector.patterns,
3416              x = Selector.xpath, le, v, len = p.length, name;
3417  
3418          var exclusion = [];
3419          while (e && le != e && (/\S/).test(e)) {
3420            le = e;
3421            for (var i = 0; i<len; i++) {
3422              name = p[i].name
3423              if (m = e.match(p[i].re)) {
3424                v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m);
3425                exclusion.push("(" + v.substring(1, v.length - 1) + ")");
3426                e = e.replace(m[0], '');
3427                break;
3428              }
3429            }
3430          }
3431          return "[not(" + exclusion.join(" and ") + ")]";
3432        },
3433        'nth-child':      function(m) {
3434          return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
3435        },
3436        'nth-last-child': function(m) {
3437          return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
3438        },
3439        'nth-of-type':    function(m) {
3440          return Selector.xpath.pseudos.nth("position() ", m);
3441        },
3442        'nth-last-of-type': function(m) {
3443          return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
3444        },
3445        'first-of-type':  function(m) {
3446          m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
3447        },
3448        'last-of-type':   function(m) {
3449          m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
3450        },
3451        'only-of-type':   function(m) {
3452          var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
3453        },
3454        nth: function(fragment, m) {
3455          var mm, formula = m[6], predicate;
3456          if (formula == 'even') formula = '2n+0';
3457          if (formula == 'odd')  formula = '2n+1';
3458          if (mm = formula.match(/^(\d+)$/)) // digit only
3459            return '[' + fragment + "= " + mm[1] + ']';
3460          if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3461            if (mm[1] == "-") mm[1] = -1;
3462            var a = mm[1] ? Number(mm[1]) : 1;
3463            var b = mm[2] ? Number(mm[2]) : 0;
3464            predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
3465            "((#{fragment} - #{b}) div #{a} >= 0)]";
3466            return new Template(predicate).evaluate({
3467              fragment: fragment, a: a, b: b });
3468          }
3469        }
3470      }
3471    },
3472  
3473    criteria: {
3474      tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
3475      className:    'n = h.className(n, r, "#{1}", c);    c = false;',
3476      id:           'n = h.id(n, r, "#{1}", c);           c = false;',
3477      attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
3478      attr: function(m) {
3479        m[3] = (m[5] || m[6]);
3480        return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
3481      },
3482      pseudo: function(m) {
3483        if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
3484        return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
3485      },
3486      descendant:   'c = "descendant";',
3487      child:        'c = "child";',
3488      adjacent:     'c = "adjacent";',
3489      laterSibling: 'c = "laterSibling";'
3490    },
3491  
3492    patterns: [
3493      { name: 'laterSibling', re: /^\s*~\s*/ },
3494      { name: 'child',        re: /^\s*>\s*/ },
3495      { name: 'adjacent',     re: /^\s*\+\s*/ },
3496      { name: 'descendant',   re: /^\s/ },
3497  
3498      { name: 'tagName',      re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
3499      { name: 'id',           re: /^#([\w\-\*]+)(\b|$)/ },
3500      { name: 'className',    re: /^\.([\w\-\*]+)(\b|$)/ },
3501      { name: 'pseudo',       re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
3502      { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
3503      { name: 'attr',         re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
3504    ],
3505  
3506    assertions: {
3507      tagName: function(element, matches) {
3508        return matches[1].toUpperCase() == element.tagName.toUpperCase();
3509      },
3510  
3511      className: function(element, matches) {
3512        return Element.hasClassName(element, matches[1]);
3513      },
3514  
3515      id: function(element, matches) {
3516        return element.id === matches[1];
3517      },
3518  
3519      attrPresence: function(element, matches) {
3520        return Element.hasAttribute(element, matches[1]);
3521      },
3522  
3523      attr: function(element, matches) {
3524        var nodeValue = Element.readAttribute(element, matches[1]);
3525        return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
3526      }
3527    },
3528  
3529    handlers: {
3530      concat: function(a, b) {
3531        for (var i = 0, node; node = b[i]; i++)
3532          a.push(node);
3533        return a;
3534      },
3535  
3536      mark: function(nodes) {
3537        var _true = Prototype.emptyFunction;
3538        for (var i = 0, node; node = nodes[i]; i++)
3539          node._countedByPrototype = _true;
3540        return nodes;
3541      },
3542  
3543      unmark: (function(){
3544  
3545        var PROPERTIES_ATTRIBUTES_MAP = (function(){
3546          var el = document.createElement('div'),
3547              isBuggy = false,
3548              propName = '_countedByPrototype',
3549              value = 'x'
3550          el[propName] = value;
3551          isBuggy = (el.getAttribute(propName) === value);
3552          el = null;
3553          return isBuggy;
3554        })();
3555  
3556        return PROPERTIES_ATTRIBUTES_MAP ?
3557          function(nodes) {
3558            for (var i = 0, node; node = nodes[i]; i++)
3559              node.removeAttribute('_countedByPrototype');
3560            return nodes;
3561          } :
3562          function(nodes) {
3563            for (var i = 0, node; node = nodes[i]; i++)
3564              node._countedByPrototype = void 0;
3565            return nodes;
3566          }
3567      })(),
3568  
3569      index: function(parentNode, reverse, ofType) {
3570        parentNode._countedByPrototype = Prototype.emptyFunction;
3571        if (reverse) {
3572          for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3573            var node = nodes[i];
3574            if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3575          }
3576        } else {
3577          for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3578            if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3579        }
3580      },
3581  
3582      unique: function(nodes) {
3583        if (nodes.length == 0) return nodes;
3584        var results = [], n;
3585        for (var i = 0, l = nodes.length; i < l; i++)
3586          if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
3587            n._countedByPrototype = Prototype.emptyFunction;
3588            results.push(Element.extend(n));
3589          }
3590        return Selector.handlers.unmark(results);
3591      },
3592  
3593      descendant: function(nodes) {
3594        var h = Selector.handlers;
3595        for (var i = 0, results = [], node; node = nodes[i]; i++)
3596          h.concat(results, node.getElementsByTagName('*'));
3597        return results;
3598      },
3599  
3600      child: function(nodes) {
3601        var h = Selector.handlers;
3602        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3603          for (var j = 0, child; child = node.childNodes[j]; j++)
3604            if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3605        }
3606        return results;
3607      },
3608  
3609      adjacent: function(nodes) {
3610        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3611          var next = this.nextElementSibling(node);
3612          if (next) results.push(next);
3613        }
3614        return results;
3615      },
3616  
3617      laterSibling: function(nodes) {
3618        var h = Selector.handlers;
3619        for (var i = 0, results = [], node; node = nodes[i]; i++)
3620          h.concat(results, Element.nextSiblings(node));
3621        return results;
3622      },
3623  
3624      nextElementSibling: function(node) {
3625        while (node = node.nextSibling)
3626          if (node.nodeType == 1) return node;
3627        return null;
3628      },
3629  
3630      previousElementSibling: function(node) {
3631        while (node = node.previousSibling)
3632          if (node.nodeType == 1) return node;
3633        return null;
3634      },
3635  
3636      tagName: function(nodes, root, tagName, combinator) {
3637        var uTagName = tagName.toUpperCase();
3638        var results = [], h = Selector.handlers;
3639        if (nodes) {
3640          if (combinator) {
3641            if (combinator == "descendant") {
3642              for (var i = 0, node; node = nodes[i]; i++)
3643                h.concat(results, node.getElementsByTagName(tagName));
3644              return results;
3645            } else nodes = this[combinator](nodes);
3646            if (tagName == "*") return nodes;
3647          }
3648          for (var i = 0, node; node = nodes[i]; i++)
3649            if (node.tagName.toUpperCase() === uTagName) results.push(node);
3650          return results;
3651        } else return root.getElementsByTagName(tagName);
3652      },
3653  
3654      id: function(nodes, root, id, combinator) {
3655        var targetNode = $(id), h = Selector.handlers;
3656  
3657        if (root == document) {
3658          if (!targetNode) return [];
3659          if (!nodes) return [targetNode];
3660        } else {
3661          if (!root.sourceIndex || root.sourceIndex < 1) {
3662            var nodes = root.getElementsByTagName('*');
3663            for (var j = 0, node; node = nodes[j]; j++) {
3664              if (node.id === id) return [node];
3665            }
3666          }
3667        }
3668  
3669        if (nodes) {
3670          if (combinator) {
3671            if (combinator == 'child') {
3672              for (var i = 0, node; node = nodes[i]; i++)
3673                if (targetNode.parentNode == node) return [targetNode];
3674            } else if (combinator == 'descendant') {
3675              for (var i = 0, node; node = nodes[i]; i++)
3676                if (Element.descendantOf(targetNode, node)) return [targetNode];
3677            } else if (combinator == 'adjacent') {
3678              for (var i = 0, node; node = nodes[i]; i++)
3679                if (Selector.handlers.previousElementSibling(targetNode) == node)
3680                  return [targetNode];
3681            } else nodes = h[combinator](nodes);
3682          }
3683          for (var i = 0, node; node = nodes[i]; i++)
3684            if (node == targetNode) return [targetNode];
3685          return [];
3686        }
3687        return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3688      },
3689  
3690      className: function(nodes, root, className, combinator) {
3691        if (nodes && combinator) nodes = this[combinator](nodes);
3692        return Selector.handlers.byClassName(nodes, root, className);
3693      },
3694  
3695      byClassName: function(nodes, root, className) {
3696        if (!nodes) nodes = Selector.handlers.descendant([root]);
3697        var needle = ' ' + className + ' ';
3698        for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
3699          nodeClassName = node.className;
3700          if (nodeClassName.length == 0) continue;
3701          if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
3702            results.push(node);
3703        }
3704        return results;
3705      },
3706  
3707      attrPresence: function(nodes, root, attr, combinator) {
3708        if (!nodes) nodes = root.getElementsByTagName("*");
3709        if (nodes && combinator) nodes = this[combinator](nodes);
3710        var results = [];
3711        for (var i = 0, node; node = nodes[i]; i++)
3712          if (Element.hasAttribute(node, attr)) results.push(node);
3713        return results;
3714      },
3715  
3716      attr: function(nodes, root, attr, value, operator, combinator) {
3717        if (!nodes) nodes = root.getElementsByTagName("*");
3718        if (nodes && combinator) nodes = this[combinator](nodes);
3719        var handler = Selector.operators[operator], results = [];
3720        for (var i = 0, node; node = nodes[i]; i++) {
3721          var nodeValue = Element.readAttribute(node, attr);
3722          if (nodeValue === null) continue;
3723          if (handler(nodeValue, value)) results.push(node);
3724        }
3725        return results;
3726      },
3727  
3728      pseudo: function(nodes, name, value, root, combinator) {
3729        if (nodes && combinator) nodes = this[combinator](nodes);
3730        if (!nodes) nodes = root.getElementsByTagName("*");
3731        return Selector.pseudos[name](nodes, value, root);
3732      }
3733    },
3734  
3735    pseudos: {
3736      'first-child': function(nodes, value, root) {
3737        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3738          if (Selector.handlers.previousElementSibling(node)) continue;
3739            results.push(node);
3740        }
3741        return results;
3742      },
3743      'last-child': function(nodes, value, root) {
3744        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3745          if (Selector.handlers.nextElementSibling(node)) continue;
3746            results.push(node);
3747        }
3748        return results;
3749      },
3750      'only-child': function(nodes, value, root) {
3751        var h = Selector.handlers;
3752        for (var i = 0, results = [], node; node = nodes[i]; i++)
3753          if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3754            results.push(node);
3755        return results;
3756      },
3757      'nth-child':        function(nodes, formula, root) {
3758        return Selector.pseudos.nth(nodes, formula, root);
3759      },
3760      'nth-last-child':   function(nodes, formula, root) {
3761        return Selector.pseudos.nth(nodes, formula, root, true);
3762      },
3763      'nth-of-type':      function(nodes, formula, root) {
3764        return Selector.pseudos.nth(nodes, formula, root, false, true);
3765      },
3766      'nth-last-of-type': function(nodes, formula, root) {
3767        return Selector.pseudos.nth(nodes, formula, root, true, true);
3768      },
3769      'first-of-type':    function(nodes, formula, root) {
3770        return Selector.pseudos.nth(nodes, "1", root, false, true);
3771      },
3772      'last-of-type':     function(nodes, formula, root) {
3773        return Selector.pseudos.nth(nodes, "1", root, true, true);
3774      },
3775      'only-of-type':     function(nodes, formula, root) {
3776        var p = Selector.pseudos;
3777        return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
3778      },
3779  
3780      getIndices: function(a, b, total) {
3781        if (a == 0) return b > 0 ? [b] : [];
3782        return $R(1, total).inject([], function(memo, i) {
3783          if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3784          return memo;
3785        });
3786      },
3787  
3788      nth: function(nodes, formula, root, reverse, ofType) {
3789        if (nodes.length == 0) return [];
3790        if (formula == 'even') formula = '2n+0';
3791        if (formula == 'odd')  formula = '2n+1';
3792        var h = Selector.handlers, results = [], indexed = [], m;
3793        h.mark(nodes);
3794        for (var i = 0, node; node = nodes[i]; i++) {
3795          if (!node.parentNode._countedByPrototype) {
3796            h.index(node.parentNode, reverse, ofType);
3797            indexed.push(node.parentNode);
3798          }
3799        }
3800        if (formula.match(/^\d+$/)) { // just a number
3801          formula = Number(formula);
3802          for (var i = 0, node; node = nodes[i]; i++)
3803            if (node.nodeIndex == formula) results.push(node);
3804        } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3805          if (m[1] == "-") m[1] = -1;
3806          var a = m[1] ? Number(m[1]) : 1;
3807          var b = m[2] ? Number(m[2]) : 0;
3808          var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3809          for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3810            for (var j = 0; j < l; j++)
3811              if (node.nodeIndex == indices[j]) results.push(node);
3812          }
3813        }
3814        h.unmark(nodes);
3815        h.unmark(indexed);
3816        return results;
3817      },
3818  
3819      'empty': function(nodes, value, root) {
3820        for (var i = 0, results = [], node; node = nodes[i]; i++) {
3821          if (node.tagName == '!' || node.firstChild) continue;
3822          results.push(node);
3823        }
3824        return results;
3825      },
3826  
3827      'not': function(nodes, selector, root) {
3828        var h = Selector.handlers, selectorType, m;
3829        var exclusions = new Selector(selector).findElements(root);
3830        h.mark(exclusions);
3831        for (var i = 0, results = [], node; node = nodes[i]; i++)
3832          if (!node._countedByPrototype) results.push(node);
3833        h.unmark(exclusions);
3834        return results;
3835      },
3836  
3837      'enabled': function(nodes, value, root) {
3838        for (var i = 0, results = [], node; node = nodes[i]; i++)
3839          if (!node.disabled && (!node.type || node.type !== 'hidden'))
3840            results.push(node);
3841        return results;
3842      },
3843  
3844      'disabled': function(nodes, value, root) {
3845        for (var i = 0, results = [], node; node = nodes[i]; i++)
3846          if (node.disabled) results.push(node);
3847        return results;
3848      },
3849  
3850      'checked': function(nodes, value, root) {
3851        for (var i = 0, results = [], node; node = nodes[i]; i++)
3852          if (node.checked) results.push(node);
3853        return results;
3854      }
3855    },
3856  
3857    operators: {
3858      '=':  function(nv, v) { return nv == v; },
3859      '!=': function(nv, v) { return nv != v; },
3860      '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
3861      '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
3862      '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
3863      '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3864      '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
3865       '-').include('-' + (v || "").toUpperCase() + '-'); }
3866    },
3867  
3868    split: function(expression) {
3869      var expressions = [];
3870      expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
3871        expressions.push(m[1].strip());
3872      });
3873      return expressions;
3874    },
3875  
3876    matchElements: function(elements, expression) {
3877      var matches = $$(expression), h = Selector.handlers;
3878      h.mark(matches);
3879      for (var i = 0, results = [], element; element = elements[i]; i++)
3880        if (element._countedByPrototype) results.push(element);
3881      h.unmark(matches);
3882      return results;
3883    },
3884  
3885    findElement: function(elements, expression, index) {
3886      if (Object.isNumber(expression)) {
3887        index = expression; expression = false;
3888      }
3889      return Selector.matchElements(elements, expression || '*')[index || 0];
3890    },
3891  
3892    findChildElements: function(element, expressions) {
3893      expressions = Selector.split(expressions.join(','));
3894      var results = [], h = Selector.handlers;
3895      for (var i = 0, l = expressions.length, selector; i < l; i++) {
3896        selector = new Selector(expressions[i].strip());
3897        h.concat(results, selector.findElements(element));
3898      }
3899      return (l > 1) ? h.unique(results) : results;
3900    }
3901  });
3902  
3903  if (Prototype.Browser.IE) {
3904    Object.extend(Selector.handlers, {
3905      concat: function(a, b) {
3906        for (var i = 0, node; node = b[i]; i++)
3907          if (node.tagName !== "!") a.push(node);
3908        return a;
3909      }
3910    });
3911  }
3912  
3913  function $$() {
3914    return Selector.findChildElements(document, $A(arguments));
3915  }
3916  
3917  var Form = {
3918    reset: function(form) {
3919      form = $(form);
3920      form.reset();
3921      return form;
3922    },
3923  
3924    serializeElements: function(elements, options) {
3925      if (typeof options != 'object') options = { hash: !!options };
3926      else if (Object.isUndefined(options.hash)) options.hash = true;
3927      var key, value, submitted = false, submit = options.submit;
3928  
3929      var data = elements.inject({ }, function(result, element) {
3930        if (!element.disabled && element.name) {
3931          key = element.name; value = $(element).getValue();
3932          if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
3933              submit !== false && (!submit || key == submit) && (submitted = true)))) {
3934            if (key in result) {
3935              if (!Object.isArray(result[key])) result[key] = [result[key]];
3936              result[key].push(value);
3937            }
3938            else result[key] = value;
3939          }
3940        }
3941        return result;
3942      });
3943  
3944      return options.hash ? data : Object.toQueryString(data);
3945    }
3946  };
3947  
3948  Form.Methods = {
3949    serialize: function(form, options) {
3950      return Form.serializeElements(Form.getElements(form), options);
3951    },
3952  
3953    getElements: function(form) {
3954      var elements = $(form).getElementsByTagName('*'),
3955          element,
3956          arr = [ ],
3957          serializers = Form.Element.Serializers;
3958      for (var i = 0; element = elements[i]; i++) {
3959        arr.push(element);
3960      }
3961      return arr.inject([], function(elements, child) {
3962        if (serializers[child.tagName.toLowerCase()])
3963          elements.push(Element.extend(child));
3964        return elements;
3965      })
3966    },
3967  
3968    getInputs: function(form, typeName, name) {
3969      form = $(form);
3970      var inputs = form.getElementsByTagName('input');
3971  
3972      if (!typeName && !name) return $A(inputs).map(Element.extend);
3973  
3974      for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
3975        var input = inputs[i];
3976        if ((typeName && input.type != typeName) || (name && input.name != name))
3977          continue;
3978        matchingInputs.push(Element.extend(input));
3979      }
3980  
3981      return matchingInputs;
3982    },
3983  
3984    disable: function(form) {
3985      form = $(form);
3986      Form.getElements(form).invoke('disable');
3987      return form;
3988    },
3989  
3990    enable: function(form) {
3991      form = $(form);
3992      Form.getElements(form).invoke('enable');
3993      return form;
3994    },
3995  
3996    findFirstElement: function(form) {
3997      var elements = $(form).getElements().findAll(function(element) {
3998        return 'hidden' != element.type && !element.disabled;
3999      });
4000      var firstByIndex = elements.findAll(function(element) {
4001        return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
4002      }).sortBy(function(element) { return element.tabIndex }).first();
4003  
4004      return firstByIndex ? firstByIndex : elements.find(function(element) {
4005        return /^(?:input|select|textarea)$/i.test(element.tagName);
4006      });
4007    },
4008  
4009    focusFirstElement: function(form) {
4010      form = $(form);
4011      form.findFirstElement().activate();
4012      return form;
4013    },
4014  
4015    request: function(form, options) {
4016      form = $(form), options = Object.clone(options || { });
4017  
4018      var params = options.parameters, action = form.readAttribute('action') || '';
4019      if (action.blank()) action = window.location.href;
4020      options.parameters = form.serialize(true);
4021  
4022      if (params) {
4023        if (Object.isString(params)) params = params.toQueryParams();
4024        Object.extend(options.parameters, params);
4025      }
4026  
4027      if (form.hasAttribute('method') && !options.method)
4028        options.method = form.method;
4029  
4030      return new Ajax.Request(action, options);
4031    }
4032  };
4033  
4034  /*--------------------------------------------------------------------------*/
4035  
4036  
4037  Form.Element = {
4038    focus: function(element) {
4039      $(element).focus();
4040      return element;
4041    },
4042  
4043    select: function(element) {
4044      $(element).select();
4045      return element;
4046    }
4047  };
4048  
4049  Form.Element.Methods = {
4050  
4051    serialize: function(element) {
4052      element = $(element);
4053      if (!element.disabled && element.name) {
4054        var value = element.getValue();
4055        if (value != undefined) {
4056          var pair = { };
4057          pair[element.name] = value;
4058          return Object.toQueryString(pair);
4059        }
4060      }
4061      return '';
4062    },
4063  
4064    getValue: function(element) {
4065      element = $(element);
4066      var method = element.tagName.toLowerCase();
4067      return Form.Element.Serializers[method](element);
4068    },
4069  
4070    setValue: function(element, value) {
4071      element = $(element);
4072      var method = element.tagName.toLowerCase();
4073      Form.Element.Serializers[method](element, value);
4074      return element;
4075    },
4076  
4077    clear: function(element) {
4078      $(element).value = '';
4079      return element;
4080    },
4081  
4082    present: function(element) {
4083      return $(element).value != '';
4084    },
4085  
4086    activate: function(element) {
4087      element = $(element);
4088      try {
4089        element.focus();
4090        if (element.select && (element.tagName.toLowerCase() != 'input' ||
4091            !(/^(?:button|reset|submit)$/i.test(element.type))))
4092          element.select();
4093      } catch (e) { }
4094      return element;
4095    },
4096  
4097    disable: function(element) {
4098      element = $(element);
4099      element.disabled = true;
4100      return element;
4101    },
4102  
4103    enable: function(element) {
4104      element = $(element);
4105      element.disabled = false;
4106      return element;
4107    }
4108  };
4109  
4110  /*--------------------------------------------------------------------------*/
4111  
4112  var Field = Form.Element;
4113  
4114  var $F = Form.Element.Methods.getValue;
4115  
4116  /*--------------------------------------------------------------------------*/
4117  
4118  Form.Element.Serializers = {
4119    input: function(element, value) {
4120      switch (element.type.toLowerCase()) {
4121        case 'checkbox':
4122        case 'radio':
4123          return Form.Element.Serializers.inputSelector(element, value);
4124        default:
4125          return Form.Element.Serializers.textarea(element, value);
4126      }
4127    },
4128  
4129    inputSelector: function(element, value) {
4130      if (Object.isUndefined(value)) return element.checked ? element.value : null;
4131      else element.checked = !!value;
4132    },
4133  
4134    textarea: function(element, value) {
4135      if (Object.isUndefined(value)) return element.value;
4136      else element.value = value;
4137    },
4138  
4139    select: function(element, value) {
4140      if (Object.isUndefined(value))
4141        return this[element.type == 'select-one' ?
4142          'selectOne' : 'selectMany'](element);
4143      else {
4144        var opt, currentValue, single = !Object.isArray(value);
4145        for (var i = 0, length = element.length; i < length; i++) {
4146          opt = element.options[i];
4147          currentValue = this.optionValue(opt);
4148          if (single) {
4149            if (currentValue == value) {
4150              opt.selected = true;
4151              return;
4152            }
4153          }
4154          else opt.selected = value.include(currentValue);
4155        }
4156      }
4157    },
4158  
4159    selectOne: function(element) {
4160      var index = element.selectedIndex;
4161      return index >= 0 ? this.optionValue(element.options[index]) : null;
4162    },
4163  
4164    selectMany: function(element) {
4165      var values, length = element.length;
4166      if (!length) return null;
4167  
4168      for (var i = 0, values = []; i < length; i++) {
4169        var opt = element.options[i];
4170        if (opt.selected) values.push(this.optionValue(opt));
4171      }
4172      return values;
4173    },
4174  
4175    optionValue: function(opt) {
4176      return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
4177    }
4178  };
4179  
4180  /*--------------------------------------------------------------------------*/
4181  
4182  
4183  Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
4184    initialize: function($super, element, frequency, callback) {
4185      $super(callback, frequency);
4186      this.element   = $(element);
4187      this.lastValue = this.getValue();
4188    },
4189  
4190    execute: function() {
4191      var value = this.getValue();
4192      if (Object.isString(this.lastValue) && Object.isString(value) ?
4193          this.lastValue != value : String(this.lastValue) != String(value)) {
4194        this.callback(this.element, value);
4195        this.lastValue = value;
4196      }
4197    }
4198  });
4199  
4200  Form.Element.Observer = Class.create(Abstract.TimedObserver, {
4201    getValue: function() {
4202      return Form.Element.getValue(this.element);
4203    }
4204  });
4205  
4206  Form.Observer = Class.create(Abstract.TimedObserver, {
4207    getValue: function() {
4208      return Form.serialize(this.element);
4209    }
4210  });
4211  
4212  /*--------------------------------------------------------------------------*/
4213  
4214  Abstract.EventObserver = Class.create({
4215    initialize: function(element, callback) {
4216      this.element  = $(element);
4217      this.callback = callback;
4218  
4219      this.lastValue = this.getValue();
4220      if (this.element.tagName.toLowerCase() == 'form')
4221        this.registerFormCallbacks();
4222      else
4223        this.registerCallback(this.element);
4224    },
4225  
4226    onElementEvent: function() {
4227      var value = this.getValue();
4228      if (this.lastValue != value) {
4229        this.callback(this.element, value);
4230        this.lastValue = value;
4231      }
4232    },
4233  
4234    registerFormCallbacks: function() {
4235      Form.getElements(this.element).each(this.registerCallback, this);
4236    },
4237  
4238    registerCallback: function(element) {
4239      if (element.type) {
4240        switch (element.type.toLowerCase()) {
4241          case 'checkbox':
4242          case 'radio':
4243            Event.observe(element, 'click', this.onElementEvent.bind(this));
4244            break;
4245          default:
4246            Event.observe(element, 'change', this.onElementEvent.bind(this));
4247            break;
4248        }
4249      }
4250    }
4251  });
4252  
4253  Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
4254    getValue: function() {
4255      return Form.Element.getValue(this.element);
4256    }
4257  });
4258  
4259  Form.EventObserver = Class.create(Abstract.EventObserver, {
4260    getValue: function() {
4261      return Form.serialize(this.element);
4262    }
4263  });
4264  (function() {
4265  
4266    var Event = {
4267      KEY_BACKSPACE: 8,
4268      KEY_TAB:       9,
4269      KEY_RETURN:   13,
4270      KEY_ESC:      27,
4271      KEY_LEFT:     37,
4272      KEY_UP:       38,
4273      KEY_RIGHT:    39,
4274      KEY_DOWN:     40,
4275      KEY_DELETE:   46,
4276      KEY_HOME:     36,
4277      KEY_END:      35,
4278      KEY_PAGEUP:   33,
4279      KEY_PAGEDOWN: 34,
4280      KEY_INSERT:   45,
4281  
4282      cache: {}
4283    };
4284  
4285    var docEl = document.documentElement;
4286    var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
4287      && 'onmouseleave' in docEl;
4288  
4289    var _isButton;
4290    if (Prototype.Browser.IE) {
4291      var buttonMap = { 0: 1, 1: 4, 2: 2 };
4292      _isButton = function(event, code) {
4293        return event.button === buttonMap[code];
4294      };
4295    } else if (Prototype.Browser.WebKit) {
4296      _isButton = function(event, code) {
4297        switch (code) {
4298          case 0: return event.which == 1 && !event.metaKey;
4299          case 1: return event.which == 1 && event.metaKey;
4300          default: return false;
4301        }
4302      };
4303    } else {
4304      _isButton = function(event, code) {
4305        return event.which ? (event.which === code + 1) : (event.button === code);
4306      };
4307    }
4308  
4309    function isLeftClick(event)   { return _isButton(event, 0) }
4310  
4311    function isMiddleClick(event) { return _isButton(event, 1) }
4312  
4313    function isRightClick(event)  { return _isButton(event, 2) }
4314  
4315    function element(event) {
4316      event = Event.extend(event);
4317  
4318      var node = event.target, type = event.type,
4319       currentTarget = event.currentTarget;
4320  
4321      if (currentTarget && currentTarget.tagName) {
4322        if (type === 'load' || type === 'error' ||
4323          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
4324            && currentTarget.type === 'radio'))
4325              node = currentTarget;
4326      }
4327  
4328      if (node.nodeType == Node.TEXT_NODE)
4329        node = node.parentNode;
4330  
4331      return Element.extend(node);
4332    }
4333  
4334    function findElement(event, expression) {
4335      var element = Event.element(event);
4336      if (!expression) return element;
4337      var elements = [element].concat(element.ancestors());
4338      return Selector.findElement(elements, expression, 0);
4339    }
4340  
4341    function pointer(event) {
4342      return { x: pointerX(event), y: pointerY(event) };
4343    }
4344  
4345    function pointerX(event) {
4346      var docElement = document.documentElement,
4347       body = document.body || { scrollLeft: 0 };
4348  
4349      return event.pageX || (event.clientX +
4350        (docElement.scrollLeft || body.scrollLeft) -
4351        (docElement.clientLeft || 0));
4352    }
4353  
4354    function pointerY(event) {
4355      var docElement = document.documentElement,
4356       body = document.body || { scrollTop: 0 };
4357  
4358      return  event.pageY || (event.clientY +
4359         (docElement.scrollTop || body.scrollTop) -
4360         (docElement.clientTop || 0));
4361    }
4362  
4363  
4364    function stop(event) {
4365      Event.extend(event);
4366      event.preventDefault();
4367      event.stopPropagation();
4368  
4369      event.stopped = true;
4370    }
4371  
4372    Event.Methods = {
4373      isLeftClick: isLeftClick,
4374      isMiddleClick: isMiddleClick,
4375      isRightClick: isRightClick,
4376  
4377      element: element,
4378      findElement: findElement,
4379  
4380      pointer: pointer,
4381      pointerX: pointerX,
4382      pointerY: pointerY,
4383  
4384      stop: stop
4385    };
4386  
4387  
4388    var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
4389      m[name] = Event.Methods[name].methodize();
4390      return m;
4391    });
4392  
4393    if (Prototype.Browser.IE) {
4394      function _relatedTarget(event) {
4395        var element;
4396        switch (event.type) {
4397          case 'mouseover': element = event.fromElement; break;
4398          case 'mouseout':  element = event.toElement;   break;
4399          default: return null;
4400        }
4401        return Element.extend(element);
4402      }
4403  
4404      Object.extend(methods, {
4405        stopPropagation: function() { this.cancelBubble = true },
4406        preventDefault:  function() { this.returnValue = false },
4407        inspect: function() { return '[object Event]' }
4408      });
4409  
4410      Event.extend = function(event, element) {
4411        if (!event) return false;
4412        if (event._extendedByPrototype) return event;
4413  
4414        event._extendedByPrototype = Prototype.emptyFunction;
4415        var pointer = Event.pointer(event);
4416  
4417        Object.extend(event, {
4418          target: event.srcElement || element,
4419          relatedTarget: _relatedTarget(event),
4420          pageX:  pointer.x,
4421          pageY:  pointer.y
4422        });
4423  
4424        return Object.extend(event, methods);
4425      };
4426    } else {
4427      Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
4428      Object.extend(Event.prototype, methods);
4429      Event.extend = Prototype.K;
4430    }
4431  
4432    function _createResponder(element, eventName, handler) {
4433      var registry = Element.retrieve(element, 'prototype_event_registry');
4434  
4435      if (Object.isUndefined(registry)) {
4436        CACHE.push(element);
4437        registry = Element.retrieve(element, 'prototype_event_registry', $H());
4438      }
4439  
4440      var respondersForEvent = registry.get(eventName);
4441      if (Object.isUndefined(respondersForEvent)) {
4442        respondersForEvent = [];
4443        registry.set(eventName, respondersForEvent);
4444      }
4445  
4446      if (respondersForEvent.pluck('handler').include(handler)) return false;
4447  
4448      var responder;
4449      if (eventName.include(":")) {
4450        responder = function(event) {
4451          if (Object.isUndefined(event.eventName))
4452            return false;
4453  
4454          if (event.eventName !== eventName)
4455            return false;
4456  
4457          Event.extend(event, element);
4458          handler.call(element, event);
4459        };
4460      } else {
4461        if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
4462         (eventName === "mouseenter" || eventName === "mouseleave")) {
4463          if (eventName === "mouseenter" || eventName === "mouseleave") {
4464            responder = function(event) {
4465              Event.extend(event, element);
4466  
4467              var parent = event.relatedTarget;
4468              while (parent && parent !== element) {
4469                try { parent = parent.parentNode; }
4470                catch(e) { parent = element; }
4471              }
4472  
4473              if (parent === element) return;
4474  
4475              handler.call(element, event);
4476            };
4477          }
4478        } else {
4479          responder = function(event) {
4480            Event.extend(event, element);
4481            handler.call(element, event);
4482          };
4483        }
4484      }
4485  
4486      responder.handler = handler;
4487      respondersForEvent.push(responder);
4488      return responder;
4489    }
4490  
4491    function _destroyCache() {
4492      for (var i = 0, length = CACHE.length; i < length; i++) {
4493        Event.stopObserving(CACHE[i]);
4494        CACHE[i] = null;
4495      }
4496    }
4497  
4498    var CACHE = [];
4499  
4500    if (Prototype.Browser.IE)
4501      window.attachEvent('onunload', _destroyCache);
4502  
4503    if (Prototype.Browser.WebKit)
4504      window.addEventListener('unload', Prototype.emptyFunction, false);
4505  
4506  
4507    var _getDOMEventName = Prototype.K;
4508  
4509    if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
4510      _getDOMEventName = function(eventName) {
4511        var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
4512        return eventName in translations ? translations[eventName] : eventName;
4513      };
4514    }
4515  
4516    function observe(element, eventName, handler) {
4517      element = $(element);
4518  
4519      var responder = _createResponder(element, eventName, handler);
4520  
4521      if (!responder) return element;
4522  
4523      if (eventName.include(':')) {
4524        if (element.addEventListener)
4525          element.addEventListener("dataavailable", responder, false);
4526        else {
4527          element.attachEvent("ondataavailable", responder);
4528          element.attachEvent("onfilterchange", responder);
4529        }
4530      } else {
4531        var actualEventName = _getDOMEventName(eventName);
4532  
4533        if (element.addEventListener)
4534          element.addEventListener(actualEventName, responder, false);
4535        else
4536          element.attachEvent("on" + actualEventName, responder);
4537      }
4538  
4539      return element;
4540    }
4541  
4542    function stopObserving(element, eventName, handler) {
4543      element = $(element);
4544  
4545      var registry = Element.retrieve(element, 'prototype_event_registry');
4546  
4547      if (Object.isUndefined(registry)) return element;
4548  
4549      if (eventName && !handler) {
4550        var responders = registry.get(eventName);
4551  
4552        if (Object.isUndefined(responders)) return element;
4553  
4554        responders.each( function(r) {
4555          Element.stopObserving(element, eventName, r.handler);
4556        });
4557        return element;
4558      } else if (!eventName) {
4559        registry.each( function(pair) {
4560          var eventName = pair.key, responders = pair.value;
4561  
4562          responders.each( function(r) {
4563            Element.stopObserving(element, eventName, r.handler);
4564          });
4565        });
4566        return element;
4567      }
4568  
4569      var responders = registry.get(eventName);
4570  
4571      if (!responders) return;
4572  
4573      var responder = responders.find( function(r) { return r.handler === handler; });
4574      if (!responder) return element;
4575  
4576      var actualEventName = _getDOMEventName(eventName);
4577  
4578      if (eventName.include(':')) {
4579        if (element.removeEventListener)
4580          element.removeEventListener("dataavailable", responder, false);
4581        else {
4582          element.detachEvent("ondataavailable", responder);
4583          element.detachEvent("onfilterchange",  responder);
4584        }
4585      } else {
4586        if (element.removeEventListener)
4587          element.removeEventListener(actualEventName, responder, false);
4588        else
4589          element.detachEvent('on' + actualEventName, responder);
4590      }
4591  
4592      registry.set(eventName, responders.without(responder));
4593  
4594      return element;
4595    }
4596  
4597    function fire(element, eventName, memo, bubble) {
4598      element = $(element);
4599  
4600      if (Object.isUndefined(bubble))
4601        bubble = true;
4602  
4603      if (element == document && document.createEvent && !element.dispatchEvent)
4604        element = document.documentElement;
4605  
4606      var event;
4607      if (document.createEvent) {
4608        event = document.createEvent('HTMLEvents');
4609        event.initEvent('dataavailable', true, true);
4610      } else {
4611        event = document.createEventObject();
4612        event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
4613      }
4614  
4615      event.eventName = eventName;
4616      event.memo = memo || { };
4617  
4618      if (document.createEvent)
4619        element.dispatchEvent(event);
4620      else
4621        element.fireEvent(event.eventType, event);
4622  
4623      return Event.extend(event);
4624    }
4625  
4626  
4627    Object.extend(Event, Event.Methods);
4628  
4629    Object.extend(Event, {
4630      fire:          fire,
4631      observe:       observe,
4632      stopObserving: stopObserving
4633    });
4634  
4635    Element.addMethods({
4636      fire:          fire,
4637  
4638      observe:       observe,
4639  
4640      stopObserving: stopObserving
4641    });
4642  
4643    Object.extend(document, {
4644      fire:          fire.methodize(),
4645  
4646      observe:       observe.methodize(),
4647  
4648      stopObserving: stopObserving.methodize(),
4649  
4650      loaded:        false
4651    });
4652  
4653    if (window.Event) Object.extend(window.Event, Event);
4654    else window.Event = Event;
4655  })();
4656  
4657  (function() {
4658    /* Support for the DOMContentLoaded event is based on work by Dan Webb,
4659       Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
4660  
4661    var timer;
4662  
4663    function fireContentLoadedEvent() {
4664      if (document.loaded) return;
4665      if (timer) window.clearTimeout(timer);
4666      document.loaded = true;
4667      document.fire('dom:loaded');
4668    }
4669  
4670    function checkReadyState() {
4671      if (document.readyState === 'complete') {
4672        document.stopObserving('readystatechange', checkReadyState);
4673        fireContentLoadedEvent();
4674      }
4675    }
4676  
4677    function pollDoScroll() {
4678      try { document.documentElement.doScroll('left'); }
4679      catch(e) {
4680        timer = pollDoScroll.defer();
4681        return;
4682      }
4683      fireContentLoadedEvent();
4684    }
4685  
4686    if (document.addEventListener) {
4687      document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
4688    } else {
4689      document.observe('readystatechange', checkReadyState);
4690      if (window == top)
4691        timer = pollDoScroll.defer();
4692    }
4693  
4694    Event.observe(window, 'load', fireContentLoadedEvent);
4695  })();
4696  
4697  Element.addMethods();
4698  
4699  /*------------------------------- DEPRECATED -------------------------------*/
4700  
4701  Hash.toQueryString = Object.toQueryString;
4702  
4703  var Toggle = { display: Element.toggle };
4704  
4705  Element.Methods.childOf = Element.Methods.descendantOf;
4706  
4707  var Insertion = {
4708    Before: function(element, content) {
4709      return Element.insert(element, {before:content});
4710    },
4711  
4712    Top: function(element, content) {
4713      return Element.insert(element, {top:content});
4714    },
4715  
4716    Bottom: function(element, content) {
4717      return Element.insert(element, {bottom:content});
4718    },
4719  
4720    After: function(element, content) {
4721      return Element.insert(element, {after:content});
4722    }
4723  };
4724  
4725  var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4726  
4727  var Position = {
4728    includeScrollOffsets: false,
4729  
4730    prepare: function() {
4731      this.deltaX =  window.pageXOffset
4732                  || document.documentElement.scrollLeft
4733                  || document.body.scrollLeft
4734                  || 0;
4735      this.deltaY =  window.pageYOffset
4736                  || document.documentElement.scrollTop
4737                  || document.body.scrollTop
4738                  || 0;
4739    },
4740  
4741    within: function(element, x, y) {
4742      if (this.includeScrollOffsets)
4743        return this.withinIncludingScrolloffsets(element, x, y);
4744      this.xcomp = x;
4745      this.ycomp = y;
4746      this.offset = Element.cumulativeOffset(element);
4747  
4748      return (y >= this.offset[1] &&
4749              y <  this.offset[1] + element.offsetHeight &&
4750              x >= this.offset[0] &&
4751              x <  this.offset[0] + element.offsetWidth);
4752    },
4753  
4754    withinIncludingScrolloffsets: function(element, x, y) {
4755      var offsetcache = Element.cumulativeScrollOffset(element);
4756  
4757      this.xcomp = x + offsetcache[0] - this.deltaX;
4758      this.ycomp = y + offsetcache[1] - this.deltaY;
4759      this.offset = Element.cumulativeOffset(element);
4760  
4761      return (this.ycomp >= this.offset[1] &&
4762              this.ycomp <  this.offset[1] + element.offsetHeight &&
4763              this.xcomp >= this.offset[0] &&
4764              this.xcomp <  this.offset[0] + element.offsetWidth);
4765    },
4766  
4767    overlap: function(mode, element) {
4768      if (!mode) return 0;
4769      if (mode == 'vertical')
4770        return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
4771          element.offsetHeight;
4772      if (mode == 'horizontal')
4773        return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
4774          element.offsetWidth;
4775    },
4776  
4777  
4778    cumulativeOffset: Element.Methods.cumulativeOffset,
4779  
4780    positionedOffset: Element.Methods.positionedOffset,
4781  
4782    absolutize: function(element) {
4783      Position.prepare();
4784      return Element.absolutize(element);
4785    },
4786  
4787    relativize: function(element) {
4788      Position.prepare();
4789      return Element.relativize(element);
4790    },
4791  
4792    realOffset: Element.Methods.cumulativeScrollOffset,
4793  
4794    offsetParent: Element.Methods.getOffsetParent,
4795  
4796    page: Element.Methods.viewportOffset,
4797  
4798    clone: function(source, target, options) {
4799      options = options || { };
4800      return Element.clonePosition(target, source, options);
4801    }
4802  };
4803  
4804  /*--------------------------------------------------------------------------*/
4805  
4806  if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
4807    function iter(name) {
4808      return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
4809    }
4810  
4811    instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
4812    function(element, className) {
4813      className = className.toString().strip();
4814      var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
4815      return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
4816    } : function(element, className) {
4817      className = className.toString().strip();
4818      var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
4819      if (!classNames && !className) return elements;
4820  
4821      var nodes = $(element).getElementsByTagName('*');
4822      className = ' ' + className + ' ';
4823  
4824      for (var i = 0, child, cn; child = nodes[i]; i++) {
4825        if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
4826            (classNames && classNames.all(function(name) {
4827              return !name.toString().blank() && cn.include(' ' + name + ' ');
4828            }))))
4829          elements.push(Element.extend(child));
4830      }
4831      return elements;
4832    };
4833  
4834    return function(className, parentElement) {
4835      return $(parentElement || document.body).getElementsByClassName(className);
4836    };
4837  }(Element.Methods);
4838  
4839  /*--------------------------------------------------------------------------*/
4840  
4841  Element.ClassNames = Class.create();
4842  Element.ClassNames.prototype = {
4843    initialize: function(element) {
4844      this.element = $(element);
4845    },
4846  
4847    _each: function(iterator) {
4848      this.element.className.split(/\s+/).select(function(name) {
4849        return name.length > 0;
4850      })._each(iterator);
4851    },
4852  
4853    set: function(className) {
4854      this.element.className = className;
4855    },
4856  
4857    add: function(classNameToAdd) {
4858      if (this.include(classNameToAdd)) return;
4859      this.set($A(this).concat(classNameToAdd).join(' '));
4860    },
4861  
4862    remove: function(classNameToRemove) {
4863      if (!this.include(classNameToRemove)) return;
4864      this.set($A(this).without(classNameToRemove).join(' '));
4865    },
4866  
4867    toString: function() {
4868      return $A(this).join(' ');
4869    }
4870  };
4871  
4872  Object.extend(Element.ClassNames.prototype, Enumerable);
4873  
4874  /*--------------------------------------------------------------------------*/


Generated: Fri May 25 03:56:23 2012 Hosted by follow the white rabbit.