prototype.js
上传用户:wangting
上传日期:2020-01-24
资源大小:2226k
文件大小:54k
源码类别:

破解

开发平台:

ASP/ASPX

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