[ Index ]

PHP Cross Reference of MyBB 1.6.5

title

Body

[close]

/jscripts/ -> prototype.js (source)

   1  /*  Prototype JavaScript framework, version 1.7
   2   *  (c) 2005-2010 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  
  11    Version: '1.7',
  12  
  13    Browser: (function(){
  14      var ua = navigator.userAgent;
  15      var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
  16      return {
  17        IE:             !!window.attachEvent && !isOpera,
  18        Opera:          isOpera,
  19        WebKit:         ua.indexOf('AppleWebKit/') > -1,
  20        Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
  21        MobileSafari:   /Apple.*Mobile/.test(ua)
  22      }
  23    })(),
  24  
  25    BrowserFeatures: {
  26      XPath: !!document.evaluate,
  27  
  28      SelectorsAPI: !!document.querySelector,
  29  
  30      ElementExtensions: (function() {
  31        var constructor = window.Element || window.HTMLElement;
  32        return !!(constructor && constructor.prototype);
  33      })(),
  34      SpecificElementExtensions: (function() {
  35        if (typeof window.HTMLDivElement !== 'undefined')
  36          return true;
  37  
  38        var div = document.createElement('div'),
  39            form = document.createElement('form'),
  40            isSupported = false;
  41  
  42        if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
  43          isSupported = true;
  44        }
  45  
  46        div = form = null;
  47  
  48        return isSupported;
  49      })()
  50    },
  51  
  52    ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  53    JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
  54  
  55    emptyFunction: function() { },
  56  
  57    K: function(x) { return x }
  58  };
  59  
  60  if (Prototype.Browser.MobileSafari)
  61    Prototype.BrowserFeatures.SpecificElementExtensions = false;
  62  /* Based on Alex Arnell's inheritance implementation. */
  63  
  64  var Class = (function() {
  65  
  66    var IS_DONTENUM_BUGGY = (function(){
  67      for (var p in { toString: 1 }) {
  68        if (p === 'toString') return false;
  69      }
  70      return true;
  71    })();
  72  
  73    function subclass() {};
  74    function create() {
  75      var parent = null, properties = $A(arguments);
  76      if (Object.isFunction(properties[0]))
  77        parent = properties.shift();
  78  
  79      function klass() {
  80        this.initialize.apply(this, arguments);
  81      }
  82  
  83      Object.extend(klass, Class.Methods);
  84      klass.superclass = parent;
  85      klass.subclasses = [];
  86  
  87      if (parent) {
  88        subclass.prototype = parent.prototype;
  89        klass.prototype = new subclass;
  90        parent.subclasses.push(klass);
  91      }
  92  
  93      for (var i = 0, length = properties.length; i < length; i++)
  94        klass.addMethods(properties[i]);
  95  
  96      if (!klass.prototype.initialize)
  97        klass.prototype.initialize = Prototype.emptyFunction;
  98  
  99      klass.prototype.constructor = klass;
 100      return klass;
 101    }
 102  
 103    function addMethods(source) {
 104      var ancestor   = this.superclass && this.superclass.prototype,
 105          properties = Object.keys(source);
 106  
 107      if (IS_DONTENUM_BUGGY) {
 108        if (source.toString != Object.prototype.toString)
 109          properties.push("toString");
 110        if (source.valueOf != Object.prototype.valueOf)
 111          properties.push("valueOf");
 112      }
 113  
 114      for (var i = 0, length = properties.length; i < length; i++) {
 115        var property = properties[i], value = source[property];
 116        if (ancestor && Object.isFunction(value) &&
 117            value.argumentNames()[0] == "$super") {
 118          var method = value;
 119          value = (function(m) {
 120            return function() { return ancestor[m].apply(this, arguments); };
 121          })(property).wrap(method);
 122  
 123          value.valueOf = method.valueOf.bind(method);
 124          value.toString = method.toString.bind(method);
 125        }
 126        this.prototype[property] = value;
 127      }
 128  
 129      return this;
 130    }
 131  
 132    return {
 133      create: create,
 134      Methods: {
 135        addMethods: addMethods
 136      }
 137    };
 138  })();
 139  (function() {
 140  
 141    var _toString = Object.prototype.toString,
 142        NULL_TYPE = 'Null',
 143        UNDEFINED_TYPE = 'Undefined',
 144        BOOLEAN_TYPE = 'Boolean',
 145        NUMBER_TYPE = 'Number',
 146        STRING_TYPE = 'String',
 147        OBJECT_TYPE = 'Object',
 148        FUNCTION_CLASS = '[object Function]',
 149        BOOLEAN_CLASS = '[object Boolean]',
 150        NUMBER_CLASS = '[object Number]',
 151        STRING_CLASS = '[object String]',
 152        ARRAY_CLASS = '[object Array]',
 153        DATE_CLASS = '[object Date]',
 154        NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
 155          typeof JSON.stringify === 'function' &&
 156          JSON.stringify(0) === '0' &&
 157          typeof JSON.stringify(Prototype.K) === 'undefined';
 158  
 159    function Type(o) {
 160      switch(o) {
 161        case null: return NULL_TYPE;
 162        case (void 0): return UNDEFINED_TYPE;
 163      }
 164      var type = typeof o;
 165      switch(type) {
 166        case 'boolean': return BOOLEAN_TYPE;
 167        case 'number':  return NUMBER_TYPE;
 168        case 'string':  return STRING_TYPE;
 169      }
 170      return OBJECT_TYPE;
 171    }
 172  
 173    function extend(destination, source) {
 174      for (var property in source)
 175        destination[property] = source[property];
 176      return destination;
 177    }
 178  
 179    function inspect(object) {
 180      try {
 181        if (isUndefined(object)) return 'undefined';
 182        if (object === null) return 'null';
 183        return object.inspect ? object.inspect() : String(object);
 184      } catch (e) {
 185        if (e instanceof RangeError) return '...';
 186        throw e;
 187      }
 188    }
 189  
 190    function toJSON(value) {
 191      return Str('', { '': value }, []);
 192    }
 193  
 194    function Str(key, holder, stack) {
 195      var value = holder[key],
 196          type = typeof value;
 197  
 198      if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
 199        value = value.toJSON(key);
 200      }
 201  
 202      var _class = _toString.call(value);
 203  
 204      switch (_class) {
 205        case NUMBER_CLASS:
 206        case BOOLEAN_CLASS:
 207        case STRING_CLASS:
 208          value = value.valueOf();
 209      }
 210  
 211      switch (value) {
 212        case null: return 'null';
 213        case true: return 'true';
 214        case false: return 'false';
 215      }
 216  
 217      type = typeof value;
 218      switch (type) {
 219        case 'string':
 220          return value.inspect(true);
 221        case 'number':
 222          return isFinite(value) ? String(value) : 'null';
 223        case 'object':
 224  
 225          for (var i = 0, length = stack.length; i < length; i++) {
 226            if (stack[i] === value) { throw new TypeError(); }
 227          }
 228          stack.push(value);
 229  
 230          var partial = [];
 231          if (_class === ARRAY_CLASS) {
 232            for (var i = 0, length = value.length; i < length; i++) {
 233              var str = Str(i, value, stack);
 234              partial.push(typeof str === 'undefined' ? 'null' : str);
 235            }
 236            partial = '[' + partial.join(',') + ']';
 237          } else {
 238            var keys = Object.keys(value);
 239            for (var i = 0, length = keys.length; i < length; i++) {
 240              var key = keys[i], str = Str(key, value, stack);
 241              if (typeof str !== "undefined") {
 242                 partial.push(key.inspect(true)+ ':' + str);
 243               }
 244            }
 245            partial = '{' + partial.join(',') + '}';
 246          }
 247          stack.pop();
 248          return partial;
 249      }
 250    }
 251  
 252    function stringify(object) {
 253      return JSON.stringify(object);
 254    }
 255  
 256    function toQueryString(object) {
 257      return $H(object).toQueryString();
 258    }
 259  
 260    function toHTML(object) {
 261      return object && object.toHTML ? object.toHTML() : String.interpret(object);
 262    }
 263  
 264    function keys(object) {
 265      if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
 266      var results = [];
 267      for (var property in object) {
 268        if (object.hasOwnProperty(property)) {
 269          results.push(property);
 270        }
 271      }
 272      return results;
 273    }
 274  
 275    function values(object) {
 276      var results = [];
 277      for (var property in object)
 278        results.push(object[property]);
 279      return results;
 280    }
 281  
 282    function clone(object) {
 283      return extend({ }, object);
 284    }
 285  
 286    function isElement(object) {
 287      return !!(object && object.nodeType == 1);
 288    }
 289  
 290    function isArray(object) {
 291      return _toString.call(object) === ARRAY_CLASS;
 292    }
 293  
 294    var hasNativeIsArray = (typeof Array.isArray == 'function')
 295      && Array.isArray([]) && !Array.isArray({});
 296  
 297    if (hasNativeIsArray) {
 298      isArray = Array.isArray;
 299    }
 300  
 301    function isHash(object) {
 302      return object instanceof Hash;
 303    }
 304  
 305    function isFunction(object) {
 306      return _toString.call(object) === FUNCTION_CLASS;
 307    }
 308  
 309    function isString(object) {
 310      return _toString.call(object) === STRING_CLASS;
 311    }
 312  
 313    function isNumber(object) {
 314      return _toString.call(object) === NUMBER_CLASS;
 315    }
 316  
 317    function isDate(object) {
 318      return _toString.call(object) === DATE_CLASS;
 319    }
 320  
 321    function isUndefined(object) {
 322      return typeof object === "undefined";
 323    }
 324  
 325    extend(Object, {
 326      extend:        extend,
 327      inspect:       inspect,
 328      toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
 329      toQueryString: toQueryString,
 330      toHTML:        toHTML,
 331      keys:          Object.keys || keys,
 332      values:        values,
 333      clone:         clone,
 334      isElement:     isElement,
 335      isArray:       isArray,
 336      isHash:        isHash,
 337      isFunction:    isFunction,
 338      isString:      isString,
 339      isNumber:      isNumber,
 340      isDate:        isDate,
 341      isUndefined:   isUndefined
 342    });
 343  })();
 344  Object.extend(Function.prototype, (function() {
 345    var slice = Array.prototype.slice;
 346  
 347    function update(array, args) {
 348      var arrayLength = array.length, length = args.length;
 349      while (length--) array[arrayLength + length] = args[length];
 350      return array;
 351    }
 352  
 353    function merge(array, args) {
 354      array = slice.call(array, 0);
 355      return update(array, args);
 356    }
 357  
 358    function argumentNames() {
 359      var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
 360        .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
 361        .replace(/\s+/g, '').split(',');
 362      return names.length == 1 && !names[0] ? [] : names;
 363    }
 364  
 365    function bind(context) {
 366      if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
 367      var __method = this, args = slice.call(arguments, 1);
 368      return function() {
 369        var a = merge(args, arguments);
 370        return __method.apply(context, a);
 371      }
 372    }
 373  
 374    function bindAsEventListener(context) {
 375      var __method = this, args = slice.call(arguments, 1);
 376      return function(event) {
 377        var a = update([event || window.event], args);
 378        return __method.apply(context, a);
 379      }
 380    }
 381  
 382    function curry() {
 383      if (!arguments.length) return this;
 384      var __method = this, args = slice.call(arguments, 0);
 385      return function() {
 386        var a = merge(args, arguments);
 387        return __method.apply(this, a);
 388      }
 389    }
 390  
 391    function delay(timeout) {
 392      var __method = this, args = slice.call(arguments, 1);
 393      timeout = timeout * 1000;
 394      return window.setTimeout(function() {
 395        return __method.apply(__method, args);
 396      }, timeout);
 397    }
 398  
 399    function defer() {
 400      var args = update([0.01], arguments);
 401      return this.delay.apply(this, args);
 402    }
 403  
 404    function wrap(wrapper) {
 405      var __method = this;
 406      return function() {
 407        var a = update([__method.bind(this)], arguments);
 408        return wrapper.apply(this, a);
 409      }
 410    }
 411  
 412    function methodize() {
 413      if (this._methodized) return this._methodized;
 414      var __method = this;
 415      return this._methodized = function() {
 416        var a = update([this], arguments);
 417        return __method.apply(null, a);
 418      };
 419    }
 420  
 421    return {
 422      argumentNames:       argumentNames,
 423      bind:                bind,
 424      bindAsEventListener: bindAsEventListener,
 425      curry:               curry,
 426      delay:               delay,
 427      defer:               defer,
 428      wrap:                wrap,
 429      methodize:           methodize
 430    }
 431  })());
 432  
 433  
 434  
 435  (function(proto) {
 436  
 437  
 438    function toISOString() {
 439      return this.getUTCFullYear() + '-' +
 440        (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
 441        this.getUTCDate().toPaddedString(2) + 'T' +
 442        this.getUTCHours().toPaddedString(2) + ':' +
 443        this.getUTCMinutes().toPaddedString(2) + ':' +
 444        this.getUTCSeconds().toPaddedString(2) + 'Z';
 445    }
 446  
 447  
 448    function toJSON() {
 449      return this.toISOString();
 450    }
 451  
 452    if (!proto.toISOString) proto.toISOString = toISOString;
 453    if (!proto.toJSON) proto.toJSON = toJSON;
 454  
 455  })(Date.prototype);
 456  
 457  
 458  RegExp.prototype.match = RegExp.prototype.test;
 459  
 460  RegExp.escape = function(str) {
 461    return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
 462  };
 463  var PeriodicalExecuter = Class.create({
 464    initialize: function(callback, frequency) {
 465      this.callback = callback;
 466      this.frequency = frequency;
 467      this.currentlyExecuting = false;
 468  
 469      this.registerCallback();
 470    },
 471  
 472    registerCallback: function() {
 473      this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
 474    },
 475  
 476    execute: function() {
 477      this.callback(this);
 478    },
 479  
 480    stop: function() {
 481      if (!this.timer) return;
 482      clearInterval(this.timer);
 483      this.timer = null;
 484    },
 485  
 486    onTimerEvent: function() {
 487      if (!this.currentlyExecuting) {
 488        try {
 489          this.currentlyExecuting = true;
 490          this.execute();
 491          this.currentlyExecuting = false;
 492        } catch(e) {
 493          this.currentlyExecuting = false;
 494          throw e;
 495        }
 496      }
 497    }
 498  });
 499  Object.extend(String, {
 500    interpret: function(value) {
 501      return value == null ? '' : String(value);
 502    },
 503    specialChar: {
 504      '\b': '\\b',
 505      '\t': '\\t',
 506      '\n': '\\n',
 507      '\f': '\\f',
 508      '\r': '\\r',
 509      '\\': '\\\\'
 510    }
 511  });
 512  
 513  Object.extend(String.prototype, (function() {
 514    var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
 515      typeof JSON.parse === 'function' &&
 516      JSON.parse('{"test": true}').test;
 517  
 518    function prepareReplacement(replacement) {
 519      if (Object.isFunction(replacement)) return replacement;
 520      var template = new Template(replacement);
 521      return function(match) { return template.evaluate(match) };
 522    }
 523  
 524    function gsub(pattern, replacement) {
 525      var result = '', source = this, match;
 526      replacement = prepareReplacement(replacement);
 527  
 528      if (Object.isString(pattern))
 529        pattern = RegExp.escape(pattern);
 530  
 531      if (!(pattern.length || pattern.source)) {
 532        replacement = replacement('');
 533        return replacement + source.split('').join(replacement) + replacement;
 534      }
 535  
 536      while (source.length > 0) {
 537        if (match = source.match(pattern)) {
 538          result += source.slice(0, match.index);
 539          result += String.interpret(replacement(match));
 540          source  = source.slice(match.index + match[0].length);
 541        } else {
 542          result += source, source = '';
 543        }
 544      }
 545      return result;
 546    }
 547  
 548    function sub(pattern, replacement, count) {
 549      replacement = prepareReplacement(replacement);
 550      count = Object.isUndefined(count) ? 1 : count;
 551  
 552      return this.gsub(pattern, function(match) {
 553        if (--count < 0) return match[0];
 554        return replacement(match);
 555      });
 556    }
 557  
 558    function scan(pattern, iterator) {
 559      this.gsub(pattern, iterator);
 560      return String(this);
 561    }
 562  
 563    function truncate(length, truncation) {
 564      length = length || 30;
 565      truncation = Object.isUndefined(truncation) ? '...' : truncation;
 566      return this.length > length ?
 567        this.slice(0, length - truncation.length) + truncation : String(this);
 568    }
 569  
 570    function strip() {
 571      return this.replace(/^\s+/, '').replace(/\s+$/, '');
 572    }
 573  
 574    function stripTags() {
 575      return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
 576    }
 577  
 578    function stripScripts() {
 579      return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
 580    }
 581  
 582    function extractScripts() {
 583      var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
 584          matchOne = new RegExp(Prototype.ScriptFragment, 'im');
 585      return (this.match(matchAll) || []).map(function(scriptTag) {
 586        return (scriptTag.match(matchOne) || ['', ''])[1];
 587      });
 588    }
 589  
 590    function evalScripts() {
 591      return this.extractScripts().map(function(script) { return eval(script) });
 592    }
 593  
 594    function escapeHTML() {
 595      return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
 596    }
 597  
 598    function unescapeHTML() {
 599      return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
 600    }
 601  
 602  
 603    function toQueryParams(separator) {
 604      var match = this.strip().match(/([^?#]*)(#.*)?$/);
 605      if (!match) return { };
 606  
 607      return match[1].split(separator || '&').inject({ }, function(hash, pair) {
 608        if ((pair = pair.split('='))[0]) {
 609          var key = decodeURIComponent(pair.shift()),
 610              value = pair.length > 1 ? pair.join('=') : pair[0];
 611  
 612          if (value != undefined) value = decodeURIComponent(value);
 613  
 614          if (key in hash) {
 615            if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
 616            hash[key].push(value);
 617          }
 618          else hash[key] = value;
 619        }
 620        return hash;
 621      });
 622    }
 623  
 624    function toArray() {
 625      return this.split('');
 626    }
 627  
 628    function succ() {
 629      return this.slice(0, this.length - 1) +
 630        String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
 631    }
 632  
 633    function times(count) {
 634      return count < 1 ? '' : new Array(count + 1).join(this);
 635    }
 636  
 637    function camelize() {
 638      return this.replace(/-+(.)?/g, function(match, chr) {
 639        return chr ? chr.toUpperCase() : '';
 640      });
 641    }
 642  
 643    function capitalize() {
 644      return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
 645    }
 646  
 647    function underscore() {
 648      return this.replace(/::/g, '/')
 649                 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
 650                 .replace(/([a-z\d])([A-Z])/g, '$1_$2')
 651                 .replace(/-/g, '_')
 652                 .toLowerCase();
 653    }
 654  
 655    function dasherize() {
 656      return this.replace(/_/g, '-');
 657    }
 658  
 659    function inspect(useDoubleQuotes) {
 660      var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
 661        if (character in String.specialChar) {
 662          return String.specialChar[character];
 663        }
 664        return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
 665      });
 666      if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
 667      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
 668    }
 669  
 670    function unfilterJSON(filter) {
 671      return this.replace(filter || Prototype.JSONFilter, '$1');
 672    }
 673  
 674    function isJSON() {
 675      var str = this;
 676      if (str.blank()) return false;
 677      str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
 678      str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
 679      str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
 680      return (/^[\],:{}\s]*$/).test(str);
 681    }
 682  
 683    function evalJSON(sanitize) {
 684      var json = this.unfilterJSON(),
 685          cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
 686      if (cx.test(json)) {
 687        json = json.replace(cx, function (a) {
 688          return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
 689        });
 690      }
 691      try {
 692        if (!sanitize || json.isJSON()) return eval('(' + json + ')');
 693      } catch (e) { }
 694      throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
 695    }
 696  
 697    function parseJSON() {
 698      var json = this.unfilterJSON();
 699      return JSON.parse(json);
 700    }
 701  
 702    function include(pattern) {
 703      return this.indexOf(pattern) > -1;
 704    }
 705  
 706    function startsWith(pattern) {
 707      return this.lastIndexOf(pattern, 0) === 0;
 708    }
 709  
 710    function endsWith(pattern) {
 711      var d = this.length - pattern.length;
 712      return d >= 0 && this.indexOf(pattern, d) === d;
 713    }
 714  
 715    function empty() {
 716      return this == '';
 717    }
 718  
 719    function blank() {
 720      return /^\s*$/.test(this);
 721    }
 722  
 723    function interpolate(object, pattern) {
 724      return new Template(this, pattern).evaluate(object);
 725    }
 726  
 727    return {
 728      gsub:           gsub,
 729      sub:            sub,
 730      scan:           scan,
 731      truncate:       truncate,
 732      strip:          String.prototype.trim || strip,
 733      stripTags:      stripTags,
 734      stripScripts:   stripScripts,
 735      extractScripts: extractScripts,
 736      evalScripts:    evalScripts,
 737      escapeHTML:     escapeHTML,
 738      unescapeHTML:   unescapeHTML,
 739      toQueryParams:  toQueryParams,
 740      parseQuery:     toQueryParams,
 741      toArray:        toArray,
 742      succ:           succ,
 743      times:          times,
 744      camelize:       camelize,
 745      capitalize:     capitalize,
 746      underscore:     underscore,
 747      dasherize:      dasherize,
 748      inspect:        inspect,
 749      unfilterJSON:   unfilterJSON,
 750      isJSON:         isJSON,
 751      evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
 752      include:        include,
 753      startsWith:     startsWith,
 754      endsWith:       endsWith,
 755      empty:          empty,
 756      blank:          blank,
 757      interpolate:    interpolate
 758    };
 759  })());
 760  
 761  var Template = Class.create({
 762    initialize: function(template, pattern) {
 763      this.template = template.toString();
 764      this.pattern = pattern || Template.Pattern;
 765    },
 766  
 767    evaluate: function(object) {
 768      if (object && Object.isFunction(object.toTemplateReplacements))
 769        object = object.toTemplateReplacements();
 770  
 771      return this.template.gsub(this.pattern, function(match) {
 772        if (object == null) return (match[1] + '');
 773  
 774        var before = match[1] || '';
 775        if (before == '\\') return match[2];
 776  
 777        var ctx = object, expr = match[3],
 778            pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
 779  
 780        match = pattern.exec(expr);
 781        if (match == null) return before;
 782  
 783        while (match != null) {
 784          var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
 785          ctx = ctx[comp];
 786          if (null == ctx || '' == match[3]) break;
 787          expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
 788          match = pattern.exec(expr);
 789        }
 790  
 791        return before + String.interpret(ctx);
 792      });
 793    }
 794  });
 795  Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
 796  
 797  var $break = { };
 798  
 799  var Enumerable = (function() {
 800    function each(iterator, context) {
 801      var index = 0;
 802      try {
 803        this._each(function(value) {
 804          iterator.call(context, value, index++);
 805        });
 806      } catch (e) {
 807        if (e != $break) throw e;
 808      }
 809      return this;
 810    }
 811  
 812    function eachSlice(number, iterator, context) {
 813      var index = -number, slices = [], array = this.toArray();
 814      if (number < 1) return array;
 815      while ((index += number) < array.length)
 816        slices.push(array.slice(index, index+number));
 817      return slices.collect(iterator, context);
 818    }
 819  
 820    function all(iterator, context) {
 821      iterator = iterator || Prototype.K;
 822      var result = true;
 823      this.each(function(value, index) {
 824        result = result && !!iterator.call(context, value, index);
 825        if (!result) throw $break;
 826      });
 827      return result;
 828    }
 829  
 830    function any(iterator, context) {
 831      iterator = iterator || Prototype.K;
 832      var result = false;
 833      this.each(function(value, index) {
 834        if (result = !!iterator.call(context, value, index))
 835          throw $break;
 836      });
 837      return result;
 838    }
 839  
 840    function collect(iterator, context) {
 841      iterator = iterator || Prototype.K;
 842      var results = [];
 843      this.each(function(value, index) {
 844        results.push(iterator.call(context, value, index));
 845      });
 846      return results;
 847    }
 848  
 849    function detect(iterator, context) {
 850      var result;
 851      this.each(function(value, index) {
 852        if (iterator.call(context, value, index)) {
 853          result = value;
 854          throw $break;
 855        }
 856      });
 857      return result;
 858    }
 859  
 860    function findAll(iterator, context) {
 861      var results = [];
 862      this.each(function(value, index) {
 863        if (iterator.call(context, value, index))
 864          results.push(value);
 865      });
 866      return results;
 867    }
 868  
 869    function grep(filter, iterator, context) {
 870      iterator = iterator || Prototype.K;
 871      var results = [];
 872  
 873      if (Object.isString(filter))
 874        filter = new RegExp(RegExp.escape(filter));
 875  
 876      this.each(function(value, index) {
 877        if (filter.match(value))
 878          results.push(iterator.call(context, value, index));
 879      });
 880      return results;
 881    }
 882  
 883    function include(object) {
 884      if (Object.isFunction(this.indexOf))
 885        if (this.indexOf(object) != -1) return true;
 886  
 887      var found = false;
 888      this.each(function(value) {
 889        if (value == object) {
 890          found = true;
 891          throw $break;
 892        }
 893      });
 894      return found;
 895    }
 896  
 897    function inGroupsOf(number, fillWith) {
 898      fillWith = Object.isUndefined(fillWith) ? null : fillWith;
 899      return this.eachSlice(number, function(slice) {
 900        while(slice.length < number) slice.push(fillWith);
 901        return slice;
 902      });
 903    }
 904  
 905    function inject(memo, iterator, context) {
 906      this.each(function(value, index) {
 907        memo = iterator.call(context, memo, value, index);
 908      });
 909      return memo;
 910    }
 911  
 912    function invoke(method) {
 913      var args = $A(arguments).slice(1);
 914      return this.map(function(value) {
 915        return value[method].apply(value, args);
 916      });
 917    }
 918  
 919    function max(iterator, context) {
 920      iterator = iterator || Prototype.K;
 921      var result;
 922      this.each(function(value, index) {
 923        value = iterator.call(context, value, index);
 924        if (result == null || value >= result)
 925          result = value;
 926      });
 927      return result;
 928    }
 929  
 930    function min(iterator, context) {
 931      iterator = iterator || Prototype.K;
 932      var result;
 933      this.each(function(value, index) {
 934        value = iterator.call(context, value, index);
 935        if (result == null || value < result)
 936          result = value;
 937      });
 938      return result;
 939    }
 940  
 941    function partition(iterator, context) {
 942      iterator = iterator || Prototype.K;
 943      var trues = [], falses = [];
 944      this.each(function(value, index) {
 945        (iterator.call(context, value, index) ?
 946          trues : falses).push(value);
 947      });
 948      return [trues, falses];
 949    }
 950  
 951    function pluck(property) {
 952      var results = [];
 953      this.each(function(value) {
 954        results.push(value[property]);
 955      });
 956      return results;
 957    }
 958  
 959    function reject(iterator, context) {
 960      var results = [];
 961      this.each(function(value, index) {
 962        if (!iterator.call(context, value, index))
 963          results.push(value);
 964      });
 965      return results;
 966    }
 967  
 968    function sortBy(iterator, context) {
 969      return this.map(function(value, index) {
 970        return {
 971          value: value,
 972          criteria: iterator.call(context, value, index)
 973        };
 974      }).sort(function(left, right) {
 975        var a = left.criteria, b = right.criteria;
 976        return a < b ? -1 : a > b ? 1 : 0;
 977      }).pluck('value');
 978    }
 979  
 980    function toArray() {
 981      return this.map();
 982    }
 983  
 984    function zip() {
 985      var iterator = Prototype.K, args = $A(arguments);
 986      if (Object.isFunction(args.last()))
 987        iterator = args.pop();
 988  
 989      var collections = [this].concat(args).map($A);
 990      return this.map(function(value, index) {
 991        return iterator(collections.pluck(index));
 992      });
 993    }
 994  
 995    function size() {
 996      return this.toArray().length;
 997    }
 998  
 999    function inspect() {
1000      return '#<Enumerable:' + this.toArray().inspect() + '>';
1001    }
1002  
1003  
1004  
1005  
1006  
1007  
1008  
1009  
1010  
1011    return {
1012      each:       each,
1013      eachSlice:  eachSlice,
1014      all:        all,
1015      every:      all,
1016      any:        any,
1017      some:       any,
1018      collect:    collect,
1019      map:        collect,
1020      detect:     detect,
1021      findAll:    findAll,
1022      select:     findAll,
1023      filter:     findAll,
1024      grep:       grep,
1025      include:    include,
1026      member:     include,
1027      inGroupsOf: inGroupsOf,
1028      inject:     inject,
1029      invoke:     invoke,
1030      max:        max,
1031      min:        min,
1032      partition:  partition,
1033      pluck:      pluck,
1034      reject:     reject,
1035      sortBy:     sortBy,
1036      toArray:    toArray,
1037      entries:    toArray,
1038      zip:        zip,
1039      size:       size,
1040      inspect:    inspect,
1041      find:       detect
1042    };
1043  })();
1044  
1045  function $A(iterable) {
1046    if (!iterable) return [];
1047    if ('toArray' in Object(iterable)) return iterable.toArray();
1048    var length = iterable.length || 0, results = new Array(length);
1049    while (length--) results[length] = iterable[length];
1050    return results;
1051  }
1052  
1053  
1054  function $w(string) {
1055    if (!Object.isString(string)) return [];
1056    string = string.strip();
1057    return string ? string.split(/\s+/) : [];
1058  }
1059  
1060  Array.from = $A;
1061  
1062  
1063  (function() {
1064    var arrayProto = Array.prototype,
1065        slice = arrayProto.slice,
1066        _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1067  
1068    function each(iterator, context) {
1069      for (var i = 0, length = this.length >>> 0; i < length; i++) {
1070        if (i in this) iterator.call(context, this[i], i, this);
1071      }
1072    }
1073    if (!_each) _each = each;
1074  
1075    function clear() {
1076      this.length = 0;
1077      return this;
1078    }
1079  
1080    function first() {
1081      return this[0];
1082    }
1083  
1084    function last() {
1085      return this[this.length - 1];
1086    }
1087  
1088    function compact() {
1089      return this.select(function(value) {
1090        return value != null;
1091      });
1092    }
1093  
1094    function flatten() {
1095      return this.inject([], function(array, value) {
1096        if (Object.isArray(value))
1097          return array.concat(value.flatten());
1098        array.push(value);
1099        return array;
1100      });
1101    }
1102  
1103    function without() {
1104      var values = slice.call(arguments, 0);
1105      return this.select(function(value) {
1106        return !values.include(value);
1107      });
1108    }
1109  
1110    function reverse(inline) {
1111      return (inline === false ? this.toArray() : this)._reverse();
1112    }
1113  
1114    function uniq(sorted) {
1115      return this.inject([], function(array, value, index) {
1116        if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1117          array.push(value);
1118        return array;
1119      });
1120    }
1121  
1122    function intersect(array) {
1123      return this.uniq().findAll(function(item) {
1124        return array.detect(function(value) { return item === value });
1125      });
1126    }
1127  
1128  
1129    function clone() {
1130      return slice.call(this, 0);
1131    }
1132  
1133    function size() {
1134      return this.length;
1135    }
1136  
1137    function inspect() {
1138      return '[' + this.map(Object.inspect).join(', ') + ']';
1139    }
1140  
1141    function indexOf(item, i) {
1142      i || (i = 0);
1143      var length = this.length;
1144      if (i < 0) i = length + i;
1145      for (; i < length; i++)
1146        if (this[i] === item) return i;
1147      return -1;
1148    }
1149  
1150    function lastIndexOf(item, i) {
1151      i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1152      var n = this.slice(0, i).reverse().indexOf(item);
1153      return (n < 0) ? n : i - n - 1;
1154    }
1155  
1156    function concat() {
1157      var array = slice.call(this, 0), item;
1158      for (var i = 0, length = arguments.length; i < length; i++) {
1159        item = arguments[i];
1160        if (Object.isArray(item) && !('callee' in item)) {
1161          for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1162            array.push(item[j]);
1163        } else {
1164          array.push(item);
1165        }
1166      }
1167      return array;
1168    }
1169  
1170    Object.extend(arrayProto, Enumerable);
1171  
1172    if (!arrayProto._reverse)
1173      arrayProto._reverse = arrayProto.reverse;
1174  
1175    Object.extend(arrayProto, {
1176      _each:     _each,
1177      clear:     clear,
1178      first:     first,
1179      last:      last,
1180      compact:   compact,
1181      flatten:   flatten,
1182      without:   without,
1183      reverse:   reverse,
1184      uniq:      uniq,
1185      intersect: intersect,
1186      clone:     clone,
1187      toArray:   clone,
1188      size:      size,
1189      inspect:   inspect
1190    });
1191  
1192    var CONCAT_ARGUMENTS_BUGGY = (function() {
1193      return [].concat(arguments)[0][0] !== 1;
1194    })(1,2)
1195  
1196    if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1197  
1198    if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1199    if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1200  })();
1201  function $H(object) {
1202    return new Hash(object);
1203  };
1204  
1205  var Hash = Class.create(Enumerable, (function() {
1206    function initialize(object) {
1207      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1208    }
1209  
1210  
1211    function _each(iterator) {
1212      for (var key in this._object) {
1213        var value = this._object[key], pair = [key, value];
1214        pair.key = key;
1215        pair.value = value;
1216        iterator(pair);
1217      }
1218    }
1219  
1220    function set(key, value) {
1221      return this._object[key] = value;
1222    }
1223  
1224    function get(key) {
1225      if (this._object[key] !== Object.prototype[key])
1226        return this._object[key];
1227    }
1228  
1229    function unset(key) {
1230      var value = this._object[key];
1231      delete this._object[key];
1232      return value;
1233    }
1234  
1235    function toObject() {
1236      return Object.clone(this._object);
1237    }
1238  
1239  
1240  
1241    function keys() {
1242      return this.pluck('key');
1243    }
1244  
1245    function values() {
1246      return this.pluck('value');
1247    }
1248  
1249    function index(value) {
1250      var match = this.detect(function(pair) {
1251        return pair.value === value;
1252      });
1253      return match && match.key;
1254    }
1255  
1256    function merge(object) {
1257      return this.clone().update(object);
1258    }
1259  
1260    function update(object) {
1261      return new Hash(object).inject(this, function(result, pair) {
1262        result.set(pair.key, pair.value);
1263        return result;
1264      });
1265    }
1266  
1267    function toQueryPair(key, value) {
1268      if (Object.isUndefined(value)) return key;
1269      return key + '=' + encodeURIComponent(String.interpret(value));
1270    }
1271  
1272    function toQueryString() {
1273      return this.inject([], function(results, pair) {
1274        var key = encodeURIComponent(pair.key), values = pair.value;
1275  
1276        if (values && typeof values == 'object') {
1277          if (Object.isArray(values)) {
1278            var queryValues = [];
1279            for (var i = 0, len = values.length, value; i < len; i++) {
1280              value = values[i];
1281              queryValues.push(toQueryPair(key, value));
1282            }
1283            return results.concat(queryValues);
1284          }
1285        } else results.push(toQueryPair(key, values));
1286        return results;
1287      }).join('&');
1288    }
1289  
1290    function inspect() {
1291      return '#<Hash:{' + this.map(function(pair) {
1292        return pair.map(Object.inspect).join(': ');
1293      }).join(', ') + '}>';
1294    }
1295  
1296    function clone() {
1297      return new Hash(this);
1298    }
1299  
1300    return {
1301      initialize:             initialize,
1302      _each:                  _each,
1303      set:                    set,
1304      get:                    get,
1305      unset:                  unset,
1306      toObject:               toObject,
1307      toTemplateReplacements: toObject,
1308      keys:                   keys,
1309      values:                 values,
1310      index:                  index,
1311      merge:                  merge,
1312      update:                 update,
1313      toQueryString:          toQueryString,
1314      inspect:                inspect,
1315      toJSON:                 toObject,
1316      clone:                  clone
1317    };
1318  })());
1319  
1320  Hash.from = $H;
1321  Object.extend(Number.prototype, (function() {
1322    function toColorPart() {
1323      return this.toPaddedString(2, 16);
1324    }
1325  
1326    function succ() {
1327      return this + 1;
1328    }
1329  
1330    function times(iterator, context) {
1331      $R(0, this, true).each(iterator, context);
1332      return this;
1333    }
1334  
1335    function toPaddedString(length, radix) {
1336      var string = this.toString(radix || 10);
1337      return '0'.times(length - string.length) + string;
1338    }
1339  
1340    function abs() {
1341      return Math.abs(this);
1342    }
1343  
1344    function round() {
1345      return Math.round(this);
1346    }
1347  
1348    function ceil() {
1349      return Math.ceil(this);
1350    }
1351  
1352    function floor() {
1353      return Math.floor(this);
1354    }
1355  
1356    return {
1357      toColorPart:    toColorPart,
1358      succ:           succ,
1359      times:          times,
1360      toPaddedString: toPaddedString,
1361      abs:            abs,
1362      round:          round,
1363      ceil:           ceil,
1364      floor:          floor
1365    };
1366  })());
1367  
1368  function $R(start, end, exclusive) {
1369    return new ObjectRange(start, end, exclusive);
1370  }
1371  
1372  var ObjectRange = Class.create(Enumerable, (function() {
1373    function initialize(start, end, exclusive) {
1374      this.start = start;
1375      this.end = end;
1376      this.exclusive = exclusive;
1377    }
1378  
1379    function _each(iterator) {
1380      var value = this.start;
1381      while (this.include(value)) {
1382        iterator(value);
1383        value = value.succ();
1384      }
1385    }
1386  
1387    function include(value) {
1388      if (value < this.start)
1389        return false;
1390      if (this.exclusive)
1391        return value < this.end;
1392      return value <= this.end;
1393    }
1394  
1395    return {
1396      initialize: initialize,
1397      _each:      _each,
1398      include:    include
1399    };
1400  })());
1401  
1402  
1403  
1404  var Abstract = { };
1405  
1406  
1407  var Try = {
1408    these: function() {
1409      var returnValue;
1410  
1411      for (var i = 0, length = arguments.length; i < length; i++) {
1412        var lambda = arguments[i];
1413        try {
1414          returnValue = lambda();
1415          break;
1416        } catch (e) { }
1417      }
1418  
1419      return returnValue;
1420    }
1421  };
1422  
1423  var Ajax = {
1424    getTransport: function() {
1425      return Try.these(
1426        function() {return new XMLHttpRequest()},
1427        function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1428        function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1429      ) || false;
1430    },
1431  
1432    activeRequestCount: 0
1433  };
1434  
1435  Ajax.Responders = {
1436    responders: [],
1437  
1438    _each: function(iterator) {
1439      this.responders._each(iterator);
1440    },
1441  
1442    register: function(responder) {
1443      if (!this.include(responder))
1444        this.responders.push(responder);
1445    },
1446  
1447    unregister: function(responder) {
1448      this.responders = this.responders.without(responder);
1449    },
1450  
1451    dispatch: function(callback, request, transport, json) {
1452      this.each(function(responder) {
1453        if (Object.isFunction(responder[callback])) {
1454          try {
1455            responder[callback].apply(responder, [request, transport, json]);
1456          } catch (e) { }
1457        }
1458      });
1459    }
1460  };
1461  
1462  Object.extend(Ajax.Responders, Enumerable);
1463  
1464  Ajax.Responders.register({
1465    onCreate:   function() { Ajax.activeRequestCount++ },
1466    onComplete: function() { Ajax.activeRequestCount-- }
1467  });
1468  Ajax.Base = Class.create({
1469    initialize: function(options) {
1470      this.options = {
1471        method:       'post',
1472        asynchronous: true,
1473        contentType:  'application/x-www-form-urlencoded',
1474        encoding:     'UTF-8',
1475        parameters:   '',
1476        evalJSON:     true,
1477        evalJS:       true
1478      };
1479      Object.extend(this.options, options || { });
1480  
1481      this.options.method = this.options.method.toLowerCase();
1482  
1483      if (Object.isHash(this.options.parameters))
1484        this.options.parameters = this.options.parameters.toObject();
1485    }
1486  });
1487  Ajax.Request = Class.create(Ajax.Base, {
1488    _complete: false,
1489  
1490    initialize: function($super, url, options) {
1491      $super(options);
1492      this.transport = Ajax.getTransport();
1493      this.request(url);
1494    },
1495  
1496    request: function(url) {
1497      this.url = url;
1498      this.method = this.options.method;
1499      var params = Object.isString(this.options.parameters) ?
1500            this.options.parameters :
1501            Object.toQueryString(this.options.parameters);
1502  
1503      if (!['get', 'post'].include(this.method)) {
1504        params += (params ? '&' : '') + "_method=" + this.method;
1505        this.method = 'post';
1506      }
1507  
1508      if (params && this.method === 'get') {
1509        this.url += (this.url.include('?') ? '&' : '?') + params;
1510      }
1511  
1512      this.parameters = params.toQueryParams();
1513  
1514      try {
1515        var response = new Ajax.Response(this);
1516        if (this.options.onCreate) this.options.onCreate(response);
1517        Ajax.Responders.dispatch('onCreate', this, response);
1518  
1519        this.transport.open(this.method.toUpperCase(), this.url,
1520          this.options.asynchronous);
1521  
1522        if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1523  
1524        this.transport.onreadystatechange = this.onStateChange.bind(this);
1525        this.setRequestHeaders();
1526  
1527        this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1528        this.transport.send(this.body);
1529  
1530        /* Force Firefox to handle ready state 4 for synchronous requests */
1531        if (!this.options.asynchronous && this.transport.overrideMimeType)
1532          this.onStateChange();
1533  
1534      }
1535      catch (e) {
1536        this.dispatchException(e);
1537      }
1538    },
1539  
1540    onStateChange: function() {
1541      var readyState = this.transport.readyState;
1542      if (readyState > 1 && !((readyState == 4) && this._complete))
1543        this.respondToReadyState(this.transport.readyState);
1544    },
1545  
1546    setRequestHeaders: function() {
1547      var headers = {
1548        'X-Requested-With': 'XMLHttpRequest',
1549        'X-Prototype-Version': Prototype.Version,
1550        'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1551      };
1552  
1553      if (this.method == 'post') {
1554        headers['Content-type'] = this.options.contentType +
1555          (this.options.encoding ? '; charset=' + this.options.encoding : '');
1556  
1557        /* Force "Connection: close" for older Mozilla browsers to work
1558         * around a bug where XMLHttpRequest sends an incorrect
1559         * Content-length header. See Mozilla Bugzilla #246651.
1560         */
1561        if (this.transport.overrideMimeType &&
1562            (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1563              headers['Connection'] = 'close';
1564      }
1565  
1566      if (typeof this.options.requestHeaders == 'object') {
1567        var extras = this.options.requestHeaders;
1568  
1569        if (Object.isFunction(extras.push))
1570          for (var i = 0, length = extras.length; i < length; i += 2)
1571            headers[extras[i]] = extras[i+1];
1572        else
1573          $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1574      }
1575  
1576      for (var name in headers)
1577        this.transport.setRequestHeader(name, headers[name]);
1578    },
1579  
1580    success: function() {
1581      var status = this.getStatus();
1582      return !status || (status >= 200 && status < 300) || status == 304;
1583    },
1584  
1585    getStatus: function() {
1586      try {
1587        if (this.transport.status === 1223) return 204;
1588        return this.transport.status || 0;
1589      } catch (e) { return 0 }
1590    },
1591  
1592    respondToReadyState: function(readyState) {
1593      var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1594  
1595      if (state == 'Complete') {
1596        try {
1597          this._complete = true;
1598          (this.options['on' + response.status]
1599           || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1600           || Prototype.emptyFunction)(response, response.headerJSON);
1601        } catch (e) {
1602          this.dispatchException(e);
1603        }
1604  
1605        var contentType = response.getHeader('Content-type');
1606        if (this.options.evalJS == 'force'
1607            || (this.options.evalJS && this.isSameOrigin() && contentType
1608            && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1609          this.evalResponse();
1610      }
1611  
1612      try {
1613        (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1614        Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1615      } catch (e) {
1616        this.dispatchException(e);
1617      }
1618  
1619      if (state == 'Complete') {
1620        this.transport.onreadystatechange = Prototype.emptyFunction;
1621      }
1622    },
1623  
1624    isSameOrigin: function() {
1625      var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1626      return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1627        protocol: location.protocol,
1628        domain: document.domain,
1629        port: location.port ? ':' + location.port : ''
1630      }));
1631    },
1632  
1633    getHeader: function(name) {
1634      try {
1635        return this.transport.getResponseHeader(name) || null;
1636      } catch (e) { return null; }
1637    },
1638  
1639    evalResponse: function() {
1640      try {
1641        return eval((this.transport.responseText || '').unfilterJSON());
1642      } catch (e) {
1643        this.dispatchException(e);
1644      }
1645    },
1646  
1647    dispatchException: function(exception) {
1648      (this.options.onException || Prototype.emptyFunction)(this, exception);
1649      Ajax.Responders.dispatch('onException', this, exception);
1650    }
1651  });
1652  
1653  Ajax.Request.Events =
1654    ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1655  
1656  
1657  
1658  
1659  
1660  
1661  
1662  
1663  Ajax.Response = Class.create({
1664    initialize: function(request){
1665      this.request = request;
1666      var transport  = this.transport  = request.transport,
1667          readyState = this.readyState = transport.readyState;
1668  
1669      if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1670        this.status       = this.getStatus();
1671        this.statusText   = this.getStatusText();
1672        this.responseText = String.interpret(transport.responseText);
1673        this.headerJSON   = this._getHeaderJSON();
1674      }
1675  
1676      if (readyState == 4) {
1677        var xml = transport.responseXML;
1678        this.responseXML  = Object.isUndefined(xml) ? null : xml;
1679        this.responseJSON = this._getResponseJSON();
1680      }
1681    },
1682  
1683    status:      0,
1684  
1685    statusText: '',
1686  
1687    getStatus: Ajax.Request.prototype.getStatus,
1688  
1689    getStatusText: function() {
1690      try {
1691        return this.transport.statusText || '';
1692      } catch (e) { return '' }
1693    },
1694  
1695    getHeader: Ajax.Request.prototype.getHeader,
1696  
1697    getAllHeaders: function() {
1698      try {
1699        return this.getAllResponseHeaders();
1700      } catch (e) { return null }
1701    },
1702  
1703    getResponseHeader: function(name) {
1704      return this.transport.getResponseHeader(name);
1705    },
1706  
1707    getAllResponseHeaders: function() {
1708      return this.transport.getAllResponseHeaders();
1709    },
1710  
1711    _getHeaderJSON: function() {
1712      var json = this.getHeader('X-JSON');
1713      if (!json) return null;
1714      json = decodeURIComponent(escape(json));
1715      try {
1716        return json.evalJSON(this.request.options.sanitizeJSON ||
1717          !this.request.isSameOrigin());
1718      } catch (e) {
1719        this.request.dispatchException(e);
1720      }
1721    },
1722  
1723    _getResponseJSON: function() {
1724      var options = this.request.options;
1725      if (!options.evalJSON || (options.evalJSON != 'force' &&
1726        !(this.getHeader('Content-type') || '').include('application/json')) ||
1727          this.responseText.blank())
1728            return null;
1729      try {
1730        return this.responseText.evalJSON(options.sanitizeJSON ||
1731          !this.request.isSameOrigin());
1732      } catch (e) {
1733        this.request.dispatchException(e);
1734      }
1735    }
1736  });
1737  
1738  Ajax.Updater = Class.create(Ajax.Request, {
1739    initialize: function($super, container, url, options) {
1740      this.container = {
1741        success: (container.success || container),
1742        failure: (container.failure || (container.success ? null : container))
1743      };
1744  
1745      options = Object.clone(options);
1746      var onComplete = options.onComplete;
1747      options.onComplete = (function(response, json) {
1748        this.updateContent(response.responseText);
1749        if (Object.isFunction(onComplete)) onComplete(response, json);
1750      }).bind(this);
1751  
1752      $super(url, options);
1753    },
1754  
1755    updateContent: function(responseText) {
1756      var receiver = this.container[this.success() ? 'success' : 'failure'],
1757          options = this.options;
1758  
1759      if (!options.evalScripts) responseText = responseText.stripScripts();
1760  
1761      if (receiver = $(receiver)) {
1762        if (options.insertion) {
1763          if (Object.isString(options.insertion)) {
1764            var insertion = { }; insertion[options.insertion] = responseText;
1765            receiver.insert(insertion);
1766          }
1767          else options.insertion(receiver, responseText);
1768        }
1769        else receiver.update(responseText);
1770      }
1771    }
1772  });
1773  
1774  Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1775    initialize: function($super, container, url, options) {
1776      $super(options);
1777      this.onComplete = this.options.onComplete;
1778  
1779      this.frequency = (this.options.frequency || 2);
1780      this.decay = (this.options.decay || 1);
1781  
1782      this.updater = { };
1783      this.container = container;
1784      this.url = url;
1785  
1786      this.start();
1787    },
1788  
1789    start: function() {
1790      this.options.onComplete = this.updateComplete.bind(this);
1791      this.onTimerEvent();
1792    },
1793  
1794    stop: function() {
1795      this.updater.options.onComplete = undefined;
1796      clearTimeout(this.timer);
1797      (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1798    },
1799  
1800    updateComplete: function(response) {
1801      if (this.options.decay) {
1802        this.decay = (response.responseText == this.lastText ?
1803          this.decay * this.options.decay : 1);
1804  
1805        this.lastText = response.responseText;
1806      }
1807      this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1808    },
1809  
1810    onTimerEvent: function() {
1811      this.updater = new Ajax.Updater(this.container, this.url, this.options);
1812    }
1813  });
1814  
1815  
1816  function $(element) {
1817    if (arguments.length > 1) {
1818      for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1819        elements.push($(arguments[i]));
1820      return elements;
1821    }
1822    if (Object.isString(element))
1823      element = document.getElementById(element);
1824    return Element.extend(element);
1825  }
1826  
1827  if (Prototype.BrowserFeatures.XPath) {
1828    document._getElementsByXPath = function(expression, parentElement) {
1829      var results = [];
1830      var query = document.evaluate(expression, $(parentElement) || document,
1831        null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1832      for (var i = 0, length = query.snapshotLength; i < length; i++)
1833        results.push(Element.extend(query.snapshotItem(i)));
1834      return results;
1835    };
1836  }
1837  
1838  /*--------------------------------------------------------------------------*/
1839  
1840  if (!Node) var Node = { };
1841  
1842  if (!Node.ELEMENT_NODE) {
1843    Object.extend(Node, {
1844      ELEMENT_NODE: 1,
1845      ATTRIBUTE_NODE: 2,
1846      TEXT_NODE: 3,
1847      CDATA_SECTION_NODE: 4,
1848      ENTITY_REFERENCE_NODE: 5,
1849      ENTITY_NODE: 6,
1850      PROCESSING_INSTRUCTION_NODE: 7,
1851      COMMENT_NODE: 8,
1852      DOCUMENT_NODE: 9,
1853      DOCUMENT_TYPE_NODE: 10,
1854      DOCUMENT_FRAGMENT_NODE: 11,
1855      NOTATION_NODE: 12
1856    });
1857  }
1858  
1859  
1860  
1861  (function(global) {
1862    function shouldUseCache(tagName, attributes) {
1863      if (tagName === 'select') return false;
1864      if ('type' in attributes) return false;
1865      return true;
1866    }
1867  
1868    var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
1869      try {
1870        var el = document.createElement('<input name="x">');
1871        return el.tagName.toLowerCase() === 'input' && el.name === 'x';
1872      }
1873      catch(err) {
1874        return false;
1875      }
1876    })();
1877  
1878    var element = global.Element;
1879  
1880    global.Element = function(tagName, attributes) {
1881      attributes = attributes || { };
1882      tagName = tagName.toLowerCase();
1883      var cache = Element.cache;
1884  
1885      if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
1886        tagName = '<' + tagName + ' name="' + attributes.name + '">';
1887        delete attributes.name;
1888        return Element.writeAttribute(document.createElement(tagName), attributes);
1889      }
1890  
1891      if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1892  
1893      var node = shouldUseCache(tagName, attributes) ?
1894       cache[tagName].cloneNode(false) : document.createElement(tagName);
1895  
1896      return Element.writeAttribute(node, attributes);
1897    };
1898  
1899    Object.extend(global.Element, element || { });
1900    if (element) global.Element.prototype = element.prototype;
1901  
1902  })(this);
1903  
1904  Element.idCounter = 1;
1905  Element.cache = { };
1906  
1907  Element._purgeElement = function(element) {
1908    var uid = element._prototypeUID;
1909    if (uid) {
1910      Element.stopObserving(element);
1911      element._prototypeUID = void 0;
1912      delete Element.Storage[uid];
1913    }
1914  }
1915  
1916  Element.Methods = {
1917    visible: function(element) {
1918      return $(element).style.display != 'none';
1919    },
1920  
1921    toggle: function(element) {
1922      element = $(element);
1923      Element[Element.visible(element) ? 'hide' : 'show'](element);
1924      return element;
1925    },
1926  
1927    hide: function(element) {
1928      element = $(element);
1929      element.style.display = 'none';
1930      return element;
1931    },
1932  
1933    show: function(element) {
1934      element = $(element);
1935      element.style.display = '';
1936      return element;
1937    },
1938  
1939    remove: function(element) {
1940      element = $(element);
1941      element.parentNode.removeChild(element);
1942      return element;
1943    },
1944  
1945    update: (function(){
1946  
1947      var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1948        var el = document.createElement("select"),
1949            isBuggy = true;
1950        el.innerHTML = "<option value=\"test\">test</option>";
1951        if (el.options && el.options[0]) {
1952          isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1953        }
1954        el = null;
1955        return isBuggy;
1956      })();
1957  
1958      var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1959        try {
1960          var el = document.createElement("table");
1961          if (el && el.tBodies) {
1962            el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1963            var isBuggy = typeof el.tBodies[0] == "undefined";
1964            el = null;
1965            return isBuggy;
1966          }
1967        } catch (e) {
1968          return true;
1969        }
1970      })();
1971  
1972      var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
1973        try {
1974          var el = document.createElement('div');
1975          el.innerHTML = "<link>";
1976          var isBuggy = (el.childNodes.length === 0);
1977          el = null;
1978          return isBuggy;
1979        } catch(e) {
1980          return true;
1981        }
1982      })();
1983  
1984      var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
1985       TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
1986  
1987      var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1988        var s = document.createElement("script"),
1989            isBuggy = false;
1990        try {
1991          s.appendChild(document.createTextNode(""));
1992          isBuggy = !s.firstChild ||
1993            s.firstChild && s.firstChild.nodeType !== 3;
1994        } catch (e) {
1995          isBuggy = true;
1996        }
1997        s = null;
1998        return isBuggy;
1999      })();
2000  
2001  
2002      function update(element, content) {
2003        element = $(element);
2004        var purgeElement = Element._purgeElement;
2005  
2006        var descendants = element.getElementsByTagName('*'),
2007         i = descendants.length;
2008        while (i--) purgeElement(descendants[i]);
2009  
2010        if (content && content.toElement)
2011          content = content.toElement();
2012  
2013        if (Object.isElement(content))
2014          return element.update().insert(content);
2015  
2016        content = Object.toHTML(content);
2017  
2018        var tagName = element.tagName.toUpperCase();
2019  
2020        if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
2021          element.text = content;
2022          return element;
2023        }
2024  
2025        if (ANY_INNERHTML_BUGGY) {
2026          if (tagName in Element._insertionTranslations.tags) {
2027            while (element.firstChild) {
2028              element.removeChild(element.firstChild);
2029            }
2030            Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2031              .each(function(node) {
2032                element.appendChild(node)
2033              });
2034          } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
2035            while (element.firstChild) {
2036              element.removeChild(element.firstChild);
2037            }
2038            var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
2039            nodes.each(function(node) { element.appendChild(node) });
2040          }
2041          else {
2042            element.innerHTML = content.stripScripts();
2043          }
2044        }
2045        else {
2046          element.innerHTML = content.stripScripts();
2047        }
2048  
2049        content.evalScripts.bind(content).defer();
2050        return element;
2051      }
2052  
2053      return update;
2054    })(),
2055  
2056    replace: function(element, content) {
2057      element = $(element);
2058      if (content && content.toElement) content = content.toElement();
2059      else if (!Object.isElement(content)) {
2060        content = Object.toHTML(content);
2061        var range = element.ownerDocument.createRange();
2062        range.selectNode(element);
2063        content.evalScripts.bind(content).defer();
2064        content = range.createContextualFragment(content.stripScripts());
2065      }
2066      element.parentNode.replaceChild(content, element);
2067      return element;
2068    },
2069  
2070    insert: function(element, insertions) {
2071      element = $(element);
2072  
2073      if (Object.isString(insertions) || Object.isNumber(insertions) ||
2074          Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2075            insertions = {bottom:insertions};
2076  
2077      var content, insert, tagName, childNodes;
2078  
2079      for (var position in insertions) {
2080        content  = insertions[position];
2081        position = position.toLowerCase();
2082        insert = Element._insertionTranslations[position];
2083  
2084        if (content && content.toElement) content = content.toElement();
2085        if (Object.isElement(content)) {
2086          insert(element, content);
2087          continue;
2088        }
2089  
2090        content = Object.toHTML(content);
2091  
2092        tagName = ((position == 'before' || position == 'after')
2093          ? element.parentNode : element).tagName.toUpperCase();
2094  
2095        childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2096  
2097        if (position == 'top' || position == 'after') childNodes.reverse();
2098        childNodes.each(insert.curry(element));
2099  
2100        content.evalScripts.bind(content).defer();
2101      }
2102  
2103      return element;
2104    },
2105  
2106    wrap: function(element, wrapper, attributes) {
2107      element = $(element);
2108      if (Object.isElement(wrapper))
2109        $(wrapper).writeAttribute(attributes || { });
2110      else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
2111      else wrapper = new Element('div', wrapper);
2112      if (element.parentNode)
2113        element.parentNode.replaceChild(wrapper, element);
2114      wrapper.appendChild(element);
2115      return wrapper;
2116    },
2117  
2118    inspect: function(element) {
2119      element = $(element);
2120      var result = '<' + element.tagName.toLowerCase();
2121      $H({'id': 'id', 'className': 'class'}).each(function(pair) {
2122        var property = pair.first(),
2123            attribute = pair.last(),
2124            value = (element[property] || '').toString();
2125        if (value) result += ' ' + attribute + '=' + value.inspect(true);
2126      });
2127      return result + '>';
2128    },
2129  
2130    recursivelyCollect: function(element, property, maximumLength) {
2131      element = $(element);
2132      maximumLength = maximumLength || -1;
2133      var elements = [];
2134  
2135      while (element = element[property]) {
2136        if (element.nodeType == 1)
2137          elements.push(Element.extend(element));
2138        if (elements.length == maximumLength)
2139          break;
2140      }
2141  
2142      return elements;
2143    },
2144  
2145    ancestors: function(element) {
2146      return Element.recursivelyCollect(element, 'parentNode');
2147    },
2148  
2149    descendants: function(element) {
2150      return Element.select(element, "*");
2151    },
2152  
2153    firstDescendant: function(element) {
2154      element = $(element).firstChild;
2155      while (element && element.nodeType != 1) element = element.nextSibling;
2156      return $(element);
2157    },
2158  
2159    immediateDescendants: function(element) {
2160      var results = [], child = $(element).firstChild;
2161      while (child) {
2162        if (child.nodeType === 1) {
2163          results.push(Element.extend(child));
2164        }
2165        child = child.nextSibling;
2166      }
2167      return results;
2168    },
2169  
2170    previousSiblings: function(element, maximumLength) {
2171      return Element.recursivelyCollect(element, 'previousSibling');
2172    },
2173  
2174    nextSiblings: function(element) {
2175      return Element.recursivelyCollect(element, 'nextSibling');
2176    },
2177  
2178    siblings: function(element) {
2179      element = $(element);
2180      return Element.previousSiblings(element).reverse()
2181        .concat(Element.nextSiblings(element));
2182    },
2183  
2184    match: function(element, selector) {
2185      element = $(element);
2186      if (Object.isString(selector))
2187        return Prototype.Selector.match(element, selector);
2188      return selector.match(element);
2189    },
2190  
2191    up: function(element, expression, index) {
2192      element = $(element);
2193      if (arguments.length == 1) return $(element.parentNode);
2194      var ancestors = Element.ancestors(element);
2195      return Object.isNumber(expression) ? ancestors[expression] :
2196        Prototype.Selector.find(ancestors, expression, index);
2197    },
2198  
2199    down: function(element, expression, index) {
2200      element = $(element);
2201      if (arguments.length == 1) return Element.firstDescendant(element);
2202      return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2203        Element.select(element, expression)[index || 0];
2204    },
2205  
2206    previous: function(element, expression, index) {
2207      element = $(element);
2208      if (Object.isNumber(expression)) index = expression, expression = false;
2209      if (!Object.isNumber(index)) index = 0;
2210  
2211      if (expression) {
2212        return Prototype.Selector.find(element.previousSiblings(), expression, index);
2213      } else {
2214        return element.recursivelyCollect("previousSibling", index + 1)[index];
2215      }
2216    },
2217  
2218    next: function(element, expression, index) {
2219      element = $(element);
2220      if (Object.isNumber(expression)) index = expression, expression = false;
2221      if (!Object.isNumber(index)) index = 0;
2222  
2223      if (expression) {
2224        return Prototype.Selector.find(element.nextSiblings(), expression, index);
2225      } else {
2226        var maximumLength = Object.isNumber(index) ? index + 1 : 1;
2227        return element.recursivelyCollect("nextSibling", index + 1)[index];
2228      }
2229    },
2230  
2231  
2232    select: function(element) {
2233      element = $(element);
2234      var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2235      return Prototype.Selector.select(expressions, element);
2236    },
2237  
2238    adjacent: function(element) {
2239      element = $(element);
2240      var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2241      return Prototype.Selector.select(expressions, element.parentNode).without(element);
2242    },
2243  
2244    identify: function(element) {
2245      element = $(element);
2246      var id = Element.readAttribute(element, 'id');
2247      if (id) return id;
2248      do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2249      Element.writeAttribute(element, 'id', id);
2250      return id;
2251    },
2252  
2253    readAttribute: function(element, name) {
2254      element = $(element);
2255      if (Prototype.Browser.IE) {
2256        var t = Element._attributeTranslations.read;
2257        if (t.values[name]) return t.values[name](element, name);
2258        if (t.names[name]) name = t.names[name];
2259        if (name.include(':')) {
2260          return (!element.attributes || !element.attributes[name]) ? null :
2261           element.attributes[name].value;
2262        }
2263      }
2264      return element.getAttribute(name);
2265    },
2266  
2267    writeAttribute: function(element, name, value) {
2268      element = $(element);
2269      var attributes = { }, t = Element._attributeTranslations.write;
2270  
2271      if (typeof name == 'object') attributes = name;
2272      else attributes[name] = Object.isUndefined(value) ? true : value;
2273  
2274      for (var attr in attributes) {
2275        name = t.names[attr] || attr;
2276        value = attributes[attr];
2277        if (t.values[attr]) name = t.values[attr](element, value);
2278        if (value === false || value === null)
2279          element.removeAttribute(name);
2280        else if (value === true)
2281          element.setAttribute(name, name);
2282        else element.setAttribute(name, value);
2283      }
2284      return element;
2285    },
2286  
2287    getHeight: function(element) {
2288      return Element.getDimensions(element).height;
2289    },
2290  
2291    getWidth: function(element) {
2292      return Element.getDimensions(element).width;
2293    },
2294  
2295    classNames: function(element) {
2296      return new Element.ClassNames(element);
2297    },
2298  
2299    hasClassName: function(element, className) {
2300      if (!(element = $(element))) return;
2301      var elementClassName = element.className;
2302      return (elementClassName.length > 0 && (elementClassName == className ||
2303        new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2304    },
2305  
2306    addClassName: function(element, className) {
2307      if (!(element = $(element))) return;
2308      if (!Element.hasClassName(element, className))
2309        element.className += (element.className ? ' ' : '') + className;
2310      return element;
2311    },
2312  
2313    removeClassName: function(element, className) {
2314      if (!(element = $(element))) return;
2315      element.className = element.className.replace(
2316        new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2317      return element;
2318    },
2319  
2320    toggleClassName: function(element, className) {
2321      if (!(element = $(element))) return;
2322      return Element[Element.hasClassName(element, className) ?
2323        'removeClassName' : 'addClassName'](element, className);
2324    },
2325  
2326    cleanWhitespace: function(element) {
2327      element = $(element);
2328      var node = element.firstChild;
2329      while (node) {
2330        var nextNode = node.nextSibling;
2331        if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2332          element.removeChild(node);
2333        node = nextNode;
2334      }
2335      return element;
2336    },
2337  
2338    empty: function(element) {
2339      return $(element).innerHTML.blank();
2340    },
2341  
2342    descendantOf: function(element, ancestor) {
2343      element = $(element), ancestor = $(ancestor);
2344  
2345      if (element.compareDocumentPosition)
2346        return (element.compareDocumentPosition(ancestor) & 8) === 8;
2347  
2348      if (ancestor.contains)
2349        return ancestor.contains(element) && ancestor !== element;
2350  
2351      while (element = element.parentNode)
2352        if (element == ancestor) return true;
2353  
2354      return false;
2355    },
2356  
2357    scrollTo: function(element) {
2358      element = $(element);
2359      var pos = Element.cumulativeOffset(element);
2360      window.scrollTo(pos[0], pos[1]);
2361      return element;
2362    },
2363  
2364    getStyle: function(element, style) {
2365      element = $(element);
2366      style = style == 'float' ? 'cssFloat' : style.camelize();
2367      var value = element.style[style];
2368      if (!value || value == 'auto') {
2369        var css = document.defaultView.getComputedStyle(element, null);
2370        value = css ? css[style] : null;
2371      }
2372      if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2373      return value == 'auto' ? null : value;
2374    },
2375  
2376    getOpacity: function(element) {
2377      return $(element).getStyle('opacity');
2378    },
2379  
2380    setStyle: function(element, styles) {
2381      element = $(element);
2382      var elementStyle = element.style, match;
2383      if (Object.isString(styles)) {
2384        element.style.cssText += ';' + styles;
2385        return styles.include('opacity') ?
2386          element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2387      }
2388      for (var property in styles)
2389        if (property == 'opacity') element.setOpacity(styles[property]);
2390        else
2391          elementStyle[(property == 'float' || property == 'cssFloat') ?
2392            (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2393              property] = styles[property];
2394  
2395      return element;
2396    },
2397  
2398    setOpacity: function(element, value) {
2399      element = $(element);
2400      element.style.opacity = (value == 1 || value === '') ? '' :
2401        (value < 0.00001) ? 0 : value;
2402      return element;
2403    },
2404  
2405    makePositioned: function(element) {
2406      element = $(element);
2407      var pos = Element.getStyle(element, 'position');
2408      if (pos == 'static' || !pos) {
2409        element._madePositioned = true;
2410        element.style.position = 'relative';
2411        if (Prototype.Browser.Opera) {
2412          element.style.top = 0;
2413          element.style.left = 0;
2414        }
2415      }
2416      return element;
2417    },
2418  
2419    undoPositioned: function(element) {
2420      element = $(element);
2421      if (element._madePositioned) {
2422        element._madePositioned = undefined;
2423        element.style.position =
2424          element.style.top =
2425          element.style.left =
2426          element.style.bottom =
2427          element.style.right = '';
2428      }
2429      return element;
2430    },
2431  
2432    makeClipping: function(element) {
2433      element = $(element);
2434      if (element._overflow) return element;
2435      element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2436      if (element._overflow !== 'hidden')
2437        element.style.overflow = 'hidden';
2438      return element;
2439    },
2440  
2441    undoClipping: function(element) {
2442      element = $(element);
2443      if (!element._overflow) return element;
2444      element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2445      element._overflow = null;
2446      return element;
2447    },
2448  
2449    clonePosition: function(element, source) {
2450      var options = Object.extend({
2451        setLeft:    true,
2452        setTop:     true,
2453        setWidth:   true,
2454        setHeight:  true,
2455        offsetTop:  0,
2456        offsetLeft: 0
2457      }, arguments[2] || { });
2458  
2459      source = $(source);
2460      var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
2461  
2462      element = $(element);
2463  
2464      if (Element.getStyle(element, 'position') == 'absolute') {
2465        parent = Element.getOffsetParent(element);
2466        delta = Element.viewportOffset(parent);
2467      }
2468  
2469      if (parent == document.body) {
2470        delta[0] -= document.body.offsetLeft;
2471        delta[1] -= document.body.offsetTop;
2472      }
2473  
2474      if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2475      if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2476      if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
2477      if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2478      return element;
2479    }
2480  };
2481  
2482  Object.extend(Element.Methods, {
2483    getElementsBySelector: Element.Methods.select,
2484  
2485    childElements: Element.Methods.immediateDescendants
2486  });
2487  
2488  Element._attributeTranslations = {
2489    write: {
2490      names: {
2491        className: 'class',
2492        htmlFor:   'for'
2493      },
2494      values: { }
2495    }
2496  };
2497  
2498  if (Prototype.Browser.Opera) {
2499    Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2500      function(proceed, element, style) {
2501        switch (style) {
2502          case 'height': case 'width':
2503            if (!Element.visible(element)) return null;
2504  
2505            var dim = parseInt(proceed(element, style), 10);
2506  
2507            if (dim !== element['offset' + style.capitalize()])
2508              return dim + 'px';
2509  
2510            var properties;
2511            if (style === 'height') {
2512              properties = ['border-top-width', 'padding-top',
2513               'padding-bottom', 'border-bottom-width'];
2514            }
2515            else {
2516              properties = ['border-left-width', 'padding-left',
2517               'padding-right', 'border-right-width'];
2518            }
2519            return properties.inject(dim, function(memo, property) {
2520              var val = proceed(element, property);
2521              return val === null ? memo : memo - parseInt(val, 10);
2522            }) + 'px';
2523          default: return proceed(element, style);
2524        }
2525      }
2526    );
2527  
2528    Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2529      function(proceed, element, attribute) {
2530        if (attribute === 'title') return element.title;
2531        return proceed(element, attribute);
2532      }
2533    );
2534  }
2535  
2536  else if (Prototype.Browser.IE) {
2537    Element.Methods.getStyle = function(element, style) {
2538      element = $(element);
2539      style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2540      var value = element.style[style];
2541      if (!value && element.currentStyle) value = element.currentStyle[style];
2542  
2543      if (style == 'opacity') {
2544        if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2545          if (value[1]) return parseFloat(value[1]) / 100;
2546        return 1.0;
2547      }
2548  
2549      if (value == 'auto') {
2550        if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2551          return element['offset' + style.capitalize()] + 'px';
2552        return null;
2553      }
2554      return value;
2555    };
2556  
2557    Element.Methods.setOpacity = function(element, value) {
2558      function stripAlpha(filter){
2559        return filter.replace(/alpha\([^\)]*\)/gi,'');
2560      }
2561      element = $(element);
2562      var currentStyle = element.currentStyle;
2563      if ((currentStyle && !currentStyle.hasLayout) ||
2564        (!currentStyle && element.style.zoom == 'normal'))
2565          element.style.zoom = 1;
2566  
2567      var filter = element.getStyle('filter'), style = element.style;
2568      if (value == 1 || value === '') {
2569        (filter = stripAlpha(filter)) ?
2570          style.filter = filter : style.removeAttribute('filter');
2571        return element;
2572      } else if (value < 0.00001) value = 0;
2573      style.filter = stripAlpha(filter) +
2574        'alpha(opacity=' + (value * 100) + ')';
2575      return element;
2576    };
2577  
2578    Element._attributeTranslations = (function(){
2579  
2580      var classProp = 'className',
2581          forProp = 'for',
2582          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'), f;
2626              el.onclick = Prototype.emptyFunction;
2627              var value = el.getAttribute('onclick');
2628  
2629              if (String(value).indexOf('{') > -1) {
2630                f = function(element, attribute) {
2631                  attribute = element.getAttribute(attribute);
2632                  if (!attribute) return null;
2633                  attribute = attribute.toString();
2634                  attribute = attribute.split('{')[1];
2635                  attribute = attribute.split('}')[0];
2636                  return attribute.strip();
2637                };
2638              }
2639              else if (value === '') {
2640                f = function(element, attribute) {
2641                  attribute = element.getAttribute(attribute);
2642                  if (!attribute) return null;
2643                  return attribute.strip();
2644                };
2645              }
2646              el = null;
2647              return f;
2648            })(),
2649            _flag: function(element, attribute) {
2650              return $(element).hasAttribute(attribute) ? attribute : null;
2651            },
2652            style: function(element) {
2653              return element.style.cssText.toLowerCase();
2654            },
2655            title: function(element) {
2656              return element.title;
2657            }
2658          }
2659        }
2660      }
2661    })();
2662  
2663    Element._attributeTranslations.write = {
2664      names: Object.extend({
2665        cellpadding: 'cellPadding',
2666        cellspacing: 'cellSpacing'
2667      }, Element._attributeTranslations.read.names),
2668      values: {
2669        checked: function(element, value) {
2670          element.checked = !!value;
2671        },
2672  
2673        style: function(element, value) {
2674          element.style.cssText = value ? value : '';
2675        }
2676      }
2677    };
2678  
2679    Element._attributeTranslations.has = {};
2680  
2681    $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2682        'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2683      Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2684      Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2685    });
2686  
2687    (function(v) {
2688      Object.extend(v, {
2689        href:        v._getAttr2,
2690        src:         v._getAttr2,
2691        type:        v._getAttr,
2692        action:      v._getAttrNode,
2693        disabled:    v._flag,
2694        checked:     v._flag,
2695        readonly:    v._flag,
2696        multiple:    v._flag,
2697        onload:      v._getEv,
2698        onunload:    v._getEv,
2699        onclick:     v._getEv,
2700        ondblclick:  v._getEv,
2701        onmousedown: v._getEv,
2702        onmouseup:   v._getEv,
2703        onmouseover: v._getEv,
2704        onmousemove: v._getEv,
2705        onmouseout:  v._getEv,
2706        onfocus:     v._getEv,
2707        onblur:      v._getEv,
2708        onkeypress:  v._getEv,
2709        onkeydown:   v._getEv,
2710        onkeyup:     v._getEv,
2711        onsubmit:    v._getEv,
2712        onreset:     v._getEv,
2713        onselect:    v._getEv,
2714        onchange:    v._getEv
2715      });
2716    })(Element._attributeTranslations.read.values);
2717  
2718    if (Prototype.BrowserFeatures.ElementExtensions) {
2719      (function() {
2720        function _descendants(element) {
2721          var nodes = element.getElementsByTagName('*'), results = [];
2722          for (var i = 0, node; node = nodes[i]; i++)
2723            if (node.tagName !== "!") // Filter out comment nodes.
2724              results.push(node);
2725          return results;
2726        }
2727  
2728        Element.Methods.down = function(element, expression, index) {
2729          element = $(element);
2730          if (arguments.length == 1) return element.firstDescendant();
2731          return Object.isNumber(expression) ? _descendants(element)[expression] :
2732            Element.select(element, expression)[index || 0];
2733        }
2734      })();
2735    }
2736  
2737  }
2738  
2739  else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2740    Element.Methods.setOpacity = function(element, value) {
2741      element = $(element);
2742      element.style.opacity = (value == 1) ? 0.999999 :
2743        (value === '') ? '' : (value < 0.00001) ? 0 : value;
2744      return element;
2745    };
2746  }
2747  
2748  else if (Prototype.Browser.WebKit) {
2749    Element.Methods.setOpacity = function(element, value) {
2750      element = $(element);
2751      element.style.opacity = (value == 1 || value === '') ? '' :
2752        (value < 0.00001) ? 0 : value;
2753  
2754      if (value == 1)
2755        if (element.tagName.toUpperCase() == 'IMG' && element.width) {
2756          element.width++; element.width--;
2757        } else try {
2758          var n = document.createTextNode(' ');
2759          element.appendChild(n);
2760          element.removeChild(n);
2761        } catch (e) { }
2762  
2763      return element;
2764    };
2765  }
2766  
2767  if ('outerHTML' in document.documentElement) {
2768    Element.Methods.replace = function(element, content) {
2769      element = $(element);
2770  
2771      if (content && content.toElement) content = content.toElement();
2772      if (Object.isElement(content)) {
2773        element.parentNode.replaceChild(content, element);
2774        return element;
2775      }
2776  
2777      content = Object.toHTML(content);
2778      var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2779  
2780      if (Element._insertionTranslations.tags[tagName]) {
2781        var nextSibling = element.next(),
2782            fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2783        parent.removeChild(element);
2784        if (nextSibling)
2785          fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2786        else
2787          fragments.each(function(node) { parent.appendChild(node) });
2788      }
2789      else element.outerHTML = content.stripScripts();
2790  
2791      content.evalScripts.bind(content).defer();
2792      return element;
2793    };
2794  }
2795  
2796  Element._returnOffset = function(l, t) {
2797    var result = [l, t];
2798    result.left = l;
2799    result.top = t;
2800    return result;
2801  };
2802  
2803  Element._getContentFromAnonymousElement = function(tagName, html, force) {
2804    var div = new Element('div'),
2805        t = Element._insertionTranslations.tags[tagName];
2806  
2807    var workaround = false;
2808    if (t) workaround = true;
2809    else if (force) {
2810      workaround = true;
2811      t = ['', '', 0];
2812    }
2813  
2814    if (workaround) {
2815      div.innerHTML = '&nbsp;' + t[0] + html + t[1];
2816      div.removeChild(div.firstChild);
2817      for (var i = t[2]; i--; ) {
2818        div = div.firstChild;
2819      }
2820    }
2821    else {
2822      div.innerHTML = html;
2823    }
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              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  if (document.documentElement.hasAttribute) {
2957    Element.hasAttribute = function(element, attribute) {
2958      return element.hasAttribute(attribute);
2959    };
2960  }
2961  else {
2962    Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
2963  }
2964  
2965  Element.addMethods = function(methods) {
2966    var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2967  
2968    if (!methods) {
2969      Object.extend(Form, Form.Methods);
2970      Object.extend(Form.Element, Form.Element.Methods);
2971      Object.extend(Element.Methods.ByTag, {
2972        "FORM":     Object.clone(Form.Methods),
2973        "INPUT":    Object.clone(Form.Element.Methods),
2974        "SELECT":   Object.clone(Form.Element.Methods),
2975        "TEXTAREA": Object.clone(Form.Element.Methods),
2976        "BUTTON":   Object.clone(Form.Element.Methods)
2977      });
2978    }
2979  
2980    if (arguments.length == 2) {
2981      var tagName = methods;
2982      methods = arguments[1];
2983    }
2984  
2985    if (!tagName) Object.extend(Element.Methods, methods || { });
2986    else {
2987      if (Object.isArray(tagName)) tagName.each(extend);
2988      else extend(tagName);
2989    }
2990  
2991    function extend(tagName) {
2992      tagName = tagName.toUpperCase();
2993      if (!Element.Methods.ByTag[tagName])
2994        Element.Methods.ByTag[tagName] = { };
2995      Object.extend(Element.Methods.ByTag[tagName], methods);
2996    }
2997  
2998    function copy(methods, destination, onlyIfAbsent) {
2999      onlyIfAbsent = onlyIfAbsent || false;
3000      for (var property in methods) {
3001        var value = methods[property];
3002        if (!Object.isFunction(value)) continue;
3003        if (!onlyIfAbsent || !(property in destination))
3004          destination[property] = value.methodize();
3005      }
3006    }
3007  
3008    function findDOMClass(tagName) {
3009      var klass;
3010      var trans = {
3011        "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3012        "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3013        "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3014        "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3015        "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3016        "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3017        "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3018        "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3019        "FrameSet", "IFRAME": "IFrame"
3020      };
3021      if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3022      if (window[klass]) return window[klass];
3023      klass = 'HTML' + tagName + 'Element';
3024      if (window[klass]) return window[klass];
3025      klass = 'HTML' + tagName.capitalize() + 'Element';
3026      if (window[klass]) return window[klass];
3027  
3028      var element = document.createElement(tagName),
3029          proto = element['__proto__'] || element.constructor.prototype;
3030  
3031      element = null;
3032      return proto;
3033    }
3034  
3035    var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3036     Element.prototype;
3037  
3038    if (F.ElementExtensions) {
3039      copy(Element.Methods, elementPrototype);
3040      copy(Element.Methods.Simulated, elementPrototype, true);
3041    }
3042  
3043    if (F.SpecificElementExtensions) {
3044      for (var tag in Element.Methods.ByTag) {
3045        var klass = findDOMClass(tag);
3046        if (Object.isUndefined(klass)) continue;
3047        copy(T[tag], klass.prototype);
3048      }
3049    }
3050  
3051    Object.extend(Element, Element.Methods);
3052    delete Element.ByTag;
3053  
3054    if (Element.extend.refresh) Element.extend.refresh();
3055    Element.cache = { };
3056  };
3057  
3058  
3059  document.viewport = {
3060  
3061    getDimensions: function() {
3062      return { width: this.getWidth(), height: this.getHeight() };
3063    },
3064  
3065    getScrollOffsets: function() {
3066      return Element._returnOffset(
3067        window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3068        window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
3069    }
3070  };
3071  
3072  (function(viewport) {
3073    var B = Prototype.Browser, doc = document, element, property = {};
3074  
3075    function getRootElement() {
3076      if (B.WebKit && !doc.evaluate)
3077        return document;
3078  
3079      if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3080        return document.body;
3081  
3082      return document.documentElement;
3083    }
3084  
3085    function define(D) {
3086      if (!element) element = getRootElement();
3087  
3088      property[D] = 'client' + D;
3089  
3090      viewport['get' + D] = function() { return element[property[D]] };
3091      return viewport['get' + D]();
3092    }
3093  
3094    viewport.getWidth  = define.curry('Width');
3095  
3096    viewport.getHeight = define.curry('Height');
3097  })(document.viewport);
3098  
3099  
3100  Element.Storage = {
3101    UID: 1
3102  };
3103  
3104  Element.addMethods({
3105    getStorage: function(element) {
3106      if (!(element = $(element))) return;
3107  
3108      var uid;
3109      if (element === window) {
3110        uid = 0;
3111      } else {
3112        if (typeof element._prototypeUID === "undefined")
3113          element._prototypeUID = Element.Storage.UID++;
3114        uid = element._prototypeUID;
3115      }
3116  
3117      if (!Element.Storage[uid])
3118        Element.Storage[uid] = $H();
3119  
3120      return Element.Storage[uid];
3121    },
3122  
3123    store: function(element, key, value) {
3124      if (!(element = $(element))) return;
3125  
3126      if (arguments.length === 2) {
3127        Element.getStorage(element).update(key);
3128      } else {
3129        Element.getStorage(element).set(key, value);
3130      }
3131  
3132      return element;
3133    },
3134  
3135    retrieve: function(element, key, defaultValue) {
3136      if (!(element = $(element))) return;
3137      var hash = Element.getStorage(element), value = hash.get(key);
3138  
3139      if (Object.isUndefined(value)) {
3140        hash.set(key, defaultValue);
3141        value = defaultValue;
3142      }
3143  
3144      return value;
3145    },
3146  
3147    clone: function(element, deep) {
3148      if (!(element = $(element))) return;
3149      var clone = element.cloneNode(deep);
3150      clone._prototypeUID = void 0;
3151      if (deep) {
3152        var descendants = Element.select(clone, '*'),
3153            i = descendants.length;
3154        while (i--) {
3155          descendants[i]._prototypeUID = void 0;
3156        }
3157      }
3158      return Element.extend(clone);
3159    },
3160  
3161    purge: function(element) {
3162      if (!(element = $(element))) return;
3163      var purgeElement = Element._purgeElement;
3164  
3165      purgeElement(element);
3166  
3167      var descendants = element.getElementsByTagName('*'),
3168       i = descendants.length;
3169  
3170      while (i--) purgeElement(descendants[i]);
3171  
3172      return null;
3173    }
3174  });
3175  
3176  (function() {
3177  
3178    function toDecimal(pctString) {
3179      var match = pctString.match(/^(\d+)%?$/i);
3180      if (!match) return null;
3181      return (Number(match[1]) / 100);
3182    }
3183  
3184    function getPixelValue(value, property, context) {
3185      var element = null;
3186      if (Object.isElement(value)) {
3187        element = value;
3188        value = element.getStyle(property);
3189      }
3190  
3191      if (value === null) {
3192        return null;
3193      }
3194  
3195      if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3196        return window.parseFloat(value);
3197      }
3198  
3199      var isPercentage = value.include('%'), isViewport = (context === document.viewport);
3200  
3201      if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
3202        var style = element.style.left, rStyle = element.runtimeStyle.left;
3203        element.runtimeStyle.left = element.currentStyle.left;
3204        element.style.left = value || 0;
3205        value = element.style.pixelLeft;
3206        element.style.left = style;
3207        element.runtimeStyle.left = rStyle;
3208  
3209        return value;
3210      }
3211  
3212      if (element && isPercentage) {
3213        context = context || element.parentNode;
3214        var decimal = toDecimal(value);
3215        var whole = null;
3216        var position = element.getStyle('position');
3217  
3218        var isHorizontal = property.include('left') || property.include('right') ||
3219         property.include('width');
3220  
3221        var isVertical =  property.include('top') || property.include('bottom') ||
3222          property.include('height');
3223  
3224        if (context === document.viewport) {
3225          if (isHorizontal) {
3226            whole = document.viewport.getWidth();
3227          } else if (isVertical) {
3228            whole = document.viewport.getHeight();
3229          }
3230        } else {
3231          if (isHorizontal) {
3232            whole = $(context).measure('width');
3233          } else if (isVertical) {
3234            whole = $(context).measure('height');
3235          }
3236        }
3237  
3238        return (whole === null) ? 0 : whole * decimal;
3239      }
3240  
3241      return 0;
3242    }
3243  
3244    function toCSSPixels(number) {
3245      if (Object.isString(number) && number.endsWith('px')) {
3246        return number;
3247      }
3248      return number + 'px';
3249    }
3250  
3251    function isDisplayed(element) {
3252      var originalElement = element;
3253      while (element && element.parentNode) {
3254        var display = element.getStyle('display');
3255        if (display === 'none') {
3256          return false;
3257        }
3258        element = $(element.parentNode);
3259      }
3260      return true;
3261    }
3262  
3263    var hasLayout = Prototype.K;
3264    if ('currentStyle' in document.documentElement) {
3265      hasLayout = function(element) {
3266        if (!element.currentStyle.hasLayout) {
3267          element.style.zoom = 1;
3268        }
3269        return element;
3270      };
3271    }
3272  
3273    function cssNameFor(key) {
3274      if (key.include('border')) key = key + '-width';
3275      return key.camelize();
3276    }
3277  
3278    Element.Layout = Class.create(Hash, {
3279      initialize: function($super, element, preCompute) {
3280        $super();
3281        this.element = $(element);
3282  
3283        Element.Layout.PROPERTIES.each( function(property) {
3284          this._set(property, null);
3285        }, this);
3286  
3287        if (preCompute) {
3288          this._preComputing = true;
3289          this._begin();
3290          Element.Layout.PROPERTIES.each( this._compute, this );
3291          this._end();
3292          this._preComputing = false;
3293        }
3294      },
3295  
3296      _set: function(property, value) {
3297        return Hash.prototype.set.call(this, property, value);
3298      },
3299  
3300      set: function(property, value) {
3301        throw "Properties of Element.Layout are read-only.";
3302      },
3303  
3304      get: function($super, property) {
3305        var value = $super(property);
3306        return value === null ? this._compute(property) : value;
3307      },
3308  
3309      _begin: function() {
3310        if (this._prepared) return;
3311  
3312        var element = this.element;
3313        if (isDisplayed(element)) {
3314          this._prepared = true;
3315          return;
3316        }
3317  
3318        var originalStyles = {
3319          position:   element.style.position   || '',
3320          width:      element.style.width      || '',
3321          visibility: element.style.visibility || '',
3322          display:    element.style.display    || ''
3323        };
3324  
3325        element.store('prototype_original_styles', originalStyles);
3326  
3327        var position = element.getStyle('position'),
3328         width = element.getStyle('width');
3329  
3330        if (width === "0px" || width === null) {
3331          element.style.display = 'block';
3332          width = element.getStyle('width');
3333        }
3334  
3335        var context = (position === 'fixed') ? document.viewport :
3336         element.parentNode;
3337  
3338        element.setStyle({
3339          position:   'absolute',
3340          visibility: 'hidden',
3341          display:    'block'
3342        });
3343  
3344        var positionedWidth = element.getStyle('width');
3345  
3346        var newWidth;
3347        if (width && (positionedWidth === width)) {
3348          newWidth = getPixelValue(element, 'width', context);
3349        } else if (position === 'absolute' || position === 'fixed') {
3350          newWidth = getPixelValue(element, 'width', context);
3351        } else {
3352          var parent = element.parentNode, pLayout = $(parent).getLayout();
3353  
3354          newWidth = pLayout.get('width') -
3355           this.get('margin-left') -
3356           this.get('border-left') -
3357           this.get('padding-left') -
3358           this.get('padding-right') -
3359           this.get('border-right') -
3360           this.get('margin-right');
3361        }
3362  
3363        element.setStyle({ width: newWidth + 'px' });
3364  
3365        this._prepared = true;
3366      },
3367  
3368      _end: function() {
3369        var element = this.element;
3370        var originalStyles = element.retrieve('prototype_original_styles');
3371        element.store('prototype_original_styles', null);
3372        element.setStyle(originalStyles);
3373        this._prepared = false;
3374      },
3375  
3376      _compute: function(property) {
3377        var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3378        if (!(property in COMPUTATIONS)) {
3379          throw "Property not found.";
3380        }
3381  
3382        return this._set(property, COMPUTATIONS[property].call(this, this.element));
3383      },
3384  
3385      toObject: function() {
3386        var args = $A(arguments);
3387        var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3388         args.join(' ').split(' ');
3389        var obj = {};
3390        keys.each( function(key) {
3391          if (!Element.Layout.PROPERTIES.include(key)) return;
3392          var value = this.get(key);
3393          if (value != null) obj[key] = value;
3394        }, this);
3395        return obj;
3396      },
3397  
3398      toHash: function() {
3399        var obj = this.toObject.apply(this, arguments);
3400        return new Hash(obj);
3401      },
3402  
3403      toCSS: function() {
3404        var args = $A(arguments);
3405        var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3406         args.join(' ').split(' ');
3407        var css = {};
3408  
3409        keys.each( function(key) {
3410          if (!Element.Layout.PROPERTIES.include(key)) return;
3411          if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3412  
3413          var value = this.get(key);
3414          if (value != null) css[cssNameFor(key)] = value + 'px';
3415        }, this);
3416        return css;
3417      },
3418  
3419      inspect: function() {
3420        return "#<Element.Layout>";
3421      }
3422    });
3423  
3424    Object.extend(Element.Layout, {
3425      PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3426  
3427      COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3428  
3429      COMPUTATIONS: {
3430        'height': function(element) {
3431          if (!this._preComputing) this._begin();
3432  
3433          var bHeight = this.get('border-box-height');
3434          if (bHeight <= 0) {
3435            if (!this._preComputing) this._end();
3436            return 0;
3437          }
3438  
3439          var bTop = this.get('border-top'),
3440           bBottom = this.get('border-bottom');
3441  
3442          var pTop = this.get('padding-top'),
3443           pBottom = this.get('padding-bottom');
3444  
3445          if (!this._preComputing) this._end();
3446  
3447          return bHeight - bTop - bBottom - pTop - pBottom;
3448        },
3449  
3450        'width': function(element) {
3451          if (!this._preComputing) this._begin();
3452  
3453          var bWidth = this.get('border-box-width');
3454          if (bWidth <= 0) {
3455            if (!this._preComputing) this._end();
3456            return 0;
3457          }
3458  
3459          var bLeft = this.get('border-left'),
3460           bRight = this.get('border-right');
3461  
3462          var pLeft = this.get('padding-left'),
3463           pRight = this.get('padding-right');
3464  
3465          if (!this._preComputing) this._end();
3466  
3467          return bWidth - bLeft - bRight - pLeft - pRight;
3468        },
3469  
3470        'padding-box-height': function(element) {
3471          var height = this.get('height'),
3472           pTop = this.get('padding-top'),
3473           pBottom = this.get('padding-bottom');
3474  
3475          return height + pTop + pBottom;
3476        },
3477  
3478        'padding-box-width': function(element) {
3479          var width = this.get('width'),
3480           pLeft = this.get('padding-left'),
3481           pRight = this.get('padding-right');
3482  
3483          return width + pLeft + pRight;
3484        },
3485  
3486        'border-box-height': function(element) {
3487          if (!this._preComputing) this._begin();
3488          var height = element.offsetHeight;
3489          if (!this._preComputing) this._end();
3490          return height;
3491        },
3492  
3493        'border-box-width': function(element) {
3494          if (!this._preComputing) this._begin();
3495          var width = element.offsetWidth;
3496          if (!this._preComputing) this._end();
3497          return width;
3498        },
3499  
3500        'margin-box-height': function(element) {
3501          var bHeight = this.get('border-box-height'),
3502           mTop = this.get('margin-top'),
3503           mBottom = this.get('margin-bottom');
3504  
3505          if (bHeight <= 0) return 0;
3506  
3507          return bHeight + mTop + mBottom;
3508        },
3509  
3510        'margin-box-width': function(element) {
3511          var bWidth = this.get('border-box-width'),
3512           mLeft = this.get('margin-left'),
3513           mRight = this.get('margin-right');
3514  
3515          if (bWidth <= 0) return 0;
3516  
3517          return bWidth + mLeft + mRight;
3518        },
3519  
3520        'top': function(element) {
3521          var offset = element.positionedOffset();
3522          return offset.top;
3523        },
3524  
3525        'bottom': function(element) {
3526          var offset = element.positionedOffset(),
3527           parent = element.getOffsetParent(),
3528           pHeight = parent.measure('height');
3529  
3530          var mHeight = this.get('border-box-height');
3531  
3532          return pHeight - mHeight - offset.top;
3533        },
3534  
3535        'left': function(element) {
3536          var offset = element.positionedOffset();
3537          return offset.left;
3538        },
3539  
3540        'right': function(element) {
3541          var offset = element.positionedOffset(),
3542           parent = element.getOffsetParent(),
3543           pWidth = parent.measure('width');
3544  
3545          var mWidth = this.get('border-box-width');
3546  
3547          return pWidth - mWidth - offset.left;
3548        },
3549  
3550        'padding-top': function(element) {
3551          return getPixelValue(element, 'paddingTop');
3552        },
3553  
3554        'padding-bottom': function(element) {
3555          return getPixelValue(element, 'paddingBottom');
3556        },
3557  
3558        'padding-left': function(element) {
3559          return getPixelValue(element, 'paddingLeft');
3560        },
3561  
3562        'padding-right': function(element) {
3563          return getPixelValue(element, 'paddingRight');
3564        },
3565  
3566        'border-top': function(element) {
3567          return getPixelValue(element, 'borderTopWidth');
3568        },
3569  
3570        'border-bottom': function(element) {
3571          return getPixelValue(element, 'borderBottomWidth');
3572        },
3573  
3574        'border-left': function(element) {
3575          return getPixelValue(element, 'borderLeftWidth');
3576        },
3577  
3578        'border-right': function(element) {
3579          return getPixelValue(element, 'borderRightWidth');
3580        },
3581  
3582        'margin-top': function(element) {
3583          return getPixelValue(element, 'marginTop');
3584        },
3585  
3586        'margin-bottom': function(element) {
3587          return getPixelValue(element, 'marginBottom');
3588        },
3589  
3590        'margin-left': function(element) {
3591          return getPixelValue(element, 'marginLeft');
3592        },
3593  
3594        'margin-right': function(element) {
3595          return getPixelValue(element, 'marginRight');
3596        }
3597      }
3598    });
3599  
3600    if ('getBoundingClientRect' in document.documentElement) {
3601      Object.extend(Element.Layout.COMPUTATIONS, {
3602        'right': function(element) {
3603          var parent = hasLayout(element.getOffsetParent());
3604          var rect = element.getBoundingClientRect(),
3605           pRect = parent.getBoundingClientRect();
3606  
3607          return (pRect.right - rect.right).round();
3608        },
3609  
3610        'bottom': function(element) {
3611          var parent = hasLayout(element.getOffsetParent());
3612          var rect = element.getBoundingClientRect(),
3613           pRect = parent.getBoundingClientRect();
3614  
3615          return (pRect.bottom - rect.bottom).round();
3616        }
3617      });
3618    }
3619  
3620    Element.Offset = Class.create({
3621      initialize: function(left, top) {
3622        this.left = left.round();
3623        this.top  = top.round();
3624  
3625        this[0] = this.left;
3626        this[1] = this.top;
3627      },
3628  
3629      relativeTo: function(offset) {
3630        return new Element.Offset(
3631          this.left - offset.left,
3632          this.top  - offset.top
3633        );
3634      },
3635  
3636      inspect: function() {
3637        return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3638      },
3639  
3640      toString: function() {
3641        return "[#{left}, #{top}]".interpolate(this);
3642      },
3643  
3644      toArray: function() {
3645        return [this.left, this.top];
3646      }
3647    });
3648  
3649    function getLayout(element, preCompute) {
3650      return new Element.Layout(element, preCompute);
3651    }
3652  
3653    function measure(element, property) {
3654      return $(element).getLayout().get(property);
3655    }
3656  
3657    function getDimensions(element) {
3658      element = $(element);
3659      var display = Element.getStyle(element, 'display');
3660  
3661      if (display && display !== 'none') {
3662        return { width: element.offsetWidth, height: element.offsetHeight };
3663      }
3664  
3665      var style = element.style;
3666      var originalStyles = {
3667        visibility: style.visibility,
3668        position:   style.position,
3669        display:    style.display
3670      };
3671  
3672      var newStyles = {
3673        visibility: 'hidden',
3674        display:    'block'
3675      };
3676  
3677      if (originalStyles.position !== 'fixed')
3678        newStyles.position = 'absolute';
3679  
3680      Element.setStyle(element, newStyles);
3681  
3682      var dimensions = {
3683        width:  element.offsetWidth,
3684        height: element.offsetHeight
3685      };
3686  
3687      Element.setStyle(element, originalStyles);
3688  
3689      return dimensions;
3690    }
3691  
3692    function getOffsetParent(element) {
3693      element = $(element);
3694  
3695      if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3696        return $(document.body);
3697  
3698      var isInline = (Element.getStyle(element, 'display') === 'inline');
3699      if (!isInline && element.offsetParent) return $(element.offsetParent);
3700  
3701      while ((element = element.parentNode) && element !== document.body) {
3702        if (Element.getStyle(element, 'position') !== 'static') {
3703          return isHtml(element) ? $(document.body) : $(element);
3704        }
3705      }
3706  
3707      return $(document.body);
3708    }
3709  
3710  
3711    function cumulativeOffset(element) {
3712      element = $(element);
3713      var valueT = 0, valueL = 0;
3714      if (element.parentNode) {
3715        do {
3716          valueT += element.offsetTop  || 0;
3717          valueL += element.offsetLeft || 0;
3718          element = element.offsetParent;
3719        } while (element);
3720      }
3721      return new Element.Offset(valueL, valueT);
3722    }
3723  
3724    function positionedOffset(element) {
3725      element = $(element);
3726  
3727      var layout = element.getLayout();
3728  
3729      var valueT = 0, valueL = 0;
3730      do {
3731        valueT += element.offsetTop  || 0;
3732        valueL += element.offsetLeft || 0;
3733        element = element.offsetParent;
3734        if (element) {
3735          if (isBody(element)) break;
3736          var p = Element.getStyle(element, 'position');
3737          if (p !== 'static') break;
3738        }
3739      } while (element);
3740  
3741      valueL -= layout.get('margin-top');
3742      valueT -= layout.get('margin-left');
3743  
3744      return new Element.Offset(valueL, valueT);
3745    }
3746  
3747    function cumulativeScrollOffset(element) {
3748      var valueT = 0, valueL = 0;
3749      do {
3750        valueT += element.scrollTop  || 0;
3751        valueL += element.scrollLeft || 0;
3752        element = element.parentNode;
3753      } while (element);
3754      return new Element.Offset(valueL, valueT);
3755    }
3756  
3757    function viewportOffset(forElement) {
3758      element = $(element);
3759      var valueT = 0, valueL = 0, docBody = document.body;
3760  
3761      var element = forElement;
3762      do {
3763        valueT += element.offsetTop  || 0;
3764        valueL += element.offsetLeft || 0;
3765        if (element.offsetParent == docBody &&
3766          Element.getStyle(element, 'position') == 'absolute') break;
3767      } while (element = element.offsetParent);
3768  
3769      element = forElement;
3770      do {
3771        if (element != docBody) {
3772          valueT -= element.scrollTop  || 0;
3773          valueL -= element.scrollLeft || 0;
3774        }
3775      } while (element = element.parentNode);
3776      return new Element.Offset(valueL, valueT);
3777    }
3778  
3779    function absolutize(element) {
3780      element = $(element);
3781  
3782      if (Element.getStyle(element, 'position') === 'absolute') {
3783        return element;
3784      }
3785  
3786      var offsetParent = getOffsetParent(element);
3787      var eOffset = element.viewportOffset(),
3788       pOffset = offsetParent.viewportOffset();
3789  
3790      var offset = eOffset.relativeTo(pOffset);
3791      var layout = element.getLayout();
3792  
3793      element.store('prototype_absolutize_original_styles', {
3794        left:   element.getStyle('left'),
3795        top:    element.getStyle('top'),
3796        width:  element.getStyle('width'),
3797        height: element.getStyle('height')
3798      });
3799  
3800      element.setStyle({
3801        position: 'absolute',
3802        top:    offset.top + 'px',
3803        left:   offset.left + 'px',
3804        width:  layout.get('width') + 'px',
3805        height: layout.get('height') + 'px'
3806      });
3807  
3808      return element;
3809    }
3810  
3811    function relativize(element) {
3812      element = $(element);
3813      if (Element.getStyle(element, 'position') === 'relative') {
3814        return element;
3815      }
3816  
3817      var originalStyles =
3818       element.retrieve('prototype_absolutize_original_styles');
3819  
3820      if (originalStyles) element.setStyle(originalStyles);
3821      return element;
3822    }
3823  
3824    if (Prototype.Browser.IE) {
3825      getOffsetParent = getOffsetParent.wrap(
3826        function(proceed, element) {
3827          element = $(element);
3828  
3829          if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3830            return $(document.body);
3831  
3832          var position = element.getStyle('position');
3833          if (position !== 'static') return proceed(element);
3834  
3835          element.setStyle({ position: 'relative' });
3836          var value = proceed(element);
3837          element.setStyle({ position: position });
3838          return value;
3839        }
3840      );
3841  
3842      positionedOffset = positionedOffset.wrap(function(proceed, element) {
3843        element = $(element);
3844        if (!element.parentNode) return new Element.Offset(0, 0);
3845        var position = element.getStyle('position');
3846        if (position !== 'static') return proceed(element);
3847  
3848        var offsetParent = element.getOffsetParent();
3849        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
3850          hasLayout(offsetParent);
3851  
3852        element.setStyle({ position: 'relative' });
3853        var value = proceed(element);
3854        element.setStyle({ position: position });
3855        return value;
3856      });
3857    } else if (Prototype.Browser.Webkit) {
3858      cumulativeOffset = function(element) {
3859        element = $(element);
3860        var valueT = 0, valueL = 0;
3861        do {
3862          valueT += element.offsetTop  || 0;
3863          valueL += element.offsetLeft || 0;
3864          if (element.offsetParent == document.body)
3865            if (Element.getStyle(element, 'position') == 'absolute') break;
3866  
3867          element = element.offsetParent;
3868        } while (element);
3869  
3870        return new Element.Offset(valueL, valueT);
3871      };
3872    }
3873  
3874  
3875    Element.addMethods({
3876      getLayout:              getLayout,
3877      measure:                measure,
3878      getDimensions:          getDimensions,
3879      getOffsetParent:        getOffsetParent,
3880      cumulativeOffset:       cumulativeOffset,
3881      positionedOffset:       positionedOffset,
3882      cumulativeScrollOffset: cumulativeScrollOffset,
3883      viewportOffset:         viewportOffset,
3884      absolutize:             absolutize,
3885      relativize:             relativize
3886    });
3887  
3888    function isBody(element) {
3889      return element.nodeName.toUpperCase() === 'BODY';
3890    }
3891  
3892    function isHtml(element) {
3893      return element.nodeName.toUpperCase() === 'HTML';
3894    }
3895  
3896    function isDocument(element) {
3897      return element.nodeType === Node.DOCUMENT_NODE;
3898    }
3899  
3900    function isDetached(element) {
3901      return element !== document.body &&
3902       !Element.descendantOf(element, document.body);
3903    }
3904  
3905    if ('getBoundingClientRect' in document.documentElement) {
3906      Element.addMethods({
3907        viewportOffset: function(element) {
3908          element = $(element);
3909          if (isDetached(element)) return new Element.Offset(0, 0);
3910  
3911          var rect = element.getBoundingClientRect(),
3912           docEl = document.documentElement;
3913          return new Element.Offset(rect.left - docEl.clientLeft,
3914           rect.top - docEl.clientTop);
3915        }
3916      });
3917    }
3918  })();
3919  window.$$ = function() {
3920    var expression = $A(arguments).join(', ');
3921    return Prototype.Selector.select(expression, document);
3922  };
3923  
3924  Prototype.Selector = (function() {
3925  
3926    function select() {
3927      throw new Error('Method "Prototype.Selector.select" must be defined.');
3928    }
3929  
3930    function match() {
3931      throw new Error('Method "Prototype.Selector.match" must be defined.');
3932    }
3933  
3934    function find(elements, expression, index) {
3935      index = index || 0;
3936      var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
3937  
3938      for (i = 0; i < length; i++) {
3939        if (match(elements[i], expression) && index == matchIndex++) {
3940          return Element.extend(elements[i]);
3941        }
3942      }
3943    }
3944  
3945    function extendElements(elements) {
3946      for (var i = 0, length = elements.length; i < length; i++) {
3947        Element.extend(elements[i]);
3948      }
3949      return elements;
3950    }
3951  
3952  
3953    var K = Prototype.K;
3954  
3955    return {
3956      select: select,
3957      match: match,
3958      find: find,
3959      extendElements: (Element.extend === K) ? K : extendElements,
3960      extendElement: Element.extend
3961    };
3962  })();
3963  /*!
3964   * Sizzle CSS Selector Engine - v1.0
3965   *  Copyright 2009, The Dojo Foundation
3966   *  Released under the MIT, BSD, and GPL Licenses.
3967   *  More information: http://sizzlejs.com/
3968   */
3969  (function(){
3970  
3971  var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
3972      done = 0,
3973      toString = Object.prototype.toString,
3974      hasDuplicate = false,
3975      baseHasDuplicate = true;
3976  
3977  [0, 0].sort(function(){
3978      baseHasDuplicate = false;
3979      return 0;
3980  });
3981  
3982  var Sizzle = function(selector, context, results, seed) {
3983      results = results || [];
3984      var origContext = context = context || document;
3985  
3986      if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3987          return [];
3988      }
3989  
3990      if ( !selector || typeof selector !== "string" ) {
3991          return results;
3992      }
3993  
3994      var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context),
3995          soFar = selector;
3996  
3997      while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
3998          soFar = m[3];
3999  
4000          parts.push( m[1] );
4001  
4002          if ( m[2] ) {
4003              extra = m[3];
4004              break;
4005          }
4006      }
4007  
4008      if ( parts.length > 1 && origPOS.exec( selector ) ) {
4009          if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4010              set = posProcess( parts[0] + parts[1], context );
4011          } else {
4012              set = Expr.relative[ parts[0] ] ?
4013                  [ context ] :
4014                  Sizzle( parts.shift(), context );
4015  
4016              while ( parts.length ) {
4017                  selector = parts.shift();
4018  
4019                  if ( Expr.relative[ selector ] )
4020                      selector += parts.shift();
4021  
4022                  set = posProcess( selector, set );
4023              }
4024          }
4025      } else {
4026          if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4027                  Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4028              var ret = Sizzle.find( parts.shift(), context, contextXML );
4029              context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
4030          }
4031  
4032          if ( context ) {
4033              var ret = seed ?
4034                  { expr: parts.pop(), set: makeArray(seed) } :
4035                  Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4036              set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
4037  
4038              if ( parts.length > 0 ) {
4039                  checkSet = makeArray(set);
4040              } else {
4041                  prune = false;
4042              }
4043  
4044              while ( parts.length ) {
4045                  var cur = parts.pop(), pop = cur;
4046  
4047                  if ( !Expr.relative[ cur ] ) {
4048                      cur = "";
4049                  } else {
4050                      pop = parts.pop();
4051                  }
4052  
4053                  if ( pop == null ) {
4054                      pop = context;
4055                  }
4056  
4057                  Expr.relative[ cur ]( checkSet, pop, contextXML );
4058              }
4059          } else {
4060              checkSet = parts = [];
4061          }
4062      }
4063  
4064      if ( !checkSet ) {
4065          checkSet = set;
4066      }
4067  
4068      if ( !checkSet ) {
4069          throw "Syntax error, unrecognized expression: " + (cur || selector);
4070      }
4071  
4072      if ( toString.call(checkSet) === "[object Array]" ) {
4073          if ( !prune ) {
4074              results.push.apply( results, checkSet );
4075          } else if ( context && context.nodeType === 1 ) {
4076              for ( var i = 0; checkSet[i] != null; i++ ) {
4077                  if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
4078                      results.push( set[i] );
4079                  }
4080              }
4081          } else {
4082              for ( var i = 0; checkSet[i] != null; i++ ) {
4083                  if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4084                      results.push( set[i] );
4085                  }
4086              }
4087          }
4088      } else {
4089          makeArray( checkSet, results );
4090      }
4091  
4092      if ( extra ) {
4093          Sizzle( extra, origContext, results, seed );
4094          Sizzle.uniqueSort( results );
4095      }
4096  
4097      return results;
4098  };
4099  
4100  Sizzle.uniqueSort = function(results){
4101      if ( sortOrder ) {
4102          hasDuplicate = baseHasDuplicate;
4103          results.sort(sortOrder);
4104  
4105          if ( hasDuplicate ) {
4106              for ( var i = 1; i < results.length; i++ ) {
4107                  if ( results[i] === results[i-1] ) {
4108                      results.splice(i--, 1);
4109                  }
4110              }
4111          }
4112      }
4113  
4114      return results;
4115  };
4116  
4117  Sizzle.matches = function(expr, set){
4118      return Sizzle(expr, null, null, set);
4119  };
4120  
4121  Sizzle.find = function(expr, context, isXML){
4122      var set, match;
4123  
4124      if ( !expr ) {
4125          return [];
4126      }
4127  
4128      for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
4129          var type = Expr.order[i], match;
4130  
4131          if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4132              var left = match[1];
4133              match.splice(1,1);
4134  
4135              if ( left.substr( left.length - 1 ) !== "\\" ) {
4136                  match[1] = (match[1] || "").replace(/\\/g, "");
4137                  set = Expr.find[ type ]( match, context, isXML );
4138                  if ( set != null ) {
4139                      expr = expr.replace( Expr.match[ type ], "" );
4140                      break;
4141                  }
4142              }
4143          }
4144      }
4145  
4146      if ( !set ) {
4147          set = context.getElementsByTagName("*");
4148      }
4149  
4150      return {set: set, expr: expr};
4151  };
4152  
4153  Sizzle.filter = function(expr, set, inplace, not){
4154      var old = expr, result = [], curLoop = set, match, anyFound,
4155          isXMLFilter = set && set[0] && isXML(set[0]);
4156  
4157      while ( expr && set.length ) {
4158          for ( var type in Expr.filter ) {
4159              if ( (match = Expr.match[ type ].exec( expr )) != null ) {
4160                  var filter = Expr.filter[ type ], found, item;
4161                  anyFound = false;
4162  
4163                  if ( curLoop == result ) {
4164                      result = [];
4165                  }
4166  
4167                  if ( Expr.preFilter[ type ] ) {
4168                      match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4169  
4170                      if ( !match ) {
4171                          anyFound = found = true;
4172                      } else if ( match === true ) {
4173                          continue;
4174                      }
4175                  }
4176  
4177                  if ( match ) {
4178                      for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
4179                          if ( item ) {
4180                              found = filter( item, match, i, curLoop );
4181                              var pass = not ^ !!found;
4182  
4183                              if ( inplace && found != null ) {
4184                                  if ( pass ) {
4185                                      anyFound = true;
4186                                  } else {
4187                                      curLoop[i] = false;
4188                                  }
4189                              } else if ( pass ) {
4190                                  result.push( item );
4191                                  anyFound = true;
4192                              }
4193                          }
4194                      }
4195                  }
4196  
4197                  if ( found !== undefined ) {
4198                      if ( !inplace ) {
4199                          curLoop = result;
4200                      }
4201  
4202                      expr = expr.replace( Expr.match[ type ], "" );
4203  
4204                      if ( !anyFound ) {
4205                          return [];
4206                      }
4207  
4208                      break;
4209                  }
4210              }
4211          }
4212  
4213          if ( expr == old ) {
4214              if ( anyFound == null ) {
4215                  throw "Syntax error, unrecognized expression: " + expr;
4216              } else {
4217                  break;
4218              }
4219          }
4220  
4221          old = expr;
4222      }
4223  
4224      return curLoop;
4225  };
4226  
4227  var Expr = Sizzle.selectors = {
4228      order: [ "ID", "NAME", "TAG" ],
4229      match: {
4230          ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4231          CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
4232          NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
4233          ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
4234          TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
4235          CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
4236          POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
4237          PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
4238      },
4239      leftMatch: {},
4240      attrMap: {
4241          "class": "className",
4242          "for": "htmlFor"
4243      },
4244      attrHandle: {
4245          href: function(elem){
4246              return elem.getAttribute("href");
4247          }
4248      },
4249      relative: {
4250          "+": function(checkSet, part, isXML){
4251              var isPartStr = typeof part === "string",
4252                  isTag = isPartStr && !/\W/.test(part),
4253                  isPartStrNotTag = isPartStr && !isTag;
4254  
4255              if ( isTag && !isXML ) {
4256                  part = part.toUpperCase();
4257              }
4258  
4259              for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4260                  if ( (elem = checkSet[i]) ) {
4261                      while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4262  
4263                      checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ?
4264                          elem || false :
4265                          elem === part;
4266                  }
4267              }
4268  
4269              if ( isPartStrNotTag ) {
4270                  Sizzle.filter( part, checkSet, true );
4271              }
4272          },
4273          ">": function(checkSet, part, isXML){
4274              var isPartStr = typeof part === "string";
4275  
4276              if ( isPartStr && !/\W/.test(part) ) {
4277                  part = isXML ? part : part.toUpperCase();
4278  
4279                  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4280                      var elem = checkSet[i];
4281                      if ( elem ) {
4282                          var parent = elem.parentNode;
4283                          checkSet[i] = parent.nodeName === part ? parent : false;
4284                      }
4285                  }
4286              } else {
4287                  for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4288                      var elem = checkSet[i];
4289                      if ( elem ) {
4290                          checkSet[i] = isPartStr ?
4291                              elem.parentNode :
4292                              elem.parentNode === part;
4293                      }
4294                  }
4295  
4296                  if ( isPartStr ) {
4297                      Sizzle.filter( part, checkSet, true );
4298                  }
4299              }
4300          },
4301          "": function(checkSet, part, isXML){
4302              var doneName = done++, checkFn = dirCheck;
4303  
4304              if ( !/\W/.test(part) ) {
4305                  var nodeCheck = part = isXML ? part : part.toUpperCase();
4306                  checkFn = dirNodeCheck;
4307              }
4308  
4309              checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
4310          },
4311          "~": function(checkSet, part, isXML){
4312              var doneName = done++, checkFn = dirCheck;
4313  
4314              if ( typeof part === "string" && !/\W/.test(part) ) {
4315                  var nodeCheck = part = isXML ? part : part.toUpperCase();
4316                  checkFn = dirNodeCheck;
4317              }
4318  
4319              checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
4320          }
4321      },
4322      find: {
4323          ID: function(match, context, isXML){
4324              if ( typeof context.getElementById !== "undefined" && !isXML ) {
4325                  var m = context.getElementById(match[1]);
4326                  return m ? [m] : [];
4327              }
4328          },
4329          NAME: function(match, context, isXML){
4330              if ( typeof context.getElementsByName !== "undefined" ) {
4331                  var ret = [], results = context.getElementsByName(match[1]);
4332  
4333                  for ( var i = 0, l = results.length; i < l; i++ ) {
4334                      if ( results[i].getAttribute("name") === match[1] ) {
4335                          ret.push( results[i] );
4336                      }
4337                  }
4338  
4339                  return ret.length === 0 ? null : ret;
4340              }
4341          },
4342          TAG: function(match, context){
4343              return context.getElementsByTagName(match[1]);
4344          }
4345      },
4346      preFilter: {
4347          CLASS: function(match, curLoop, inplace, result, not, isXML){
4348              match = " " + match[1].replace(/\\/g, "") + " ";
4349  
4350              if ( isXML ) {
4351                  return match;
4352              }
4353  
4354              for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
4355                  if ( elem ) {
4356                      if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) {
4357                          if ( !inplace )
4358                              result.push( elem );
4359                      } else if ( inplace ) {
4360                          curLoop[i] = false;
4361                      }
4362                  }
4363              }
4364  
4365              return false;
4366          },
4367          ID: function(match){
4368              return match[1].replace(/\\/g, "");
4369          },
4370          TAG: function(match, curLoop){
4371              for ( var i = 0; curLoop[i] === false; i++ ){}
4372              return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase();
4373          },
4374          CHILD: function(match){
4375              if ( match[1] == "nth" ) {
4376                  var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
4377                      match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" ||
4378                      !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
4379  
4380                  match[2] = (test[1] + (test[2] || 1)) - 0;
4381                  match[3] = test[3] - 0;
4382              }
4383  
4384              match[0] = done++;
4385  
4386              return match;
4387          },
4388          ATTR: function(match, curLoop, inplace, result, not, isXML){
4389              var name = match[1].replace(/\\/g, "");
4390  
4391              if ( !isXML && Expr.attrMap[name] ) {
4392                  match[1] = Expr.attrMap[name];
4393              }
4394  
4395              if ( match[2] === "~=" ) {
4396                  match[4] = " " + match[4] + " ";
4397              }
4398  
4399              return match;
4400          },
4401          PSEUDO: function(match, curLoop, inplace, result, not){
4402              if ( match[1] === "not" ) {
4403                  if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
4404                      match[3] = Sizzle(match[3], null, null, curLoop);
4405                  } else {
4406                      var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
4407                      if ( !inplace ) {
4408                          result.push.apply( result, ret );
4409                      }
4410                      return false;
4411                  }
4412              } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
4413                  return true;
4414              }
4415  
4416              return match;
4417          },
4418          POS: function(match){
4419              match.unshift( true );
4420              return match;
4421          }
4422      },
4423      filters: {
4424          enabled: function(elem){
4425              return elem.disabled === false && elem.type !== "hidden";
4426          },
4427          disabled: function(elem){
4428              return elem.disabled === true;
4429          },
4430          checked: function(elem){
4431              return elem.checked === true;
4432          },
4433          selected: function(elem){
4434              elem.parentNode.selectedIndex;
4435              return elem.selected === true;
4436          },
4437          parent: function(elem){
4438              return !!elem.firstChild;
4439          },
4440          empty: function(elem){
4441              return !elem.firstChild;
4442          },
4443          has: function(elem, i, match){
4444              return !!Sizzle( match[3], elem ).length;
4445          },
4446          header: function(elem){
4447              return /h\d/i.test( elem.nodeName );
4448          },
4449          text: function(elem){
4450              return "text" === elem.type;
4451          },
4452          radio: function(elem){
4453              return "radio" === elem.type;
4454          },
4455          checkbox: function(elem){
4456              return "checkbox" === elem.type;
4457          },
4458          file: function(elem){
4459              return "file" === elem.type;
4460          },
4461          password: function(elem){
4462              return "password" === elem.type;
4463          },
4464          submit: function(elem){
4465              return "submit" === elem.type;
4466          },
4467          image: function(elem){
4468              return "image" === elem.type;
4469          },
4470          reset: function(elem){
4471              return "reset" === elem.type;
4472          },
4473          button: function(elem){
4474              return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON";
4475          },
4476          input: function(elem){
4477              return /input|select|textarea|button/i.test(elem.nodeName);
4478          }
4479      },
4480      setFilters: {
4481          first: function(elem, i){
4482              return i === 0;
4483          },
4484          last: function(elem, i, match, array){
4485              return i === array.length - 1;
4486          },
4487          even: function(elem, i){
4488              return i % 2 === 0;
4489          },
4490          odd: function(elem, i){
4491              return i % 2 === 1;
4492          },
4493          lt: function(elem, i, match){
4494              return i < match[3] - 0;
4495          },
4496          gt: function(elem, i, match){
4497              return i > match[3] - 0;
4498          },
4499          nth: function(elem, i, match){
4500              return match[3] - 0 == i;
4501          },
4502          eq: function(elem, i, match){
4503              return match[3] - 0 == i;
4504          }
4505      },
4506      filter: {
4507          PSEUDO: function(elem, match, i, array){
4508              var name = match[1], filter = Expr.filters[ name ];
4509  
4510              if ( filter ) {
4511                  return filter( elem, i, match, array );
4512              } else if ( name === "contains" ) {
4513                  return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0;
4514              } else if ( name === "not" ) {
4515                  var not = match[3];
4516  
4517                  for ( var i = 0, l = not.length; i < l; i++ ) {
4518                      if ( not[i] === elem ) {
4519                          return false;
4520                      }
4521                  }
4522  
4523                  return true;
4524              }
4525          },
4526          CHILD: function(elem, match){
4527              var type = match[1], node = elem;
4528              switch (type) {
4529                  case 'only':
4530                  case 'first':
4531                      while ( (node = node.previousSibling) )  {
4532                          if ( node.nodeType === 1 ) return false;
4533                      }
4534                      if ( type == 'first') return true;
4535                      node = elem;
4536                  case 'last':
4537                      while ( (node = node.nextSibling) )  {
4538                          if ( node.nodeType === 1 ) return false;
4539                      }
4540                      return true;
4541                  case 'nth':
4542                      var first = match[2], last = match[3];
4543  
4544                      if ( first == 1 && last == 0 ) {
4545                          return true;
4546                      }
4547  
4548                      var doneName = match[0],
4549                          parent = elem.parentNode;
4550  
4551                      if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
4552                          var count = 0;
4553                          for ( node = parent.firstChild; node; node = node.nextSibling ) {
4554                              if ( node.nodeType === 1 ) {
4555                                  node.nodeIndex = ++count;
4556                              }
4557                          }
4558                          parent.sizcache = doneName;
4559                      }
4560  
4561                      var diff = elem.nodeIndex - last;
4562                      if ( first == 0 ) {
4563                          return diff == 0;
4564                      } else {
4565                          return ( diff % first == 0 && diff / first >= 0 );
4566                      }
4567              }
4568          },
4569          ID: function(elem, match){
4570              return elem.nodeType === 1 && elem.getAttribute("id") === match;
4571          },
4572          TAG: function(elem, match){
4573              return (match === "*" && elem.nodeType === 1) || elem.nodeName === match;
4574          },
4575          CLASS: function(elem, match){
4576              return (" " + (elem.className || elem.getAttribute("class")) + " ")
4577                  .indexOf( match ) > -1;
4578          },
4579          ATTR: function(elem, match){
4580              var name = match[1],
4581                  result = Expr.attrHandle[ name ] ?
4582                      Expr.attrHandle[ name ]( elem ) :
4583                      elem[ name ] != null ?
4584                          elem[ name ] :
4585                          elem.getAttribute( name ),
4586                  value = result + "",
4587                  type = match[2],
4588                  check = match[4];
4589  
4590              return result == null ?
4591                  type === "!=" :
4592                  type === "=" ?
4593                  value === check :
4594                  type === "*=" ?
4595                  value.indexOf(check) >= 0 :
4596                  type === "~=" ?
4597                  (" " + value + " ").indexOf(check) >= 0 :
4598                  !check ?
4599                  value && result !== false :
4600                  type === "!=" ?
4601                  value != check :
4602                  type === "^=" ?
4603                  value.indexOf(check) === 0 :
4604                  type === "$=" ?
4605                  value.substr(value.length - check.length) === check :
4606                  type === "|=" ?
4607                  value === check || value.substr(0, check.length + 1) === check + "-" :
4608                  false;
4609          },
4610          POS: function(elem, match, i, array){
4611              var name = match[2], filter = Expr.setFilters[ name ];
4612  
4613              if ( filter ) {
4614                  return filter( elem, i, match, array );
4615              }
4616          }
4617      }
4618  };
4619  
4620  var origPOS = Expr.match.POS;
4621  
4622  for ( var type in Expr.match ) {
4623      Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
4624      Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
4625  }
4626  
4627  var makeArray = function(array, results) {
4628      array = Array.prototype.slice.call( array, 0 );
4629  
4630      if ( results ) {
4631          results.push.apply( results, array );
4632          return results;
4633      }
4634  
4635      return array;
4636  };
4637  
4638  try {
4639      Array.prototype.slice.call( document.documentElement.childNodes, 0 );
4640  
4641  } catch(e){
4642      makeArray = function(array, results) {
4643          var ret = results || [];
4644  
4645          if ( toString.call(array) === "[object Array]" ) {
4646              Array.prototype.push.apply( ret, array );
4647          } else {
4648              if ( typeof array.length === "number" ) {
4649                  for ( var i = 0, l = array.length; i < l; i++ ) {
4650                      ret.push( array[i] );
4651                  }
4652              } else {
4653                  for ( var i = 0; array[i]; i++ ) {
4654                      ret.push( array[i] );
4655                  }
4656              }
4657          }
4658  
4659          return ret;
4660      };
4661  }
4662  
4663  var sortOrder;
4664  
4665  if ( document.documentElement.compareDocumentPosition ) {
4666      sortOrder = function( a, b ) {
4667          if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
4668              if ( a == b ) {
4669                  hasDuplicate = true;
4670              }
4671              return 0;
4672          }
4673  
4674          var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4675          if ( ret === 0 ) {
4676              hasDuplicate = true;
4677          }
4678          return ret;
4679      };
4680  } else if ( "sourceIndex" in document.documentElement ) {
4681      sortOrder = function( a, b ) {
4682          if ( !a.sourceIndex || !b.sourceIndex ) {
4683              if ( a == b ) {
4684                  hasDuplicate = true;
4685              }
4686              return 0;
4687          }
4688  
4689          var ret = a.sourceIndex - b.sourceIndex;
4690          if ( ret === 0 ) {
4691              hasDuplicate = true;
4692          }
4693          return ret;
4694      };
4695  } else if ( document.createRange ) {
4696      sortOrder = function( a, b ) {
4697          if ( !a.ownerDocument || !b.ownerDocument ) {
4698              if ( a == b ) {
4699                  hasDuplicate = true;
4700              }
4701              return 0;
4702          }
4703  
4704          var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4705          aRange.setStart(a, 0);
4706          aRange.setEnd(a, 0);
4707          bRange.setStart(b, 0);
4708          bRange.setEnd(b, 0);
4709          var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4710          if ( ret === 0 ) {
4711              hasDuplicate = true;
4712          }
4713          return ret;
4714      };
4715  }
4716  
4717  (function(){
4718      var form = document.createElement("div"),
4719          id = "script" + (new Date).getTime();
4720      form.innerHTML = "<a name='" + id + "'/>";
4721  
4722      var root = document.documentElement;
4723      root.insertBefore( form, root.firstChild );
4724  
4725      if ( !!document.getElementById( id ) ) {
4726          Expr.find.ID = function(match, context, isXML){
4727              if ( typeof context.getElementById !== "undefined" && !isXML ) {
4728                  var m = context.getElementById(match[1]);
4729                  return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4730              }
4731          };
4732  
4733          Expr.filter.ID = function(elem, match){
4734              var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4735              return elem.nodeType === 1 && node && node.nodeValue === match;
4736          };
4737      }
4738  
4739      root.removeChild( form );
4740      root = form = null; // release memory in IE
4741  })();
4742  
4743  (function(){
4744  
4745      var div = document.createElement("div");
4746      div.appendChild( document.createComment("") );
4747  
4748      if ( div.getElementsByTagName("*").length > 0 ) {
4749          Expr.find.TAG = function(match, context){
4750              var results = context.getElementsByTagName(match[1]);
4751  
4752              if ( match[1] === "*" ) {
4753                  var tmp = [];
4754  
4755                  for ( var i = 0; results[i]; i++ ) {
4756                      if ( results[i].nodeType === 1 ) {
4757                          tmp.push( results[i] );
4758                      }
4759                  }
4760  
4761                  results = tmp;
4762              }
4763  
4764              return results;
4765          };
4766      }
4767  
4768      div.innerHTML = "<a href='#'></a>";
4769      if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4770              div.firstChild.getAttribute("href") !== "#" ) {
4771          Expr.attrHandle.href = function(elem){
4772              return elem.getAttribute("href", 2);
4773          };
4774      }
4775  
4776      div = null; // release memory in IE
4777  })();
4778  
4779  if ( document.querySelectorAll ) (function(){
4780      var oldSizzle = Sizzle, div = document.createElement("div");
4781      div.innerHTML = "<p class='TEST'></p>";
4782  
4783      if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4784          return;
4785      }
4786  
4787      Sizzle = function(query, context, extra, seed){
4788          context = context || document;
4789  
4790          if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4791              try {
4792                  return makeArray( context.querySelectorAll(query), extra );
4793              } catch(e){}
4794          }
4795  
4796          return oldSizzle(query, context, extra, seed);
4797      };
4798  
4799      for ( var prop in oldSizzle ) {
4800          Sizzle[ prop ] = oldSizzle[ prop ];
4801      }
4802  
4803      div = null; // release memory in IE
4804  })();
4805  
4806  if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){
4807      var div = document.createElement("div");
4808      div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4809  
4810      if ( div.getElementsByClassName("e").length === 0 )
4811          return;
4812  
4813      div.lastChild.className = "e";
4814  
4815      if ( div.getElementsByClassName("e").length === 1 )
4816          return;
4817  
4818      Expr.order.splice(1, 0, "CLASS");
4819      Expr.find.CLASS = function(match, context, isXML) {
4820          if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4821              return context.getElementsByClassName(match[1]);
4822          }
4823      };
4824  
4825      div = null; // release memory in IE
4826  })();
4827  
4828  function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4829      var sibDir = dir == "previousSibling" && !isXML;
4830      for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4831          var elem = checkSet[i];
4832          if ( elem ) {
4833              if ( sibDir && elem.nodeType === 1 ){
4834                  elem.sizcache = doneName;
4835                  elem.sizset = i;
4836              }
4837              elem = elem[dir];
4838              var match = false;
4839  
4840              while ( elem ) {
4841                  if ( elem.sizcache === doneName ) {
4842                      match = checkSet[elem.sizset];
4843                      break;
4844                  }
4845  
4846                  if ( elem.nodeType === 1 && !isXML ){
4847                      elem.sizcache = doneName;
4848                      elem.sizset = i;
4849                  }
4850  
4851                  if ( elem.nodeName === cur ) {
4852                      match = elem;
4853                      break;
4854                  }
4855  
4856                  elem = elem[dir];
4857              }
4858  
4859              checkSet[i] = match;
4860          }
4861      }
4862  }
4863  
4864  function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4865      var sibDir = dir == "previousSibling" && !isXML;
4866      for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4867          var elem = checkSet[i];
4868          if ( elem ) {
4869              if ( sibDir && elem.nodeType === 1 ) {
4870                  elem.sizcache = doneName;
4871                  elem.sizset = i;
4872              }
4873              elem = elem[dir];
4874              var match = false;
4875  
4876              while ( elem ) {
4877                  if ( elem.sizcache === doneName ) {
4878                      match = checkSet[elem.sizset];
4879                      break;
4880                  }
4881  
4882                  if ( elem.nodeType === 1 ) {
4883                      if ( !isXML ) {
4884                          elem.sizcache = doneName;
4885                          elem.sizset = i;
4886                      }
4887                      if ( typeof cur !== "string" ) {
4888                          if ( elem === cur ) {
4889                              match = true;
4890                              break;
4891                          }
4892  
4893                      } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4894                          match = elem;
4895                          break;
4896                      }
4897                  }
4898  
4899                  elem = elem[dir];
4900              }
4901  
4902              checkSet[i] = match;
4903          }
4904      }
4905  }
4906  
4907  var contains = document.compareDocumentPosition ?  function(a, b){
4908      return a.compareDocumentPosition(b) & 16;
4909  } : function(a, b){
4910      return a !== b && (a.contains ? a.contains(b) : true);
4911  };
4912  
4913  var isXML = function(elem){
4914      return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
4915          !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
4916  };
4917  
4918  var posProcess = function(selector, context){
4919      var tmpSet = [], later = "", match,
4920          root = context.nodeType ? [context] : context;
4921  
4922      while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4923          later += match[0];
4924          selector = selector.replace( Expr.match.PSEUDO, "" );
4925      }
4926  
4927      selector = Expr.relative[selector] ? selector + "*" : selector;
4928  
4929      for ( var i = 0, l = root.length; i < l; i++ ) {
4930          Sizzle( selector, root[i], tmpSet );
4931      }
4932  
4933      return Sizzle.filter( later, tmpSet );
4934  };
4935  
4936  
4937  window.Sizzle = Sizzle;
4938  
4939  })();
4940  
4941  Prototype._original_property = window.Sizzle;
4942  
4943  ;(function(engine) {
4944    var extendElements = Prototype.Selector.extendElements;
4945  
4946    function select(selector, scope) {
4947      return extendElements(engine(selector, scope || document));
4948    }
4949  
4950    function match(element, selector) {
4951      return engine.matches(selector, [element]).length == 1;
4952    }
4953  
4954    Prototype.Selector.engine = engine;
4955    Prototype.Selector.select = select;
4956    Prototype.Selector.match = match;
4957  })(Sizzle);
4958  
4959  window.Sizzle = Prototype._original_property;
4960  delete Prototype._original_property;
4961  
4962  var Form = {
4963    reset: function(form) {
4964      form = $(form);
4965      form.reset();
4966      return form;
4967    },
4968  
4969    serializeElements: function(elements, options) {
4970      if (typeof options != 'object') options = { hash: !!options };
4971      else if (Object.isUndefined(options.hash)) options.hash = true;
4972      var key, value, submitted = false, submit = options.submit, accumulator, initial;
4973  
4974      if (options.hash) {
4975        initial = {};
4976        accumulator = function(result, key, value) {
4977          if (key in result) {
4978            if (!Object.isArray(result[key])) result[key] = [result[key]];
4979            result[key].push(value);
4980          } else result[key] = value;
4981          return result;
4982        };
4983      } else {
4984        initial = '';
4985        accumulator = function(result, key, value) {
4986          return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value);
4987        }
4988      }
4989  
4990      return elements.inject(initial, function(result, element) {
4991        if (!element.disabled && element.name) {
4992          key = element.name; value = $(element).getValue();
4993          if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
4994              submit !== false && (!submit || key == submit) && (submitted = true)))) {
4995            result = accumulator(result, key, value);
4996          }
4997        }
4998        return result;
4999      });
5000    }
5001  };
5002  
5003  Form.Methods = {
5004    serialize: function(form, options) {
5005      return Form.serializeElements(Form.getElements(form), options);
5006    },
5007  
5008    getElements: function(form) {
5009      var elements = $(form).getElementsByTagName('*'),
5010          element,
5011          arr = [ ],
5012          serializers = Form.Element.Serializers;
5013      for (var i = 0; element = elements[i]; i++) {
5014        arr.push(element);
5015      }
5016      return arr.inject([], function(elements, child) {
5017        if (serializers[child.tagName.toLowerCase()])
5018          elements.push(Element.extend(child));
5019        return elements;
5020      })
5021    },
5022  
5023    getInputs: function(form, typeName, name) {
5024      form = $(form);
5025      var inputs = form.getElementsByTagName('input');
5026  
5027      if (!typeName && !name) return $A(inputs).map(Element.extend);
5028  
5029      for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
5030        var input = inputs[i];
5031        if ((typeName && input.type != typeName) || (name && input.name != name))
5032          continue;
5033        matchingInputs.push(Element.extend(input));
5034      }
5035  
5036      return matchingInputs;
5037    },
5038  
5039    disable: function(form) {
5040      form = $(form);
5041      Form.getElements(form).invoke('disable');
5042      return form;
5043    },
5044  
5045    enable: function(form) {
5046      form = $(form);
5047      Form.getElements(form).invoke('enable');
5048      return form;
5049    },
5050  
5051    findFirstElement: function(form) {
5052      var elements = $(form).getElements().findAll(function(element) {
5053        return 'hidden' != element.type && !element.disabled;
5054      });
5055      var firstByIndex = elements.findAll(function(element) {
5056        return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
5057      }).sortBy(function(element) { return element.tabIndex }).first();
5058  
5059      return firstByIndex ? firstByIndex : elements.find(function(element) {
5060        return /^(?:input|select|textarea)$/i.test(element.tagName);
5061      });
5062    },
5063  
5064    focusFirstElement: function(form) {
5065      form = $(form);
5066      var element = form.findFirstElement();
5067      if (element) element.activate();
5068      return form;
5069    },
5070  
5071    request: function(form, options) {
5072      form = $(form), options = Object.clone(options || { });
5073  
5074      var params = options.parameters, action = form.readAttribute('action') || '';
5075      if (action.blank()) action = window.location.href;
5076      options.parameters = form.serialize(true);
5077  
5078      if (params) {
5079        if (Object.isString(params)) params = params.toQueryParams();
5080        Object.extend(options.parameters, params);
5081      }
5082  
5083      if (form.hasAttribute('method') && !options.method)
5084        options.method = form.method;
5085  
5086      return new Ajax.Request(action, options);
5087    }
5088  };
5089  
5090  /*--------------------------------------------------------------------------*/
5091  
5092  
5093  Form.Element = {
5094    focus: function(element) {
5095      $(element).focus();
5096      return element;
5097    },
5098  
5099    select: function(element) {
5100      $(element).select();
5101      return element;
5102    }
5103  };
5104  
5105  Form.Element.Methods = {
5106  
5107    serialize: function(element) {
5108      element = $(element);
5109      if (!element.disabled && element.name) {
5110        var value = element.getValue();
5111        if (value != undefined) {
5112          var pair = { };
5113          pair[element.name] = value;
5114          return Object.toQueryString(pair);
5115        }
5116      }
5117      return '';
5118    },
5119  
5120    getValue: function(element) {
5121      element = $(element);
5122      var method = element.tagName.toLowerCase();
5123      return Form.Element.Serializers[method](element);
5124    },
5125  
5126    setValue: function(element, value) {
5127      element = $(element);
5128      var method = element.tagName.toLowerCase();
5129      Form.Element.Serializers[method](element, value);
5130      return element;
5131    },
5132  
5133    clear: function(element) {
5134      $(element).value = '';
5135      return element;
5136    },
5137  
5138    present: function(element) {
5139      return $(element).value != '';
5140    },
5141  
5142    activate: function(element) {
5143      element = $(element);
5144      try {
5145        element.focus();
5146        if (element.select && (element.tagName.toLowerCase() != 'input' ||
5147            !(/^(?:button|reset|submit)$/i.test(element.type))))
5148          element.select();
5149      } catch (e) { }
5150      return element;
5151    },
5152  
5153    disable: function(element) {
5154      element = $(element);
5155      element.disabled = true;
5156      return element;
5157    },
5158  
5159    enable: function(element) {
5160      element = $(element);
5161      element.disabled = false;
5162      return element;
5163    }
5164  };
5165  
5166  /*--------------------------------------------------------------------------*/
5167  
5168  var Field = Form.Element;
5169  
5170  var $F = Form.Element.Methods.getValue;
5171  
5172  /*--------------------------------------------------------------------------*/
5173  
5174  Form.Element.Serializers = (function() {
5175    function input(element, value) {
5176      switch (element.type.toLowerCase()) {
5177        case 'checkbox':
5178        case 'radio':
5179          return inputSelector(element, value);
5180        default:
5181          return valueSelector(element, value);
5182      }
5183    }
5184  
5185    function inputSelector(element, value) {
5186      if (Object.isUndefined(value))
5187        return element.checked ? element.value : null;
5188      else element.checked = !!value;
5189    }
5190  
5191    function valueSelector(element, value) {
5192      if (Object.isUndefined(value)) return element.value;
5193      else element.value = value;
5194    }
5195  
5196    function select(element, value) {
5197      if (Object.isUndefined(value))
5198        return (element.type === 'select-one' ? selectOne : selectMany)(element);
5199  
5200      var opt, currentValue, single = !Object.isArray(value);
5201      for (var i = 0, length = element.length; i < length; i++) {
5202        opt = element.options[i];
5203        currentValue = this.optionValue(opt);
5204        if (single) {
5205          if (currentValue == value) {
5206            opt.selected = true;
5207            return;
5208          }
5209        }
5210        else opt.selected = value.include(currentValue);
5211      }
5212    }
5213  
5214    function selectOne(element) {
5215      var index = element.selectedIndex;
5216      return index >= 0 ? optionValue(element.options[index]) : null;
5217    }
5218  
5219    function selectMany(element) {
5220      var values, length = element.length;
5221      if (!length) return null;
5222  
5223      for (var i = 0, values = []; i < length; i++) {
5224        var opt = element.options[i];
5225        if (opt.selected) values.push(optionValue(opt));
5226      }
5227      return values;
5228    }
5229  
5230    function optionValue(opt) {
5231      return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
5232    }
5233  
5234    return {
5235      input:         input,
5236      inputSelector: inputSelector,
5237      textarea:      valueSelector,
5238      select:        select,
5239      selectOne:     selectOne,
5240      selectMany:    selectMany,
5241      optionValue:   optionValue,
5242      button:        valueSelector
5243    };
5244  })();
5245  
5246  /*--------------------------------------------------------------------------*/
5247  
5248  
5249  Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
5250    initialize: function($super, element, frequency, callback) {
5251      $super(callback, frequency);
5252      this.element   = $(element);
5253      this.lastValue = this.getValue();
5254    },
5255  
5256    execute: function() {
5257      var value = this.getValue();
5258      if (Object.isString(this.lastValue) && Object.isString(value) ?
5259          this.lastValue != value : String(this.lastValue) != String(value)) {
5260        this.callback(this.element, value);
5261        this.lastValue = value;
5262      }
5263    }
5264  });
5265  
5266  Form.Element.Observer = Class.create(Abstract.TimedObserver, {
5267    getValue: function() {
5268      return Form.Element.getValue(this.element);
5269    }
5270  });
5271  
5272  Form.Observer = Class.create(Abstract.TimedObserver, {
5273    getValue: function() {
5274      return Form.serialize(this.element);
5275    }
5276  });
5277  
5278  /*--------------------------------------------------------------------------*/
5279  
5280  Abstract.EventObserver = Class.create({
5281    initialize: function(element, callback) {
5282      this.element  = $(element);
5283      this.callback = callback;
5284  
5285      this.lastValue = this.getValue();
5286      if (this.element.tagName.toLowerCase() == 'form')
5287        this.registerFormCallbacks();
5288      else
5289        this.registerCallback(this.element);
5290    },
5291  
5292    onElementEvent: function() {
5293      var value = this.getValue();
5294      if (this.lastValue != value) {
5295        this.callback(this.element, value);
5296        this.lastValue = value;
5297      }
5298    },
5299  
5300    registerFormCallbacks: function() {
5301      Form.getElements(this.element).each(this.registerCallback, this);
5302    },
5303  
5304    registerCallback: function(element) {
5305      if (element.type) {
5306        switch (element.type.toLowerCase()) {
5307          case 'checkbox':
5308          case 'radio':
5309            Event.observe(element, 'click', this.onElementEvent.bind(this));
5310            break;
5311          default:
5312            Event.observe(element, 'change', this.onElementEvent.bind(this));
5313            break;
5314        }
5315      }
5316    }
5317  });
5318  
5319  Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
5320    getValue: function() {
5321      return Form.Element.getValue(this.element);
5322    }
5323  });
5324  
5325  Form.EventObserver = Class.create(Abstract.EventObserver, {
5326    getValue: function() {
5327      return Form.serialize(this.element);
5328    }
5329  });
5330  (function() {
5331  
5332    var Event = {
5333      KEY_BACKSPACE: 8,
5334      KEY_TAB:       9,
5335      KEY_RETURN:   13,
5336      KEY_ESC:      27,
5337      KEY_LEFT:     37,
5338      KEY_UP:       38,
5339      KEY_RIGHT:    39,
5340      KEY_DOWN:     40,
5341      KEY_DELETE:   46,
5342      KEY_HOME:     36,
5343      KEY_END:      35,
5344      KEY_PAGEUP:   33,
5345      KEY_PAGEDOWN: 34,
5346      KEY_INSERT:   45,
5347  
5348      cache: {}
5349    };
5350  
5351    var docEl = document.documentElement;
5352    var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
5353      && 'onmouseleave' in docEl;
5354  
5355  
5356  
5357    var isIELegacyEvent = function(event) { return false; };
5358  
5359    if (window.attachEvent) {
5360      if (window.addEventListener) {
5361        isIELegacyEvent = function(event) {
5362          return !(event instanceof window.Event);
5363        };
5364      } else {
5365        isIELegacyEvent = function(event) { return true; };
5366      }
5367    }
5368  
5369    var _isButton;
5370  
5371    function _isButtonForDOMEvents(event, code) {
5372      return event.which ? (event.which === code + 1) : (event.button === code);
5373    }
5374  
5375    var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
5376    function _isButtonForLegacyEvents(event, code) {
5377      return event.button === legacyButtonMap[code];
5378    }
5379  
5380    function _isButtonForWebKit(event, code) {
5381      switch (code) {
5382        case 0: return event.which == 1 && !event.metaKey;
5383        case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
5384        case 2: return event.which == 3;
5385        default: return false;
5386      }
5387    }
5388  
5389    if (window.attachEvent) {
5390      if (!window.addEventListener) {
5391        _isButton = _isButtonForLegacyEvents;
5392      } else {
5393        _isButton = function(event, code) {
5394          return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
5395           _isButtonForDOMEvents(event, code);
5396        }
5397      }
5398    } else if (Prototype.Browser.WebKit) {
5399      _isButton = _isButtonForWebKit;
5400    } else {
5401      _isButton = _isButtonForDOMEvents;
5402    }
5403  
5404    function isLeftClick(event)   { return _isButton(event, 0) }
5405  
5406    function isMiddleClick(event) { return _isButton(event, 1) }
5407  
5408    function isRightClick(event)  { return _isButton(event, 2) }
5409  
5410    function element(event) {
5411      event = Event.extend(event);
5412  
5413      var node = event.target, type = event.type,
5414       currentTarget = event.currentTarget;
5415  
5416      if (currentTarget && currentTarget.tagName) {
5417        if (type === 'load' || type === 'error' ||
5418          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
5419            && currentTarget.type === 'radio'))
5420              node = currentTarget;
5421      }
5422  
5423      if (node.nodeType == Node.TEXT_NODE)
5424        node = node.parentNode;
5425  
5426      return Element.extend(node);
5427    }
5428  
5429    function findElement(event, expression) {
5430      var element = Event.element(event);
5431  
5432      if (!expression) return element;
5433      while (element) {
5434        if (Object.isElement(element) && Prototype.Selector.match(element, expression)) {
5435          return Element.extend(element);
5436        }
5437        element = element.parentNode;
5438      }
5439    }
5440  
5441    function pointer(event) {
5442      return { x: pointerX(event), y: pointerY(event) };
5443    }
5444  
5445    function pointerX(event) {
5446      var docElement = document.documentElement,
5447       body = document.body || { scrollLeft: 0 };
5448  
5449      return event.pageX || (event.clientX +
5450        (docElement.scrollLeft || body.scrollLeft) -
5451        (docElement.clientLeft || 0));
5452    }
5453  
5454    function pointerY(event) {
5455      var docElement = document.documentElement,
5456       body = document.body || { scrollTop: 0 };
5457  
5458      return  event.pageY || (event.clientY +
5459         (docElement.scrollTop || body.scrollTop) -
5460         (docElement.clientTop || 0));
5461    }
5462  
5463  
5464    function stop(event) {
5465      Event.extend(event);
5466      event.preventDefault();
5467      event.stopPropagation();
5468  
5469      event.stopped = true;
5470    }
5471  
5472  
5473    Event.Methods = {
5474      isLeftClick:   isLeftClick,
5475      isMiddleClick: isMiddleClick,
5476      isRightClick:  isRightClick,
5477  
5478      element:     element,
5479      findElement: findElement,
5480  
5481      pointer:  pointer,
5482      pointerX: pointerX,
5483      pointerY: pointerY,
5484  
5485      stop: stop
5486    };
5487  
5488    var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
5489      m[name] = Event.Methods[name].methodize();
5490      return m;
5491    });
5492  
5493    if (window.attachEvent) {
5494      function _relatedTarget(event) {
5495        var element;
5496        switch (event.type) {
5497          case 'mouseover':
5498          case 'mouseenter':
5499            element = event.fromElement;
5500            break;
5501          case 'mouseout':
5502          case 'mouseleave':
5503            element = event.toElement;
5504            break;
5505          default:
5506            return null;
5507        }
5508        return Element.extend(element);
5509      }
5510  
5511      var additionalMethods = {
5512        stopPropagation: function() { this.cancelBubble = true },
5513        preventDefault:  function() { this.returnValue = false },
5514        inspect: function() { return '[object Event]' }
5515      };
5516  
5517      Event.extend = function(event, element) {
5518        if (!event) return false;
5519  
5520        if (!isIELegacyEvent(event)) return event;
5521  
5522        if (event._extendedByPrototype) return event;
5523        event._extendedByPrototype = Prototype.emptyFunction;
5524  
5525        var pointer = Event.pointer(event);
5526  
5527        Object.extend(event, {
5528          target: event.srcElement || element,
5529          relatedTarget: _relatedTarget(event),
5530          pageX:  pointer.x,
5531          pageY:  pointer.y
5532        });
5533  
5534        Object.extend(event, methods);
5535        Object.extend(event, additionalMethods);
5536  
5537        return event;
5538      };
5539    } else {
5540      Event.extend = Prototype.K;
5541    }
5542  
5543    if (window.addEventListener) {
5544      Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
5545      Object.extend(Event.prototype, methods);
5546    }
5547  
5548    function _createResponder(element, eventName, handler) {
5549      var registry = Element.retrieve(element, 'prototype_event_registry');
5550  
5551      if (Object.isUndefined(registry)) {
5552        CACHE.push(element);
5553        registry = Element.retrieve(element, 'prototype_event_registry', $H());
5554      }
5555  
5556      var respondersForEvent = registry.get(eventName);
5557      if (Object.isUndefined(respondersForEvent)) {
5558        respondersForEvent = [];
5559        registry.set(eventName, respondersForEvent);
5560      }
5561  
5562      if (respondersForEvent.pluck('handler').include(handler)) return false;
5563  
5564      var responder;
5565      if (eventName.include(":")) {
5566        responder = function(event) {
5567          if (Object.isUndefined(event.eventName))
5568            return false;
5569  
5570          if (event.eventName !== eventName)
5571            return false;
5572  
5573          Event.extend(event, element);
5574          handler.call(element, event);
5575        };
5576      } else {
5577        if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
5578         (eventName === "mouseenter" || eventName === "mouseleave")) {
5579          if (eventName === "mouseenter" || eventName === "mouseleave") {
5580            responder = function(event) {
5581              Event.extend(event, element);
5582  
5583              var parent = event.relatedTarget;
5584              while (parent && parent !== element) {
5585                try { parent = parent.parentNode; }
5586                catch(e) { parent = element; }
5587              }
5588  
5589              if (parent === element) return;
5590  
5591              handler.call(element, event);
5592            };
5593          }
5594        } else {
5595          responder = function(event) {
5596            Event.extend(event, element);
5597            handler.call(element, event);
5598          };
5599        }
5600      }
5601  
5602      responder.handler = handler;
5603      respondersForEvent.push(responder);
5604      return responder;
5605    }
5606  
5607    function _destroyCache() {
5608      for (var i = 0, length = CACHE.length; i < length; i++) {
5609        Event.stopObserving(CACHE[i]);
5610        CACHE[i] = null;
5611      }
5612    }
5613  
5614    var CACHE = [];
5615  
5616    if (Prototype.Browser.IE)
5617      window.attachEvent('onunload', _destroyCache);
5618  
5619    if (Prototype.Browser.WebKit)
5620      window.addEventListener('unload', Prototype.emptyFunction, false);
5621  
5622  
5623    var _getDOMEventName = Prototype.K,
5624        translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
5625  
5626    if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
5627      _getDOMEventName = function(eventName) {
5628        return (translations[eventName] || eventName);
5629      };
5630    }
5631  
5632    function observe(element, eventName, handler) {
5633      element = $(element);
5634  
5635      var responder = _createResponder(element, eventName, handler);
5636  
5637      if (!responder) return element;
5638  
5639      if (eventName.include(':')) {
5640        if (element.addEventListener)
5641          element.addEventListener("dataavailable", responder, false);
5642        else {
5643          element.attachEvent("ondataavailable", responder);
5644          element.attachEvent("onlosecapture", responder);
5645        }
5646      } else {
5647        var actualEventName = _getDOMEventName(eventName);
5648  
5649        if (element.addEventListener)
5650          element.addEventListener(actualEventName, responder, false);
5651        else
5652          element.attachEvent("on" + actualEventName, responder);
5653      }
5654  
5655      return element;
5656    }
5657  
5658    function stopObserving(element, eventName, handler) {
5659      element = $(element);
5660  
5661      var registry = Element.retrieve(element, 'prototype_event_registry');
5662      if (!registry) return element;
5663  
5664      if (!eventName) {
5665        registry.each( function(pair) {
5666          var eventName = pair.key;
5667          stopObserving(element, eventName);
5668        });
5669        return element;
5670      }
5671  
5672      var responders = registry.get(eventName);
5673      if (!responders) return element;
5674  
5675      if (!handler) {
5676        responders.each(function(r) {
5677          stopObserving(element, eventName, r.handler);
5678        });
5679        return element;
5680      }
5681  
5682      var i = responders.length, responder;
5683      while (i--) {
5684        if (responders[i].handler === handler) {
5685          responder = responders[i];
5686          break;
5687        }
5688      }
5689      if (!responder) return element;
5690  
5691      if (eventName.include(':')) {
5692        if (element.removeEventListener)
5693          element.removeEventListener("dataavailable", responder, false);
5694        else {
5695          element.detachEvent("ondataavailable", responder);
5696          element.detachEvent("onlosecapture", responder);
5697        }
5698      } else {
5699        var actualEventName = _getDOMEventName(eventName);
5700        if (element.removeEventListener)
5701          element.removeEventListener(actualEventName, responder, false);
5702        else
5703          element.detachEvent('on' + actualEventName, responder);
5704      }
5705  
5706      registry.set(eventName, responders.without(responder));
5707  
5708      return element;
5709    }
5710  
5711    function fire(element, eventName, memo, bubble) {
5712      element = $(element);
5713  
5714      if (Object.isUndefined(bubble))
5715        bubble = true;
5716  
5717      if (element == document && document.createEvent && !element.dispatchEvent)
5718        element = document.documentElement;
5719  
5720      var event;
5721      if (document.createEvent) {
5722        event = document.createEvent('HTMLEvents');
5723        event.initEvent('dataavailable', bubble, true);
5724      } else {
5725        event = document.createEventObject();
5726        event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
5727      }
5728  
5729      event.eventName = eventName;
5730      event.memo = memo || { };
5731  
5732      if (document.createEvent)
5733        element.dispatchEvent(event);
5734      else
5735        element.fireEvent(event.eventType, event);
5736  
5737      return Event.extend(event);
5738    }
5739  
5740    Event.Handler = Class.create({
5741      initialize: function(element, eventName, selector, callback) {
5742        this.element   = $(element);
5743        this.eventName = eventName;
5744        this.selector  = selector;
5745        this.callback  = callback;
5746        this.handler   = this.handleEvent.bind(this);
5747      },
5748  
5749      start: function() {
5750        Event.observe(this.element, this.eventName, this.handler);
5751        return this;
5752      },
5753  
5754      stop: function() {
5755        Event.stopObserving(this.element, this.eventName, this.handler);
5756        return this;
5757      },
5758  
5759      handleEvent: function(event) {
5760        var element = Event.findElement(event, this.selector);
5761        if (element) this.callback.call(this.element, event, element);
5762      }
5763    });
5764  
5765    function on(element, eventName, selector, callback) {
5766      element = $(element);
5767      if (Object.isFunction(selector) && Object.isUndefined(callback)) {
5768        callback = selector, selector = null;
5769      }
5770  
5771      return new Event.Handler(element, eventName, selector, callback).start();
5772    }
5773  
5774    Object.extend(Event, Event.Methods);
5775  
5776    Object.extend(Event, {
5777      fire:          fire,
5778      observe:       observe,
5779      stopObserving: stopObserving,
5780      on:            on
5781    });
5782  
5783    Element.addMethods({
5784      fire:          fire,
5785  
5786      observe:       observe,
5787  
5788      stopObserving: stopObserving,
5789  
5790      on:            on
5791    });
5792  
5793    Object.extend(document, {
5794      fire:          fire.methodize(),
5795  
5796      observe:       observe.methodize(),
5797  
5798      stopObserving: stopObserving.methodize(),
5799  
5800      on:            on.methodize(),
5801  
5802      loaded:        false
5803    });
5804  
5805    if (window.Event) Object.extend(window.Event, Event);
5806    else window.Event = Event;
5807  })();
5808  
5809  (function() {
5810    /* Support for the DOMContentLoaded event is based on work by Dan Webb,
5811       Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
5812  
5813    var timer;
5814  
5815    function fireContentLoadedEvent() {
5816      if (document.loaded) return;
5817      if (timer) window.clearTimeout(timer);
5818      document.loaded = true;
5819      document.fire('dom:loaded');
5820    }
5821  
5822    function checkReadyState() {
5823      if (document.readyState === 'complete') {
5824        document.stopObserving('readystatechange', checkReadyState);
5825        fireContentLoadedEvent();
5826      }
5827    }
5828  
5829    function pollDoScroll() {
5830      try { document.documentElement.doScroll('left'); }
5831      catch(e) {
5832        timer = pollDoScroll.defer();
5833        return;
5834      }
5835      fireContentLoadedEvent();
5836    }
5837  
5838    if (document.addEventListener) {
5839      document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
5840    } else {
5841      document.observe('readystatechange', checkReadyState);
5842      if (window == top)
5843        timer = pollDoScroll.defer();
5844    }
5845  
5846    Event.observe(window, 'load', fireContentLoadedEvent);
5847  })();
5848  
5849  
5850  Element.addMethods();
5851  /*------------------------------- DEPRECATED -------------------------------*/
5852  
5853  Hash.toQueryString = Object.toQueryString;
5854  
5855  var Toggle = { display: Element.toggle };
5856  
5857  Element.Methods.childOf = Element.Methods.descendantOf;
5858  
5859  var Insertion = {
5860    Before: function(element, content) {
5861      return Element.insert(element, {before:content});
5862    },
5863  
5864    Top: function(element, content) {
5865      return Element.insert(element, {top:content});
5866    },
5867  
5868    Bottom: function(element, content) {
5869      return Element.insert(element, {bottom:content});
5870    },
5871  
5872    After: function(element, content) {
5873      return Element.insert(element, {after:content});
5874    }
5875  };
5876  
5877  var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
5878  
5879  var Position = {
5880    includeScrollOffsets: false,
5881  
5882    prepare: function() {
5883      this.deltaX =  window.pageXOffset
5884                  || document.documentElement.scrollLeft
5885                  || document.body.scrollLeft
5886                  || 0;
5887      this.deltaY =  window.pageYOffset
5888                  || document.documentElement.scrollTop
5889                  || document.body.scrollTop
5890                  || 0;
5891    },
5892  
5893    within: function(element, x, y) {
5894      if (this.includeScrollOffsets)
5895        return this.withinIncludingScrolloffsets(element, x, y);
5896      this.xcomp = x;
5897      this.ycomp = y;
5898      this.offset = Element.cumulativeOffset(element);
5899  
5900      return (y >= this.offset[1] &&
5901              y <  this.offset[1] + element.offsetHeight &&
5902              x >= this.offset[0] &&
5903              x <  this.offset[0] + element.offsetWidth);
5904    },
5905  
5906    withinIncludingScrolloffsets: function(element, x, y) {
5907      var offsetcache = Element.cumulativeScrollOffset(element);
5908  
5909      this.xcomp = x + offsetcache[0] - this.deltaX;
5910      this.ycomp = y + offsetcache[1] - this.deltaY;
5911      this.offset = Element.cumulativeOffset(element);
5912  
5913      return (this.ycomp >= this.offset[1] &&
5914              this.ycomp <  this.offset[1] + element.offsetHeight &&
5915              this.xcomp >= this.offset[0] &&
5916              this.xcomp <  this.offset[0] + element.offsetWidth);
5917    },
5918  
5919    overlap: function(mode, element) {
5920      if (!mode) return 0;
5921      if (mode == 'vertical')
5922        return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
5923          element.offsetHeight;
5924      if (mode == 'horizontal')
5925        return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
5926          element.offsetWidth;
5927    },
5928  
5929  
5930    cumulativeOffset: Element.Methods.cumulativeOffset,
5931  
5932    positionedOffset: Element.Methods.positionedOffset,
5933  
5934    absolutize: function(element) {
5935      Position.prepare();
5936      return Element.absolutize(element);
5937    },
5938  
5939    relativize: function(element) {
5940      Position.prepare();
5941      return Element.relativize(element);
5942    },
5943  
5944    realOffset: Element.Methods.cumulativeScrollOffset,
5945  
5946    offsetParent: Element.Methods.getOffsetParent,
5947  
5948    page: Element.Methods.viewportOffset,
5949  
5950    clone: function(source, target, options) {
5951      options = options || { };
5952      return Element.clonePosition(target, source, options);
5953    }
5954  };
5955  
5956  /*--------------------------------------------------------------------------*/
5957  
5958  if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
5959    function iter(name) {
5960      return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
5961    }
5962  
5963    instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
5964    function(element, className) {
5965      className = className.toString().strip();
5966      var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
5967      return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
5968    } : function(element, className) {
5969      className = className.toString().strip();
5970      var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
5971      if (!classNames && !className) return elements;
5972  
5973      var nodes = $(element).getElementsByTagName('*');
5974      className = ' ' + className + ' ';
5975  
5976      for (var i = 0, child, cn; child = nodes[i]; i++) {
5977        if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
5978            (classNames && classNames.all(function(name) {
5979              return !name.toString().blank() && cn.include(' ' + name + ' ');
5980            }))))
5981          elements.push(Element.extend(child));
5982      }
5983      return elements;
5984    };
5985  
5986    return function(className, parentElement) {
5987      return $(parentElement || document.body).getElementsByClassName(className);
5988    };
5989  }(Element.Methods);
5990  
5991  /*--------------------------------------------------------------------------*/
5992  
5993  Element.ClassNames = Class.create();
5994  Element.ClassNames.prototype = {
5995    initialize: function(element) {
5996      this.element = $(element);
5997    },
5998  
5999    _each: function(iterator) {
6000      this.element.className.split(/\s+/).select(function(name) {
6001        return name.length > 0;
6002      })._each(iterator);
6003    },
6004  
6005    set: function(className) {
6006      this.element.className = className;
6007    },
6008  
6009    add: function(classNameToAdd) {
6010      if (this.include(classNameToAdd)) return;
6011      this.set($A(this).concat(classNameToAdd).join(' '));
6012    },
6013  
6014    remove: function(classNameToRemove) {
6015      if (!this.include(classNameToRemove)) return;
6016      this.set($A(this).without(classNameToRemove).join(' '));
6017    },
6018  
6019    toString: function() {
6020      return $A(this).join(' ');
6021    }
6022  };
6023  
6024  Object.extend(Element.ClassNames.prototype, Enumerable);
6025  
6026  /*--------------------------------------------------------------------------*/
6027  
6028  (function() {
6029    window.Selector = Class.create({
6030      initialize: function(expression) {
6031        this.expression = expression.strip();
6032      },
6033  
6034      findElements: function(rootElement) {
6035        return Prototype.Selector.select(this.expression, rootElement);
6036      },
6037  
6038      match: function(element) {
6039        return Prototype.Selector.match(element, this.expression);
6040      },
6041  
6042      toString: function() {
6043        return this.expression;
6044      },
6045  
6046      inspect: function() {
6047        return "#<Selector: " + this.expression + ">";
6048      }
6049    });
6050  
6051    Object.extend(Selector, {
6052      matchElements: function(elements, expression) {
6053        var match = Prototype.Selector.match,
6054            results = [];
6055  
6056        for (var i = 0, length = elements.length; i < length; i++) {
6057          var element = elements[i];
6058          if (match(element, expression)) {
6059            results.push(Element.extend(element));
6060          }
6061        }
6062        return results;
6063      },
6064  
6065      findElement: function(elements, expression, index) {
6066        index = index || 0;
6067        var matchIndex = 0, element;
6068        for (var i = 0, length = elements.length; i < length; i++) {
6069          element = elements[i];
6070          if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
6071            return Element.extend(element);
6072          }
6073        }
6074      },
6075  
6076      findChildElements: function(element, expressions) {
6077        var selector = expressions.toArray().join(', ');
6078        return Prototype.Selector.select(selector, element || document);
6079      }
6080    });
6081  })();


Generated: Sun Dec 11 14:16:27 2011 Cross-referenced by PHPXref 0.7.1