[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/se3master/var/www/se3/elements/js/ -> prototype.js (source)

   1  /*  Prototype JavaScript framework, version 1.5.0
   2   *  (c) 2005-2007 Sam Stephenson
   3   *
   4   *  Prototype is freely distributable under the terms of an MIT-style license.
   5   *  For details, see the Prototype web site: http://prototype.conio.net/
   6   *
   7  /*--------------------------------------------------------------------------*/
   8  
   9  var Prototype = {
  10    Version: '1.5.0',
  11    BrowserFeatures: {
  12      XPath: !!document.evaluate
  13    },
  14  
  15    ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
  16    emptyFunction: function() {},
  17    K: function(x) { return x }
  18  }
  19  
  20  var Class = {
  21    create: function() {
  22      return function() {
  23        this.initialize.apply(this, arguments);
  24      }
  25    }
  26  }
  27  
  28  var Abstract = new Object();
  29  
  30  Object.extend = function(destination, source) {
  31    for (var property in source) {
  32      destination[property] = source[property];
  33    }
  34    return destination;
  35  }
  36  
  37  Object.extend(Object, {
  38    inspect: function(object) {
  39      try {
  40        if (object === undefined) return 'undefined';
  41        if (object === null) return 'null';
  42        return object.inspect ? object.inspect() : object.toString();
  43      } catch (e) {
  44        if (e instanceof RangeError) return '...';
  45        throw e;
  46      }
  47    },
  48  
  49    keys: function(object) {
  50      var keys = [];
  51      for (var property in object)
  52        keys.push(property);
  53      return keys;
  54    },
  55  
  56    values: function(object) {
  57      var values = [];
  58      for (var property in object)
  59        values.push(object[property]);
  60      return values;
  61    },
  62  
  63    clone: function(object) {
  64      return Object.extend({}, object);
  65    }
  66  });
  67  
  68  Function.prototype.bind = function() {
  69    var __method = this, args = $A(arguments), object = args.shift();
  70    return function() {
  71      return __method.apply(object, args.concat($A(arguments)));
  72    }
  73  }
  74  
  75  Function.prototype.bindAsEventListener = function(object) {
  76    var __method = this, args = $A(arguments), object = args.shift();
  77    return function(event) {
  78      return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
  79    }
  80  }
  81  
  82  Object.extend(Number.prototype, {
  83    toColorPart: function() {
  84      var digits = this.toString(16);
  85      if (this < 16) return '0' + digits;
  86      return digits;
  87    },
  88  
  89    succ: function() {
  90      return this + 1;
  91    },
  92  
  93    times: function(iterator) {
  94      $R(0, this, true).each(iterator);
  95      return this;
  96    }
  97  });
  98  
  99  var Try = {
 100    these: function() {
 101      var returnValue;
 102  
 103      for (var i = 0, length = arguments.length; i < length; i++) {
 104        var lambda = arguments[i];
 105        try {
 106          returnValue = lambda();
 107          break;
 108        } catch (e) {}
 109      }
 110  
 111      return returnValue;
 112    }
 113  }
 114  
 115  /*--------------------------------------------------------------------------*/
 116  
 117  var PeriodicalExecuter = Class.create();
 118  PeriodicalExecuter.prototype = {
 119    initialize: function(callback, frequency) {
 120      this.callback = callback;
 121      this.frequency = frequency;
 122      this.currentlyExecuting = false;
 123  
 124      this.registerCallback();
 125    },
 126  
 127    registerCallback: function() {
 128      this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
 129    },
 130  
 131    stop: function() {
 132      if (!this.timer) return;
 133      clearInterval(this.timer);
 134      this.timer = null;
 135    },
 136  
 137    onTimerEvent: function() {
 138      if (!this.currentlyExecuting) {
 139        try {
 140          this.currentlyExecuting = true;
 141          this.callback(this);
 142        } finally {
 143          this.currentlyExecuting = false;
 144        }
 145      }
 146    }
 147  }
 148  String.interpret = function(value){
 149    return value == null ? '' : String(value);
 150  }
 151  
 152  Object.extend(String.prototype, {
 153    gsub: function(pattern, replacement) {
 154      var result = '', source = this, match;
 155      replacement = arguments.callee.prepareReplacement(replacement);
 156  
 157      while (source.length > 0) {
 158        if (match = source.match(pattern)) {
 159          result += source.slice(0, match.index);
 160          result += String.interpret(replacement(match));
 161          source  = source.slice(match.index + match[0].length);
 162        } else {
 163          result += source, source = '';
 164        }
 165      }
 166      return result;
 167    },
 168  
 169    sub: function(pattern, replacement, count) {
 170      replacement = this.gsub.prepareReplacement(replacement);
 171      count = count === undefined ? 1 : count;
 172  
 173      return this.gsub(pattern, function(match) {
 174        if (--count < 0) return match[0];
 175        return replacement(match);
 176      });
 177    },
 178  
 179    scan: function(pattern, iterator) {
 180      this.gsub(pattern, iterator);
 181      return this;
 182    },
 183  
 184    truncate: function(length, truncation) {
 185      length = length || 30;
 186      truncation = truncation === undefined ? '...' : truncation;
 187      return this.length > length ?
 188        this.slice(0, length - truncation.length) + truncation : this;
 189    },
 190  
 191    strip: function() {
 192      return this.replace(/^\s+/, '').replace(/\s+$/, '');
 193    },
 194  
 195    stripTags: function() {
 196      return this.replace(/<\/?[^>]+>/gi, '');
 197    },
 198  
 199    stripScripts: function() {
 200      return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
 201    },
 202  
 203    extractScripts: function() {
 204      var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
 205      var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
 206      return (this.match(matchAll) || []).map(function(scriptTag) {
 207        return (scriptTag.match(matchOne) || ['', ''])[1];
 208      });
 209    },
 210  
 211    evalScripts: function() {
 212      return this.extractScripts().map(function(script) { return eval(script) });
 213    },
 214  
 215    escapeHTML: function() {
 216      var div = document.createElement('div');
 217      var text = document.createTextNode(this);
 218      div.appendChild(text);
 219      return div.innerHTML;
 220    },
 221  
 222    unescapeHTML: function() {
 223      var div = document.createElement('div');
 224      div.innerHTML = this.stripTags();
 225      return div.childNodes[0] ? (div.childNodes.length > 1 ?
 226        $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
 227        div.childNodes[0].nodeValue) : '';
 228    },
 229  
 230    toQueryParams: function(separator) {
 231      var match = this.strip().match(/([^?#]*)(#.*)?$/);
 232      if (!match) return {};
 233  
 234      return match[1].split(separator || '&').inject({}, function(hash, pair) {
 235        if ((pair = pair.split('='))[0]) {
 236          var name = decodeURIComponent(pair[0]);
 237          var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
 238  
 239          if (hash[name] !== undefined) {
 240            if (hash[name].constructor != Array)
 241              hash[name] = [hash[name]];
 242            if (value) hash[name].push(value);
 243          }
 244          else hash[name] = value;
 245        }
 246        return hash;
 247      });
 248    },
 249  
 250    toArray: function() {
 251      return this.split('');
 252    },
 253  
 254    succ: function() {
 255      return this.slice(0, this.length - 1) +
 256        String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
 257    },
 258  
 259    camelize: function() {
 260      var parts = this.split('-'), len = parts.length;
 261      if (len == 1) return parts[0];
 262  
 263      var camelized = this.charAt(0) == '-'
 264        ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
 265        : parts[0];
 266  
 267      for (var i = 1; i < len; i++)
 268        camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
 269  
 270      return camelized;
 271    },
 272  
 273    capitalize: function(){
 274      return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
 275    },
 276  
 277    underscore: function() {
 278      return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
 279    },
 280  
 281    dasherize: function() {
 282      return this.gsub(/_/,'-');
 283    },
 284  
 285    inspect: function(useDoubleQuotes) {
 286      var escapedString = this.replace(/\\/g, '\\\\');
 287      if (useDoubleQuotes)
 288        return '"' + escapedString.replace(/"/g, '\\"') + '"';
 289      else
 290        return "'" + escapedString.replace(/'/g, '\\\'') + "'";
 291    }
 292  });
 293  
 294  String.prototype.gsub.prepareReplacement = function(replacement) {
 295    if (typeof replacement == 'function') return replacement;
 296    var template = new Template(replacement);
 297    return function(match) { return template.evaluate(match) };
 298  }
 299  
 300  String.prototype.parseQuery = String.prototype.toQueryParams;
 301  
 302  var Template = Class.create();
 303  Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
 304  Template.prototype = {
 305    initialize: function(template, pattern) {
 306      this.template = template.toString();
 307      this.pattern  = pattern || Template.Pattern;
 308    },
 309  
 310    evaluate: function(object) {
 311      return this.template.gsub(this.pattern, function(match) {
 312        var before = match[1];
 313        if (before == '\\') return match[2];
 314        return before + String.interpret(object[match[3]]);
 315      });
 316    }
 317  }
 318  
 319  var $break    = new Object();
 320  var $continue = new Object();
 321  
 322  var Enumerable = {
 323    each: function(iterator) {
 324      var index = 0;
 325      try {
 326        this._each(function(value) {
 327          try {
 328            iterator(value, index++);
 329          } catch (e) {
 330            if (e != $continue) throw e;
 331          }
 332        });
 333      } catch (e) {
 334        if (e != $break) throw e;
 335      }
 336      return this;
 337    },
 338  
 339    eachSlice: function(number, iterator) {
 340      var index = -number, slices = [], array = this.toArray();
 341      while ((index += number) < array.length)
 342        slices.push(array.slice(index, index+number));
 343      return slices.map(iterator);
 344    },
 345  
 346    all: function(iterator) {
 347      var result = true;
 348      this.each(function(value, index) {
 349        result = result && !!(iterator || Prototype.K)(value, index);
 350        if (!result) throw $break;
 351      });
 352      return result;
 353    },
 354  
 355    any: function(iterator) {
 356      var result = false;
 357      this.each(function(value, index) {
 358        if (result = !!(iterator || Prototype.K)(value, index))
 359          throw $break;
 360      });
 361      return result;
 362    },
 363  
 364    collect: function(iterator) {
 365      var results = [];
 366      this.each(function(value, index) {
 367        results.push((iterator || Prototype.K)(value, index));
 368      });
 369      return results;
 370    },
 371  
 372    detect: function(iterator) {
 373      var result;
 374      this.each(function(value, index) {
 375        if (iterator(value, index)) {
 376          result = value;
 377          throw $break;
 378        }
 379      });
 380      return result;
 381    },
 382  
 383    findAll: function(iterator) {
 384      var results = [];
 385      this.each(function(value, index) {
 386        if (iterator(value, index))
 387          results.push(value);
 388      });
 389      return results;
 390    },
 391  
 392    grep: function(pattern, iterator) {
 393      var results = [];
 394      this.each(function(value, index) {
 395        var stringValue = value.toString();
 396        if (stringValue.match(pattern))
 397          results.push((iterator || Prototype.K)(value, index));
 398      })
 399      return results;
 400    },
 401  
 402    include: function(object) {
 403      var found = false;
 404      this.each(function(value) {
 405        if (value == object) {
 406          found = true;
 407          throw $break;
 408        }
 409      });
 410      return found;
 411    },
 412  
 413    inGroupsOf: function(number, fillWith) {
 414      fillWith = fillWith === undefined ? null : fillWith;
 415      return this.eachSlice(number, function(slice) {
 416        while(slice.length < number) slice.push(fillWith);
 417        return slice;
 418      });
 419    },
 420  
 421    inject: function(memo, iterator) {
 422      this.each(function(value, index) {
 423        memo = iterator(memo, value, index);
 424      });
 425      return memo;
 426    },
 427  
 428    invoke: function(method) {
 429      var args = $A(arguments).slice(1);
 430      return this.map(function(value) {
 431        return value[method].apply(value, args);
 432      });
 433    },
 434  
 435    max: function(iterator) {
 436      var result;
 437      this.each(function(value, index) {
 438        value = (iterator || Prototype.K)(value, index);
 439        if (result == undefined || value >= result)
 440          result = value;
 441      });
 442      return result;
 443    },
 444  
 445    min: function(iterator) {
 446      var result;
 447      this.each(function(value, index) {
 448        value = (iterator || Prototype.K)(value, index);
 449        if (result == undefined || value < result)
 450          result = value;
 451      });
 452      return result;
 453    },
 454  
 455    partition: function(iterator) {
 456      var trues = [], falses = [];
 457      this.each(function(value, index) {
 458        ((iterator || Prototype.K)(value, index) ?
 459          trues : falses).push(value);
 460      });
 461      return [trues, falses];
 462    },
 463  
 464    pluck: function(property) {
 465      var results = [];
 466      this.each(function(value, index) {
 467        results.push(value[property]);
 468      });
 469      return results;
 470    },
 471  
 472    reject: function(iterator) {
 473      var results = [];
 474      this.each(function(value, index) {
 475        if (!iterator(value, index))
 476          results.push(value);
 477      });
 478      return results;
 479    },
 480  
 481    sortBy: function(iterator) {
 482      return this.map(function(value, index) {
 483        return {value: value, criteria: iterator(value, index)};
 484      }).sort(function(left, right) {
 485        var a = left.criteria, b = right.criteria;
 486        return a < b ? -1 : a > b ? 1 : 0;
 487      }).pluck('value');
 488    },
 489  
 490    toArray: function() {
 491      return this.map();
 492    },
 493  
 494    zip: function() {
 495      var iterator = Prototype.K, args = $A(arguments);
 496      if (typeof args.last() == 'function')
 497        iterator = args.pop();
 498  
 499      var collections = [this].concat(args).map($A);
 500      return this.map(function(value, index) {
 501        return iterator(collections.pluck(index));
 502      });
 503    },
 504  
 505    size: function() {
 506      return this.toArray().length;
 507    },
 508  
 509    inspect: function() {
 510      return '#<Enumerable:' + this.toArray().inspect() + '>';
 511    }
 512  }
 513  
 514  Object.extend(Enumerable, {
 515    map:     Enumerable.collect,
 516    find:    Enumerable.detect,
 517    select:  Enumerable.findAll,
 518    member:  Enumerable.include,
 519    entries: Enumerable.toArray
 520  });
 521  var $A = Array.from = function(iterable) {
 522    if (!iterable) return [];
 523    if (iterable.toArray) {
 524      return iterable.toArray();
 525    } else {
 526      var results = [];
 527      for (var i = 0, length = iterable.length; i < length; i++)
 528        results.push(iterable[i]);
 529      return results;
 530    }
 531  }
 532  
 533  Object.extend(Array.prototype, Enumerable);
 534  
 535  if (!Array.prototype._reverse)
 536    Array.prototype._reverse = Array.prototype.reverse;
 537  
 538  Object.extend(Array.prototype, {
 539    _each: function(iterator) {
 540      for (var i = 0, length = this.length; i < length; i++)
 541        iterator(this[i]);
 542    },
 543  
 544    clear: function() {
 545      this.length = 0;
 546      return this;
 547    },
 548  
 549    first: function() {
 550      return this[0];
 551    },
 552  
 553    last: function() {
 554      return this[this.length - 1];
 555    },
 556  
 557    compact: function() {
 558      return this.select(function(value) {
 559        return value != null;
 560      });
 561    },
 562  
 563    flatten: function() {
 564      return this.inject([], function(array, value) {
 565        return array.concat(value && value.constructor == Array ?
 566          value.flatten() : [value]);
 567      });
 568    },
 569  
 570    without: function() {
 571      var values = $A(arguments);
 572      return this.select(function(value) {
 573        return !values.include(value);
 574      });
 575    },
 576  
 577    indexOf: function(object) {
 578      for (var i = 0, length = this.length; i < length; i++)
 579        if (this[i] == object) return i;
 580      return -1;
 581    },
 582  
 583    reverse: function(inline) {
 584      return (inline !== false ? this : this.toArray())._reverse();
 585    },
 586  
 587    reduce: function() {
 588      return this.length > 1 ? this : this[0];
 589    },
 590  
 591    uniq: function() {
 592      return this.inject([], function(array, value) {
 593        return array.include(value) ? array : array.concat([value]);
 594      });
 595    },
 596  
 597    clone: function() {
 598      return [].concat(this);
 599    },
 600  
 601    size: function() {
 602      return this.length;
 603    },
 604  
 605    inspect: function() {
 606      return '[' + this.map(Object.inspect).join(', ') + ']';
 607    }
 608  });
 609  
 610  Array.prototype.toArray = Array.prototype.clone;
 611  
 612  function $w(string){
 613    string = string.strip();
 614    return string ? string.split(/\s+/) : [];
 615  }
 616  
 617  if(window.opera){
 618    Array.prototype.concat = function(){
 619      var array = [];
 620      for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
 621      for(var i = 0, length = arguments.length; i < length; i++) {
 622        if(arguments[i].constructor == Array) {
 623          for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
 624            array.push(arguments[i][j]);
 625        } else {
 626          array.push(arguments[i]);
 627        }
 628      }
 629      return array;
 630    }
 631  }
 632  var Hash = function(obj) {
 633    Object.extend(this, obj || {});
 634  };
 635  
 636  Object.extend(Hash, {
 637    toQueryString: function(obj) {
 638      var parts = [];
 639  
 640        this.prototype._each.call(obj, function(pair) {
 641        if (!pair.key) return;
 642  
 643        if (pair.value && pair.value.constructor == Array) {
 644          var values = pair.value.compact();
 645          if (values.length < 2) pair.value = values.reduce();
 646          else {
 647              key = encodeURIComponent(pair.key);
 648            values.each(function(value) {
 649              value = value != undefined ? encodeURIComponent(value) : '';
 650              parts.push(key + '=' + encodeURIComponent(value));
 651            });
 652            return;
 653          }
 654        }
 655        if (pair.value == undefined) pair[1] = '';
 656        parts.push(pair.map(encodeURIComponent).join('='));
 657        });
 658  
 659      return parts.join('&');
 660    }
 661  });
 662  
 663  Object.extend(Hash.prototype, Enumerable);
 664  Object.extend(Hash.prototype, {
 665    _each: function(iterator) {
 666      for (var key in this) {
 667        var value = this[key];
 668        if (value && value == Hash.prototype[key]) continue;
 669  
 670        var pair = [key, value];
 671        pair.key = key;
 672        pair.value = value;
 673        iterator(pair);
 674      }
 675    },
 676  
 677    keys: function() {
 678      return this.pluck('key');
 679    },
 680  
 681    values: function() {
 682      return this.pluck('value');
 683    },
 684  
 685    merge: function(hash) {
 686      return $H(hash).inject(this, function(mergedHash, pair) {
 687        mergedHash[pair.key] = pair.value;
 688        return mergedHash;
 689      });
 690    },
 691  
 692    remove: function() {
 693      var result;
 694      for(var i = 0, length = arguments.length; i < length; i++) {
 695        var value = this[arguments[i]];
 696        if (value !== undefined){
 697          if (result === undefined) result = value;
 698          else {
 699            if (result.constructor != Array) result = [result];
 700            result.push(value)
 701          }
 702        }
 703        delete this[arguments[i]];
 704      }
 705      return result;
 706    },
 707  
 708    toQueryString: function() {
 709      return Hash.toQueryString(this);
 710    },
 711  
 712    inspect: function() {
 713      return '#<Hash:{' + this.map(function(pair) {
 714        return pair.map(Object.inspect).join(': ');
 715      }).join(', ') + '}>';
 716    }
 717  });
 718  
 719  function $H(object) {
 720    if (object && object.constructor == Hash) return object;
 721    return new Hash(object);
 722  };
 723  ObjectRange = Class.create();
 724  Object.extend(ObjectRange.prototype, Enumerable);
 725  Object.extend(ObjectRange.prototype, {
 726    initialize: function(start, end, exclusive) {
 727      this.start = start;
 728      this.end = end;
 729      this.exclusive = exclusive;
 730    },
 731  
 732    _each: function(iterator) {
 733      var value = this.start;
 734      while (this.include(value)) {
 735        iterator(value);
 736        value = value.succ();
 737      }
 738    },
 739  
 740    include: function(value) {
 741      if (value < this.start)
 742        return false;
 743      if (this.exclusive)
 744        return value < this.end;
 745      return value <= this.end;
 746    }
 747  });
 748  
 749  var $R = function(start, end, exclusive) {
 750    return new ObjectRange(start, end, exclusive);
 751  }
 752  
 753  var Ajax = {
 754    getTransport: function() {
 755      return Try.these(
 756        function() {return new XMLHttpRequest()},
 757        function() {return new ActiveXObject('Msxml2.XMLHTTP')},
 758        function() {return new ActiveXObject('Microsoft.XMLHTTP')}
 759      ) || false;
 760    },
 761  
 762    activeRequestCount: 0
 763  }
 764  
 765  Ajax.Responders = {
 766    responders: [],
 767  
 768    _each: function(iterator) {
 769      this.responders._each(iterator);
 770    },
 771  
 772    register: function(responder) {
 773      if (!this.include(responder))
 774        this.responders.push(responder);
 775    },
 776  
 777    unregister: function(responder) {
 778      this.responders = this.responders.without(responder);
 779    },
 780  
 781    dispatch: function(callback, request, transport, json) {
 782      this.each(function(responder) {
 783        if (typeof responder[callback] == 'function') {
 784          try {
 785            responder[callback].apply(responder, [request, transport, json]);
 786          } catch (e) {}
 787        }
 788      });
 789    }
 790  };
 791  
 792  Object.extend(Ajax.Responders, Enumerable);
 793  
 794  Ajax.Responders.register({
 795    onCreate: function() {
 796      Ajax.activeRequestCount++;
 797    },
 798    onComplete: function() {
 799      Ajax.activeRequestCount--;
 800    }
 801  });
 802  
 803  Ajax.Base = function() {};
 804  Ajax.Base.prototype = {
 805    setOptions: function(options) {
 806      this.options = {
 807        method:       'post',
 808        asynchronous: true,
 809        contentType:  'application/x-www-form-urlencoded',
 810        encoding:     'UTF-8',
 811        parameters:   ''
 812      }
 813      Object.extend(this.options, options || {});
 814  
 815      this.options.method = this.options.method.toLowerCase();
 816      if (typeof this.options.parameters == 'string')
 817        this.options.parameters = this.options.parameters.toQueryParams();
 818    }
 819  }
 820  
 821  Ajax.Request = Class.create();
 822  Ajax.Request.Events =
 823    ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
 824  
 825  Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
 826    _complete: false,
 827  
 828    initialize: function(url, options) {
 829      this.transport = Ajax.getTransport();
 830      this.setOptions(options);
 831      this.request(url);
 832    },
 833  
 834    request: function(url) {
 835      this.url = url;
 836      this.method = this.options.method;
 837      var params = this.options.parameters;
 838  
 839      if (!['get', 'post'].include(this.method)) {
 840        // simulate other verbs over post
 841        params['_method'] = this.method;
 842        this.method = 'post';
 843      }
 844  
 845      params = Hash.toQueryString(params);
 846      if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
 847  
 848      // when GET, append parameters to URL
 849      if (this.method == 'get' && params)
 850        this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
 851  
 852      try {
 853        Ajax.Responders.dispatch('onCreate', this, this.transport);
 854  
 855        this.transport.open(this.method.toUpperCase(), this.url,
 856          this.options.asynchronous);
 857  
 858        if (this.options.asynchronous)
 859          setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
 860  
 861        this.transport.onreadystatechange = this.onStateChange.bind(this);
 862        this.setRequestHeaders();
 863  
 864        var body = this.method == 'post' ? (this.options.postBody || params) : null;
 865  
 866        this.transport.send(body);
 867  
 868        /* Force Firefox to handle ready state 4 for synchronous requests */
 869        if (!this.options.asynchronous && this.transport.overrideMimeType)
 870          this.onStateChange();
 871  
 872      }
 873      catch (e) {
 874        this.dispatchException(e);
 875      }
 876    },
 877  
 878    onStateChange: function() {
 879      var readyState = this.transport.readyState;
 880      if (readyState > 1 && !((readyState == 4) && this._complete))
 881        this.respondToReadyState(this.transport.readyState);
 882    },
 883  
 884    setRequestHeaders: function() {
 885      var headers = {
 886        'X-Requested-With': 'XMLHttpRequest',
 887        'X-Prototype-Version': Prototype.Version,
 888        'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
 889      };
 890  
 891      if (this.method == 'post') {
 892        headers['Content-type'] = this.options.contentType +
 893          (this.options.encoding ? '; charset=' + this.options.encoding : '');
 894  
 895        /* Force "Connection: close" for older Mozilla browsers to work
 896         * around a bug where XMLHttpRequest sends an incorrect
 897         * Content-length header. See Mozilla Bugzilla #246651.
 898         */
 899        if (this.transport.overrideMimeType &&
 900            (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
 901              headers['Connection'] = 'close';
 902      }
 903  
 904      // user-defined headers
 905      if (typeof this.options.requestHeaders == 'object') {
 906        var extras = this.options.requestHeaders;
 907  
 908        if (typeof extras.push == 'function')
 909          for (var i = 0, length = extras.length; i < length; i += 2)
 910            headers[extras[i]] = extras[i+1];
 911        else
 912          $H(extras).each(function(pair) { headers[pair.key] = pair.value });
 913      }
 914  
 915      for (var name in headers)
 916        this.transport.setRequestHeader(name, headers[name]);
 917    },
 918  
 919    success: function() {
 920      return !this.transport.status
 921          || (this.transport.status >= 200 && this.transport.status < 300);
 922    },
 923  
 924    respondToReadyState: function(readyState) {
 925      var state = Ajax.Request.Events[readyState];
 926      var transport = this.transport, json = this.evalJSON();
 927  
 928      if (state == 'Complete') {
 929        try {
 930          this._complete = true;
 931          (this.options['on' + this.transport.status]
 932           || this.options['on' + (this.success() ? 'Success' : 'Failure')]
 933           || Prototype.emptyFunction)(transport, json);
 934        } catch (e) {
 935          this.dispatchException(e);
 936        }
 937  
 938        if ((this.getHeader('Content-type') || 'text/javascript').strip().
 939          match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
 940            this.evalResponse();
 941      }
 942  
 943      try {
 944        (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
 945        Ajax.Responders.dispatch('on' + state, this, transport, json);
 946      } catch (e) {
 947        this.dispatchException(e);
 948      }
 949  
 950      if (state == 'Complete') {
 951        // avoid memory leak in MSIE: clean up
 952        this.transport.onreadystatechange = Prototype.emptyFunction;
 953      }
 954    },
 955  
 956    getHeader: function(name) {
 957      try {
 958        return this.transport.getResponseHeader(name);
 959      } catch (e) { return null }
 960    },
 961  
 962    evalJSON: function() {
 963      try {
 964        var json = this.getHeader('X-JSON');
 965        return json ? eval('(' + json + ')') : null;
 966      } catch (e) { return null }
 967    },
 968  
 969    evalResponse: function() {
 970      try {
 971        return eval(this.transport.responseText);
 972      } catch (e) {
 973        this.dispatchException(e);
 974      }
 975    },
 976  
 977    dispatchException: function(exception) {
 978      (this.options.onException || Prototype.emptyFunction)(this, exception);
 979      Ajax.Responders.dispatch('onException', this, exception);
 980    }
 981  });
 982  
 983  Ajax.Updater = Class.create();
 984  
 985  Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
 986    initialize: function(container, url, options) {
 987      this.container = {
 988        success: (container.success || container),
 989        failure: (container.failure || (container.success ? null : container))
 990      }
 991  
 992      this.transport = Ajax.getTransport();
 993      this.setOptions(options);
 994  
 995      var onComplete = this.options.onComplete || Prototype.emptyFunction;
 996      this.options.onComplete = (function(transport, param) {
 997        this.updateContent();
 998        onComplete(transport, param);
 999      }).bind(this);
1000  
1001      this.request(url);
1002    },
1003  
1004    updateContent: function() {
1005      var receiver = this.container[this.success() ? 'success' : 'failure'];
1006      var response = this.transport.responseText;
1007  
1008      if (!this.options.evalScripts) response = response.stripScripts();
1009  
1010      if (receiver = $(receiver)) {
1011        if (this.options.insertion)
1012          new this.options.insertion(receiver, response);
1013        else
1014          receiver.update(response);
1015      }
1016  
1017      if (this.success()) {
1018        if (this.onComplete)
1019          setTimeout(this.onComplete.bind(this), 10);
1020      }
1021    }
1022  });
1023  
1024  Ajax.PeriodicalUpdater = Class.create();
1025  Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1026    initialize: function(container, url, options) {
1027      this.setOptions(options);
1028      this.onComplete = this.options.onComplete;
1029  
1030      this.frequency = (this.options.frequency || 2);
1031      this.decay = (this.options.decay || 1);
1032  
1033      this.updater = {};
1034      this.container = container;
1035      this.url = url;
1036  
1037      this.start();
1038    },
1039  
1040    start: function() {
1041      this.options.onComplete = this.updateComplete.bind(this);
1042      this.onTimerEvent();
1043    },
1044  
1045    stop: function() {
1046      this.updater.options.onComplete = undefined;
1047      clearTimeout(this.timer);
1048      (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1049    },
1050  
1051    updateComplete: function(request) {
1052      if (this.options.decay) {
1053        this.decay = (request.responseText == this.lastText ?
1054          this.decay * this.options.decay : 1);
1055  
1056        this.lastText = request.responseText;
1057      }
1058      this.timer = setTimeout(this.onTimerEvent.bind(this),
1059        this.decay * this.frequency * 1000);
1060    },
1061  
1062    onTimerEvent: function() {
1063      this.updater = new Ajax.Updater(this.container, this.url, this.options);
1064    }
1065  });
1066  function $(element) {
1067    if (arguments.length > 1) {
1068      for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1069        elements.push($(arguments[i]));
1070      return elements;
1071    }
1072    if (typeof element == 'string')
1073      element = document.getElementById(element);
1074    return Element.extend(element);
1075  }
1076  
1077  if (Prototype.BrowserFeatures.XPath) {
1078    document._getElementsByXPath = function(expression, parentElement) {
1079      var results = [];
1080      var query = document.evaluate(expression, $(parentElement) || document,
1081        null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1082      for (var i = 0, length = query.snapshotLength; i < length; i++)
1083        results.push(query.snapshotItem(i));
1084      return results;
1085    };
1086  }
1087  
1088  document.getElementsByClassName = function(className, parentElement) {
1089    if (Prototype.BrowserFeatures.XPath) {
1090      var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1091      return document._getElementsByXPath(q, parentElement);
1092    } else {
1093      var children = ($(parentElement) || document.body).getElementsByTagName('*');
1094      var elements = [], child;
1095      for (var i = 0, length = children.length; i < length; i++) {
1096        child = children[i];
1097        if (Element.hasClassName(child, className))
1098          elements.push(Element.extend(child));
1099      }
1100      return elements;
1101    }
1102  };
1103  
1104  /*--------------------------------------------------------------------------*/
1105  
1106  if (!window.Element)
1107    var Element = new Object();
1108  
1109  Element.extend = function(element) {
1110    if (!element || _nativeExtensions || element.nodeType == 3) return element;
1111  
1112    if (!element._extended && element.tagName && element != window) {
1113      var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1114  
1115      if (element.tagName == 'FORM')
1116        Object.extend(methods, Form.Methods);
1117      if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1118        Object.extend(methods, Form.Element.Methods);
1119  
1120      Object.extend(methods, Element.Methods.Simulated);
1121  
1122      for (var property in methods) {
1123        var value = methods[property];
1124        if (typeof value == 'function' && !(property in element))
1125          element[property] = cache.findOrStore(value);
1126      }
1127    }
1128  
1129    element._extended = true;
1130    return element;
1131  };
1132  
1133  Element.extend.cache = {
1134    findOrStore: function(value) {
1135      return this[value] = this[value] || function() {
1136        return value.apply(null, [this].concat($A(arguments)));
1137      }
1138    }
1139  };
1140  
1141  Element.Methods = {
1142    visible: function(element) {
1143      return $(element).style.display != 'none';
1144    },
1145  
1146    toggle: function(element) {
1147      element = $(element);
1148      Element[Element.visible(element) ? 'hide' : 'show'](element);
1149      return element;
1150    },
1151  
1152    hide: function(element) {
1153      $(element).style.display = 'none';
1154      return element;
1155    },
1156  
1157    show: function(element) {
1158      $(element).style.display = '';
1159      return element;
1160    },
1161  
1162    remove: function(element) {
1163      element = $(element);
1164      element.parentNode.removeChild(element);
1165      return element;
1166    },
1167  
1168    update: function(element, html) {
1169      html = typeof html == 'undefined' ? '' : html.toString();
1170      $(element).innerHTML = html.stripScripts();
1171      setTimeout(function() {html.evalScripts()}, 10);
1172      return element;
1173    },
1174  
1175    replace: function(element, html) {
1176      element = $(element);
1177      html = typeof html == 'undefined' ? '' : html.toString();
1178      if (element.outerHTML) {
1179        element.outerHTML = html.stripScripts();
1180      } else {
1181        var range = element.ownerDocument.createRange();
1182        range.selectNodeContents(element);
1183        element.parentNode.replaceChild(
1184          range.createContextualFragment(html.stripScripts()), element);
1185      }
1186      setTimeout(function() {html.evalScripts()}, 10);
1187      return element;
1188    },
1189  
1190    inspect: function(element) {
1191      element = $(element);
1192      var result = '<' + element.tagName.toLowerCase();
1193      $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1194        var property = pair.first(), attribute = pair.last();
1195        var value = (element[property] || '').toString();
1196        if (value) result += ' ' + attribute + '=' + value.inspect(true);
1197      });
1198      return result + '>';
1199    },
1200  
1201    recursivelyCollect: function(element, property) {
1202      element = $(element);
1203      var elements = [];
1204      while (element = element[property])
1205        if (element.nodeType == 1)
1206          elements.push(Element.extend(element));
1207      return elements;
1208    },
1209  
1210    ancestors: function(element) {
1211      return $(element).recursivelyCollect('parentNode');
1212    },
1213  
1214    descendants: function(element) {
1215      return $A($(element).getElementsByTagName('*'));
1216    },
1217  
1218    immediateDescendants: function(element) {
1219      if (!(element = $(element).firstChild)) return [];
1220      while (element && element.nodeType != 1) element = element.nextSibling;
1221      if (element) return [element].concat($(element).nextSiblings());
1222      return [];
1223    },
1224  
1225    previousSiblings: function(element) {
1226      return $(element).recursivelyCollect('previousSibling');
1227    },
1228  
1229    nextSiblings: function(element) {
1230      return $(element).recursivelyCollect('nextSibling');
1231    },
1232  
1233    siblings: function(element) {
1234      element = $(element);
1235      return element.previousSiblings().reverse().concat(element.nextSiblings());
1236    },
1237  
1238    match: function(element, selector) {
1239      if (typeof selector == 'string')
1240        selector = new Selector(selector);
1241      return selector.match($(element));
1242    },
1243  
1244    up: function(element, expression, index) {
1245      return Selector.findElement($(element).ancestors(), expression, index);
1246    },
1247  
1248    down: function(element, expression, index) {
1249      return Selector.findElement($(element).descendants(), expression, index);
1250    },
1251  
1252    previous: function(element, expression, index) {
1253      return Selector.findElement($(element).previousSiblings(), expression, index);
1254    },
1255  
1256    next: function(element, expression, index) {
1257      return Selector.findElement($(element).nextSiblings(), expression, index);
1258    },
1259  
1260    getElementsBySelector: function() {
1261      var args = $A(arguments), element = $(args.shift());
1262      return Selector.findChildElements(element, args);
1263    },
1264  
1265    getElementsByClassName: function(element, className) {
1266      return document.getElementsByClassName(className, element);
1267    },
1268  
1269    readAttribute: function(element, name) {
1270      element = $(element);
1271      if (document.all && !window.opera) {
1272        var t = Element._attributeTranslations;
1273        if (t.values[name]) return t.values[name](element, name);
1274        if (t.names[name])  name = t.names[name];
1275        var attribute = element.attributes[name];
1276        if(attribute) return attribute.nodeValue;
1277      }
1278      return element.getAttribute(name);
1279    },
1280  
1281    getHeight: function(element) {
1282      return $(element).getDimensions().height;
1283    },
1284  
1285    getWidth: function(element) {
1286      return $(element).getDimensions().width;
1287    },
1288  
1289    classNames: function(element) {
1290      return new Element.ClassNames(element);
1291    },
1292  
1293    hasClassName: function(element, className) {
1294      if (!(element = $(element))) return;
1295      var elementClassName = element.className;
1296      if (elementClassName.length == 0) return false;
1297      if (elementClassName == className ||
1298          elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1299        return true;
1300      return false;
1301    },
1302  
1303    addClassName: function(element, className) {
1304      if (!(element = $(element))) return;
1305      Element.classNames(element).add(className);
1306      return element;
1307    },
1308  
1309    removeClassName: function(element, className) {
1310      if (!(element = $(element))) return;
1311      Element.classNames(element).remove(className);
1312      return element;
1313    },
1314  
1315    toggleClassName: function(element, className) {
1316      if (!(element = $(element))) return;
1317      Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1318      return element;
1319    },
1320  
1321    observe: function() {
1322      Event.observe.apply(Event, arguments);
1323      return $A(arguments).first();
1324    },
1325  
1326    stopObserving: function() {
1327      Event.stopObserving.apply(Event, arguments);
1328      return $A(arguments).first();
1329    },
1330  
1331    // removes whitespace-only text node children
1332    cleanWhitespace: function(element) {
1333      element = $(element);
1334      var node = element.firstChild;
1335      while (node) {
1336        var nextNode = node.nextSibling;
1337        if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1338          element.removeChild(node);
1339        node = nextNode;
1340      }
1341      return element;
1342    },
1343  
1344    empty: function(element) {
1345      return $(element).innerHTML.match(/^\s*$/);
1346    },
1347  
1348    descendantOf: function(element, ancestor) {
1349      element = $(element), ancestor = $(ancestor);
1350      while (element = element.parentNode)
1351        if (element == ancestor) return true;
1352      return false;
1353    },
1354  
1355    scrollTo: function(element) {
1356      element = $(element);
1357      var pos = Position.cumulativeOffset(element);
1358      window.scrollTo(pos[0], pos[1]);
1359      return element;
1360    },
1361  
1362    getStyle: function(element, style) {
1363      element = $(element);
1364      if (['float','cssFloat'].include(style))
1365        style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366      style = style.camelize();
1367      var value = element.style[style];
1368      if (!value) {
1369        if (document.defaultView && document.defaultView.getComputedStyle) {
1370          var css = document.defaultView.getComputedStyle(element, null);
1371          value = css ? css[style] : null;
1372        } else if (element.currentStyle) {
1373          value = element.currentStyle[style];
1374        }
1375      }
1376  
1377      if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1378        value = element['offset'+style.capitalize()] + 'px';
1379  
1380      if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1381        if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1382      if(style == 'opacity') {
1383        if(value) return parseFloat(value);
1384        if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1385          if(value[1]) return parseFloat(value[1]) / 100;
1386        return 1.0;
1387      }
1388      return value == 'auto' ? null : value;
1389    },
1390  
1391    setStyle: function(element, style) {
1392      element = $(element);
1393      for (var name in style) {
1394        var value = style[name];
1395        if(name == 'opacity') {
1396          if (value == 1) {
1397            value = (/Gecko/.test(navigator.userAgent) &&
1398              !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1399            if(/MSIE/.test(navigator.userAgent) && !window.opera)
1400              element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1401          } else if(value === '') {
1402            if(/MSIE/.test(navigator.userAgent) && !window.opera)
1403              element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1404          } else {
1405            if(value < 0.00001) value = 0;
1406            if(/MSIE/.test(navigator.userAgent) && !window.opera)
1407              element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1408                'alpha(opacity='+value*100+')';
1409          }
1410        } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1411        element.style[name.camelize()] = value;
1412      }
1413      return element;
1414    },
1415  
1416    getDimensions: function(element) {
1417      element = $(element);
1418      var display = $(element).getStyle('display');
1419      if (display != 'none' && display != null) // Safari bug
1420        return {width: element.offsetWidth, height: element.offsetHeight};
1421  
1422      // All *Width and *Height properties give 0 on elements with display none,
1423      // so enable the element temporarily
1424      var els = element.style;
1425      var originalVisibility = els.visibility;
1426      var originalPosition = els.position;
1427      var originalDisplay = els.display;
1428      els.visibility = 'hidden';
1429      els.position = 'absolute';
1430      els.display = 'block';
1431      var originalWidth = element.clientWidth;
1432      var originalHeight = element.clientHeight;
1433      els.display = originalDisplay;
1434      els.position = originalPosition;
1435      els.visibility = originalVisibility;
1436      return {width: originalWidth, height: originalHeight};
1437    },
1438  
1439    makePositioned: function(element) {
1440      element = $(element);
1441      var pos = Element.getStyle(element, 'position');
1442      if (pos == 'static' || !pos) {
1443        element._madePositioned = true;
1444        element.style.position = 'relative';
1445        // Opera returns the offset relative to the positioning context, when an
1446        // element is position relative but top and left have not been defined
1447        if (window.opera) {
1448          element.style.top = 0;
1449          element.style.left = 0;
1450        }
1451      }
1452      return element;
1453    },
1454  
1455    undoPositioned: function(element) {
1456      element = $(element);
1457      if (element._madePositioned) {
1458        element._madePositioned = undefined;
1459        element.style.position =
1460          element.style.top =
1461          element.style.left =
1462          element.style.bottom =
1463          element.style.right = '';
1464      }
1465      return element;
1466    },
1467  
1468    makeClipping: function(element) {
1469      element = $(element);
1470      if (element._overflow) return element;
1471      element._overflow = element.style.overflow || 'auto';
1472      if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1473        element.style.overflow = 'hidden';
1474      return element;
1475    },
1476  
1477    undoClipping: function(element) {
1478      element = $(element);
1479      if (!element._overflow) return element;
1480      element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1481      element._overflow = null;
1482      return element;
1483    }
1484  };
1485  
1486  Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1487  
1488  Element._attributeTranslations = {};
1489  
1490  Element._attributeTranslations.names = {
1491    colspan:   "colSpan",
1492    rowspan:   "rowSpan",
1493    valign:    "vAlign",
1494    datetime:  "dateTime",
1495    accesskey: "accessKey",
1496    tabindex:  "tabIndex",
1497    enctype:   "encType",
1498    maxlength: "maxLength",
1499    readonly:  "readOnly",
1500    longdesc:  "longDesc"
1501  };
1502  
1503  Element._attributeTranslations.values = {
1504    _getAttr: function(element, attribute) {
1505      return element.getAttribute(attribute, 2);
1506    },
1507  
1508    _flag: function(element, attribute) {
1509      return $(element).hasAttribute(attribute) ? attribute : null;
1510    },
1511  
1512    style: function(element) {
1513      return element.style.cssText.toLowerCase();
1514    },
1515  
1516    title: function(element) {
1517      var node = element.getAttributeNode('title');
1518      return node.specified ? node.nodeValue : null;
1519    }
1520  };
1521  
1522  Object.extend(Element._attributeTranslations.values, {
1523    href: Element._attributeTranslations.values._getAttr,
1524    src:  Element._attributeTranslations.values._getAttr,
1525    disabled: Element._attributeTranslations.values._flag,
1526    checked:  Element._attributeTranslations.values._flag,
1527    readonly: Element._attributeTranslations.values._flag,
1528    multiple: Element._attributeTranslations.values._flag
1529  });
1530  
1531  Element.Methods.Simulated = {
1532    hasAttribute: function(element, attribute) {
1533      var t = Element._attributeTranslations;
1534      attribute = t.names[attribute] || attribute;
1535      return $(element).getAttributeNode(attribute).specified;
1536    }
1537  };
1538  
1539  // IE is missing .innerHTML support for TABLE-related elements
1540  if (document.all && !window.opera){
1541    Element.Methods.update = function(element, html) {
1542      element = $(element);
1543      html = typeof html == 'undefined' ? '' : html.toString();
1544      var tagName = element.tagName.toUpperCase();
1545      if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1546        var div = document.createElement('div');
1547        switch (tagName) {
1548          case 'THEAD':
1549          case 'TBODY':
1550            div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
1551            depth = 2;
1552            break;
1553          case 'TR':
1554            div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
1555            depth = 3;
1556            break;
1557          case 'TD':
1558            div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
1559            depth = 4;
1560        }
1561        $A(element.childNodes).each(function(node){
1562          element.removeChild(node)
1563        });
1564        depth.times(function(){ div = div.firstChild });
1565  
1566        $A(div.childNodes).each(
1567          function(node){ element.appendChild(node) });
1568      } else {
1569        element.innerHTML = html.stripScripts();
1570      }
1571      setTimeout(function() {html.evalScripts()}, 10);
1572      return element;
1573    }
1574  };
1575  
1576  Object.extend(Element, Element.Methods);
1577  
1578  var _nativeExtensions = false;
1579  
1580  if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1581    ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
1582      var className = 'HTML' + tag + 'Element';
1583      if(window[className]) return;
1584      var klass = window[className] = {};
1585      klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
1586    });
1587  
1588  Element.addMethods = function(methods) {
1589    Object.extend(Element.Methods, methods || {});
1590  
1591    function copy(methods, destination, onlyIfAbsent) {
1592      onlyIfAbsent = onlyIfAbsent || false;
1593      var cache = Element.extend.cache;
1594      for (var property in methods) {
1595        var value = methods[property];
1596        if (!onlyIfAbsent || !(property in destination))
1597          destination[property] = cache.findOrStore(value);
1598      }
1599    }
1600  
1601    if (typeof HTMLElement != 'undefined') {
1602      copy(Element.Methods, HTMLElement.prototype);
1603      copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1604      copy(Form.Methods, HTMLFormElement.prototype);
1605      [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
1606        copy(Form.Element.Methods, klass.prototype);
1607      });
1608      _nativeExtensions = true;
1609    }
1610  }
1611  
1612  var Toggle = new Object();
1613  Toggle.display = Element.toggle;
1614  
1615  /*--------------------------------------------------------------------------*/
1616  
1617  Abstract.Insertion = function(adjacency) {
1618    this.adjacency = adjacency;
1619  }
1620  
1621  Abstract.Insertion.prototype = {
1622    initialize: function(element, content) {
1623      this.element = $(element);
1624      this.content = content.stripScripts();
1625  
1626      if (this.adjacency && this.element.insertAdjacentHTML) {
1627        try {
1628          this.element.insertAdjacentHTML(this.adjacency, this.content);
1629        } catch (e) {
1630          var tagName = this.element.tagName.toUpperCase();
1631          if (['TBODY', 'TR'].include(tagName)) {
1632            this.insertContent(this.contentFromAnonymousTable());
1633          } else {
1634            throw e;
1635          }
1636        }
1637      } else {
1638        this.range = this.element.ownerDocument.createRange();
1639        if (this.initializeRange) this.initializeRange();
1640        this.insertContent([this.range.createContextualFragment(this.content)]);
1641      }
1642  
1643      setTimeout(function() {content.evalScripts()}, 10);
1644    },
1645  
1646    contentFromAnonymousTable: function() {
1647      var div = document.createElement('div');
1648      div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1649      return $A(div.childNodes[0].childNodes[0].childNodes);
1650    }
1651  }
1652  
1653  var Insertion = new Object();
1654  
1655  Insertion.Before = Class.create();
1656  Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1657    initializeRange: function() {
1658      this.range.setStartBefore(this.element);
1659    },
1660  
1661    insertContent: function(fragments) {
1662      fragments.each((function(fragment) {
1663        this.element.parentNode.insertBefore(fragment, this.element);
1664      }).bind(this));
1665    }
1666  });
1667  
1668  Insertion.Top = Class.create();
1669  Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1670    initializeRange: function() {
1671      this.range.selectNodeContents(this.element);
1672      this.range.collapse(true);
1673    },
1674  
1675    insertContent: function(fragments) {
1676      fragments.reverse(false).each((function(fragment) {
1677        this.element.insertBefore(fragment, this.element.firstChild);
1678      }).bind(this));
1679    }
1680  });
1681  
1682  Insertion.Bottom = Class.create();
1683  Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1684    initializeRange: function() {
1685      this.range.selectNodeContents(this.element);
1686      this.range.collapse(this.element);
1687    },
1688  
1689    insertContent: function(fragments) {
1690      fragments.each((function(fragment) {
1691        this.element.appendChild(fragment);
1692      }).bind(this));
1693    }
1694  });
1695  
1696  Insertion.After = Class.create();
1697  Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1698    initializeRange: function() {
1699      this.range.setStartAfter(this.element);
1700    },
1701  
1702    insertContent: function(fragments) {
1703      fragments.each((function(fragment) {
1704        this.element.parentNode.insertBefore(fragment,
1705          this.element.nextSibling);
1706      }).bind(this));
1707    }
1708  });
1709  
1710  /*--------------------------------------------------------------------------*/
1711  
1712  Element.ClassNames = Class.create();
1713  Element.ClassNames.prototype = {
1714    initialize: function(element) {
1715      this.element = $(element);
1716    },
1717  
1718    _each: function(iterator) {
1719      this.element.className.split(/\s+/).select(function(name) {
1720        return name.length > 0;
1721      })._each(iterator);
1722    },
1723  
1724    set: function(className) {
1725      this.element.className = className;
1726    },
1727  
1728    add: function(classNameToAdd) {
1729      if (this.include(classNameToAdd)) return;
1730      this.set($A(this).concat(classNameToAdd).join(' '));
1731    },
1732  
1733    remove: function(classNameToRemove) {
1734      if (!this.include(classNameToRemove)) return;
1735      this.set($A(this).without(classNameToRemove).join(' '));
1736    },
1737  
1738    toString: function() {
1739      return $A(this).join(' ');
1740    }
1741  };
1742  
1743  Object.extend(Element.ClassNames.prototype, Enumerable);
1744  var Selector = Class.create();
1745  Selector.prototype = {
1746    initialize: function(expression) {
1747      this.params = {classNames: []};
1748      this.expression = expression.toString().strip();
1749      this.parseExpression();
1750      this.compileMatcher();
1751    },
1752  
1753    parseExpression: function() {
1754      function abort(message) { throw 'Parse error in selector: ' + message; }
1755  
1756      if (this.expression == '')  abort('empty expression');
1757  
1758      var params = this.params, expr = this.expression, match, modifier, clause, rest;
1759      while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
1760        params.attributes = params.attributes || [];
1761        params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
1762        expr = match[1];
1763      }
1764  
1765      if (expr == '*') return this.params.wildcard = true;
1766  
1767      while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
1768        modifier = match[1], clause = match[2], rest = match[3];
1769        switch (modifier) {
1770          case '#':       params.id = clause; break;
1771          case '.':       params.classNames.push(clause); break;
1772          case '':
1773          case undefined: params.tagName = clause.toUpperCase(); break;
1774          default:        abort(expr.inspect());
1775        }
1776        expr = rest;
1777      }
1778  
1779      if (expr.length > 0) abort(expr.inspect());
1780    },
1781  
1782    buildMatchExpression: function() {
1783      var params = this.params, conditions = [], clause;
1784  
1785      if (params.wildcard)
1786        conditions.push('true');
1787      if (clause = params.id)
1788        conditions.push('element.readAttribute("id") == ' + clause.inspect());
1789      if (clause = params.tagName)
1790        conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1791      if ((clause = params.classNames).length > 0)
1792        for (var i = 0, length = clause.length; i < length; i++)
1793          conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1794      if (clause = params.attributes) {
1795        clause.each(function(attribute) {
1796          var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1797          var splitValueBy = function(delimiter) {
1798            return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1799          }
1800  
1801          switch (attribute.operator) {
1802            case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
1803            case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
1804            case '|=':      conditions.push(
1805                              splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
1806                            ); break;
1807            case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
1808            case '':
1809            case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1810            default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
1811          }
1812        });
1813      }
1814  
1815      return conditions.join(' && ');
1816    },
1817  
1818    compileMatcher: function() {
1819      this.match = new Function('element', 'if (!element.tagName) return false; \
1820        element = $(element); \
1821        return ' + this.buildMatchExpression());
1822    },
1823  
1824    findElements: function(scope) {
1825      var element;
1826  
1827      if (element = $(this.params.id))
1828        if (this.match(element))
1829          if (!scope || Element.childOf(element, scope))
1830            return [element];
1831  
1832      scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
1833  
1834      var results = [];
1835      for (var i = 0, length = scope.length; i < length; i++)
1836        if (this.match(element = scope[i]))
1837          results.push(Element.extend(element));
1838  
1839      return results;
1840    },
1841  
1842    toString: function() {
1843      return this.expression;
1844    }
1845  }
1846  
1847  Object.extend(Selector, {
1848    matchElements: function(elements, expression) {
1849      var selector = new Selector(expression);
1850      return elements.select(selector.match.bind(selector)).map(Element.extend);
1851    },
1852  
1853    findElement: function(elements, expression, index) {
1854      if (typeof expression == 'number') index = expression, expression = false;
1855      return Selector.matchElements(elements, expression || '*')[index || 0];
1856    },
1857  
1858    findChildElements: function(element, expressions) {
1859      return expressions.map(function(expression) {
1860        return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1861          var selector = new Selector(expr);
1862          return results.inject([], function(elements, result) {
1863            return elements.concat(selector.findElements(result || element));
1864          });
1865        });
1866      }).flatten();
1867    }
1868  });
1869  
1870  function $$() {
1871    return Selector.findChildElements(document, $A(arguments));
1872  }
1873  var Form = {
1874    reset: function(form) {
1875      $(form).reset();
1876      return form;
1877    },
1878  
1879    serializeElements: function(elements, getHash) {
1880      var data = elements.inject({}, function(result, element) {
1881        if (!element.disabled && element.name) {
1882          var key = element.name, value = $(element).getValue();
1883          if (value != undefined) {
1884            if (result[key]) {
1885              if (result[key].constructor != Array) result[key] = [result[key]];
1886              result[key].push(value);
1887            }
1888            else result[key] = value;
1889          }
1890        }
1891        return result;
1892      });
1893  
1894      return getHash ? data : Hash.toQueryString(data);
1895    }
1896  };
1897  
1898  Form.Methods = {
1899    serialize: function(form, getHash) {
1900      return Form.serializeElements(Form.getElements(form), getHash);
1901    },
1902  
1903    getElements: function(form) {
1904      return $A($(form).getElementsByTagName('*')).inject([],
1905        function(elements, child) {
1906          if (Form.Element.Serializers[child.tagName.toLowerCase()])
1907            elements.push(Element.extend(child));
1908          return elements;
1909        }
1910      );
1911    },
1912  
1913    getInputs: function(form, typeName, name) {
1914      form = $(form);
1915      var inputs = form.getElementsByTagName('input');
1916  
1917      if (!typeName && !name) return $A(inputs).map(Element.extend);
1918  
1919      for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
1920        var input = inputs[i];
1921        if ((typeName && input.type != typeName) || (name && input.name != name))
1922          continue;
1923        matchingInputs.push(Element.extend(input));
1924      }
1925  
1926      return matchingInputs;
1927    },
1928  
1929    disable: function(form) {
1930      form = $(form);
1931      form.getElements().each(function(element) {
1932        element.blur();
1933        element.disabled = 'true';
1934      });
1935      return form;
1936    },
1937  
1938    enable: function(form) {
1939      form = $(form);
1940      form.getElements().each(function(element) {
1941        element.disabled = '';
1942      });
1943      return form;
1944    },
1945  
1946    findFirstElement: function(form) {
1947      return $(form).getElements().find(function(element) {
1948        return element.type != 'hidden' && !element.disabled &&
1949          ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1950      });
1951    },
1952  
1953    focusFirstElement: function(form) {
1954      form = $(form);
1955      form.findFirstElement().activate();
1956      return form;
1957    }
1958  }
1959  
1960  Object.extend(Form, Form.Methods);
1961  
1962  /*--------------------------------------------------------------------------*/
1963  
1964  Form.Element = {
1965    focus: function(element) {
1966      $(element).focus();
1967      return element;
1968    },
1969  
1970    select: function(element) {
1971      $(element).select();
1972      return element;
1973    }
1974  }
1975  
1976  Form.Element.Methods = {
1977    serialize: function(element) {
1978      element = $(element);
1979      if (!element.disabled && element.name) {
1980        var value = element.getValue();
1981        if (value != undefined) {
1982          var pair = {};
1983          pair[element.name] = value;
1984          return Hash.toQueryString(pair);
1985        }
1986      }
1987      return '';
1988    },
1989  
1990    getValue: function(element) {
1991      element = $(element);
1992      var method = element.tagName.toLowerCase();
1993      return Form.Element.Serializers[method](element);
1994    },
1995  
1996    clear: function(element) {
1997      $(element).value = '';
1998      return element;
1999    },
2000  
2001    present: function(element) {
2002      return $(element).value != '';
2003    },
2004  
2005    activate: function(element) {
2006      element = $(element);
2007      element.focus();
2008      if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2009        !['button', 'reset', 'submit'].include(element.type) ) )
2010        element.select();
2011      return element;
2012    },
2013  
2014    disable: function(element) {
2015      element = $(element);
2016      element.disabled = true;
2017      return element;
2018    },
2019  
2020    enable: function(element) {
2021      element = $(element);
2022      element.blur();
2023      element.disabled = false;
2024      return element;
2025    }
2026  }
2027  
2028  Object.extend(Form.Element, Form.Element.Methods);
2029  var Field = Form.Element;
2030  var $F = Form.Element.getValue;
2031  
2032  /*--------------------------------------------------------------------------*/
2033  
2034  Form.Element.Serializers = {
2035    input: function(element) {
2036      switch (element.type.toLowerCase()) {
2037        case 'checkbox':
2038        case 'radio':
2039          return Form.Element.Serializers.inputSelector(element);
2040        default:
2041          return Form.Element.Serializers.textarea(element);
2042      }
2043    },
2044  
2045    inputSelector: function(element) {
2046      return element.checked ? element.value : null;
2047    },
2048  
2049    textarea: function(element) {
2050      return element.value;
2051    },
2052  
2053    select: function(element) {
2054      return this[element.type == 'select-one' ?
2055        'selectOne' : 'selectMany'](element);
2056    },
2057  
2058    selectOne: function(element) {
2059      var index = element.selectedIndex;
2060      return index >= 0 ? this.optionValue(element.options[index]) : null;
2061    },
2062  
2063    selectMany: function(element) {
2064      var values, length = element.length;
2065      if (!length) return null;
2066  
2067      for (var i = 0, values = []; i < length; i++) {
2068        var opt = element.options[i];
2069        if (opt.selected) values.push(this.optionValue(opt));
2070      }
2071      return values;
2072    },
2073  
2074    optionValue: function(opt) {
2075      // extend element because hasAttribute may not be native
2076      return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2077    }
2078  }
2079  
2080  /*--------------------------------------------------------------------------*/
2081  
2082  Abstract.TimedObserver = function() {}
2083  Abstract.TimedObserver.prototype = {
2084    initialize: function(element, frequency, callback) {
2085      this.frequency = frequency;
2086      this.element   = $(element);
2087      this.callback  = callback;
2088  
2089      this.lastValue = this.getValue();
2090      this.registerCallback();
2091    },
2092  
2093    registerCallback: function() {
2094      setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2095    },
2096  
2097    onTimerEvent: function() {
2098      var value = this.getValue();
2099      var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2100        ? this.lastValue != value : String(this.lastValue) != String(value));
2101      if (changed) {
2102        this.callback(this.element, value);
2103        this.lastValue = value;
2104      }
2105    }
2106  }
2107  
2108  Form.Element.Observer = Class.create();
2109  Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2110    getValue: function() {
2111      return Form.Element.getValue(this.element);
2112    }
2113  });
2114  
2115  Form.Observer = Class.create();
2116  Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2117    getValue: function() {
2118      return Form.serialize(this.element);
2119    }
2120  });
2121  
2122  /*--------------------------------------------------------------------------*/
2123  
2124  Abstract.EventObserver = function() {}
2125  Abstract.EventObserver.prototype = {
2126    initialize: function(element, callback) {
2127      this.element  = $(element);
2128      this.callback = callback;
2129  
2130      this.lastValue = this.getValue();
2131      if (this.element.tagName.toLowerCase() == 'form')
2132        this.registerFormCallbacks();
2133      else
2134        this.registerCallback(this.element);
2135    },
2136  
2137    onElementEvent: function() {
2138      var value = this.getValue();
2139      if (this.lastValue != value) {
2140        this.callback(this.element, value);
2141        this.lastValue = value;
2142      }
2143    },
2144  
2145    registerFormCallbacks: function() {
2146      Form.getElements(this.element).each(this.registerCallback.bind(this));
2147    },
2148  
2149    registerCallback: function(element) {
2150      if (element.type) {
2151        switch (element.type.toLowerCase()) {
2152          case 'checkbox':
2153          case 'radio':
2154            Event.observe(element, 'click', this.onElementEvent.bind(this));
2155            break;
2156          default:
2157            Event.observe(element, 'change', this.onElementEvent.bind(this));
2158            break;
2159        }
2160      }
2161    }
2162  }
2163  
2164  Form.Element.EventObserver = Class.create();
2165  Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2166    getValue: function() {
2167      return Form.Element.getValue(this.element);
2168    }
2169  });
2170  
2171  Form.EventObserver = Class.create();
2172  Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2173    getValue: function() {
2174      return Form.serialize(this.element);
2175    }
2176  });
2177  if (!window.Event) {
2178    var Event = new Object();
2179  }
2180  
2181  Object.extend(Event, {
2182    KEY_BACKSPACE: 8,
2183    KEY_TAB:       9,
2184    KEY_RETURN:   13,
2185    KEY_ESC:      27,
2186    KEY_LEFT:     37,
2187    KEY_UP:       38,
2188    KEY_RIGHT:    39,
2189    KEY_DOWN:     40,
2190    KEY_DELETE:   46,
2191    KEY_HOME:     36,
2192    KEY_END:      35,
2193    KEY_PAGEUP:   33,
2194    KEY_PAGEDOWN: 34,
2195  
2196    element: function(event) {
2197      return event.target || event.srcElement;
2198    },
2199  
2200    isLeftClick: function(event) {
2201      return (((event.which) && (event.which == 1)) ||
2202              ((event.button) && (event.button == 1)));
2203    },
2204  
2205    pointerX: function(event) {
2206      return event.pageX || (event.clientX +
2207        (document.documentElement.scrollLeft || document.body.scrollLeft));
2208    },
2209  
2210    pointerY: function(event) {
2211      return event.pageY || (event.clientY +
2212        (document.documentElement.scrollTop || document.body.scrollTop));
2213    },
2214  
2215    stop: function(event) {
2216      if (event.preventDefault) {
2217        event.preventDefault();
2218        event.stopPropagation();
2219      } else {
2220        event.returnValue = false;
2221        event.cancelBubble = true;
2222      }
2223    },
2224  
2225    // find the first node with the given tagName, starting from the
2226    // node the event was triggered on; traverses the DOM upwards
2227    findElement: function(event, tagName) {
2228      var element = Event.element(event);
2229      while (element.parentNode && (!element.tagName ||
2230          (element.tagName.toUpperCase() != tagName.toUpperCase())))
2231        element = element.parentNode;
2232      return element;
2233    },
2234  
2235    observers: false,
2236  
2237    _observeAndCache: function(element, name, observer, useCapture) {
2238      if (!this.observers) this.observers = [];
2239      if (element.addEventListener) {
2240        this.observers.push([element, name, observer, useCapture]);
2241        element.addEventListener(name, observer, useCapture);
2242      } else if (element.attachEvent) {
2243        this.observers.push([element, name, observer, useCapture]);
2244        element.attachEvent('on' + name, observer);
2245      }
2246    },
2247  
2248    unloadCache: function() {
2249      if (!Event.observers) return;
2250      for (var i = 0, length = Event.observers.length; i < length; i++) {
2251        Event.stopObserving.apply(this, Event.observers[i]);
2252        Event.observers[i][0] = null;
2253      }
2254      Event.observers = false;
2255    },
2256  
2257    observe: function(element, name, observer, useCapture) {
2258      element = $(element);
2259      useCapture = useCapture || false;
2260  
2261      if (name == 'keypress' &&
2262          (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2263          || element.attachEvent))
2264        name = 'keydown';
2265  
2266      Event._observeAndCache(element, name, observer, useCapture);
2267    },
2268  
2269    stopObserving: function(element, name, observer, useCapture) {
2270      element = $(element);
2271      useCapture = useCapture || false;
2272  
2273      if (name == 'keypress' &&
2274          (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2275          || element.detachEvent))
2276        name = 'keydown';
2277  
2278      if (element.removeEventListener) {
2279        element.removeEventListener(name, observer, useCapture);
2280      } else if (element.detachEvent) {
2281        try {
2282          element.detachEvent('on' + name, observer);
2283        } catch (e) {}
2284      }
2285    }
2286  });
2287  
2288  /* prevent memory leaks in IE */
2289  if (navigator.appVersion.match(/\bMSIE\b/))
2290    Event.observe(window, 'unload', Event.unloadCache, false);
2291  var Position = {
2292    // set to true if needed, warning: firefox performance problems
2293    // NOT neeeded for page scrolling, only if draggable contained in
2294    // scrollable elements
2295    includeScrollOffsets: false,
2296  
2297    // must be called before calling withinIncludingScrolloffset, every time the
2298    // page is scrolled
2299    prepare: function() {
2300      this.deltaX =  window.pageXOffset
2301                  || document.documentElement.scrollLeft
2302                  || document.body.scrollLeft
2303                  || 0;
2304      this.deltaY =  window.pageYOffset
2305                  || document.documentElement.scrollTop
2306                  || document.body.scrollTop
2307                  || 0;
2308    },
2309  
2310    realOffset: function(element) {
2311      var valueT = 0, valueL = 0;
2312      do {
2313        valueT += element.scrollTop  || 0;
2314        valueL += element.scrollLeft || 0;
2315        element = element.parentNode;
2316      } while (element);
2317      return [valueL, valueT];
2318    },
2319  
2320    cumulativeOffset: function(element) {
2321      var valueT = 0, valueL = 0;
2322      do {
2323        valueT += element.offsetTop  || 0;
2324        valueL += element.offsetLeft || 0;
2325        element = element.offsetParent;
2326      } while (element);
2327      return [valueL, valueT];
2328    },
2329  
2330    positionedOffset: function(element) {
2331      var valueT = 0, valueL = 0;
2332      do {
2333        valueT += element.offsetTop  || 0;
2334        valueL += element.offsetLeft || 0;
2335        element = element.offsetParent;
2336        if (element) {
2337          if(element.tagName=='BODY') break;
2338          var p = Element.getStyle(element, 'position');
2339          if (p == 'relative' || p == 'absolute') break;
2340        }
2341      } while (element);
2342      return [valueL, valueT];
2343    },
2344  
2345    offsetParent: function(element) {
2346      if (element.offsetParent) return element.offsetParent;
2347      if (element == document.body) return element;
2348  
2349      while ((element = element.parentNode) && element != document.body)
2350        if (Element.getStyle(element, 'position') != 'static')
2351          return element;
2352  
2353      return document.body;
2354    },
2355  
2356    // caches x/y coordinate pair to use with overlap
2357    within: function(element, x, y) {
2358      if (this.includeScrollOffsets)
2359        return this.withinIncludingScrolloffsets(element, x, y);
2360      this.xcomp = x;
2361      this.ycomp = y;
2362      this.offset = this.cumulativeOffset(element);
2363  
2364      return (y >= this.offset[1] &&
2365              y <  this.offset[1] + element.offsetHeight &&
2366              x >= this.offset[0] &&
2367              x <  this.offset[0] + element.offsetWidth);
2368    },
2369  
2370    withinIncludingScrolloffsets: function(element, x, y) {
2371      var offsetcache = this.realOffset(element);
2372  
2373      this.xcomp = x + offsetcache[0] - this.deltaX;
2374      this.ycomp = y + offsetcache[1] - this.deltaY;
2375      this.offset = this.cumulativeOffset(element);
2376  
2377      return (this.ycomp >= this.offset[1] &&
2378              this.ycomp <  this.offset[1] + element.offsetHeight &&
2379              this.xcomp >= this.offset[0] &&
2380              this.xcomp <  this.offset[0] + element.offsetWidth);
2381    },
2382  
2383    // within must be called directly before
2384    overlap: function(mode, element) {
2385      if (!mode) return 0;
2386      if (mode == 'vertical')
2387        return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
2388          element.offsetHeight;
2389      if (mode == 'horizontal')
2390        return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
2391          element.offsetWidth;
2392    },
2393  
2394    page: function(forElement) {
2395      var valueT = 0, valueL = 0;
2396  
2397      var element = forElement;
2398      do {
2399        valueT += element.offsetTop  || 0;
2400        valueL += element.offsetLeft || 0;
2401  
2402        // Safari fix
2403        if (element.offsetParent==document.body)
2404          if (Element.getStyle(element,'position')=='absolute') break;
2405  
2406      } while (element = element.offsetParent);
2407  
2408      element = forElement;
2409      do {
2410        if (!window.opera || element.tagName=='BODY') {
2411          valueT -= element.scrollTop  || 0;
2412          valueL -= element.scrollLeft || 0;
2413        }
2414      } while (element = element.parentNode);
2415  
2416      return [valueL, valueT];
2417    },
2418  
2419    clone: function(source, target) {
2420      var options = Object.extend({
2421        setLeft:    true,
2422        setTop:     true,
2423        setWidth:   true,
2424        setHeight:  true,
2425        offsetTop:  0,
2426        offsetLeft: 0
2427      }, arguments[2] || {})
2428  
2429      // find page position of source
2430      source = $(source);
2431      var p = Position.page(source);
2432  
2433      // find coordinate system to use
2434      target = $(target);
2435      var delta = [0, 0];
2436      var parent = null;
2437      // delta [0,0] will do fine with position: fixed elements,
2438      // position:absolute needs offsetParent deltas
2439      if (Element.getStyle(target,'position') == 'absolute') {
2440        parent = Position.offsetParent(target);
2441        delta = Position.page(parent);
2442      }
2443  
2444      // correct by body offsets (fixes Safari)
2445      if (parent == document.body) {
2446        delta[0] -= document.body.offsetLeft;
2447        delta[1] -= document.body.offsetTop;
2448      }
2449  
2450      // set position
2451      if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2452      if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2453      if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
2454      if(options.setHeight) target.style.height = source.offsetHeight + 'px';
2455    },
2456  
2457    absolutize: function(element) {
2458      element = $(element);
2459      if (element.style.position == 'absolute') return;
2460      Position.prepare();
2461  
2462      var offsets = Position.positionedOffset(element);
2463      var top     = offsets[1];
2464      var left    = offsets[0];
2465      var width   = element.clientWidth;
2466      var height  = element.clientHeight;
2467  
2468      element._originalLeft   = left - parseFloat(element.style.left  || 0);
2469      element._originalTop    = top  - parseFloat(element.style.top || 0);
2470      element._originalWidth  = element.style.width;
2471      element._originalHeight = element.style.height;
2472  
2473      element.style.position = 'absolute';
2474      element.style.top    = top + 'px';
2475      element.style.left   = left + 'px';
2476      element.style.width  = width + 'px';
2477      element.style.height = height + 'px';
2478    },
2479  
2480    relativize: function(element) {
2481      element = $(element);
2482      if (element.style.position == 'relative') return;
2483      Position.prepare();
2484  
2485      element.style.position = 'relative';
2486      var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
2487      var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2488  
2489      element.style.top    = top + 'px';
2490      element.style.left   = left + 'px';
2491      element.style.height = element._originalHeight;
2492      element.style.width  = element._originalWidth;
2493    }
2494  }
2495  
2496  // Safari returns margins on body which is incorrect if the child is absolutely
2497  // positioned.  For performance reasons, redefine Position.cumulativeOffset for
2498  // KHTML/WebKit only.
2499  if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2500    Position.cumulativeOffset = function(element) {
2501      var valueT = 0, valueL = 0;
2502      do {
2503        valueT += element.offsetTop  || 0;
2504        valueL += element.offsetLeft || 0;
2505        if (element.offsetParent == document.body)
2506          if (Element.getStyle(element, 'position') == 'absolute') break;
2507  
2508        element = element.offsetParent;
2509      } while (element);
2510  
2511      return [valueL, valueT];
2512    }
2513  }
2514  
2515  Element.addMethods();


Generated: Tue Mar 17 22:47:18 2015 Cross-referenced by PHPXref 0.7.1