1  /*  Prototype JavaScript framework, version 1.4.0
   2   *  (c) 2005 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.4.0',
  11    ScriptFragment: '(?:)((\n|\r|.)*?)(?:<\/script>)',
  12  
  13    emptyFunction: function() {},
  14    K: function(x) {return x}
  15  }
  16  
  17  var Class = {
  18    create: function() {
  19      return function() {
  20        this.initialize.apply(this, arguments);
  21      }
  22    }
  23  }
  24  
  25  var Abstract = new Object();
  26  
  27  Object.extend = function(destination, source) {
  28    for (property in source) {
  29      destination[property] = source[property];
  30    }
  31    return destination;
  32  }
  33  
  34  Object.inspect = function(object) {
  35    try {
  36      if (object  undefined) return 'undefined';
  37      if (object  null) return 'null';
  38      return object.inspect ? object.inspect() : object.toString();
  39    } catch (e) {
  40      if (e instanceof RangeError) return '...';
  41      throw e;
  42    }
  43  }
  44  
  45  Function.prototype.bind = function() {
  46    var __method = this, args = $A(arguments), object = args.shift();
  47    return function() {
  48      return __method.apply(object, args.concat($A(arguments)));
  49    }
  50  }
  51  
  52  Function.prototype.bindAsEventListener = function(object) {
  53    var __method = this;
  54    return function(event) {
  55      return __method.call(object, event || window.event);
  56    }
  57  }
  58  
  59  Object.extend(Number.prototype, {
  60    toColorPart: function() {
  61      var digits = this.toString(16);
  62      if (this < 16) return '0' + digits;
  63      return digits;
  64    },
  65  
  66    succ: function() {
  67      return this + 1;
  68    },
  69  
  70    times: function(iterator) {
  71      $R(0, this, true).each(iterator);
  72      return this;
  73    }
  74  });
  75  
  76  var Try = {
  77    these: function() {
  78      var returnValue;
  79  
  80      for (var i = 0; i < arguments.length; i++) {
  81        var lambda = arguments[i];
  82        try {
  83          returnValue = lambda();
  84          break;
  85        } catch (e) {}
  86      }
  87  
  88      return returnValue;
  89    }
  90  }
  91  
  92  /*--------------------------------------------------------------------------*/
  93  
  94  var PeriodicalExecuter = Class.create();
  95  PeriodicalExecuter.prototype = {
  96    initialize: function(callback, frequency) {
  97      this.callback = callback;
  98      this.frequency = frequency;
  99      this.currentlyExecuting = false;
 100  
 101      this.registerCallback();
 102    },
 103  
 104    registerCallback: function() {
 105      setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
 106    },
 107  
 108    onTimerEvent: function() {
 109      if (!this.currentlyExecuting) {
 110        try {
 111          this.currentlyExecuting = true;
 112          this.callback();
 113        } finally {
 114          this.currentlyExecuting = false;
 115        }
 116      }
 117    }
 118  }
 119  
 120  /*--------------------------------------------------------------------------*/
 121  
 122  function $() {
 123    var elements = new Array();
 124  
 125    for (var i = 0; i < arguments.length; i++) {
 126      var element = arguments[i];
 127      if (typeof element == 'string')
 128        element = document.getElementById(element);
 129  
 130      if (arguments.length == 1)
 131        return element;
 132  
 133      elements.push(element);
 134    }
 135  
 136    return elements;
 137  }
 138  Object.extend(String.prototype, {
 139    stripTags: function() {
 140      return this.replace(/<\/?[^>]+>/gi, '');
 141    },
 142  
 143    stripScripts: function() {
 144      return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
 145    },
 146  
 147    extractScripts: function() {
 148      var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
 149      var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
 150      return (this.match(matchAll) || []).map(function(scriptTag) {
 151        return (scriptTag.match(matchOne) || ['', ''])[1];
 152      });
 153    },
 154  
 155    evalScripts: function() {
 156      return this.extractScripts().map(eval);
 157    },
 158  
 159    escapeHTML: function() {
 160      var div = document.createElement('div');
 161      var text = document.createTextNode(this);
 162      div.appendChild(text);
 163      return div.innerHTML;
 164    },
 165  
 166    unescapeHTML: function() {
 167      var div = document.createElement('div');
 168      div.innerHTML = this.stripTags();
 169      return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
 170    },
 171  
 172    toQueryParams: function() {
 173      var pairs = this.match(/^\??(.*)$/)[1].split('&');
 174      return pairs.inject({}, function(params, pairString) {
 175        var pair = pairString.split('=');
 176        params[pair[0]] = pair[1];
 177        return params;
 178      });
 179    },
 180  
 181    toArray: function() {
 182      return this.split('');
 183    },
 184  
 185    camelize: function() {
 186      var oStringList = this.split('-');
 187      if (oStringList.length  1) return oStringList[0];
 188  
 189      var camelizedString = this.indexOf('-')  0
 190        ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
 191        : oStringList[0];
 192  
 193      for (var i = 1, len = oStringList.length; i < len; i++) {
 194        var s = oStringList[i];
 195        camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
 196      }
 197  
 198      return camelizedString;
 199    },
 200  
 201    inspect: function() {
 202      return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
 203    }
 204  });
 205  
 206  String.prototype.parseQuery = String.prototype.toQueryParams;
 207  
 208  var $break    = new Object();
 209  var $continue = new Object();
 210  
 211  var Enumerable = {
 212    each: function(iterator) {
 213      var index = 0;
 214      try {
 215        this._each(function(value) {
 216          try {
 217            iterator(value, index++);
 218          } catch (e) {
 219            if (e != $continue) throw e;
 220          }
 221        });
 222      } catch (e) {
 223        if (e != $break) throw e;
 224      }
 225    },
 226  
 227    all: function(iterator) {
 228      var result = true;
 229      this.each(function(value, index) {
 230        result = result && !!(iterator || Prototype.K)(value, index);
 231        if (!result) throw $break;
 232      });
 233      return result;
 234    },
 235  
 236    any: function(iterator) {
 237      var result = true;
 238      this.each(function(value, index) {
 239        if (result = !!(iterator || Prototype.K)(value, index))
 240          throw $break;
 241      });
 242      return result;
 243    },
 244  
 245    collect: function(iterator) {
 246      var results = [];
 247      this.each(function(value, index) {
 248        results.push(iterator(value, index));
 249      });
 250      return results;
 251    },
 252  
 253    detect: function (iterator) {
 254      var result;
 255      this.each(function(value, index) {
 256        if (iterator(value, index)) {
 257          result = value;
 258          throw $break;
 259        }
 260      });
 261      return result;
 262    },
 263  
 264    findAll: function(iterator) {
 265      var results = [];
 266      this.each(function(value, index) {
 267        if (iterator(value, index))
 268          results.push(value);
 269      });
 270      return results;
 271    },
 272  
 273    grep: function(pattern, iterator) {
 274      var results = [];
 275      this.each(function(value, index) {
 276        var stringValue = value.toString();
 277        if (stringValue.match(pattern))
 278          results.push((iterator || Prototype.K)(value, index));
 279      })
 280      return results;
 281    },
 282  
 283    include: function(object) {
 284      var found = false;
 285      this.each(function(value) {
 286        if (value == object) {
 287          found = true;
 288          throw $break;
 289        }
 290      });
 291      return found;
 292    },
 293  
 294    inject: function(memo, iterator) {
 295      this.each(function(value, index) {
 296        memo = iterator(memo, value, index);
 297      });
 298      return memo;
 299    },
 300  
 301    invoke: function(method) {
 302      var args = $A(arguments).slice(1);
 303      return this.collect(function(value) {
 304        return value[method].apply(value, args);
 305      });
 306    },
 307  
 308    max: function(iterator) {
 309      var result;
 310      this.each(function(value, index) {
 311        value = (iterator || Prototype.K)(value, index);
 312        if (value >= (result || value))
 313          result = value;
 314      });
 315      return result;
 316    },
 317  
 318    min: function(iterator) {
 319      var result;
 320      this.each(function(value, index) {
 321        value = (iterator || Prototype.K)(value, index);
 322        if (value <= (result || value))
 323          result = value;
 324      });
 325      return result;
 326    },
 327  
 328    partition: function(iterator) {
 329      var trues = [], falses = [];
 330      this.each(function(value, index) {
 331        ((iterator || Prototype.K)(value, index) ?
 332          trues : falses).push(value);
 333      });
 334      return [trues, falses];
 335    },
 336  
 337    pluck: function(property) {
 338      var results = [];
 339      this.each(function(value, index) {
 340        results.push(value[property]);
 341      });
 342      return results;
 343    },
 344  
 345    reject: function(iterator) {
 346      var results = [];
 347      this.each(function(value, index) {
 348        if (!iterator(value, index))
 349          results.push(value);
 350      });
 351      return results;
 352    },
 353  
 354    sortBy: function(iterator) {
 355      return this.collect(function(value, index) {
 356        return {value: value, criteria: iterator(value, index)};
 357      }).sort(function(left, right) {
 358        var a = left.criteria, b = right.criteria;
 359        return a < b ? -1 : a > b ? 1 : 0;
 360      }).pluck('value');
 361    },
 362  
 363    toArray: function() {
 364      return this.collect(Prototype.K);
 365    },
 366  
 367    zip: function() {
 368      var iterator = Prototype.K, args = $A(arguments);
 369      if (typeof args.last() == 'function')
 370        iterator = args.pop();
 371  
 372      var collections = [this].concat(args).map($A);
 373      return this.map(function(value, index) {
 374        iterator(value = collections.pluck(index));
 375        return value;
 376      });
 377    },
 378  
 379    inspect: function() {
 380      return '#';
 381    }
 382  }
 383  
 384  Object.extend(Enumerable, {
 385    map:     Enumerable.collect,
 386    find:    Enumerable.detect,
 387    select:  Enumerable.findAll,
 388    member:  Enumerable.include,
 389    entries: Enumerable.toArray
 390  });
 391  var $A = Array.from = function(iterable) {
 392    if (!iterable) return [];
 393    if (iterable.toArray) {
 394      return iterable.toArray();
 395    } else {
 396      var results = [];
 397      for (var i = 0; i < iterable.length; i++)
 398        results.push(iterable[i]);
 399      return results;
 400    }
 401  }
 402  
 403  Object.extend(Array.prototype, Enumerable);
 404  
 405  Array.prototype._reverse = Array.prototype.reverse;
 406  
 407  Object.extend(Array.prototype, {
 408    _each: function(iterator) {
 409      for (var i = 0; i < this.length; i++)
 410        iterator(this[i]);
 411    },
 412  
 413    clear: function() {
 414      this.length = 0;
 415      return this;
 416    },
 417  
 418    first: function() {
 419      return this[0];
 420    },
 421  
 422    last: function() {
 423      return this[this.length - 1];
 424    },
 425  
 426    compact: function() {
 427      return this.select(function(value) {
 428        return value != undefined || value != null;
 429      });
 430    },
 431  
 432    flatten: function() {
 433      return this.inject([], function(array, value) {
 434        return array.concat(value.constructor == Array ?
 435          value.flatten() : [value]);
 436      });
 437    },
 438  
 439    without: function() {
 440      var values = $A(arguments);
 441      return this.select(function(value) {
 442        return !values.include(value);
 443      });
 444    },
 445  
 446    indexOf: function(object) {
 447      for (var i = 0; i < this.length; i++)
 448        if (this[i]  object) return i;
 449      return -1;
 450    },
 451  
 452    reverse: function(inline) {
 453      return (inline ! false ? this : this.toArray())._reverse();
 454    },
 455  
 456    shift: function() {
 457      var result = this[0];
 458      for (var i = 0; i < this.length - 1; i++)
 459        this[i] = this[i + 1];
 460      this.length--;
 461      return result;
 462    },
 463  
 464    inspect: function() {
 465      return '[' + this.map(Object.inspect).join(', ') + ']';
 466    }
 467  });
 468  var Hash = {
 469    _each: function(iterator) {
 470      for (key in this) {
 471        var value = this[key];
 472        if (typeof value == 'function') continue;
 473  
 474        var pair = [key, value];
 475        pair.key = key;
 476        pair.value = value;
 477        iterator(pair);
 478      }
 479    },
 480  
 481    keys: function() {
 482      return this.pluck('key');
 483    },
 484  
 485    values: function() {
 486      return this.pluck('value');
 487    },
 488  
 489    merge: function(hash) {
 490      return $H(hash).inject($H(this), function(mergedHash, pair) {
 491        mergedHash[pair.key] = pair.value;
 492        return mergedHash;
 493      });
 494    },
 495  
 496    toQueryString: function() {
 497      return this.map(function(pair) {
 498        return pair.map(encodeURIComponent).join('=');
 499      }).join('&');
 500    },
 501  
 502    inspect: function() {
 503      return '#';
 506    }
 507  }
 508  
 509  function $H(object) {
 510    var hash = Object.extend({}, object || {});
 511    Object.extend(hash, Enumerable);
 512    Object.extend(hash, Hash);
 513    return hash;
 514  }
 515  ObjectRange = Class.create();
 516  Object.extend(ObjectRange.prototype, Enumerable);
 517  Object.extend(ObjectRange.prototype, {
 518    initialize: function(start, end, exclusive) {
 519      this.start = start;
 520      this.end = end;
 521      this.exclusive = exclusive;
 522    },
 523  
 524    _each: function(iterator) {
 525      var value = this.start;
 526      do {
 527        iterator(value);
 528        value = value.succ();
 529      } while (this.include(value));
 530    },
 531  
 532    include: function(value) {
 533      if (value < this.start)
 534        return false;
 535      if (this.exclusive)
 536        return value < this.end;
 537      return value <= this.end;
 538    }
 539  });
 540  
 541  var $R = function(start, end, exclusive) {
 542    return new ObjectRange(start, end, exclusive);
 543  }
 544  
 545  var Ajax = {
 546    getTransport: function() {
 547      return Try.these(
 548        function() {return new ActiveXObject('Msxml2.XMLHTTP')},
 549        function() {return new ActiveXObject('Microsoft.XMLHTTP')},
 550        function() {return new XMLHttpRequest()}
 551      ) || false;
 552    },
 553  
 554    activeRequestCount: 0
 555  }
 556  
 557  Ajax.Responders = {
 558    responders: [],
 559  
 560    _each: function(iterator) {
 561      this.responders._each(iterator);
 562    },
 563  
 564    register: function(responderToAdd) {
 565      if (!this.include(responderToAdd))
 566        this.responders.push(responderToAdd);
 567    },
 568  
 569    unregister: function(responderToRemove) {
 570      this.responders = this.responders.without(responderToRemove);
 571    },
 572  
 573    dispatch: function(callback, request, transport, json) {
 574      this.each(function(responder) {
 575        if (responder[callback] && typeof responder[callback] == 'function') {
 576          try {
 577            responder[callback].apply(responder, [request, transport, json]);
 578          } catch (e) {}
 579        }
 580      });
 581    }
 582  };
 583  
 584  Object.extend(Ajax.Responders, Enumerable);
 585  
 586  Ajax.Responders.register({
 587    onCreate: function() {
 588      Ajax.activeRequestCount++;
 589    },
 590  
 591    onComplete: function() {
 592      Ajax.activeRequestCount--;
 593    }
 594  });
 595  
 596  Ajax.Base = function() {};
 597  Ajax.Base.prototype = {
 598    setOptions: function(options) {
 599      this.options = {
 600        method:       'post',
 601        asynchronous: true,
 602        parameters:   ''
 603      }
 604      Object.extend(this.options, options || {});
 605    },
 606  
 607    responseIsSuccess: function() {
 608      return this.transport.status  undefined
 609          || this.transport.status  0
 610          || (this.transport.status >= 200 && this.transport.status < 300);
 611    },
 612  
 613    responseIsFailure: function() {
 614      return !this.responseIsSuccess();
 615    }
 616  }
 617  
 618  Ajax.Request = Class.create();
 619  Ajax.Request.Events =
 620    ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
 621  
 622  Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
 623    initialize: function(url, options) {
 624      this.transport = Ajax.getTransport();
 625      this.setOptions(options);
 626      this.request(url);
 627    },
 628  
 629    request: function(url) {
 630      var parameters = this.options.parameters || '';
 631      if (parameters.length > 0) parameters += '&_=';
 632  
 633      try {
 634        this.url = url;
 635        if (this.options.method == 'get' && parameters.length > 0)
 636          this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
 637  
 638        Ajax.Responders.dispatch('onCreate', this, this.transport);
 639  
 640        this.transport.open(this.options.method, this.url,
 641          this.options.asynchronous);
 642  
 643        if (this.options.asynchronous) {
 644          this.transport.onreadystatechange = this.onStateChange.bind(this);
 645          setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
 646        }
 647  
 648        this.setRequestHeaders();
 649  
 650        var body = this.options.postBody ? this.options.postBody : parameters;
 651        this.transport.send(this.options.method == 'post' ? body : null);
 652  
 653      } catch (e) {
 654        this.dispatchException(e);
 655      }
 656    },
 657  
 658    setRequestHeaders: function() {
 659      var requestHeaders =
 660        ['X-Requested-With', 'XMLHttpRequest',
 661         'X-Prototype-Version', Prototype.Version];
 662  
 663      if (this.options.method == 'post') {
 664        requestHeaders.push('Content-type',
 665          'application/x-www-form-urlencoded');
 666  
 667        /* Force "Connection: close" for Mozilla browsers to work around
 668         * a bug where XMLHttpReqeuest sends an incorrect Content-length
 669         * header. See Mozilla Bugzilla #246651.
 670         */
 671        if (this.transport.overrideMimeType)
 672          requestHeaders.push('Connection', 'close');
 673      }
 674  
 675      if (this.options.requestHeaders)
 676        requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
 677  
 678      for (var i = 0; i < requestHeaders.length; i += 2)
 679        this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
 680    },
 681  
 682    onStateChange: function() {
 683      var readyState = this.transport.readyState;
 684      if (readyState != 1)
 685        this.respondToReadyState(this.transport.readyState);
 686    },
 687  
 688    header: function(name) {
 689      try {
 690        return this.transport.getResponseHeader(name);
 691      } catch (e) {}
 692    },
 693  
 694    evalJSON: function() {
 695      try {
 696        return eval(this.header('X-JSON'));
 697      } catch (e) {}
 698    },
 699  
 700    evalResponse: function() {
 701      try {
 702        return eval(this.transport.responseText);
 703      } catch (e) {
 704        this.dispatchException(e);
 705      }
 706    },
 707  
 708    respondToReadyState: function(readyState) {
 709      var event = Ajax.Request.Events[readyState];
 710      var transport = this.transport, json = this.evalJSON();
 711  
 712      if (event  'Complete') {
 713        try {
 714          (this.options['on' + this.transport.status]
 715           || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
 716           || Prototype.emptyFunction)(transport, json);
 717        } catch (e) {
 718          this.dispatchException(e);
 719        }
 720  
 721        if ((this.header('Content-type') || '').match(/^text\/javascript/i))
 722          this.evalResponse();
 723      }
 724  
 725      try {
 726        (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
 727        Ajax.Responders.dispatch('on' + event, this, transport, json);
 728      } catch (e) {
 729        this.dispatchException(e);
 730      }
 731  
 732      /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
 733      if (event  'Complete')
 734        this.transport.onreadystatechange = Prototype.emptyFunction;
 735    },
 736  
 737    dispatchException: function(exception) {
 738      (this.options.onException || Prototype.emptyFunction)(this, exception);
 739      Ajax.Responders.dispatch('onException', this, exception);
 740    }
 741  });
 742  
 743  Ajax.Updater = Class.create();
 744  
 745  Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
 746    initialize: function(container, url, options) {
 747      this.containers = {
 748        success: container.success ? $(container.success) : $(container),
 749        failure: container.failure ? $(container.failure) :
 750          (container.success ? null : $(container))
 751      }
 752  
 753      this.transport = Ajax.getTransport();
 754      this.setOptions(options);
 755  
 756      var onComplete = this.options.onComplete || Prototype.emptyFunction;
 757      this.options.onComplete = (function(transport, object) {
 758        this.updateContent();
 759        onComplete(transport, object);
 760      }).bind(this);
 761  
 762      this.request(url);
 763    },
 764  
 765    updateContent: function() {
 766      var receiver = this.responseIsSuccess() ?
 767        this.containers.success : this.containers.failure;
 768      var response = this.transport.responseText;
 769  
 770      if (!this.options.evalScripts)
 771        response = response.stripScripts();
 772  
 773      if (receiver) {
 774        if (this.options.insertion) {
 775          new this.options.insertion(receiver, response);
 776        } else {
 777          Element.update(receiver, response);
 778        }
 779      }
 780  
 781      if (this.responseIsSuccess()) {
 782        if (this.onComplete)
 783          setTimeout(this.onComplete.bind(this), 10);
 784      }
 785    }
 786  });
 787  
 788  Ajax.PeriodicalUpdater = Class.create();
 789  Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
 790    initialize: function(container, url, options) {
 791      this.setOptions(options);
 792      this.onComplete = this.options.onComplete;
 793  
 794      this.frequency = (this.options.frequency || 2);
 795      this.decay = (this.options.decay || 1);
 796  
 797      this.updater = {};
 798      this.container = container;
 799      this.url = url;
 800  
 801      this.start();
 802    },
 803  
 804    start: function() {
 805      this.options.onComplete = this.updateComplete.bind(this);
 806      this.onTimerEvent();
 807    },
 808  
 809    stop: function() {
 810      this.updater.onComplete = undefined;
 811      clearTimeout(this.timer);
 812      (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
 813    },
 814  
 815    updateComplete: function(request) {
 816      if (this.options.decay) {
 817        this.decay = (request.responseText == this.lastText ?
 818          this.decay * this.options.decay : 1);
 819  
 820        this.lastText = request.responseText;
 821      }
 822      this.timer = setTimeout(this.onTimerEvent.bind(this),
 823        this.decay * this.frequency * 1000);
 824    },
 825  
 826    onTimerEvent: function() {
 827      this.updater = new Ajax.Updater(this.container, this.url, this.options);
 828    }
 829  });
 830  document.getElementsByClassName = function(className, parentElement) {
 831    var children = ($(parentElement) || document.body).getElementsByTagName('*');
 832    return $A(children).inject([], function(elements, child) {
 833      if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
 834        elements.push(child);
 835      return elements;
 836    });
 837  }
 838  
 839  /*--------------------------------------------------------------------------*/
 840  
 841  if (!window.Element) {
 842    var Element = new Object();
 843  }
 844  
 845  Object.extend(Element, {
 846    visible: function(element) {
 847      return $(element).style.display != 'none';
 848    },
 849  
 850    toggle: function() {
 851      for (var i = 0; i < arguments.length; i++) {
 852        var element = $(arguments[i]);
 853        Element[Element.visible(element) ? 'hide' : 'show'](element);
 854      }
 855    },
 856  
 857    hide: function() {
 858      for (var i = 0; i < arguments.length; i++) {
 859        var element = $(arguments[i]);
 860        element.style.display = 'none';
 861      }
 862    },
 863  
 864    show: function() {
 865      for (var i = 0; i < arguments.length; i++) {
 866        var element = $(arguments[i]);
 867        element.style.display = '';
 868      }
 869    },
 870  
 871    remove: function(element) {
 872      element = $(element);
 873      element.parentNode.removeChild(element);
 874    },
 875  
 876    update: function(element, html) {
 877      $(element).innerHTML = html.stripScripts();
 878      setTimeout(function() {html.evalScripts()}, 10);
 879    },
 880  
 881    getHeight: function(element) {
 882      element = $(element);
 883      return element.offsetHeight;
 884    },
 885  
 886    classNames: function(element) {
 887      return new Element.ClassNames(element);
 888    },
 889  
 890    hasClassName: function(element, className) {
 891      if (!(element = $(element))) return;
 892      return Element.classNames(element).include(className);
 893    },
 894  
 895    addClassName: function(element, className) {
 896      if (!(element = $(element))) return;
 897      return Element.classNames(element).add(className);
 898    },
 899  
 900    removeClassName: function(element, className) {
 901      if (!(element = $(element))) return;
 902      return Element.classNames(element).remove(className);
 903    },
 904  
 905    // removes whitespace-only text node children
 906    cleanWhitespace: function(element) {
 907      element = $(element);
 908      for (var i = 0; i < element.childNodes.length; i++) {
 909        var node = element.childNodes[i];
 910        if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
 911          Element.remove(node);
 912      }
 913    },
 914  
 915    empty: function(element) {
 916      return $(element).innerHTML.match(/^\s*$/);
 917    },
 918  
 919    scrollTo: function(element) {
 920      element = $(element);
 921      var x = element.x ? element.x : element.offsetLeft,
 922          y = element.y ? element.y : element.offsetTop;
 923      window.scrollTo(x, y);
 924    },
 925  
 926    getStyle: function(element, style) {
 927      element = $(element);
 928      var value = element.style[style.camelize()];
 929      if (!value) {
 930        if (document.defaultView && document.defaultView.getComputedStyle) {
 931          var css = document.defaultView.getComputedStyle(element, null);
 932          value = css ? css.getPropertyValue(style) : null;
 933        } else if (element.currentStyle) {
 934          value = element.currentStyle[style.camelize()];
 935        }
 936      }
 937  
 938      if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
 939        if (Element.getStyle(element, 'position') == 'static') value = 'auto';
 940  
 941      return value == 'auto' ? null : value;
 942    },
 943  
 944    setStyle: function(element, style) {
 945      element = $(element);
 946      for (name in style)
 947        element.style[name.camelize()] = style[name];
 948    },
 949  
 950    getDimensions: function(element) {
 951      element = $(element);
 952      if (Element.getStyle(element, 'display') != 'none')
 953        return {width: element.offsetWidth, height: element.offsetHeight};
 954  
 955      // All *Width and *Height properties give 0 on elements with display none,
 956      // so enable the element temporarily
 957      var els = element.style;
 958      var originalVisibility = els.visibility;
 959      var originalPosition = els.position;
 960      els.visibility = 'hidden';
 961      els.position = 'absolute';
 962      els.display = '';
 963      var originalWidth = element.clientWidth;
 964      var originalHeight = element.clientHeight;
 965      els.display = 'none';
 966      els.position = originalPosition;
 967      els.visibility = originalVisibility;
 968      return {width: originalWidth, height: originalHeight};
 969    },
 970  
 971    makePositioned: function(element) {
 972      element = $(element);
 973      var pos = Element.getStyle(element, 'position');
 974      if (pos == 'static' || !pos) {
 975        element._madePositioned = true;
 976        element.style.position = 'relative';
 977        // Opera returns the offset relative to the positioning context, when an
 978        // element is position relative but top and left have not been defined
 979        if (window.opera) {
 980          element.style.top = 0;
 981          element.style.left = 0;
 982        }
 983      }
 984    },
 985  
 986    undoPositioned: function(element) {
 987      element = $(element);
 988      if (element._madePositioned) {
 989        element._madePositioned = undefined;
 990        element.style.position =
 991          element.style.top =
 992          element.style.left =
 993          element.style.bottom =
 994          element.style.right = '';
 995      }
 996    },
 997  
 998    makeClipping: function(element) {
 999      element = $(element);
1000      if (element._overflow) return;
1001      element._overflow = element.style.overflow;
1002      if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1003        element.style.overflow = 'hidden';
1004    },
1005  
1006    undoClipping: function(element) {
1007      element = $(element);
1008      if (element._overflow) return;
1009      element.style.overflow = element._overflow;
1010      element._overflow = undefined;
1011    }
1012  });
1013  
1014  var Toggle = new Object();
1015  Toggle.display = Element.toggle;
1016  
1017  /*--------------------------------------------------------------------------*/
1018  
1019  Abstract.Insertion = function(adjacency) {
1020    this.adjacency = adjacency;
1021  }
1022  
1023  Abstract.Insertion.prototype = {
1024    initialize: function(element, content) {
1025      this.element = $(element);
1026      this.content = content.stripScripts();
1027  
1028      if (this.adjacency && this.element.insertAdjacentHTML) {
1029        try {
1030          this.element.insertAdjacentHTML(this.adjacency, this.content);
1031        } catch (e) {
1032          if (this.element.tagName.toLowerCase() == 'tbody') {
1033            this.insertContent(this.contentFromAnonymousTable());
1034          } else {
1035            throw e;
1036          }
1037        }
1038      } else {
1039        this.range = this.element.ownerDocument.createRange();
1040        if (this.initializeRange) this.initializeRange();
1041        this.insertContent([this.range.createContextualFragment(this.content)]);
1042      }
1043  
1044      setTimeout(function() {content.evalScripts()}, 10);
1045    },
1046  
1047    contentFromAnonymousTable: function() {
1048      var div = document.createElement('div');
1049      div.innerHTML = '' + this.content + '
'; 1050 return $A(div.childNodes[0].childNodes[0].childNodes); 1051 } 1052 } 1053 1054 var Insertion = new Object(); 1055 1056 Insertion.Before = Class.create(); 1057 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { 1058 initializeRange: function() { 1059 this.range.setStartBefore(this.element); 1060 }, 1061 1062 insertContent: function(fragments) { 1063 fragments.each((function(fragment) { 1064 this.element.parentNode.insertBefore(fragment, this.element); 1065 }).bind(this)); 1066 } 1067 }); 1068 1069 Insertion.Top = Class.create(); 1070 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), { 1071 initializeRange: function() { 1072 this.range.selectNodeContents(this.element); 1073 this.range.collapse(true); 1074 }, 1075 1076 insertContent: function(fragments) { 1077 fragments.reverse(false).each((function(fragment) { 1078 this.element.insertBefore(fragment, this.element.firstChild); 1079 }).bind(this)); 1080 } 1081 }); 1082 1083 Insertion.Bottom = Class.create(); 1084 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), { 1085 initializeRange: function() { 1086 this.range.selectNodeContents(this.element); 1087 this.range.collapse(this.element); 1088 }, 1089 1090 insertContent: function(fragments) { 1091 fragments.each((function(fragment) { 1092 this.element.appendChild(fragment); 1093 }).bind(this)); 1094 } 1095 }); 1096 1097 Insertion.After = Class.create(); 1098 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), { 1099 initializeRange: function() { 1100 this.range.setStartAfter(this.element); 1101 }, 1102 1103 insertContent: function(fragments) { 1104 fragments.each((function(fragment) { 1105 this.element.parentNode.insertBefore(fragment, 1106 this.element.nextSibling); 1107 }).bind(this)); 1108 } 1109 }); 1110 1111 /*--------------------------------------------------------------------------*/ 1112 1113 Element.ClassNames = Class.create(); 1114 Element.ClassNames.prototype = { 1115 initialize: function(element) { 1116 this.element = $(element); 1117 }, 1118 1119 _each: function(iterator) { 1120 this.element.className.split(/\s+/).select(function(name) { 1121 return name.length > 0; 1122 })._each(iterator); 1123 }, 1124 1125 set: function(className) { 1126 this.element.className = className; 1127 }, 1128 1129 add: function(classNameToAdd) { 1130 if (this.include(classNameToAdd)) return; 1131 this.set(this.toArray().concat(classNameToAdd).join(' ')); 1132 }, 1133 1134 remove: function(classNameToRemove) { 1135 if (!this.include(classNameToRemove)) return; 1136 this.set(this.select(function(className) { 1137 return className != classNameToRemove; 1138 }).join(' ')); 1139 }, 1140 1141 toString: function() { 1142 return this.toArray().join(' '); 1143 } 1144 } 1145 1146 Object.extend(Element.ClassNames.prototype, Enumerable); 1147 var Field = { 1148 clear: function() { 1149 for (var i = 0; i < arguments.length; i++) 1150 $(arguments[i]).value = ''; 1151 }, 1152 1153 focus: function(element) { 1154 $(element).focus(); 1155 }, 1156 1157 present: function() { 1158 for (var i = 0; i < arguments.length; i++) 1159 if ($(arguments[i]).value == '') return false; 1160 return true; 1161 }, 1162 1163 select: function(element) { 1164 $(element).select(); 1165 }, 1166 1167 activate: function(element) { 1168 element = $(element); 1169 element.focus(); 1170 if (element.select) 1171 element.select(); 1172 } 1173 } 1174 1175 /*--------------------------------------------------------------------------*/ 1176 1177 var Form = { 1178 serialize: function(form) { 1179 var elements = Form.getElements($(form)); 1180 var queryComponents = new Array(); 1181 1182 for (var i = 0; i < elements.length; i++) { 1183 var queryComponent = Form.Element.serialize(elements[i]); 1184 if (queryComponent) 1185 queryComponents.push(queryComponent); 1186 } 1187 1188 return queryComponents.join('&'); 1189 }, 1190 1191 getElements: function(form) { 1192 form = $(form); 1193 var elements = new Array(); 1194 1195 for (tagName in Form.Element.Serializers) { 1196 var tagElements = form.getElementsByTagName(tagName); 1197 for (var j = 0; j < tagElements.length; j++) 1198 elements.push(tagElements[j]); 1199 } 1200 return elements; 1201 }, 1202 1203 getInputs: function(form, typeName, name) { 1204 form = $(form); 1205 var inputs = form.getElementsByTagName('input'); 1206 1207 if (!typeName && !name) 1208 return inputs; 1209 1210 var matchingInputs = new Array(); 1211 for (var i = 0; i < inputs.length; i++) { 1212 var input = inputs[i]; 1213 if ((typeName && input.type != typeName) || 1214 (name && input.name != name)) 1215 continue; 1216 matchingInputs.push(input); 1217 } 1218 1219 return matchingInputs; 1220 }, 1221 1222 disable: function(form) { 1223 var elements = Form.getElements(form); 1224 for (var i = 0; i < elements.length; i++) { 1225 var element = elements[i]; 1226 element.blur(); 1227 element.disabled = 'true'; 1228 } 1229 }, 1230 1231 enable: function(form) { 1232 var elements = Form.getElements(form); 1233 for (var i = 0; i < elements.length; i++) { 1234 var element = elements[i]; 1235 element.disabled = ''; 1236 } 1237 }, 1238 1239 findFirstElement: function(form) { 1240 return Form.getElements(form).find(function(element) { 1241 return element.type != 'hidden' && !element.disabled && 1242 ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); 1243 }); 1244 }, 1245 1246 focusFirstElement: function(form) { 1247 Field.activate(Form.findFirstElement(form)); 1248 }, 1249 1250 reset: function(form) { 1251 $(form).reset(); 1252 } 1253 } 1254 1255 Form.Element = { 1256 serialize: function(element) { 1257 element = $(element); 1258 var method = element.tagName.toLowerCase(); 1259 var parameter = Form.Element.Serializers[method](element); 1260 1261 if (parameter) { 1262 var key = encodeURIComponent(parameter[0]); 1263 if (key.length == 0) return; 1264 1265 if (parameter[1].constructor != Array) 1266 parameter[1] = [parameter[1]]; 1267 1268 return parameter[1].map(function(value) { 1269 return key + '=' + encodeURIComponent(value); 1270 }).join('&'); 1271 } 1272 }, 1273 1274 getValue: function(element) { 1275 element = $(element); 1276 var method = element.tagName.toLowerCase(); 1277 var parameter = Form.Element.Serializers[method](element); 1278 1279 if (parameter) 1280 return parameter[1]; 1281 } 1282 } 1283 1284 Form.Element.Serializers = { 1285 input: function(element) { 1286 switch (element.type.toLowerCase()) { 1287 case 'submit': 1288 case 'hidden': 1289 case 'password': 1290 case 'text': 1291 return Form.Element.Serializers.textarea(element); 1292 case 'checkbox': 1293 case 'radio': 1294 return Form.Element.Serializers.inputSelector(element); 1295 } 1296 return false; 1297 }, 1298 1299 inputSelector: function(element) { 1300 if (element.checked) 1301 return [element.name, element.value]; 1302 }, 1303 1304 textarea: function(element) { 1305 return [element.name, element.value]; 1306 }, 1307 1308 select: function(element) { 1309 return Form.Element.Serializers[element.type == 'select-one' ? 1310 'selectOne' : 'selectMany'](element); 1311 }, 1312 1313 selectOne: function(element) { 1314 var value = '', opt, index = element.selectedIndex; 1315 if (index >= 0) { 1316 opt = element.options[index]; 1317 value = opt.value; 1318 if (!value && !('value' in opt)) 1319 value = opt.text; 1320 } 1321 return [element.name, value]; 1322 }, 1323 1324 selectMany: function(element) { 1325 var value = new Array(); 1326 for (var i = 0; i < element.length; i++) { 1327 var opt = element.options[i]; 1328 if (opt.selected) { 1329 var optValue = opt.value; 1330 if (!optValue && !('value' in opt)) 1331 optValue = opt.text; 1332 value.push(optValue); 1333 } 1334 } 1335 return [element.name, value]; 1336 } 1337 } 1338 1339 /*--------------------------------------------------------------------------*/ 1340 1341 var $F = Form.Element.getValue; 1342 1343 /*--------------------------------------------------------------------------*/ 1344 1345 Abstract.TimedObserver = function() {} 1346 Abstract.TimedObserver.prototype = { 1347 initialize: function(element, frequency, callback) { 1348 this.frequency = frequency; 1349 this.element = $(element); 1350 this.callback = callback; 1351 1352 this.lastValue = this.getValue(); 1353 this.registerCallback(); 1354 }, 1355 1356 registerCallback: function() { 1357 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); 1358 }, 1359 1360 onTimerEvent: function() { 1361 var value = this.getValue(); 1362 if (this.lastValue != value) { 1363 this.callback(this.element, value); 1364 this.lastValue = value; 1365 } 1366 } 1367 } 1368 1369 Form.Element.Observer = Class.create(); 1370 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 1371 getValue: function() { 1372 return Form.Element.getValue(this.element); 1373 } 1374 }); 1375 1376 Form.Observer = Class.create(); 1377 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), { 1378 getValue: function() { 1379 return Form.serialize(this.element); 1380 } 1381 }); 1382 1383 /*--------------------------------------------------------------------------*/ 1384 1385 Abstract.EventObserver = function() {} 1386 Abstract.EventObserver.prototype = { 1387 initialize: function(element, callback) { 1388 this.element = $(element); 1389 this.callback = callback; 1390 1391 this.lastValue = this.getValue(); 1392 if (this.element.tagName.toLowerCase() == 'form') 1393 this.registerFormCallbacks(); 1394 else 1395 this.registerCallback(this.element); 1396 }, 1397 1398 onElementEvent: function() { 1399 var value = this.getValue(); 1400 if (this.lastValue != value) { 1401 this.callback(this.element, value); 1402 this.lastValue = value; 1403 } 1404 }, 1405 1406 registerFormCallbacks: function() { 1407 var elements = Form.getElements(this.element); 1408 for (var i = 0; i < elements.length; i++) 1409 this.registerCallback(elements[i]); 1410 }, 1411 1412 registerCallback: function(element) { 1413 if (element.type) { 1414 switch (element.type.toLowerCase()) { 1415 case 'checkbox': 1416 case 'radio': 1417 Event.observe(element, 'click', this.onElementEvent.bind(this)); 1418 break; 1419 case 'password': 1420 case 'text': 1421 case 'textarea': 1422 case 'select-one': 1423 case 'select-multiple': 1424 Event.observe(element, 'change', this.onElementEvent.bind(this)); 1425 break; 1426 } 1427 } 1428 } 1429 } 1430 1431 Form.Element.EventObserver = Class.create(); 1432 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 1433 getValue: function() { 1434 return Form.Element.getValue(this.element); 1435 } 1436 }); 1437 1438 Form.EventObserver = Class.create(); 1439 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), { 1440 getValue: function() { 1441 return Form.serialize(this.element); 1442 } 1443 }); 1444 if (!window.Event) { 1445 var Event = new Object(); 1446 } 1447 1448 Object.extend(Event, { 1449 KEY_BACKSPACE: 8, 1450 KEY_TAB: 9, 1451 KEY_RETURN: 13, 1452 KEY_ESC: 27, 1453 KEY_LEFT: 37, 1454 KEY_UP: 38, 1455 KEY_RIGHT: 39, 1456 KEY_DOWN: 40, 1457 KEY_DELETE: 46, 1458 1459 element: function(event) { 1460 return event.target || event.srcElement; 1461 }, 1462 1463 isLeftClick: function(event) { 1464 return (((event.which) && (event.which 1)) || 1465 ((event.button) && (event.button 1))); 1466 }, 1467 1468 pointerX: function(event) { 1469 return event.pageX || (event.clientX + 1470 (document.documentElement.scrollLeft || document.body.scrollLeft)); 1471 }, 1472 1473 pointerY: function(event) { 1474 return event.pageY || (event.clientY + 1475 (document.documentElement.scrollTop || document.body.scrollTop)); 1476 }, 1477 1478 stop: function(event) { 1479 if (event.preventDefault) { 1480 event.preventDefault(); 1481 event.stopPropagation(); 1482 } else { 1483 event.returnValue = false; 1484 event.cancelBubble = true; 1485 } 1486 }, 1487 1488 // find the first node with the given tagName, starting from the 1489 // node the event was triggered on; traverses the DOM upwards 1490 findElement: function(event, tagName) { 1491 var element = Event.element(event); 1492 while (element.parentNode && (!element.tagName || 1493 (element.tagName.toUpperCase() != tagName.toUpperCase()))) 1494 element = element.parentNode; 1495 return element; 1496 }, 1497 1498 observers: false, 1499 1500 _observeAndCache: function(element, name, observer, useCapture) { 1501 if (!this.observers) this.observers = []; 1502 if (element.addEventListener) { 1503 this.observers.push([element, name, observer, useCapture]); 1504 element.addEventListener(name, observer, useCapture); 1505 } else if (element.attachEvent) { 1506 this.observers.push([element, name, observer, useCapture]); 1507 element.attachEvent('on' + name, observer); 1508 } 1509 }, 1510 1511 unloadCache: function() { 1512 if (!Event.observers) return; 1513 for (var i = 0; i < Event.observers.length; i++) { 1514 Event.stopObserving.apply(this, Event.observers[i]); 1515 Event.observers[i][0] = null; 1516 } 1517 Event.observers = false; 1518 }, 1519 1520 observe: function(element, name, observer, useCapture) { 1521 var element = $(element); 1522 useCapture = useCapture || false; 1523 1524 if (name == 'keypress' && 1525 (navigator.appVersion.match(/Konqueror|Safari|KHTML/) 1526 || element.attachEvent)) 1527 name = 'keydown'; 1528 1529 this._observeAndCache(element, name, observer, useCapture); 1530 }, 1531 1532 stopObserving: function(element, name, observer, useCapture) { 1533 var element = $(element); 1534 useCapture = useCapture || false; 1535 1536 if (name == 'keypress' && 1537 (navigator.appVersion.match(/Konqueror|Safari|KHTML/) 1538 || element.detachEvent)) 1539 name = 'keydown'; 1540 1541 if (element.removeEventListener) { 1542 element.removeEventListener(name, observer, useCapture); 1543 } else if (element.detachEvent) { 1544 element.detachEvent('on' + name, observer); 1545 } 1546 } 1547 }); 1548 1549 /* prevent memory leaks in IE */ 1550 Event.observe(window, 'unload', Event.unloadCache, false); 1551 var Position = { 1552 // set to true if needed, warning: firefox performance problems 1553 // NOT neeeded for page scrolling, only if draggable contained in 1554 // scrollable elements 1555 includeScrollOffsets: false, 1556 1557 // must be called before calling withinIncludingScrolloffset, every time the 1558 // page is scrolled 1559 prepare: function() { 1560 this.deltaX = window.pageXOffset 1561 || document.documentElement.scrollLeft 1562 || document.body.scrollLeft 1563 || 0; 1564 this.deltaY = window.pageYOffset 1565 || document.documentElement.scrollTop 1566 || document.body.scrollTop 1567 || 0; 1568 }, 1569 1570 realOffset: function(element) { 1571 var valueT = 0, valueL = 0; 1572 do { 1573 valueT += element.scrollTop || 0; 1574 valueL += element.scrollLeft || 0; 1575 element = element.parentNode; 1576 } while (element); 1577 return [valueL, valueT]; 1578 }, 1579 1580 cumulativeOffset: function(element) { 1581 var valueT = 0, valueL = 0; 1582 do { 1583 valueT += element.offsetTop || 0; 1584 valueL += element.offsetLeft || 0; 1585 element = element.offsetParent; 1586 } while (element); 1587 return [valueL, valueT]; 1588 }, 1589 1590 positionedOffset: function(element) { 1591 var valueT = 0, valueL = 0; 1592 do { 1593 valueT += element.offsetTop || 0; 1594 valueL += element.offsetLeft || 0; 1595 element = element.offsetParent; 1596 if (element) { 1597 p = Element.getStyle(element, 'position'); 1598 if (p 'relative' || p 'absolute') break; 1599 } 1600 } while (element); 1601 return [valueL, valueT]; 1602 }, 1603 1604 offsetParent: function(element) { 1605 if (element.offsetParent) return element.offsetParent; 1606 if (element == document.body) return element; 1607 1608 while ((element = element.parentNode) && element != document.body) 1609 if (Element.getStyle(element, 'position') != 'static') 1610 return element; 1611 1612 return document.body; 1613 }, 1614 1615 // caches x/y coordinate pair to use with overlap 1616 within: function(element, x, y) { 1617 if (this.includeScrollOffsets) 1618 return this.withinIncludingScrolloffsets(element, x, y); 1619 this.xcomp = x; 1620 this.ycomp = y; 1621 this.offset = this.cumulativeOffset(element); 1622 1623 return (y >= this.offset[1] && 1624 y < this.offset[1] + element.offsetHeight && 1625 x >= this.offset[0] && 1626 x < this.offset[0] + element.offsetWidth); 1627 }, 1628 1629 withinIncludingScrolloffsets: function(element, x, y) { 1630 var offsetcache = this.realOffset(element); 1631 1632 this.xcomp = x + offsetcache[0] - this.deltaX; 1633 this.ycomp = y + offsetcache[1] - this.deltaY; 1634 this.offset = this.cumulativeOffset(element); 1635 1636 return (this.ycomp >= this.offset[1] && 1637 this.ycomp < this.offset[1] + element.offsetHeight && 1638 this.xcomp >= this.offset[0] && 1639 this.xcomp < this.offset[0] + element.offsetWidth); 1640 }, 1641 1642 // within must be called directly before 1643 overlap: function(mode, element) { 1644 if (!mode) return 0; 1645 if (mode 'vertical') 1646 return ((this.offset[1] + element.offsetHeight) - this.ycomp) / 1647 element.offsetHeight; 1648 if (mode 'horizontal') 1649 return ((this.offset[0] + element.offsetWidth) - this.xcomp) / 1650 element.offsetWidth; 1651 }, 1652 1653 clone: function(source, target) { 1654 source = $(source); 1655 target = $(target); 1656 target.style.position = 'absolute'; 1657 var offsets = this.cumulativeOffset(source); 1658 target.style.top = offsets[1] + 'px'; 1659 target.style.left = offsets[0] + 'px'; 1660 target.style.width = source.offsetWidth + 'px'; 1661 target.style.height = source.offsetHeight + 'px'; 1662 }, 1663 1664 page: function(forElement) { 1665 var valueT = 0, valueL = 0; 1666 1667 var element = forElement; 1668 do { 1669 valueT += element.offsetTop || 0; 1670 valueL += element.offsetLeft || 0; 1671 1672 // Safari fix 1673 if (element.offsetParent==document.body) 1674 if (Element.getStyle(element,'position')=='absolute') break; 1675 1676 } while (element = element.offsetParent); 1677 1678 element = forElement; 1679 do { 1680 valueT -= element.scrollTop || 0; 1681 valueL -= element.scrollLeft || 0; 1682 } while (element = element.parentNode); 1683 1684 return [valueL, valueT]; 1685 }, 1686 1687 clone: function(source, target) { 1688 var options = Object.extend({ 1689 setLeft: true, 1690 setTop: true, 1691 setWidth: true, 1692 setHeight: true, 1693 offsetTop: 0, 1694 offsetLeft: 0 1695 }, arguments[2] || {}) 1696 1697 // find page position of source 1698 source = $(source); 1699 var p = Position.page(source); 1700 1701 // find coordinate system to use 1702 target = $(target); 1703 var delta = [0, 0]; 1704 var parent = null; 1705 // delta [0,0] will do fine with position: fixed elements, 1706 // position:absolute needs offsetParent deltas 1707 if (Element.getStyle(target,'position') == 'absolute') { 1708 parent = Position.offsetParent(target); 1709 delta = Position.page(parent); 1710 } 1711 1712 // correct by body offsets (fixes Safari) 1713 if (parent == document.body) { 1714 delta[0] -= document.body.offsetLeft; 1715 delta[1] -= document.body.offsetTop; 1716 } 1717 1718 // set position 1719 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; 1720 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; 1721 if(options.setWidth) target.style.width = source.offsetWidth + 'px'; 1722 if(options.setHeight) target.style.height = source.offsetHeight + 'px'; 1723 }, 1724 1725 absolutize: function(element) { 1726 element = $(element); 1727 if (element.style.position == 'absolute') return; 1728 Position.prepare(); 1729 1730 var offsets = Position.positionedOffset(element); 1731 var top = offsets[1]; 1732 var left = offsets[0]; 1733 var width = element.clientWidth; 1734 var height = element.clientHeight; 1735 1736 element._originalLeft = left - parseFloat(element.style.left || 0); 1737 element._originalTop = top - parseFloat(element.style.top || 0); 1738 element._originalWidth = element.style.width; 1739 element._originalHeight = element.style.height; 1740 1741 element.style.position = 'absolute'; 1742 element.style.top = top + 'px';; 1743 element.style.left = left + 'px';; 1744 element.style.width = width + 'px';; 1745 element.style.height = height + 'px';; 1746 }, 1747 1748 relativize: function(element) { 1749 element = $(element); 1750 if (element.style.position == 'relative') return; 1751 Position.prepare(); 1752 1753 element.style.position = 'relative'; 1754 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); 1755 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); 1756 1757 element.style.top = top + 'px'; 1758 element.style.left = left + 'px'; 1759 element.style.height = element._originalHeight; 1760 element.style.width = element._originalWidth; 1761 } 1762 } 1763 1764 // Safari returns margins on body which is incorrect if the child is absolutely 1765 // positioned. For performance reasons, redefine Position.cumulativeOffset for 1766 // KHTML/WebKit only. 1767 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) { 1768 Position.cumulativeOffset = function(element) { 1769 var valueT = 0, valueL = 0; 1770 do { 1771 valueT += element.offsetTop || 0; 1772 valueL += element.offsetLeft || 0; 1773 if (element.offsetParent document.body) 1774 if (Element.getStyle(element, 'position') 'absolute') break; 1775 1776 element = element.offsetParent; 1777 } while (element); 1778 1779 return [valueL, valueT]; 1780 } 1781 }