Special XML conversion code for Firefox >= 20.0
[infodrom/rico3] / minsrc / prototype.js
1 /*  Prototype JavaScript framework, version 1.7
2  *  (c) 2005-2010 Sam Stephenson
3  *
4  *  Prototype is freely distributable under the terms of an MIT-style license.
5  *  For details, see the Prototype web site: http://www.prototypejs.org/
6  *
7  *--------------------------------------------------------------------------*/
8
9 var Prototype = {
10
11   Version: '1.7',
12
13   Browser: (function(){
14     var ua = navigator.userAgent;
15     var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
16     return {
17       IE:             !!window.attachEvent && !isOpera,
18       Opera:          isOpera,
19       WebKit:         ua.indexOf('AppleWebKit/') > -1,
20       Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
21       MobileSafari:   /Apple.*Mobile/.test(ua)
22     }
23   })(),
24
25   BrowserFeatures: {
26     XPath: !!document.evaluate,
27
28     SelectorsAPI: !!document.querySelector,
29
30     ElementExtensions: (function() {
31       var constructor = window.Element || window.HTMLElement;
32       return !!(constructor && constructor.prototype);
33     })(),
34     SpecificElementExtensions: (function() {
35       if (typeof window.HTMLDivElement !== 'undefined')
36         return true;
37
38       var div = document.createElement('div'),
39           form = document.createElement('form'),
40           isSupported = false;
41
42       if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
43         isSupported = true;
44       }
45
46       div = form = null;
47
48       return isSupported;
49     })()
50   },
51
52   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
53   JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
54
55   emptyFunction: function() { },
56
57   K: function(x) { return x }
58 };
59
60 if (Prototype.Browser.MobileSafari)
61   Prototype.BrowserFeatures.SpecificElementExtensions = false;
62
63
64 var Abstract = { };
65
66
67 var Try = {
68   these: function() {
69     var returnValue;
70
71     for (var i = 0, length = arguments.length; i < length; i++) {
72       var lambda = arguments[i];
73       try {
74         returnValue = lambda();
75         break;
76       } catch (e) { }
77     }
78
79     return returnValue;
80   }
81 };
82
83 /* Based on Alex Arnell's inheritance implementation. */
84
85 var Class = (function() {
86
87   var IS_DONTENUM_BUGGY = (function(){
88     for (var p in { toString: 1 }) {
89       if (p === 'toString') return false;
90     }
91     return true;
92   })();
93
94   function subclass() {};
95   function create() {
96     var parent = null, properties = $A(arguments);
97     if (Object.isFunction(properties[0]))
98       parent = properties.shift();
99
100     function klass() {
101       this.initialize.apply(this, arguments);
102     }
103
104     Object.extend(klass, Class.Methods);
105     klass.superclass = parent;
106     klass.subclasses = [];
107
108     if (parent) {
109       subclass.prototype = parent.prototype;
110       klass.prototype = new subclass;
111       parent.subclasses.push(klass);
112     }
113
114     for (var i = 0, length = properties.length; i < length; i++)
115       klass.addMethods(properties[i]);
116
117     if (!klass.prototype.initialize)
118       klass.prototype.initialize = Prototype.emptyFunction;
119
120     klass.prototype.constructor = klass;
121     return klass;
122   }
123
124   function addMethods(source) {
125     var ancestor   = this.superclass && this.superclass.prototype,
126         properties = Object.keys(source);
127
128     if (IS_DONTENUM_BUGGY) {
129       if (source.toString != Object.prototype.toString)
130         properties.push("toString");
131       if (source.valueOf != Object.prototype.valueOf)
132         properties.push("valueOf");
133     }
134
135     for (var i = 0, length = properties.length; i < length; i++) {
136       var property = properties[i], value = source[property];
137       if (ancestor && Object.isFunction(value) &&
138           value.argumentNames()[0] == "$super") {
139         var method = value;
140         value = (function(m) {
141           return function() { return ancestor[m].apply(this, arguments); };
142         })(property).wrap(method);
143
144         value.valueOf = method.valueOf.bind(method);
145         value.toString = method.toString.bind(method);
146       }
147       this.prototype[property] = value;
148     }
149
150     return this;
151   }
152
153   return {
154     create: create,
155     Methods: {
156       addMethods: addMethods
157     }
158   };
159 })();
160 (function() {
161
162   var _toString = Object.prototype.toString,
163       NULL_TYPE = 'Null',
164       UNDEFINED_TYPE = 'Undefined',
165       BOOLEAN_TYPE = 'Boolean',
166       NUMBER_TYPE = 'Number',
167       STRING_TYPE = 'String',
168       OBJECT_TYPE = 'Object',
169       FUNCTION_CLASS = '[object Function]',
170       BOOLEAN_CLASS = '[object Boolean]',
171       NUMBER_CLASS = '[object Number]',
172       STRING_CLASS = '[object String]',
173       ARRAY_CLASS = '[object Array]',
174       DATE_CLASS = '[object Date]',
175       NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
176         typeof JSON.stringify === 'function' &&
177         JSON.stringify(0) === '0' &&
178         typeof JSON.stringify(Prototype.K) === 'undefined';
179
180   function Type(o) {
181     switch(o) {
182       case null: return NULL_TYPE;
183       case (void 0): return UNDEFINED_TYPE;
184     }
185     var type = typeof o;
186     switch(type) {
187       case 'boolean': return BOOLEAN_TYPE;
188       case 'number':  return NUMBER_TYPE;
189       case 'string':  return STRING_TYPE;
190     }
191     return OBJECT_TYPE;
192   }
193
194   function extend(destination, source) {
195     for (var property in source)
196       destination[property] = source[property];
197     return destination;
198   }
199
200   function inspect(object) {
201     try {
202       if (isUndefined(object)) return 'undefined';
203       if (object === null) return 'null';
204       return object.inspect ? object.inspect() : String(object);
205     } catch (e) {
206       if (e instanceof RangeError) return '...';
207       throw e;
208     }
209   }
210
211   function toJSON(value) {
212     return Str('', { '': value }, []);
213   }
214
215   function Str(key, holder, stack) {
216     var value = holder[key],
217         type = typeof value;
218
219     if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
220       value = value.toJSON(key);
221     }
222
223     var _class = _toString.call(value);
224
225     switch (_class) {
226       case NUMBER_CLASS:
227       case BOOLEAN_CLASS:
228       case STRING_CLASS:
229         value = value.valueOf();
230     }
231
232     switch (value) {
233       case null: return 'null';
234       case true: return 'true';
235       case false: return 'false';
236     }
237
238     type = typeof value;
239     switch (type) {
240       case 'string':
241         return value.inspect(true);
242       case 'number':
243         return isFinite(value) ? String(value) : 'null';
244       case 'object':
245
246         for (var i = 0, length = stack.length; i < length; i++) {
247           if (stack[i] === value) { throw new TypeError(); }
248         }
249         stack.push(value);
250
251         var partial = [];
252         if (_class === ARRAY_CLASS) {
253           for (var i = 0, length = value.length; i < length; i++) {
254             var str = Str(i, value, stack);
255             partial.push(typeof str === 'undefined' ? 'null' : str);
256           }
257           partial = '[' + partial.join(',') + ']';
258         } else {
259           var keys = Object.keys(value);
260           for (var i = 0, length = keys.length; i < length; i++) {
261             var key = keys[i], str = Str(key, value, stack);
262             if (typeof str !== "undefined") {
263                partial.push(key.inspect(true)+ ':' + str);
264              }
265           }
266           partial = '{' + partial.join(',') + '}';
267         }
268         stack.pop();
269         return partial;
270     }
271   }
272
273   function stringify(object) {
274     return JSON.stringify(object);
275   }
276
277   function toQueryString(object) {
278     return $H(object).toQueryString();
279   }
280
281   function toHTML(object) {
282     return object && object.toHTML ? object.toHTML() : String.interpret(object);
283   }
284
285   function keys(object) {
286     if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
287     var results = [];
288     for (var property in object) {
289       if (object.hasOwnProperty(property)) {
290         results.push(property);
291       }
292     }
293     return results;
294   }
295
296   function values(object) {
297     var results = [];
298     for (var property in object)
299       results.push(object[property]);
300     return results;
301   }
302
303   function clone(object) {
304     return extend({ }, object);
305   }
306
307   function isElement(object) {
308     return !!(object && object.nodeType == 1);
309   }
310
311   function isArray(object) {
312     return _toString.call(object) === ARRAY_CLASS;
313   }
314
315   var hasNativeIsArray = (typeof Array.isArray == 'function')
316     && Array.isArray([]) && !Array.isArray({});
317
318   if (hasNativeIsArray) {
319     isArray = Array.isArray;
320   }
321
322   function isHash(object) {
323     return object instanceof Hash;
324   }
325
326   function isFunction(object) {
327     return _toString.call(object) === FUNCTION_CLASS;
328   }
329
330   function isString(object) {
331     return _toString.call(object) === STRING_CLASS;
332   }
333
334   function isNumber(object) {
335     return _toString.call(object) === NUMBER_CLASS;
336   }
337
338   function isDate(object) {
339     return _toString.call(object) === DATE_CLASS;
340   }
341
342   function isUndefined(object) {
343     return typeof object === "undefined";
344   }
345
346   extend(Object, {
347     extend:        extend,
348     inspect:       inspect,
349     toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
350     toQueryString: toQueryString,
351     toHTML:        toHTML,
352     keys:          Object.keys || keys,
353     values:        values,
354     clone:         clone,
355     isElement:     isElement,
356     isArray:       isArray,
357     isHash:        isHash,
358     isFunction:    isFunction,
359     isString:      isString,
360     isNumber:      isNumber,
361     isDate:        isDate,
362     isUndefined:   isUndefined
363   });
364 })();
365 Object.extend(Function.prototype, (function() {
366   var slice = Array.prototype.slice;
367
368   function update(array, args) {
369     var arrayLength = array.length, length = args.length;
370     while (length--) array[arrayLength + length] = args[length];
371     return array;
372   }
373
374   function merge(array, args) {
375     array = slice.call(array, 0);
376     return update(array, args);
377   }
378
379   function argumentNames() {
380     var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
381       .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
382       .replace(/\s+/g, '').split(',');
383     return names.length == 1 && !names[0] ? [] : names;
384   }
385
386   function bind(context) {
387     if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
388     var __method = this, args = slice.call(arguments, 1);
389     return function() {
390       var a = merge(args, arguments);
391       return __method.apply(context, a);
392     }
393   }
394
395   function bindAsEventListener(context) {
396     var __method = this, args = slice.call(arguments, 1);
397     return function(event) {
398       var a = update([event || window.event], args);
399       return __method.apply(context, a);
400     }
401   }
402
403   function curry() {
404     if (!arguments.length) return this;
405     var __method = this, args = slice.call(arguments, 0);
406     return function() {
407       var a = merge(args, arguments);
408       return __method.apply(this, a);
409     }
410   }
411
412   function delay(timeout) {
413     var __method = this, args = slice.call(arguments, 1);
414     timeout = timeout * 1000;
415     return window.setTimeout(function() {
416       return __method.apply(__method, args);
417     }, timeout);
418   }
419
420   function defer() {
421     var args = update([0.01], arguments);
422     return this.delay.apply(this, args);
423   }
424
425   function wrap(wrapper) {
426     var __method = this;
427     return function() {
428       var a = update([__method.bind(this)], arguments);
429       return wrapper.apply(this, a);
430     }
431   }
432
433   function methodize() {
434     if (this._methodized) return this._methodized;
435     var __method = this;
436     return this._methodized = function() {
437       var a = update([this], arguments);
438       return __method.apply(null, a);
439     };
440   }
441
442   return {
443     argumentNames:       argumentNames,
444     bind:                bind,
445     bindAsEventListener: bindAsEventListener,
446     curry:               curry,
447     delay:               delay,
448     defer:               defer,
449     wrap:                wrap,
450     methodize:           methodize
451   }
452 })());
453
454
455
456 (function(proto) {
457
458
459   function toISOString() {
460     return this.getUTCFullYear() + '-' +
461       (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
462       this.getUTCDate().toPaddedString(2) + 'T' +
463       this.getUTCHours().toPaddedString(2) + ':' +
464       this.getUTCMinutes().toPaddedString(2) + ':' +
465       this.getUTCSeconds().toPaddedString(2) + 'Z';
466   }
467
468
469   function toJSON() {
470     return this.toISOString();
471   }
472
473   if (!proto.toISOString) proto.toISOString = toISOString;
474   if (!proto.toJSON) proto.toJSON = toJSON;
475
476 })(Date.prototype);
477
478
479 RegExp.prototype.match = RegExp.prototype.test;
480
481 RegExp.escape = function(str) {
482   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
483 };
484 var PeriodicalExecuter = Class.create({
485   initialize: function(callback, frequency) {
486     this.callback = callback;
487     this.frequency = frequency;
488     this.currentlyExecuting = false;
489
490     this.registerCallback();
491   },
492
493   registerCallback: function() {
494     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
495   },
496
497   execute: function() {
498     this.callback(this);
499   },
500
501   stop: function() {
502     if (!this.timer) return;
503     clearInterval(this.timer);
504     this.timer = null;
505   },
506
507   onTimerEvent: function() {
508     if (!this.currentlyExecuting) {
509       try {
510         this.currentlyExecuting = true;
511         this.execute();
512         this.currentlyExecuting = false;
513       } catch(e) {
514         this.currentlyExecuting = false;
515         throw e;
516       }
517     }
518   }
519 });
520 Object.extend(String, {
521   interpret: function(value) {
522     return value == null ? '' : String(value);
523   },
524   specialChar: {
525     '\b': '\\b',
526     '\t': '\\t',
527     '\n': '\\n',
528     '\f': '\\f',
529     '\r': '\\r',
530     '\\': '\\\\'
531   }
532 });
533
534 Object.extend(String.prototype, (function() {
535   var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
536     typeof JSON.parse === 'function' &&
537     JSON.parse('{"test": true}').test;
538
539   function prepareReplacement(replacement) {
540     if (Object.isFunction(replacement)) return replacement;
541     var template = new Template(replacement);
542     return function(match) { return template.evaluate(match) };
543   }
544
545   function gsub(pattern, replacement) {
546     var result = '', source = this, match;
547     replacement = prepareReplacement(replacement);
548
549     if (Object.isString(pattern))
550       pattern = RegExp.escape(pattern);
551
552     if (!(pattern.length || pattern.source)) {
553       replacement = replacement('');
554       return replacement + source.split('').join(replacement) + replacement;
555     }
556
557     while (source.length > 0) {
558       if (match = source.match(pattern)) {
559         result += source.slice(0, match.index);
560         result += String.interpret(replacement(match));
561         source  = source.slice(match.index + match[0].length);
562       } else {
563         result += source, source = '';
564       }
565     }
566     return result;
567   }
568
569   function sub(pattern, replacement, count) {
570     replacement = prepareReplacement(replacement);
571     count = Object.isUndefined(count) ? 1 : count;
572
573     return this.gsub(pattern, function(match) {
574       if (--count < 0) return match[0];
575       return replacement(match);
576     });
577   }
578
579   function scan(pattern, iterator) {
580     this.gsub(pattern, iterator);
581     return String(this);
582   }
583
584   function truncate(length, truncation) {
585     length = length || 30;
586     truncation = Object.isUndefined(truncation) ? '...' : truncation;
587     return this.length > length ?
588       this.slice(0, length - truncation.length) + truncation : String(this);
589   }
590
591   function strip() {
592     return this.replace(/^\s+/, '').replace(/\s+$/, '');
593   }
594
595   function stripTags() {
596     return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
597   }
598
599   function stripScripts() {
600     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
601   }
602
603   function extractScripts() {
604     var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
605         matchOne = new RegExp(Prototype.ScriptFragment, 'im');
606     return (this.match(matchAll) || []).map(function(scriptTag) {
607       return (scriptTag.match(matchOne) || ['', ''])[1];
608     });
609   }
610
611   function evalScripts() {
612     return this.extractScripts().map(function(script) { return eval(script) });
613   }
614
615   function escapeHTML() {
616     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
617   }
618
619   function unescapeHTML() {
620     return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
621   }
622
623
624   function toQueryParams(separator) {
625     var match = this.strip().match(/([^?#]*)(#.*)?$/);
626     if (!match) return { };
627
628     return match[1].split(separator || '&').inject({ }, function(hash, pair) {
629       if ((pair = pair.split('='))[0]) {
630         var key = decodeURIComponent(pair.shift()),
631             value = pair.length > 1 ? pair.join('=') : pair[0];
632
633         if (value != undefined) value = decodeURIComponent(value);
634
635         if (key in hash) {
636           if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
637           hash[key].push(value);
638         }
639         else hash[key] = value;
640       }
641       return hash;
642     });
643   }
644
645   function toArray() {
646     return this.split('');
647   }
648
649   function succ() {
650     return this.slice(0, this.length - 1) +
651       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
652   }
653
654   function times(count) {
655     return count < 1 ? '' : new Array(count + 1).join(this);
656   }
657
658   function camelize() {
659     return this.replace(/-+(.)?/g, function(match, chr) {
660       return chr ? chr.toUpperCase() : '';
661     });
662   }
663
664   function capitalize() {
665     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
666   }
667
668   function underscore() {
669     return this.replace(/::/g, '/')
670                .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
671                .replace(/([a-z\d])([A-Z])/g, '$1_$2')
672                .replace(/-/g, '_')
673                .toLowerCase();
674   }
675
676   function dasherize() {
677     return this.replace(/_/g, '-');
678   }
679
680   function inspect(useDoubleQuotes) {
681     var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
682       if (character in String.specialChar) {
683         return String.specialChar[character];
684       }
685       return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
686     });
687     if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
688     return "'" + escapedString.replace(/'/g, '\\\'') + "'";
689   }
690
691   function unfilterJSON(filter) {
692     return this.replace(filter || Prototype.JSONFilter, '$1');
693   }
694
695   function isJSON() {
696     var str = this;
697     if (str.blank()) return false;
698     str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
699     str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
700     str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
701     return (/^[\],:{}\s]*$/).test(str);
702   }
703
704   function evalJSON(sanitize) {
705     var json = this.unfilterJSON(),
706         cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
707     if (cx.test(json)) {
708       json = json.replace(cx, function (a) {
709         return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
710       });
711     }
712     try {
713       if (!sanitize || json.isJSON()) return eval('(' + json + ')');
714     } catch (e) { }
715     throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
716   }
717
718   function parseJSON() {
719     var json = this.unfilterJSON();
720     return JSON.parse(json);
721   }
722
723   function include(pattern) {
724     return this.indexOf(pattern) > -1;
725   }
726
727   function startsWith(pattern) {
728     return this.lastIndexOf(pattern, 0) === 0;
729   }
730
731   function endsWith(pattern) {
732     var d = this.length - pattern.length;
733     return d >= 0 && this.indexOf(pattern, d) === d;
734   }
735
736   function empty() {
737     return this == '';
738   }
739
740   function blank() {
741     return /^\s*$/.test(this);
742   }
743
744   function interpolate(object, pattern) {
745     return new Template(this, pattern).evaluate(object);
746   }
747
748   return {
749     gsub:           gsub,
750     sub:            sub,
751     scan:           scan,
752     truncate:       truncate,
753     strip:          String.prototype.trim || strip,
754     stripTags:      stripTags,
755     stripScripts:   stripScripts,
756     extractScripts: extractScripts,
757     evalScripts:    evalScripts,
758     escapeHTML:     escapeHTML,
759     unescapeHTML:   unescapeHTML,
760     toQueryParams:  toQueryParams,
761     parseQuery:     toQueryParams,
762     toArray:        toArray,
763     succ:           succ,
764     times:          times,
765     camelize:       camelize,
766     capitalize:     capitalize,
767     underscore:     underscore,
768     dasherize:      dasherize,
769     inspect:        inspect,
770     unfilterJSON:   unfilterJSON,
771     isJSON:         isJSON,
772     evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
773     include:        include,
774     startsWith:     startsWith,
775     endsWith:       endsWith,
776     empty:          empty,
777     blank:          blank,
778     interpolate:    interpolate
779   };
780 })());
781
782 var Template = Class.create({
783   initialize: function(template, pattern) {
784     this.template = template.toString();
785     this.pattern = pattern || Template.Pattern;
786   },
787
788   evaluate: function(object) {
789     if (object && Object.isFunction(object.toTemplateReplacements))
790       object = object.toTemplateReplacements();
791
792     return this.template.gsub(this.pattern, function(match) {
793       if (object == null) return (match[1] + '');
794
795       var before = match[1] || '';
796       if (before == '\\') return match[2];
797
798       var ctx = object, expr = match[3],
799           pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
800
801       match = pattern.exec(expr);
802       if (match == null) return before;
803
804       while (match != null) {
805         var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
806         ctx = ctx[comp];
807         if (null == ctx || '' == match[3]) break;
808         expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
809         match = pattern.exec(expr);
810       }
811
812       return before + String.interpret(ctx);
813     });
814   }
815 });
816 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
817
818 var $break = { };
819
820 var Enumerable = (function() {
821   function each(iterator, context) {
822     var index = 0;
823     try {
824       this._each(function(value) {
825         iterator.call(context, value, index++);
826       });
827     } catch (e) {
828       if (e != $break) throw e;
829     }
830     return this;
831   }
832
833   function eachSlice(number, iterator, context) {
834     var index = -number, slices = [], array = this.toArray();
835     if (number < 1) return array;
836     while ((index += number) < array.length)
837       slices.push(array.slice(index, index+number));
838     return slices.collect(iterator, context);
839   }
840
841   function all(iterator, context) {
842     iterator = iterator || Prototype.K;
843     var result = true;
844     this.each(function(value, index) {
845       result = result && !!iterator.call(context, value, index);
846       if (!result) throw $break;
847     });
848     return result;
849   }
850
851   function any(iterator, context) {
852     iterator = iterator || Prototype.K;
853     var result = false;
854     this.each(function(value, index) {
855       if (result = !!iterator.call(context, value, index))
856         throw $break;
857     });
858     return result;
859   }
860
861   function collect(iterator, context) {
862     iterator = iterator || Prototype.K;
863     var results = [];
864     this.each(function(value, index) {
865       results.push(iterator.call(context, value, index));
866     });
867     return results;
868   }
869
870   function detect(iterator, context) {
871     var result;
872     this.each(function(value, index) {
873       if (iterator.call(context, value, index)) {
874         result = value;
875         throw $break;
876       }
877     });
878     return result;
879   }
880
881   function findAll(iterator, context) {
882     var results = [];
883     this.each(function(value, index) {
884       if (iterator.call(context, value, index))
885         results.push(value);
886     });
887     return results;
888   }
889
890   function grep(filter, iterator, context) {
891     iterator = iterator || Prototype.K;
892     var results = [];
893
894     if (Object.isString(filter))
895       filter = new RegExp(RegExp.escape(filter));
896
897     this.each(function(value, index) {
898       if (filter.match(value))
899         results.push(iterator.call(context, value, index));
900     });
901     return results;
902   }
903
904   function include(object) {
905     if (Object.isFunction(this.indexOf))
906       if (this.indexOf(object) != -1) return true;
907
908     var found = false;
909     this.each(function(value) {
910       if (value == object) {
911         found = true;
912         throw $break;
913       }
914     });
915     return found;
916   }
917
918   function inGroupsOf(number, fillWith) {
919     fillWith = Object.isUndefined(fillWith) ? null : fillWith;
920     return this.eachSlice(number, function(slice) {
921       while(slice.length < number) slice.push(fillWith);
922       return slice;
923     });
924   }
925
926   function inject(memo, iterator, context) {
927     this.each(function(value, index) {
928       memo = iterator.call(context, memo, value, index);
929     });
930     return memo;
931   }
932
933   function invoke(method) {
934     var args = $A(arguments).slice(1);
935     return this.map(function(value) {
936       return value[method].apply(value, args);
937     });
938   }
939
940   function max(iterator, context) {
941     iterator = iterator || Prototype.K;
942     var result;
943     this.each(function(value, index) {
944       value = iterator.call(context, value, index);
945       if (result == null || value >= result)
946         result = value;
947     });
948     return result;
949   }
950
951   function min(iterator, context) {
952     iterator = iterator || Prototype.K;
953     var result;
954     this.each(function(value, index) {
955       value = iterator.call(context, value, index);
956       if (result == null || value < result)
957         result = value;
958     });
959     return result;
960   }
961
962   function partition(iterator, context) {
963     iterator = iterator || Prototype.K;
964     var trues = [], falses = [];
965     this.each(function(value, index) {
966       (iterator.call(context, value, index) ?
967         trues : falses).push(value);
968     });
969     return [trues, falses];
970   }
971
972   function pluck(property) {
973     var results = [];
974     this.each(function(value) {
975       results.push(value[property]);
976     });
977     return results;
978   }
979
980   function reject(iterator, context) {
981     var results = [];
982     this.each(function(value, index) {
983       if (!iterator.call(context, value, index))
984         results.push(value);
985     });
986     return results;
987   }
988
989   function sortBy(iterator, context) {
990     return this.map(function(value, index) {
991       return {
992         value: value,
993         criteria: iterator.call(context, value, index)
994       };
995     }).sort(function(left, right) {
996       var a = left.criteria, b = right.criteria;
997       return a < b ? -1 : a > b ? 1 : 0;
998     }).pluck('value');
999   }
1000
1001   function toArray() {
1002     return this.map();
1003   }
1004
1005   function zip() {
1006     var iterator = Prototype.K, args = $A(arguments);
1007     if (Object.isFunction(args.last()))
1008       iterator = args.pop();
1009
1010     var collections = [this].concat(args).map($A);
1011     return this.map(function(value, index) {
1012       return iterator(collections.pluck(index));
1013     });
1014   }
1015
1016   function size() {
1017     return this.toArray().length;
1018   }
1019
1020   function inspect() {
1021     return '#<Enumerable:' + this.toArray().inspect() + '>';
1022   }
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032   return {
1033     each:       each,
1034     eachSlice:  eachSlice,
1035     all:        all,
1036     every:      all,
1037     any:        any,
1038     some:       any,
1039     collect:    collect,
1040     map:        collect,
1041     detect:     detect,
1042     findAll:    findAll,
1043     select:     findAll,
1044     filter:     findAll,
1045     grep:       grep,
1046     include:    include,
1047     member:     include,
1048     inGroupsOf: inGroupsOf,
1049     inject:     inject,
1050     invoke:     invoke,
1051     max:        max,
1052     min:        min,
1053     partition:  partition,
1054     pluck:      pluck,
1055     reject:     reject,
1056     sortBy:     sortBy,
1057     toArray:    toArray,
1058     entries:    toArray,
1059     zip:        zip,
1060     size:       size,
1061     inspect:    inspect,
1062     find:       detect
1063   };
1064 })();
1065
1066 function $A(iterable) {
1067   if (!iterable) return [];
1068   if ('toArray' in Object(iterable)) return iterable.toArray();
1069   var length = iterable.length || 0, results = new Array(length);
1070   while (length--) results[length] = iterable[length];
1071   return results;
1072 }
1073
1074
1075 function $w(string) {
1076   if (!Object.isString(string)) return [];
1077   string = string.strip();
1078   return string ? string.split(/\s+/) : [];
1079 }
1080
1081 Array.from = $A;
1082
1083
1084 (function() {
1085   var arrayProto = Array.prototype,
1086       slice = arrayProto.slice,
1087       _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1088
1089   function each(iterator, context) {
1090     for (var i = 0, length = this.length >>> 0; i < length; i++) {
1091       if (i in this) iterator.call(context, this[i], i, this);
1092     }
1093   }
1094   if (!_each) _each = each;
1095
1096   function clear() {
1097     this.length = 0;
1098     return this;
1099   }
1100
1101   function first() {
1102     return this[0];
1103   }
1104
1105   function last() {
1106     return this[this.length - 1];
1107   }
1108
1109   function compact() {
1110     return this.select(function(value) {
1111       return value != null;
1112     });
1113   }
1114
1115   function flatten() {
1116     return this.inject([], function(array, value) {
1117       if (Object.isArray(value))
1118         return array.concat(value.flatten());
1119       array.push(value);
1120       return array;
1121     });
1122   }
1123
1124   function without() {
1125     var values = slice.call(arguments, 0);
1126     return this.select(function(value) {
1127       return !values.include(value);
1128     });
1129   }
1130
1131   function reverse(inline) {
1132     return (inline === false ? this.toArray() : this)._reverse();
1133   }
1134
1135   function uniq(sorted) {
1136     return this.inject([], function(array, value, index) {
1137       if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1138         array.push(value);
1139       return array;
1140     });
1141   }
1142
1143   function intersect(array) {
1144     return this.uniq().findAll(function(item) {
1145       return array.detect(function(value) { return item === value });
1146     });
1147   }
1148
1149
1150   function clone() {
1151     return slice.call(this, 0);
1152   }
1153
1154   function size() {
1155     return this.length;
1156   }
1157
1158   function inspect() {
1159     return '[' + this.map(Object.inspect).join(', ') + ']';
1160   }
1161
1162   function indexOf(item, i) {
1163     i || (i = 0);
1164     var length = this.length;
1165     if (i < 0) i = length + i;
1166     for (; i < length; i++)
1167       if (this[i] === item) return i;
1168     return -1;
1169   }
1170
1171   function lastIndexOf(item, i) {
1172     i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1173     var n = this.slice(0, i).reverse().indexOf(item);
1174     return (n < 0) ? n : i - n - 1;
1175   }
1176
1177   function concat() {
1178     var array = slice.call(this, 0), item;
1179     for (var i = 0, length = arguments.length; i < length; i++) {
1180       item = arguments[i];
1181       if (Object.isArray(item) && !('callee' in item)) {
1182         for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1183           array.push(item[j]);
1184       } else {
1185         array.push(item);
1186       }
1187     }
1188     return array;
1189   }
1190
1191   Object.extend(arrayProto, Enumerable);
1192
1193   if (!arrayProto._reverse)
1194     arrayProto._reverse = arrayProto.reverse;
1195
1196   Object.extend(arrayProto, {
1197     _each:     _each,
1198     clear:     clear,
1199     first:     first,
1200     last:      last,
1201     compact:   compact,
1202     flatten:   flatten,
1203     without:   without,
1204     reverse:   reverse,
1205     uniq:      uniq,
1206     intersect: intersect,
1207     clone:     clone,
1208     toArray:   clone,
1209     size:      size,
1210     inspect:   inspect
1211   });
1212
1213   var CONCAT_ARGUMENTS_BUGGY = (function() {
1214     return [].concat(arguments)[0][0] !== 1;
1215   })(1,2)
1216
1217   if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1218
1219   if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1220   if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1221 })();
1222 function $H(object) {
1223   return new Hash(object);
1224 };
1225
1226 var Hash = Class.create(Enumerable, (function() {
1227   function initialize(object) {
1228     this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1229   }
1230
1231
1232   function _each(iterator) {
1233     for (var key in this._object) {
1234       var value = this._object[key], pair = [key, value];
1235       pair.key = key;
1236       pair.value = value;
1237       iterator(pair);
1238     }
1239   }
1240
1241   function set(key, value) {
1242     return this._object[key] = value;
1243   }
1244
1245   function get(key) {
1246     if (this._object[key] !== Object.prototype[key])
1247       return this._object[key];
1248   }
1249
1250   function unset(key) {
1251     var value = this._object[key];
1252     delete this._object[key];
1253     return value;
1254   }
1255
1256   function toObject() {
1257     return Object.clone(this._object);
1258   }
1259
1260
1261
1262   function keys() {
1263     return this.pluck('key');
1264   }
1265
1266   function values() {
1267     return this.pluck('value');
1268   }
1269
1270   function index(value) {
1271     var match = this.detect(function(pair) {
1272       return pair.value === value;
1273     });
1274     return match && match.key;
1275   }
1276
1277   function merge(object) {
1278     return this.clone().update(object);
1279   }
1280
1281   function update(object) {
1282     return new Hash(object).inject(this, function(result, pair) {
1283       result.set(pair.key, pair.value);
1284       return result;
1285     });
1286   }
1287
1288   function toQueryPair(key, value) {
1289     if (Object.isUndefined(value)) return key;
1290     return key + '=' + encodeURIComponent(String.interpret(value));
1291   }
1292
1293   function toQueryString() {
1294     return this.inject([], function(results, pair) {
1295       var key = encodeURIComponent(pair.key), values = pair.value;
1296
1297       if (values && typeof values == 'object') {
1298         if (Object.isArray(values)) {
1299           var queryValues = [];
1300           for (var i = 0, len = values.length, value; i < len; i++) {
1301             value = values[i];
1302             queryValues.push(toQueryPair(key, value));
1303           }
1304           return results.concat(queryValues);
1305         }
1306       } else results.push(toQueryPair(key, values));
1307       return results;
1308     }).join('&');
1309   }
1310
1311   function inspect() {
1312     return '#<Hash:{' + this.map(function(pair) {
1313       return pair.map(Object.inspect).join(': ');
1314     }).join(', ') + '}>';
1315   }
1316
1317   function clone() {
1318     return new Hash(this);
1319   }
1320
1321   return {
1322     initialize:             initialize,
1323     _each:                  _each,
1324     set:                    set,
1325     get:                    get,
1326     unset:                  unset,
1327     toObject:               toObject,
1328     toTemplateReplacements: toObject,
1329     keys:                   keys,
1330     values:                 values,
1331     index:                  index,
1332     merge:                  merge,
1333     update:                 update,
1334     toQueryString:          toQueryString,
1335     inspect:                inspect,
1336     toJSON:                 toObject,
1337     clone:                  clone
1338   };
1339 })());
1340
1341 Hash.from = $H;
1342 Object.extend(Number.prototype, (function() {
1343   function toColorPart() {
1344     return this.toPaddedString(2, 16);
1345   }
1346
1347   function succ() {
1348     return this + 1;
1349   }
1350
1351   function times(iterator, context) {
1352     $R(0, this, true).each(iterator, context);
1353     return this;
1354   }
1355
1356   function toPaddedString(length, radix) {
1357     var string = this.toString(radix || 10);
1358     return '0'.times(length - string.length) + string;
1359   }
1360
1361   function abs() {
1362     return Math.abs(this);
1363   }
1364
1365   function round() {
1366     return Math.round(this);
1367   }
1368
1369   function ceil() {
1370     return Math.ceil(this);
1371   }
1372
1373   function floor() {
1374     return Math.floor(this);
1375   }
1376
1377   return {
1378     toColorPart:    toColorPart,
1379     succ:           succ,
1380     times:          times,
1381     toPaddedString: toPaddedString,
1382     abs:            abs,
1383     round:          round,
1384     ceil:           ceil,
1385     floor:          floor
1386   };
1387 })());
1388
1389 function $R(start, end, exclusive) {
1390   return new ObjectRange(start, end, exclusive);
1391 }
1392
1393 var ObjectRange = Class.create(Enumerable, (function() {
1394   function initialize(start, end, exclusive) {
1395     this.start = start;
1396     this.end = end;
1397     this.exclusive = exclusive;
1398   }
1399
1400   function _each(iterator) {
1401     var value = this.start;
1402     while (this.include(value)) {
1403       iterator(value);
1404       value = value.succ();
1405     }
1406   }
1407
1408   function include(value) {
1409     if (value < this.start)
1410       return false;
1411     if (this.exclusive)
1412       return value < this.end;
1413     return value <= this.end;
1414   }
1415
1416   return {
1417     initialize: initialize,
1418     _each:      _each,
1419     include:    include
1420   };
1421 })());
1422
1423
1424
1425 var Ajax = {
1426   getTransport: function() {
1427     return Try.these(
1428       function() {return new XMLHttpRequest()},
1429       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1430       function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1431     ) || false;
1432   },
1433
1434   activeRequestCount: 0
1435 };
1436
1437 Ajax.Responders = {
1438   responders: [],
1439
1440   _each: function(iterator) {
1441     this.responders._each(iterator);
1442   },
1443
1444   register: function(responder) {
1445     if (!this.include(responder))
1446       this.responders.push(responder);
1447   },
1448
1449   unregister: function(responder) {
1450     this.responders = this.responders.without(responder);
1451   },
1452
1453   dispatch: function(callback, request, transport, json) {
1454     this.each(function(responder) {
1455       if (Object.isFunction(responder[callback])) {
1456         try {
1457           responder[callback].apply(responder, [request, transport, json]);
1458         } catch (e) { }
1459       }
1460     });
1461   }
1462 };
1463
1464 Object.extend(Ajax.Responders, Enumerable);
1465
1466 Ajax.Responders.register({
1467   onCreate:   function() { Ajax.activeRequestCount++ },
1468   onComplete: function() { Ajax.activeRequestCount-- }
1469 });
1470 Ajax.Base = Class.create({
1471   initialize: function(options) {
1472     this.options = {
1473       method:       'post',
1474       asynchronous: true,
1475       contentType:  'application/x-www-form-urlencoded',
1476       encoding:     'UTF-8',
1477       parameters:   '',
1478       evalJSON:     true,
1479       evalJS:       true
1480     };
1481     Object.extend(this.options, options || { });
1482
1483     this.options.method = this.options.method.toLowerCase();
1484
1485     if (Object.isHash(this.options.parameters))
1486       this.options.parameters = this.options.parameters.toObject();
1487   }
1488 });
1489 Ajax.Request = Class.create(Ajax.Base, {
1490   _complete: false,
1491
1492   initialize: function($super, url, options) {
1493     $super(options);
1494     this.transport = Ajax.getTransport();
1495     this.request(url);
1496   },
1497
1498   request: function(url) {
1499     this.url = url;
1500     this.method = this.options.method;
1501     var params = Object.isString(this.options.parameters) ?
1502           this.options.parameters :
1503           Object.toQueryString(this.options.parameters);
1504
1505     if (!['get', 'post'].include(this.method)) {
1506       params += (params ? '&' : '') + "_method=" + this.method;
1507       this.method = 'post';
1508     }
1509
1510     if (params && this.method === 'get') {
1511       this.url += (this.url.include('?') ? '&' : '?') + params;
1512     }
1513
1514     this.parameters = params.toQueryParams();
1515
1516     try {
1517       var response = new Ajax.Response(this);
1518       if (this.options.onCreate) this.options.onCreate(response);
1519       Ajax.Responders.dispatch('onCreate', this, response);
1520
1521       this.transport.open(this.method.toUpperCase(), this.url,
1522         this.options.asynchronous);
1523
1524       if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1525
1526       this.transport.onreadystatechange = this.onStateChange.bind(this);
1527       this.setRequestHeaders();
1528
1529       this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1530       this.transport.send(this.body);
1531
1532       /* Force Firefox to handle ready state 4 for synchronous requests */
1533       if (!this.options.asynchronous && this.transport.overrideMimeType)
1534         this.onStateChange();
1535
1536     }
1537     catch (e) {
1538       this.dispatchException(e);
1539     }
1540   },
1541
1542   onStateChange: function() {
1543     var readyState = this.transport.readyState;
1544     if (readyState > 1 && !((readyState == 4) && this._complete))
1545       this.respondToReadyState(this.transport.readyState);
1546   },
1547
1548   setRequestHeaders: function() {
1549     var headers = {
1550       'X-Requested-With': 'XMLHttpRequest',
1551       'X-Prototype-Version': Prototype.Version,
1552       'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1553     };
1554
1555     if (this.method == 'post') {
1556       headers['Content-type'] = this.options.contentType +
1557         (this.options.encoding ? '; charset=' + this.options.encoding : '');
1558
1559       /* Force "Connection: close" for older Mozilla browsers to work
1560        * around a bug where XMLHttpRequest sends an incorrect
1561        * Content-length header. See Mozilla Bugzilla #246651.
1562        */
1563       if (this.transport.overrideMimeType &&
1564           (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1565             headers['Connection'] = 'close';
1566     }
1567
1568     if (typeof this.options.requestHeaders == 'object') {
1569       var extras = this.options.requestHeaders;
1570
1571       if (Object.isFunction(extras.push))
1572         for (var i = 0, length = extras.length; i < length; i += 2)
1573           headers[extras[i]] = extras[i+1];
1574       else
1575         $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1576     }
1577
1578     for (var name in headers)
1579       this.transport.setRequestHeader(name, headers[name]);
1580   },
1581
1582   success: function() {
1583     var status = this.getStatus();
1584     return !status || (status >= 200 && status < 300) || status == 304;
1585   },
1586
1587   getStatus: function() {
1588     try {
1589       if (this.transport.status === 1223) return 204;
1590       return this.transport.status || 0;
1591     } catch (e) { return 0 }
1592   },
1593
1594   respondToReadyState: function(readyState) {
1595     var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1596
1597     if (state == 'Complete') {
1598       try {
1599         this._complete = true;
1600         (this.options['on' + response.status]
1601          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1602          || Prototype.emptyFunction)(response, response.headerJSON);
1603       } catch (e) {
1604         this.dispatchException(e);
1605       }
1606
1607       var contentType = response.getHeader('Content-type');
1608       if (this.options.evalJS == 'force'
1609           || (this.options.evalJS && this.isSameOrigin() && contentType
1610           && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1611         this.evalResponse();
1612     }
1613
1614     try {
1615       (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1616       Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1617     } catch (e) {
1618       this.dispatchException(e);
1619     }
1620
1621     if (state == 'Complete') {
1622       this.transport.onreadystatechange = Prototype.emptyFunction;
1623     }
1624   },
1625
1626   isSameOrigin: function() {
1627     var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1628     return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1629       protocol: location.protocol,
1630       domain: document.domain,
1631       port: location.port ? ':' + location.port : ''
1632     }));
1633   },
1634
1635   getHeader: function(name) {
1636     try {
1637       return this.transport.getResponseHeader(name) || null;
1638     } catch (e) { return null; }
1639   },
1640
1641   evalResponse: function() {
1642     try {
1643       return eval((this.transport.responseText || '').unfilterJSON());
1644     } catch (e) {
1645       this.dispatchException(e);
1646     }
1647   },
1648
1649   dispatchException: function(exception) {
1650     (this.options.onException || Prototype.emptyFunction)(this, exception);
1651     Ajax.Responders.dispatch('onException', this, exception);
1652   }
1653 });
1654
1655 Ajax.Request.Events =
1656   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1657
1658
1659
1660
1661
1662
1663
1664
1665 Ajax.Response = Class.create({
1666   initialize: function(request){
1667     this.request = request;
1668     var transport  = this.transport  = request.transport,
1669         readyState = this.readyState = transport.readyState;
1670
1671     if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1672       this.status       = this.getStatus();
1673       this.statusText   = this.getStatusText();
1674       this.responseText = String.interpret(transport.responseText);
1675       this.headerJSON   = this._getHeaderJSON();
1676     }
1677
1678     if (readyState == 4) {
1679       var xml = transport.responseXML;
1680       this.responseXML  = Object.isUndefined(xml) ? null : xml;
1681       this.responseJSON = this._getResponseJSON();
1682     }
1683   },
1684
1685   status:      0,
1686
1687   statusText: '',
1688
1689   getStatus: Ajax.Request.prototype.getStatus,
1690
1691   getStatusText: function() {
1692     try {
1693       return this.transport.statusText || '';
1694     } catch (e) { return '' }
1695   },
1696
1697   getHeader: Ajax.Request.prototype.getHeader,
1698
1699   getAllHeaders: function() {
1700     try {
1701       return this.getAllResponseHeaders();
1702     } catch (e) { return null }
1703   },
1704
1705   getResponseHeader: function(name) {
1706     return this.transport.getResponseHeader(name);
1707   },
1708
1709   getAllResponseHeaders: function() {
1710     return this.transport.getAllResponseHeaders();
1711   },
1712
1713   _getHeaderJSON: function() {
1714     var json = this.getHeader('X-JSON');
1715     if (!json) return null;
1716     json = decodeURIComponent(escape(json));
1717     try {
1718       return json.evalJSON(this.request.options.sanitizeJSON ||
1719         !this.request.isSameOrigin());
1720     } catch (e) {
1721       this.request.dispatchException(e);
1722     }
1723   },
1724
1725   _getResponseJSON: function() {
1726     var options = this.request.options;
1727     if (!options.evalJSON || (options.evalJSON != 'force' &&
1728       !(this.getHeader('Content-type') || '').include('application/json')) ||
1729         this.responseText.blank())
1730           return null;
1731     try {
1732       return this.responseText.evalJSON(options.sanitizeJSON ||
1733         !this.request.isSameOrigin());
1734     } catch (e) {
1735       this.request.dispatchException(e);
1736     }
1737   }
1738 });
1739
1740 Ajax.Updater = Class.create(Ajax.Request, {
1741   initialize: function($super, container, url, options) {
1742     this.container = {
1743       success: (container.success || container),
1744       failure: (container.failure || (container.success ? null : container))
1745     };
1746
1747     options = Object.clone(options);
1748     var onComplete = options.onComplete;
1749     options.onComplete = (function(response, json) {
1750       this.updateContent(response.responseText);
1751       if (Object.isFunction(onComplete)) onComplete(response, json);
1752     }).bind(this);
1753
1754     $super(url, options);
1755   },
1756
1757   updateContent: function(responseText) {
1758     var receiver = this.container[this.success() ? 'success' : 'failure'],
1759         options = this.options;
1760
1761     if (!options.evalScripts) responseText = responseText.stripScripts();
1762
1763     if (receiver = $(receiver)) {
1764       if (options.insertion) {
1765         if (Object.isString(options.insertion)) {
1766           var insertion = { }; insertion[options.insertion] = responseText;
1767           receiver.insert(insertion);
1768         }
1769         else options.insertion(receiver, responseText);
1770       }
1771       else receiver.update(responseText);
1772     }
1773   }
1774 });
1775
1776 Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1777   initialize: function($super, container, url, options) {
1778     $super(options);
1779     this.onComplete = this.options.onComplete;
1780
1781     this.frequency = (this.options.frequency || 2);
1782     this.decay = (this.options.decay || 1);
1783
1784     this.updater = { };
1785     this.container = container;
1786     this.url = url;
1787
1788     this.start();
1789   },
1790
1791   start: function() {
1792     this.options.onComplete = this.updateComplete.bind(this);
1793     this.onTimerEvent();
1794   },
1795
1796   stop: function() {
1797     this.updater.options.onComplete = undefined;
1798     clearTimeout(this.timer);
1799     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1800   },
1801
1802   updateComplete: function(response) {
1803     if (this.options.decay) {
1804       this.decay = (response.responseText == this.lastText ?
1805         this.decay * this.options.decay : 1);
1806
1807       this.lastText = response.responseText;
1808     }
1809     this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1810   },
1811
1812   onTimerEvent: function() {
1813     this.updater = new Ajax.Updater(this.container, this.url, this.options);
1814   }
1815 });
1816
1817
1818 function $(element) {
1819   if (arguments.length > 1) {
1820     for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1821       elements.push($(arguments[i]));
1822     return elements;
1823   }
1824   if (Object.isString(element))
1825     element = document.getElementById(element);
1826   return Element.extend(element);
1827 }
1828
1829 if (Prototype.BrowserFeatures.XPath) {
1830   document._getElementsByXPath = function(expression, parentElement) {
1831     var results = [];
1832     var query = document.evaluate(expression, $(parentElement) || document,
1833       null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1834     for (var i = 0, length = query.snapshotLength; i < length; i++)
1835       results.push(Element.extend(query.snapshotItem(i)));
1836     return results;
1837   };
1838 }
1839
1840 /*--------------------------------------------------------------------------*/
1841
1842 if (!Node) var Node = { };
1843
1844 if (!Node.ELEMENT_NODE) {
1845   Object.extend(Node, {
1846     ELEMENT_NODE: 1,
1847     ATTRIBUTE_NODE: 2,
1848     TEXT_NODE: 3,
1849     CDATA_SECTION_NODE: 4,
1850     ENTITY_REFERENCE_NODE: 5,
1851     ENTITY_NODE: 6,
1852     PROCESSING_INSTRUCTION_NODE: 7,
1853     COMMENT_NODE: 8,
1854     DOCUMENT_NODE: 9,
1855     DOCUMENT_TYPE_NODE: 10,
1856     DOCUMENT_FRAGMENT_NODE: 11,
1857     NOTATION_NODE: 12
1858   });
1859 }
1860
1861
1862
1863 (function(global) {
1864   function shouldUseCache(tagName, attributes) {
1865     if (tagName === 'select') return false;
1866     if ('type' in attributes) return false;
1867     return true;
1868   }
1869
1870   var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
1871     try {
1872       var el = document.createElement('<input name="x">');
1873       return el.tagName.toLowerCase() === 'input' && el.name === 'x';
1874     }
1875     catch(err) {
1876       return false;
1877     }
1878   })();
1879
1880   var element = global.Element;
1881
1882   global.Element = function(tagName, attributes) {
1883     attributes = attributes || { };
1884     tagName = tagName.toLowerCase();
1885     var cache = Element.cache;
1886
1887     if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
1888       tagName = '<' + tagName + ' name="' + attributes.name + '">';
1889       delete attributes.name;
1890       return Element.writeAttribute(document.createElement(tagName), attributes);
1891     }
1892
1893     if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1894
1895     var node = shouldUseCache(tagName, attributes) ?
1896      cache[tagName].cloneNode(false) : document.createElement(tagName);
1897
1898     return Element.writeAttribute(node, attributes);
1899   };
1900
1901   Object.extend(global.Element, element || { });
1902   if (element) global.Element.prototype = element.prototype;
1903
1904 })(this);
1905
1906 Element.idCounter = 1;
1907 Element.cache = { };
1908
1909 Element._purgeElement = function(element) {
1910   var uid = element._prototypeUID;
1911   if (uid) {
1912     Element.stopObserving(element);
1913     element._prototypeUID = void 0;
1914     delete Element.Storage[uid];
1915   }
1916 }
1917
1918 Element.Methods = {
1919   visible: function(element) {
1920     return $(element).style.display != 'none';
1921   },
1922
1923   toggle: function(element) {
1924     element = $(element);
1925     Element[Element.visible(element) ? 'hide' : 'show'](element);
1926     return element;
1927   },
1928
1929   hide: function(element) {
1930     element = $(element);
1931     element.style.display = 'none';
1932     return element;
1933   },
1934
1935   show: function(element) {
1936     element = $(element);
1937     element.style.display = '';
1938     return element;
1939   },
1940
1941   remove: function(element) {
1942     element = $(element);
1943     element.parentNode.removeChild(element);
1944     return element;
1945   },
1946
1947   update: (function(){
1948
1949     var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1950       var el = document.createElement("select"),
1951           isBuggy = true;
1952       el.innerHTML = "<option value=\"test\">test</option>";
1953       if (el.options && el.options[0]) {
1954         isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1955       }
1956       el = null;
1957       return isBuggy;
1958     })();
1959
1960     var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1961       try {
1962         var el = document.createElement("table");
1963         if (el && el.tBodies) {
1964           el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1965           var isBuggy = typeof el.tBodies[0] == "undefined";
1966           el = null;
1967           return isBuggy;
1968         }
1969       } catch (e) {
1970         return true;
1971       }
1972     })();
1973
1974     var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
1975       try {
1976         var el = document.createElement('div');
1977         el.innerHTML = "<link>";
1978         var isBuggy = (el.childNodes.length === 0);
1979         el = null;
1980         return isBuggy;
1981       } catch(e) {
1982         return true;
1983       }
1984     })();
1985
1986     var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
1987      TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
1988
1989     var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1990       var s = document.createElement("script"),
1991           isBuggy = false;
1992       try {
1993         s.appendChild(document.createTextNode(""));
1994         isBuggy = !s.firstChild ||
1995           s.firstChild && s.firstChild.nodeType !== 3;
1996       } catch (e) {
1997         isBuggy = true;
1998       }
1999       s = null;
2000       return isBuggy;
2001     })();
2002
2003
2004     function update(element, content) {
2005       element = $(element);
2006       var purgeElement = Element._purgeElement;
2007
2008       var descendants = element.getElementsByTagName('*'),
2009        i = descendants.length;
2010       while (i--) purgeElement(descendants[i]);
2011
2012       if (content && content.toElement)
2013         content = content.toElement();
2014
2015       if (Object.isElement(content))
2016         return element.update().insert(content);
2017
2018       content = Object.toHTML(content);
2019
2020       var tagName = element.tagName.toUpperCase();
2021
2022       if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
2023         element.text = content;
2024         return element;
2025       }
2026
2027       if (ANY_INNERHTML_BUGGY) {
2028         if (tagName in Element._insertionTranslations.tags) {
2029           while (element.firstChild) {
2030             element.removeChild(element.firstChild);
2031           }
2032           Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2033             .each(function(node) {
2034               element.appendChild(node)
2035             });
2036         } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
2037           while (element.firstChild) {
2038             element.removeChild(element.firstChild);
2039           }
2040           var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true);
2041           nodes.each(function(node) { element.appendChild(node) });
2042         }
2043         else {
2044           element.innerHTML = content.stripScripts();
2045         }
2046       }
2047       else {
2048         element.innerHTML = content.stripScripts();
2049       }
2050
2051       content.evalScripts.bind(content).defer();
2052       return element;
2053     }
2054
2055     return update;
2056   })(),
2057
2058   replace: function(element, content) {
2059     element = $(element);
2060     if (content && content.toElement) content = content.toElement();
2061     else if (!Object.isElement(content)) {
2062       content = Object.toHTML(content);
2063       var range = element.ownerDocument.createRange();
2064       range.selectNode(element);
2065       content.evalScripts.bind(content).defer();
2066       content = range.createContextualFragment(content.stripScripts());
2067     }
2068     element.parentNode.replaceChild(content, element);
2069     return element;
2070   },
2071
2072   insert: function(element, insertions) {
2073     element = $(element);
2074
2075     if (Object.isString(insertions) || Object.isNumber(insertions) ||
2076         Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2077           insertions = {bottom:insertions};
2078
2079     var content, insert, tagName, childNodes;
2080
2081     for (var position in insertions) {
2082       content  = insertions[position];
2083       position = position.toLowerCase();
2084       insert = Element._insertionTranslations[position];
2085
2086       if (content && content.toElement) content = content.toElement();
2087       if (Object.isElement(content)) {
2088         insert(element, content);
2089         continue;
2090       }
2091
2092       content = Object.toHTML(content);
2093
2094       tagName = ((position == 'before' || position == 'after')
2095         ? element.parentNode : element).tagName.toUpperCase();
2096
2097       childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2098
2099       if (position == 'top' || position == 'after') childNodes.reverse();
2100       childNodes.each(insert.curry(element));
2101
2102       content.evalScripts.bind(content).defer();
2103     }
2104
2105     return element;
2106   },
2107
2108   wrap: function(element, wrapper, attributes) {
2109     element = $(element);
2110     if (Object.isElement(wrapper))
2111       $(wrapper).writeAttribute(attributes || { });
2112     else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
2113     else wrapper = new Element('div', wrapper);
2114     if (element.parentNode)
2115       element.parentNode.replaceChild(wrapper, element);
2116     wrapper.appendChild(element);
2117     return wrapper;
2118   },
2119
2120   inspect: function(element) {
2121     element = $(element);
2122     var result = '<' + element.tagName.toLowerCase();
2123     $H({'id': 'id', 'className': 'class'}).each(function(pair) {
2124       var property = pair.first(),
2125           attribute = pair.last(),
2126           value = (element[property] || '').toString();
2127       if (value) result += ' ' + attribute + '=' + value.inspect(true);
2128     });
2129     return result + '>';
2130   },
2131
2132   recursivelyCollect: function(element, property, maximumLength) {
2133     element = $(element);
2134     maximumLength = maximumLength || -1;
2135     var elements = [];
2136
2137     while (element = element[property]) {
2138       if (element.nodeType == 1)
2139         elements.push(Element.extend(element));
2140       if (elements.length == maximumLength)
2141         break;
2142     }
2143
2144     return elements;
2145   },
2146
2147   ancestors: function(element) {
2148     return Element.recursivelyCollect(element, 'parentNode');
2149   },
2150
2151   descendants: function(element) {
2152     return Element.select(element, "*");
2153   },
2154
2155   firstDescendant: function(element) {
2156     element = $(element).firstChild;
2157     while (element && element.nodeType != 1) element = element.nextSibling;
2158     return $(element);
2159   },
2160
2161   immediateDescendants: function(element) {
2162     var results = [], child = $(element).firstChild;
2163     while (child) {
2164       if (child.nodeType === 1) {
2165         results.push(Element.extend(child));
2166       }
2167       child = child.nextSibling;
2168     }
2169     return results;
2170   },
2171
2172   previousSiblings: function(element, maximumLength) {
2173     return Element.recursivelyCollect(element, 'previousSibling');
2174   },
2175
2176   nextSiblings: function(element) {
2177     return Element.recursivelyCollect(element, 'nextSibling');
2178   },
2179
2180   siblings: function(element) {
2181     element = $(element);
2182     return Element.previousSiblings(element).reverse()
2183       .concat(Element.nextSiblings(element));
2184   },
2185
2186   match: function(element, selector) {
2187     element = $(element);
2188     if (Object.isString(selector))
2189       return Prototype.Selector.match(element, selector);
2190     return selector.match(element);
2191   },
2192
2193   up: function(element, expression, index) {
2194     element = $(element);
2195     if (arguments.length == 1) return $(element.parentNode);
2196     var ancestors = Element.ancestors(element);
2197     return Object.isNumber(expression) ? ancestors[expression] :
2198       Prototype.Selector.find(ancestors, expression, index);
2199   },
2200
2201   down: function(element, expression, index) {
2202     element = $(element);
2203     if (arguments.length == 1) return Element.firstDescendant(element);
2204     return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2205       Element.select(element, expression)[index || 0];
2206   },
2207
2208   previous: function(element, expression, index) {
2209     element = $(element);
2210     if (Object.isNumber(expression)) index = expression, expression = false;
2211     if (!Object.isNumber(index)) index = 0;
2212
2213     if (expression) {
2214       return Prototype.Selector.find(element.previousSiblings(), expression, index);
2215     } else {
2216       return element.recursivelyCollect("previousSibling", index + 1)[index];
2217     }
2218   },
2219
2220   next: function(element, expression, index) {
2221     element = $(element);
2222     if (Object.isNumber(expression)) index = expression, expression = false;
2223     if (!Object.isNumber(index)) index = 0;
2224
2225     if (expression) {
2226       return Prototype.Selector.find(element.nextSiblings(), expression, index);
2227     } else {
2228       var maximumLength = Object.isNumber(index) ? index + 1 : 1;
2229       return element.recursivelyCollect("nextSibling", index + 1)[index];
2230     }
2231   },
2232
2233
2234   select: function(element) {
2235     element = $(element);
2236     var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2237     return Prototype.Selector.select(expressions, element);
2238   },
2239
2240   adjacent: function(element) {
2241     element = $(element);
2242     var expressions = Array.prototype.slice.call(arguments, 1).join(', ');
2243     return Prototype.Selector.select(expressions, element.parentNode).without(element);
2244   },
2245
2246   identify: function(element) {
2247     element = $(element);
2248     var id = Element.readAttribute(element, 'id');
2249     if (id) return id;
2250     do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2251     Element.writeAttribute(element, 'id', id);
2252     return id;
2253   },
2254
2255   readAttribute: function(element, name) {
2256     element = $(element);
2257     if (Prototype.Browser.IE) {
2258       var t = Element._attributeTranslations.read;
2259       if (t.values[name]) return t.values[name](element, name);
2260       if (t.names[name]) name = t.names[name];
2261       if (name.include(':')) {
2262         return (!element.attributes || !element.attributes[name]) ? null :
2263          element.attributes[name].value;
2264       }
2265     }
2266     return element.getAttribute(name);
2267   },
2268
2269   writeAttribute: function(element, name, value) {
2270     element = $(element);
2271     var attributes = { }, t = Element._attributeTranslations.write;
2272
2273     if (typeof name == 'object') attributes = name;
2274     else attributes[name] = Object.isUndefined(value) ? true : value;
2275
2276     for (var attr in attributes) {
2277       name = t.names[attr] || attr;
2278       value = attributes[attr];
2279       if (t.values[attr]) name = t.values[attr](element, value);
2280       if (value === false || value === null)
2281         element.removeAttribute(name);
2282       else if (value === true)
2283         element.setAttribute(name, name);
2284       else element.setAttribute(name, value);
2285     }
2286     return element;
2287   },
2288
2289   getHeight: function(element) {
2290     return Element.getDimensions(element).height;
2291   },
2292
2293   getWidth: function(element) {
2294     return Element.getDimensions(element).width;
2295   },
2296
2297   classNames: function(element) {
2298     return new Element.ClassNames(element);
2299   },
2300
2301   hasClassName: function(element, className) {
2302     if (!(element = $(element))) return;
2303     var elementClassName = element.className;
2304     return (elementClassName.length > 0 && (elementClassName == className ||
2305       new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2306   },
2307
2308   addClassName: function(element, className) {
2309     if (!(element = $(element))) return;
2310     if (!Element.hasClassName(element, className))
2311       element.className += (element.className ? ' ' : '') + className;
2312     return element;
2313   },
2314
2315   removeClassName: function(element, className) {
2316     if (!(element = $(element))) return;
2317     element.className = element.className.replace(
2318       new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2319     return element;
2320   },
2321
2322   toggleClassName: function(element, className) {
2323     if (!(element = $(element))) return;
2324     return Element[Element.hasClassName(element, className) ?
2325       'removeClassName' : 'addClassName'](element, className);
2326   },
2327
2328   cleanWhitespace: function(element) {
2329     element = $(element);
2330     var node = element.firstChild;
2331     while (node) {
2332       var nextNode = node.nextSibling;
2333       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2334         element.removeChild(node);
2335       node = nextNode;
2336     }
2337     return element;
2338   },
2339
2340   empty: function(element) {
2341     return $(element).innerHTML.blank();
2342   },
2343
2344   descendantOf: function(element, ancestor) {
2345     element = $(element), ancestor = $(ancestor);
2346
2347     if (element.compareDocumentPosition)
2348       return (element.compareDocumentPosition(ancestor) & 8) === 8;
2349
2350     if (ancestor.contains)
2351       return ancestor.contains(element) && ancestor !== element;
2352
2353     while (element = element.parentNode)
2354       if (element == ancestor) return true;
2355
2356     return false;
2357   },
2358
2359   scrollTo: function(element) {
2360     element = $(element);
2361     var pos = Element.cumulativeOffset(element);
2362     window.scrollTo(pos[0], pos[1]);
2363     return element;
2364   },
2365
2366   getStyle: function(element, style) {
2367     element = $(element);
2368     style = style == 'float' ? 'cssFloat' : style.camelize();
2369     var value = element.style[style];
2370     if (!value || value == 'auto') {
2371       var css = document.defaultView.getComputedStyle(element, null);
2372       value = css ? css[style] : null;
2373     }
2374     if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2375     return value == 'auto' ? null : value;
2376   },
2377
2378   getOpacity: function(element) {
2379     return $(element).getStyle('opacity');
2380   },
2381
2382   setStyle: function(element, styles) {
2383     element = $(element);
2384     var elementStyle = element.style, match;
2385     if (Object.isString(styles)) {
2386       element.style.cssText += ';' + styles;
2387       return styles.include('opacity') ?
2388         element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2389     }
2390     for (var property in styles)
2391       if (property == 'opacity') element.setOpacity(styles[property]);
2392       else
2393         elementStyle[(property == 'float' || property == 'cssFloat') ?
2394           (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2395             property] = styles[property];
2396
2397     return element;
2398   },
2399
2400   setOpacity: function(element, value) {
2401     element = $(element);
2402     element.style.opacity = (value == 1 || value === '') ? '' :
2403       (value < 0.00001) ? 0 : value;
2404     return element;
2405   },
2406
2407   makePositioned: function(element) {
2408     element = $(element);
2409     var pos = Element.getStyle(element, 'position');
2410     if (pos == 'static' || !pos) {
2411       element._madePositioned = true;
2412       element.style.position = 'relative';
2413       if (Prototype.Browser.Opera) {
2414         element.style.top = 0;
2415         element.style.left = 0;
2416       }
2417     }
2418     return element;
2419   },
2420
2421   undoPositioned: function(element) {
2422     element = $(element);
2423     if (element._madePositioned) {
2424       element._madePositioned = undefined;
2425       element.style.position =
2426         element.style.top =
2427         element.style.left =
2428         element.style.bottom =
2429         element.style.right = '';
2430     }
2431     return element;
2432   },
2433
2434   makeClipping: function(element) {
2435     element = $(element);
2436     if (element._overflow) return element;
2437     element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2438     if (element._overflow !== 'hidden')
2439       element.style.overflow = 'hidden';
2440     return element;
2441   },
2442
2443   undoClipping: function(element) {
2444     element = $(element);
2445     if (!element._overflow) return element;
2446     element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2447     element._overflow = null;
2448     return element;
2449   },
2450
2451   clonePosition: function(element, source) {
2452     var options = Object.extend({
2453       setLeft:    true,
2454       setTop:     true,
2455       setWidth:   true,
2456       setHeight:  true,
2457       offsetTop:  0,
2458       offsetLeft: 0
2459     }, arguments[2] || { });
2460
2461     source = $(source);
2462     var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
2463
2464     element = $(element);
2465
2466     if (Element.getStyle(element, 'position') == 'absolute') {
2467       parent = Element.getOffsetParent(element);
2468       delta = Element.viewportOffset(parent);
2469     }
2470
2471     if (parent == document.body) {
2472       delta[0] -= document.body.offsetLeft;
2473       delta[1] -= document.body.offsetTop;
2474     }
2475
2476     if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2477     if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2478     if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
2479     if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2480     return element;
2481   }
2482 };
2483
2484 Object.extend(Element.Methods, {
2485   getElementsBySelector: Element.Methods.select,
2486
2487   childElements: Element.Methods.immediateDescendants
2488 });
2489
2490 Element._attributeTranslations = {
2491   write: {
2492     names: {
2493       className: 'class',
2494       htmlFor:   'for'
2495     },
2496     values: { }
2497   }
2498 };
2499
2500 if (Prototype.Browser.Opera) {
2501   Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2502     function(proceed, element, style) {
2503       switch (style) {
2504         case 'height': case 'width':
2505           if (!Element.visible(element)) return null;
2506
2507           var dim = parseInt(proceed(element, style), 10);
2508
2509           if (dim !== element['offset' + style.capitalize()])
2510             return dim + 'px';
2511
2512           var properties;
2513           if (style === 'height') {
2514             properties = ['border-top-width', 'padding-top',
2515              'padding-bottom', 'border-bottom-width'];
2516           }
2517           else {
2518             properties = ['border-left-width', 'padding-left',
2519              'padding-right', 'border-right-width'];
2520           }
2521           return properties.inject(dim, function(memo, property) {
2522             var val = proceed(element, property);
2523             return val === null ? memo : memo - parseInt(val, 10);
2524           }) + 'px';
2525         default: return proceed(element, style);
2526       }
2527     }
2528   );
2529
2530   Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2531     function(proceed, element, attribute) {
2532       if (attribute === 'title') return element.title;
2533       return proceed(element, attribute);
2534     }
2535   );
2536 }
2537
2538 else if (Prototype.Browser.IE) {
2539   Element.Methods.getStyle = function(element, style) {
2540     element = $(element);
2541     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2542     var value = element.style[style];
2543     if (!value && element.currentStyle) value = element.currentStyle[style];
2544
2545     if (style == 'opacity') {
2546       if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2547         if (value[1]) return parseFloat(value[1]) / 100;
2548       return 1.0;
2549     }
2550
2551     if (value == 'auto') {
2552       if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2553         return element['offset' + style.capitalize()] + 'px';
2554       return null;
2555     }
2556     return value;
2557   };
2558
2559   Element.Methods.setOpacity = function(element, value) {
2560     function stripAlpha(filter){
2561       return filter.replace(/alpha\([^\)]*\)/gi,'');
2562     }
2563     element = $(element);
2564     var currentStyle = element.currentStyle;
2565     if ((currentStyle && !currentStyle.hasLayout) ||
2566       (!currentStyle && element.style.zoom == 'normal'))
2567         element.style.zoom = 1;
2568
2569     var filter = element.getStyle('filter'), style = element.style;
2570     if (value == 1 || value === '') {
2571       (filter = stripAlpha(filter)) ?
2572         style.filter = filter : style.removeAttribute('filter');
2573       return element;
2574     } else if (value < 0.00001) value = 0;
2575     style.filter = stripAlpha(filter) +
2576       'alpha(opacity=' + (value * 100) + ')';
2577     return element;
2578   };
2579
2580   Element._attributeTranslations = (function(){
2581
2582     var classProp = 'className',
2583         forProp = 'for',
2584         el = document.createElement('div');
2585
2586     el.setAttribute(classProp, 'x');
2587
2588     if (el.className !== 'x') {
2589       el.setAttribute('class', 'x');
2590       if (el.className === 'x') {
2591         classProp = 'class';
2592       }
2593     }
2594     el = null;
2595
2596     el = document.createElement('label');
2597     el.setAttribute(forProp, 'x');
2598     if (el.htmlFor !== 'x') {
2599       el.setAttribute('htmlFor', 'x');
2600       if (el.htmlFor === 'x') {
2601         forProp = 'htmlFor';
2602       }
2603     }
2604     el = null;
2605
2606     return {
2607       read: {
2608         names: {
2609           'class':      classProp,
2610           'className':  classProp,
2611           'for':        forProp,
2612           'htmlFor':    forProp
2613         },
2614         values: {
2615           _getAttr: function(element, attribute) {
2616             return element.getAttribute(attribute);
2617           },
2618           _getAttr2: function(element, attribute) {
2619             return element.getAttribute(attribute, 2);
2620           },
2621           _getAttrNode: function(element, attribute) {
2622             var node = element.getAttributeNode(attribute);
2623             return node ? node.value : "";
2624           },
2625           _getEv: (function(){
2626
2627             var el = document.createElement('div'), f;
2628             el.onclick = Prototype.emptyFunction;
2629             var value = el.getAttribute('onclick');
2630
2631             if (String(value).indexOf('{') > -1) {
2632               f = function(element, attribute) {
2633                 attribute = element.getAttribute(attribute);
2634                 if (!attribute) return null;
2635                 attribute = attribute.toString();
2636                 attribute = attribute.split('{')[1];
2637                 attribute = attribute.split('}')[0];
2638                 return attribute.strip();
2639               };
2640             }
2641             else if (value === '') {
2642               f = function(element, attribute) {
2643                 attribute = element.getAttribute(attribute);
2644                 if (!attribute) return null;
2645                 return attribute.strip();
2646               };
2647             }
2648             el = null;
2649             return f;
2650           })(),
2651           _flag: function(element, attribute) {
2652             return $(element).hasAttribute(attribute) ? attribute : null;
2653           },
2654           style: function(element) {
2655             return element.style.cssText.toLowerCase();
2656           },
2657           title: function(element) {
2658             return element.title;
2659           }
2660         }
2661       }
2662     }
2663   })();
2664
2665   Element._attributeTranslations.write = {
2666     names: Object.extend({
2667       cellpadding: 'cellPadding',
2668       cellspacing: 'cellSpacing'
2669     }, Element._attributeTranslations.read.names),
2670     values: {
2671       checked: function(element, value) {
2672         element.checked = !!value;
2673       },
2674
2675       style: function(element, value) {
2676         element.style.cssText = value ? value : '';
2677       }
2678     }
2679   };
2680
2681   Element._attributeTranslations.has = {};
2682
2683   $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2684       'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2685     Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2686     Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2687   });
2688
2689   (function(v) {
2690     Object.extend(v, {
2691       href:        v._getAttr2,
2692       src:         v._getAttr2,
2693       type:        v._getAttr,
2694       action:      v._getAttrNode,
2695       disabled:    v._flag,
2696       checked:     v._flag,
2697       readonly:    v._flag,
2698       multiple:    v._flag,
2699       onload:      v._getEv,
2700       onunload:    v._getEv,
2701       onclick:     v._getEv,
2702       ondblclick:  v._getEv,
2703       onmousedown: v._getEv,
2704       onmouseup:   v._getEv,
2705       onmouseover: v._getEv,
2706       onmousemove: v._getEv,
2707       onmouseout:  v._getEv,
2708       onfocus:     v._getEv,
2709       onblur:      v._getEv,
2710       onkeypress:  v._getEv,
2711       onkeydown:   v._getEv,
2712       onkeyup:     v._getEv,
2713       onsubmit:    v._getEv,
2714       onreset:     v._getEv,
2715       onselect:    v._getEv,
2716       onchange:    v._getEv
2717     });
2718   })(Element._attributeTranslations.read.values);
2719
2720   if (Prototype.BrowserFeatures.ElementExtensions) {
2721     (function() {
2722       function _descendants(element) {
2723         var nodes = element.getElementsByTagName('*'), results = [];
2724         for (var i = 0, node; node = nodes[i]; i++)
2725           if (node.tagName !== "!") // Filter out comment nodes.
2726             results.push(node);
2727         return results;
2728       }
2729
2730       Element.Methods.down = function(element, expression, index) {
2731         element = $(element);
2732         if (arguments.length == 1) return element.firstDescendant();
2733         return Object.isNumber(expression) ? _descendants(element)[expression] :
2734           Element.select(element, expression)[index || 0];
2735       }
2736     })();
2737   }
2738
2739 }
2740
2741 else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2742   Element.Methods.setOpacity = function(element, value) {
2743     element = $(element);
2744     element.style.opacity = (value == 1) ? 0.999999 :
2745       (value === '') ? '' : (value < 0.00001) ? 0 : value;
2746     return element;
2747   };
2748 }
2749
2750 else if (Prototype.Browser.WebKit) {
2751   Element.Methods.setOpacity = function(element, value) {
2752     element = $(element);
2753     element.style.opacity = (value == 1 || value === '') ? '' :
2754       (value < 0.00001) ? 0 : value;
2755
2756     if (value == 1)
2757       if (element.tagName.toUpperCase() == 'IMG' && element.width) {
2758         element.width++; element.width--;
2759       } else try {
2760         var n = document.createTextNode(' ');
2761         element.appendChild(n);
2762         element.removeChild(n);
2763       } catch (e) { }
2764
2765     return element;
2766   };
2767 }
2768
2769 if ('outerHTML' in document.documentElement) {
2770   Element.Methods.replace = function(element, content) {
2771     element = $(element);
2772
2773     if (content && content.toElement) content = content.toElement();
2774     if (Object.isElement(content)) {
2775       element.parentNode.replaceChild(content, element);
2776       return element;
2777     }
2778
2779     content = Object.toHTML(content);
2780     var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2781
2782     if (Element._insertionTranslations.tags[tagName]) {
2783       var nextSibling = element.next(),
2784           fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2785       parent.removeChild(element);
2786       if (nextSibling)
2787         fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2788       else
2789         fragments.each(function(node) { parent.appendChild(node) });
2790     }
2791     else element.outerHTML = content.stripScripts();
2792
2793     content.evalScripts.bind(content).defer();
2794     return element;
2795   };
2796 }
2797
2798 Element._returnOffset = function(l, t) {
2799   var result = [l, t];
2800   result.left = l;
2801   result.top = t;
2802   return result;
2803 };
2804
2805 Element._getContentFromAnonymousElement = function(tagName, html, force) {
2806   var div = new Element('div'),
2807       t = Element._insertionTranslations.tags[tagName];
2808
2809   var workaround = false;
2810   if (t) workaround = true;
2811   else if (force) {
2812     workaround = true;
2813     t = ['', '', 0];
2814   }
2815
2816   if (workaround) {
2817     div.innerHTML = '&nbsp;' + t[0] + html + t[1];
2818     div.removeChild(div.firstChild);
2819     for (var i = t[2]; i--; ) {
2820       div = div.firstChild;
2821     }
2822   }
2823   else {
2824     div.innerHTML = html;
2825   }
2826   return $A(div.childNodes);
2827 };
2828
2829 Element._insertionTranslations = {
2830   before: function(element, node) {
2831     element.parentNode.insertBefore(node, element);
2832   },
2833   top: function(element, node) {
2834     element.insertBefore(node, element.firstChild);
2835   },
2836   bottom: function(element, node) {
2837     element.appendChild(node);
2838   },
2839   after: function(element, node) {
2840     element.parentNode.insertBefore(node, element.nextSibling);
2841   },
2842   tags: {
2843     TABLE:  ['<table>',                '</table>',                   1],
2844     TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
2845     TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
2846     TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2847     SELECT: ['<select>',               '</select>',                  1]
2848   }
2849 };
2850
2851 (function() {
2852   var tags = Element._insertionTranslations.tags;
2853   Object.extend(tags, {
2854     THEAD: tags.TBODY,
2855     TFOOT: tags.TBODY,
2856     TH:    tags.TD
2857   });
2858 })();
2859
2860 Element.Methods.Simulated = {
2861   hasAttribute: function(element, attribute) {
2862     attribute = Element._attributeTranslations.has[attribute] || attribute;
2863     var node = $(element).getAttributeNode(attribute);
2864     return !!(node && node.specified);
2865   }
2866 };
2867
2868 Element.Methods.ByTag = { };
2869
2870 Object.extend(Element, Element.Methods);
2871
2872 (function(div) {
2873
2874   if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
2875     window.HTMLElement = { };
2876     window.HTMLElement.prototype = div['__proto__'];
2877     Prototype.BrowserFeatures.ElementExtensions = true;
2878   }
2879
2880   div = null;
2881
2882 })(document.createElement('div'));
2883
2884 Element.extend = (function() {
2885
2886   function checkDeficiency(tagName) {
2887     if (typeof window.Element != 'undefined') {
2888       var proto = window.Element.prototype;
2889       if (proto) {
2890         var id = '_' + (Math.random()+'').slice(2),
2891             el = document.createElement(tagName);
2892         proto[id] = 'x';
2893         var isBuggy = (el[id] !== 'x');
2894         delete proto[id];
2895         el = null;
2896         return isBuggy;
2897       }
2898     }
2899     return false;
2900   }
2901
2902   function extendElementWith(element, methods) {
2903     for (var property in methods) {
2904       var value = methods[property];
2905       if (Object.isFunction(value) && !(property in element))
2906         element[property] = value.methodize();
2907     }
2908   }
2909
2910   var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
2911
2912   if (Prototype.BrowserFeatures.SpecificElementExtensions) {
2913     if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
2914       return function(element) {
2915         if (element && typeof element._extendedByPrototype == 'undefined') {
2916           var t = element.tagName;
2917           if (t && (/^(?:object|applet|embed)$/i.test(t))) {
2918             extendElementWith(element, Element.Methods);
2919             extendElementWith(element, Element.Methods.Simulated);
2920             extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
2921           }
2922         }
2923         return element;
2924       }
2925     }
2926     return Prototype.K;
2927   }
2928
2929   var Methods = { }, ByTag = Element.Methods.ByTag;
2930
2931   var extend = Object.extend(function(element) {
2932     if (!element || typeof element._extendedByPrototype != 'undefined' ||
2933         element.nodeType != 1 || element == window) return element;
2934
2935     var methods = Object.clone(Methods),
2936         tagName = element.tagName.toUpperCase();
2937
2938     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2939
2940     extendElementWith(element, methods);
2941
2942     element._extendedByPrototype = Prototype.emptyFunction;
2943     return element;
2944
2945   }, {
2946     refresh: function() {
2947       if (!Prototype.BrowserFeatures.ElementExtensions) {
2948         Object.extend(Methods, Element.Methods);
2949         Object.extend(Methods, Element.Methods.Simulated);
2950       }
2951     }
2952   });
2953
2954   extend.refresh();
2955   return extend;
2956 })();
2957
2958 if (document.documentElement.hasAttribute) {
2959   Element.hasAttribute = function(element, attribute) {
2960     return element.hasAttribute(attribute);
2961   };
2962 }
2963 else {
2964   Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
2965 }
2966
2967 Element.addMethods = function(methods) {
2968   var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2969
2970   if (!methods) {
2971     Object.extend(Form, Form.Methods);
2972     Object.extend(Form.Element, Form.Element.Methods);
2973     Object.extend(Element.Methods.ByTag, {
2974       "FORM":     Object.clone(Form.Methods),
2975       "INPUT":    Object.clone(Form.Element.Methods),
2976       "SELECT":   Object.clone(Form.Element.Methods),
2977       "TEXTAREA": Object.clone(Form.Element.Methods),
2978       "BUTTON":   Object.clone(Form.Element.Methods)
2979     });
2980   }
2981
2982   if (arguments.length == 2) {
2983     var tagName = methods;
2984     methods = arguments[1];
2985   }
2986
2987   if (!tagName) Object.extend(Element.Methods, methods || { });
2988   else {
2989     if (Object.isArray(tagName)) tagName.each(extend);
2990     else extend(tagName);
2991   }
2992
2993   function extend(tagName) {
2994     tagName = tagName.toUpperCase();
2995     if (!Element.Methods.ByTag[tagName])
2996       Element.Methods.ByTag[tagName] = { };
2997     Object.extend(Element.Methods.ByTag[tagName], methods);
2998   }
2999
3000   function copy(methods, destination, onlyIfAbsent) {
3001     onlyIfAbsent = onlyIfAbsent || false;
3002     for (var property in methods) {
3003       var value = methods[property];
3004       if (!Object.isFunction(value)) continue;
3005       if (!onlyIfAbsent || !(property in destination))
3006         destination[property] = value.methodize();
3007     }
3008   }
3009
3010   function findDOMClass(tagName) {
3011     var klass;
3012     var trans = {
3013       "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3014       "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3015       "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3016       "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3017       "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3018       "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3019       "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3020       "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3021       "FrameSet", "IFRAME": "IFrame"
3022     };
3023     if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3024     if (window[klass]) return window[klass];
3025     klass = 'HTML' + tagName + 'Element';
3026     if (window[klass]) return window[klass];
3027     klass = 'HTML' + tagName.capitalize() + 'Element';
3028     if (window[klass]) return window[klass];
3029
3030     var element = document.createElement(tagName),
3031         proto = element['__proto__'] || element.constructor.prototype;
3032
3033     element = null;
3034     return proto;
3035   }
3036
3037   var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3038    Element.prototype;
3039
3040   if (F.ElementExtensions) {
3041     copy(Element.Methods, elementPrototype);
3042     copy(Element.Methods.Simulated, elementPrototype, true);
3043   }
3044
3045   if (F.SpecificElementExtensions) {
3046     for (var tag in Element.Methods.ByTag) {
3047       var klass = findDOMClass(tag);
3048       if (Object.isUndefined(klass)) continue;
3049       copy(T[tag], klass.prototype);
3050     }
3051   }
3052
3053   Object.extend(Element, Element.Methods);
3054   delete Element.ByTag;
3055
3056   if (Element.extend.refresh) Element.extend.refresh();
3057   Element.cache = { };
3058 };
3059
3060
3061 document.viewport = {
3062
3063   getDimensions: function() {
3064     return { width: this.getWidth(), height: this.getHeight() };
3065   },
3066
3067   getScrollOffsets: function() {
3068     return Element._returnOffset(
3069       window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3070       window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
3071   }
3072 };
3073
3074 (function(viewport) {
3075   var B = Prototype.Browser, doc = document, element, property = {};
3076
3077   function getRootElement() {
3078     if (B.WebKit && !doc.evaluate)
3079       return document;
3080
3081     if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3082       return document.body;
3083
3084     return document.documentElement;
3085   }
3086
3087   function define(D) {
3088     if (!element) element = getRootElement();
3089
3090     property[D] = 'client' + D;
3091
3092     viewport['get' + D] = function() { return element[property[D]] };
3093     return viewport['get' + D]();
3094   }
3095
3096   viewport.getWidth  = define.curry('Width');
3097
3098   viewport.getHeight = define.curry('Height');
3099 })(document.viewport);
3100
3101
3102 Element.Storage = {
3103   UID: 1
3104 };
3105
3106 Element.addMethods({
3107   getStorage: function(element) {
3108     if (!(element = $(element))) return;
3109
3110     var uid;
3111     if (element === window) {
3112       uid = 0;
3113     } else {
3114       if (typeof element._prototypeUID === "undefined")
3115         element._prototypeUID = Element.Storage.UID++;
3116       uid = element._prototypeUID;
3117     }
3118
3119     if (!Element.Storage[uid])
3120       Element.Storage[uid] = $H();
3121
3122     return Element.Storage[uid];
3123   },
3124
3125   store: function(element, key, value) {
3126     if (!(element = $(element))) return;
3127
3128     if (arguments.length === 2) {
3129       Element.getStorage(element).update(key);
3130     } else {
3131       Element.getStorage(element).set(key, value);
3132     }
3133
3134     return element;
3135   },
3136
3137   retrieve: function(element, key, defaultValue) {
3138     if (!(element = $(element))) return;
3139     var hash = Element.getStorage(element), value = hash.get(key);
3140
3141     if (Object.isUndefined(value)) {
3142       hash.set(key, defaultValue);
3143       value = defaultValue;
3144     }
3145
3146     return value;
3147   },
3148
3149   clone: function(element, deep) {
3150     if (!(element = $(element))) return;
3151     var clone = element.cloneNode(deep);
3152     clone._prototypeUID = void 0;
3153     if (deep) {
3154       var descendants = Element.select(clone, '*'),
3155           i = descendants.length;
3156       while (i--) {
3157         descendants[i]._prototypeUID = void 0;
3158       }
3159     }
3160     return Element.extend(clone);
3161   },
3162
3163   purge: function(element) {
3164     if (!(element = $(element))) return;
3165     var purgeElement = Element._purgeElement;
3166
3167     purgeElement(element);
3168
3169     var descendants = element.getElementsByTagName('*'),
3170      i = descendants.length;
3171
3172     while (i--) purgeElement(descendants[i]);
3173
3174     return null;
3175   }
3176 });
3177
3178 (function() {
3179
3180   function toDecimal(pctString) {
3181     var match = pctString.match(/^(\d+)%?$/i);
3182     if (!match) return null;
3183     return (Number(match[1]) / 100);
3184   }
3185
3186   function getPixelValue(value, property, context) {
3187     var element = null;
3188     if (Object.isElement(value)) {
3189       element = value;
3190       value = element.getStyle(property);
3191     }
3192
3193     if (value === null) {
3194       return null;
3195     }
3196
3197     if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3198       return window.parseFloat(value);
3199     }
3200
3201     var isPercentage = value.include('%'), isViewport = (context === document.viewport);
3202
3203     if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
3204       var style = element.style.left, rStyle = element.runtimeStyle.left;
3205       element.runtimeStyle.left = element.currentStyle.left;
3206       element.style.left = value || 0;
3207       value = element.style.pixelLeft;
3208       element.style.left = style;
3209       element.runtimeStyle.left = rStyle;
3210
3211       return value;
3212     }
3213
3214     if (element && isPercentage) {
3215       context = context || element.parentNode;
3216       var decimal = toDecimal(value);
3217       var whole = null;
3218       var position = element.getStyle('position');
3219
3220       var isHorizontal = property.include('left') || property.include('right') ||
3221        property.include('width');
3222
3223       var isVertical =  property.include('top') || property.include('bottom') ||
3224         property.include('height');
3225
3226       if (context === document.viewport) {
3227         if (isHorizontal) {
3228           whole = document.viewport.getWidth();
3229         } else if (isVertical) {
3230           whole = document.viewport.getHeight();
3231         }
3232       } else {
3233         if (isHorizontal) {
3234           whole = $(context).measure('width');
3235         } else if (isVertical) {
3236           whole = $(context).measure('height');
3237         }
3238       }
3239
3240       return (whole === null) ? 0 : whole * decimal;
3241     }
3242
3243     return 0;
3244   }
3245
3246   function toCSSPixels(number) {
3247     if (Object.isString(number) && number.endsWith('px')) {
3248       return number;
3249     }
3250     return number + 'px';
3251   }
3252
3253   function isDisplayed(element) {
3254     var originalElement = element;
3255     while (element && element.parentNode) {
3256       var display = element.getStyle('display');
3257       if (display === 'none') {
3258         return false;
3259       }
3260       element = $(element.parentNode);
3261     }
3262     return true;
3263   }
3264
3265   var hasLayout = Prototype.K;
3266   if ('currentStyle' in document.documentElement) {
3267     hasLayout = function(element) {
3268       if (!element.currentStyle.hasLayout) {
3269         element.style.zoom = 1;
3270       }
3271       return element;
3272     };
3273   }
3274
3275   function cssNameFor(key) {
3276     if (key.include('border')) key = key + '-width';
3277     return key.camelize();
3278   }
3279
3280   Element.Layout = Class.create(Hash, {
3281     initialize: function($super, element, preCompute) {
3282       $super();
3283       this.element = $(element);
3284
3285       Element.Layout.PROPERTIES.each( function(property) {
3286         this._set(property, null);
3287       }, this);
3288
3289       if (preCompute) {
3290         this._preComputing = true;
3291         this._begin();
3292         Element.Layout.PROPERTIES.each( this._compute, this );
3293         this._end();
3294         this._preComputing = false;
3295       }
3296     },
3297
3298     _set: function(property, value) {
3299       return Hash.prototype.set.call(this, property, value);
3300     },
3301
3302     set: function(property, value) {
3303       throw "Properties of Element.Layout are read-only.";
3304     },
3305
3306     get: function($super, property) {
3307       var value = $super(property);
3308       return value === null ? this._compute(property) : value;
3309     },
3310
3311     _begin: function() {
3312       if (this._prepared) return;
3313
3314       var element = this.element;
3315       if (isDisplayed(element)) {
3316         this._prepared = true;
3317         return;
3318       }
3319
3320       var originalStyles = {
3321         position:   element.style.position   || '',
3322         width:      element.style.width      || '',
3323         visibility: element.style.visibility || '',
3324         display:    element.style.display    || ''
3325       };
3326
3327       element.store('prototype_original_styles', originalStyles);
3328
3329       var position = element.getStyle('position'),
3330        width = element.getStyle('width');
3331
3332       if (width === "0px" || width === null) {
3333         element.style.display = 'block';
3334         width = element.getStyle('width');
3335       }
3336
3337       var context = (position === 'fixed') ? document.viewport :
3338        element.parentNode;
3339
3340       element.setStyle({
3341         position:   'absolute',
3342         visibility: 'hidden',
3343         display:    'block'
3344       });
3345
3346       var positionedWidth = element.getStyle('width');
3347
3348       var newWidth;
3349       if (width && (positionedWidth === width)) {
3350         newWidth = getPixelValue(element, 'width', context);
3351       } else if (position === 'absolute' || position === 'fixed') {
3352         newWidth = getPixelValue(element, 'width', context);
3353       } else {
3354         var parent = element.parentNode, pLayout = $(parent).getLayout();
3355
3356         newWidth = pLayout.get('width') -
3357          this.get('margin-left') -
3358          this.get('border-left') -
3359          this.get('padding-left') -
3360          this.get('padding-right') -
3361          this.get('border-right') -
3362          this.get('margin-right');
3363       }
3364
3365       element.setStyle({ width: newWidth + 'px' });
3366
3367       this._prepared = true;
3368     },
3369
3370     _end: function() {
3371       var element = this.element;
3372       var originalStyles = element.retrieve('prototype_original_styles');
3373       element.store('prototype_original_styles', null);
3374       element.setStyle(originalStyles);
3375       this._prepared = false;
3376     },
3377
3378     _compute: function(property) {
3379       var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3380       if (!(property in COMPUTATIONS)) {
3381         throw "Property not found.";
3382       }
3383
3384       return this._set(property, COMPUTATIONS[property].call(this, this.element));
3385     },
3386
3387     toObject: function() {
3388       var args = $A(arguments);
3389       var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3390        args.join(' ').split(' ');
3391       var obj = {};
3392       keys.each( function(key) {
3393         if (!Element.Layout.PROPERTIES.include(key)) return;
3394         var value = this.get(key);
3395         if (value != null) obj[key] = value;
3396       }, this);
3397       return obj;
3398     },
3399
3400     toHash: function() {
3401       var obj = this.toObject.apply(this, arguments);
3402       return new Hash(obj);
3403     },
3404
3405     toCSS: function() {
3406       var args = $A(arguments);
3407       var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3408        args.join(' ').split(' ');
3409       var css = {};
3410
3411       keys.each( function(key) {
3412         if (!Element.Layout.PROPERTIES.include(key)) return;
3413         if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3414
3415         var value = this.get(key);
3416         if (value != null) css[cssNameFor(key)] = value + 'px';
3417       }, this);
3418       return css;
3419     },
3420
3421     inspect: function() {
3422       return "#<Element.Layout>";
3423     }
3424   });
3425
3426   Object.extend(Element.Layout, {
3427     PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3428
3429     COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3430
3431     COMPUTATIONS: {
3432       'height': function(element) {
3433         if (!this._preComputing) this._begin();
3434
3435         var bHeight = this.get('border-box-height');
3436         if (bHeight <= 0) {
3437           if (!this._preComputing) this._end();
3438           return 0;
3439         }
3440
3441         var bTop = this.get('border-top'),
3442          bBottom = this.get('border-bottom');
3443
3444         var pTop = this.get('padding-top'),
3445          pBottom = this.get('padding-bottom');
3446
3447         if (!this._preComputing) this._end();
3448
3449         return bHeight - bTop - bBottom - pTop - pBottom;
3450       },
3451
3452       'width': function(element) {
3453         if (!this._preComputing) this._begin();
3454
3455         var bWidth = this.get('border-box-width');
3456         if (bWidth <= 0) {
3457           if (!this._preComputing) this._end();
3458           return 0;
3459         }
3460
3461         var bLeft = this.get('border-left'),
3462          bRight = this.get('border-right');
3463
3464         var pLeft = this.get('padding-left'),
3465          pRight = this.get('padding-right');
3466
3467         if (!this._preComputing) this._end();
3468
3469         return bWidth - bLeft - bRight - pLeft - pRight;
3470       },
3471
3472       'padding-box-height': function(element) {
3473         var height = this.get('height'),
3474          pTop = this.get('padding-top'),
3475          pBottom = this.get('padding-bottom');
3476
3477         return height + pTop + pBottom;
3478       },
3479
3480       'padding-box-width': function(element) {
3481         var width = this.get('width'),
3482          pLeft = this.get('padding-left'),
3483          pRight = this.get('padding-right');
3484
3485         return width + pLeft + pRight;
3486       },
3487
3488       'border-box-height': function(element) {
3489         if (!this._preComputing) this._begin();
3490         var height = element.offsetHeight;
3491         if (!this._preComputing) this._end();
3492         return height;
3493       },
3494
3495       'border-box-width': function(element) {
3496         if (!this._preComputing) this._begin();
3497         var width = element.offsetWidth;
3498         if (!this._preComputing) this._end();
3499         return width;
3500       },
3501
3502       'margin-box-height': function(element) {
3503         var bHeight = this.get('border-box-height'),
3504          mTop = this.get('margin-top'),
3505          mBottom = this.get('margin-bottom');
3506
3507         if (bHeight <= 0) return 0;
3508
3509         return bHeight + mTop + mBottom;
3510       },
3511
3512       'margin-box-width': function(element) {
3513         var bWidth = this.get('border-box-width'),
3514          mLeft = this.get('margin-left'),
3515          mRight = this.get('margin-right');
3516
3517         if (bWidth <= 0) return 0;
3518
3519         return bWidth + mLeft + mRight;
3520       },
3521
3522       'top': function(element) {
3523         var offset = element.positionedOffset();
3524         return offset.top;
3525       },
3526
3527       'bottom': function(element) {
3528         var offset = element.positionedOffset(),
3529          parent = element.getOffsetParent(),
3530          pHeight = parent.measure('height');
3531
3532         var mHeight = this.get('border-box-height');
3533
3534         return pHeight - mHeight - offset.top;
3535       },
3536
3537       'left': function(element) {
3538         var offset = element.positionedOffset();
3539         return offset.left;
3540       },
3541
3542       'right': function(element) {
3543         var offset = element.positionedOffset(),
3544          parent = element.getOffsetParent(),
3545          pWidth = parent.measure('width');
3546
3547         var mWidth = this.get('border-box-width');
3548
3549         return pWidth - mWidth - offset.left;
3550       },
3551
3552       'padding-top': function(element) {
3553         return getPixelValue(element, 'paddingTop');
3554       },
3555
3556       'padding-bottom': function(element) {
3557         return getPixelValue(element, 'paddingBottom');
3558       },
3559
3560       'padding-left': function(element) {
3561         return getPixelValue(element, 'paddingLeft');
3562       },
3563
3564       'padding-right': function(element) {
3565         return getPixelValue(element, 'paddingRight');
3566       },
3567
3568       'border-top': function(element) {
3569         return getPixelValue(element, 'borderTopWidth');
3570       },
3571
3572       'border-bottom': function(element) {
3573         return getPixelValue(element, 'borderBottomWidth');
3574       },
3575
3576       'border-left': function(element) {
3577         return getPixelValue(element, 'borderLeftWidth');
3578       },
3579
3580       'border-right': function(element) {
3581         return getPixelValue(element, 'borderRightWidth');
3582       },
3583
3584       'margin-top': function(element) {
3585         return getPixelValue(element, 'marginTop');
3586       },
3587
3588       'margin-bottom': function(element) {
3589         return getPixelValue(element, 'marginBottom');
3590       },
3591
3592       'margin-left': function(element) {
3593         return getPixelValue(element, 'marginLeft');
3594       },
3595
3596       'margin-right': function(element) {
3597         return getPixelValue(element, 'marginRight');
3598       }
3599     }
3600   });
3601
3602   if ('getBoundingClientRect' in document.documentElement) {
3603     Object.extend(Element.Layout.COMPUTATIONS, {
3604       'right': function(element) {
3605         var parent = hasLayout(element.getOffsetParent());
3606         var rect = element.getBoundingClientRect(),
3607          pRect = parent.getBoundingClientRect();
3608
3609         return (pRect.right - rect.right).round();
3610       },
3611
3612       'bottom': function(element) {
3613         var parent = hasLayout(element.getOffsetParent());
3614         var rect = element.getBoundingClientRect(),
3615          pRect = parent.getBoundingClientRect();
3616
3617         return (pRect.bottom - rect.bottom).round();
3618       }
3619     });
3620   }
3621
3622   Element.Offset = Class.create({
3623     initialize: function(left, top) {
3624       this.left = left.round();
3625       this.top  = top.round();
3626
3627       this[0] = this.left;
3628       this[1] = this.top;
3629     },
3630
3631     relativeTo: function(offset) {
3632       return new Element.Offset(
3633         this.left - offset.left,
3634         this.top  - offset.top
3635       );
3636     },
3637
3638     inspect: function() {
3639       return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3640     },
3641
3642     toString: function() {
3643       return "[#{left}, #{top}]".interpolate(this);
3644     },
3645
3646     toArray: function() {
3647       return [this.left, this.top];
3648     }
3649   });
3650
3651   function getLayout(element, preCompute) {
3652     return new Element.Layout(element, preCompute);
3653   }
3654
3655   function measure(element, property) {
3656     return $(element).getLayout().get(property);
3657   }
3658
3659   function getDimensions(element) {
3660     element = $(element);
3661     var display = Element.getStyle(element, 'display');
3662
3663     if (display && display !== 'none') {
3664       return { width: element.offsetWidth, height: element.offsetHeight };
3665     }
3666
3667     var style = element.style;
3668     var originalStyles = {
3669       visibility: style.visibility,
3670       position:   style.position,
3671       display:    style.display
3672     };
3673
3674     var newStyles = {
3675       visibility: 'hidden',
3676       display:    'block'
3677     };
3678
3679     if (originalStyles.position !== 'fixed')
3680       newStyles.position = 'absolute';
3681
3682     Element.setStyle(element, newStyles);
3683
3684     var dimensions = {
3685       width:  element.offsetWidth,
3686       height: element.offsetHeight
3687     };
3688
3689     Element.setStyle(element, originalStyles);
3690
3691     return dimensions;
3692   }
3693
3694   function getOffsetParent(element) {
3695     element = $(element);
3696
3697     if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3698       return $(document.body);
3699
3700     var isInline = (Element.getStyle(element, 'display') === 'inline');
3701     if (!isInline && element.offsetParent) return $(element.offsetParent);
3702
3703     while ((element = element.parentNode) && element !== document.body) {
3704       if (Element.getStyle(element, 'position') !== 'static') {
3705         return isHtml(element) ? $(document.body) : $(element);
3706       }
3707     }
3708
3709     return $(document.body);
3710   }
3711
3712
3713   function cumulativeOffset(element) {
3714     element = $(element);
3715     var valueT = 0, valueL = 0;
3716     if (element.parentNode) {
3717       do {
3718         valueT += element.offsetTop  || 0;
3719         valueL += element.offsetLeft || 0;
3720         element = element.offsetParent;
3721       } while (element);
3722     }
3723     return new Element.Offset(valueL, valueT);
3724   }
3725
3726   function positionedOffset(element) {
3727     element = $(element);
3728
3729     var layout = element.getLayout();
3730
3731     var valueT = 0, valueL = 0;
3732     do {
3733       valueT += element.offsetTop  || 0;
3734       valueL += element.offsetLeft || 0;
3735       element = element.offsetParent;
3736       if (element) {
3737         if (isBody(element)) break;
3738         var p = Element.getStyle(element, 'position');
3739         if (p !== 'static') break;
3740       }
3741     } while (element);
3742
3743     valueL -= layout.get('margin-top');
3744     valueT -= layout.get('margin-left');
3745
3746     return new Element.Offset(valueL, valueT);
3747   }
3748
3749   function cumulativeScrollOffset(element) {
3750     var valueT = 0, valueL = 0;
3751     do {
3752       valueT += element.scrollTop  || 0;
3753       valueL += element.scrollLeft || 0;
3754       element = element.parentNode;
3755     } while (element);
3756     return new Element.Offset(valueL, valueT);
3757   }
3758
3759   function viewportOffset(forElement) {
3760     element = $(element);
3761     var valueT = 0, valueL = 0, docBody = document.body;
3762
3763     var element = forElement;
3764     do {
3765       valueT += element.offsetTop  || 0;
3766       valueL += element.offsetLeft || 0;
3767       if (element.offsetParent == docBody &&
3768         Element.getStyle(element, 'position') == 'absolute') break;
3769     } while (element = element.offsetParent);
3770
3771     element = forElement;
3772     do {
3773       if (element != docBody) {
3774         valueT -= element.scrollTop  || 0;
3775         valueL -= element.scrollLeft || 0;
3776       }
3777     } while (element = element.parentNode);
3778     return new Element.Offset(valueL, valueT);
3779   }
3780
3781   function absolutize(element) {
3782     element = $(element);
3783
3784     if (Element.getStyle(element, 'position') === 'absolute') {
3785       return element;
3786     }
3787
3788     var offsetParent = getOffsetParent(element);
3789     var eOffset = element.viewportOffset(),
3790      pOffset = offsetParent.viewportOffset();
3791
3792     var offset = eOffset.relativeTo(pOffset);
3793     var layout = element.getLayout();
3794
3795     element.store('prototype_absolutize_original_styles', {
3796       left:   element.getStyle('left'),
3797       top:    element.getStyle('top'),
3798       width:  element.getStyle('width'),
3799       height: element.getStyle('height')
3800     });
3801
3802     element.setStyle({
3803       position: 'absolute',
3804       top:    offset.top + 'px',
3805       left:   offset.left + 'px',
3806       width:  layout.get('width') + 'px',
3807       height: layout.get('height') + 'px'
3808     });
3809
3810     return element;
3811   }
3812
3813   function relativize(element) {
3814     element = $(element);
3815     if (Element.getStyle(element, 'position') === 'relative') {
3816       return element;
3817     }
3818
3819     var originalStyles =
3820      element.retrieve('prototype_absolutize_original_styles');
3821
3822     if (originalStyles) element.setStyle(originalStyles);
3823     return element;
3824   }
3825
3826   if (Prototype.Browser.IE) {
3827     getOffsetParent = getOffsetParent.wrap(
3828       function(proceed, element) {
3829         element = $(element);
3830
3831         if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
3832           return $(document.body);
3833
3834         var position = element.getStyle('position');
3835         if (position !== 'static') return proceed(element);
3836
3837         element.setStyle({ position: 'relative' });
3838         var value = proceed(element);
3839         element.setStyle({ position: position });
3840         return value;
3841       }
3842     );
3843
3844     positionedOffset = positionedOffset.wrap(function(proceed, element) {
3845       element = $(element);
3846       if (!element.parentNode) return new Element.Offset(0, 0);
3847       var position = element.getStyle('position');
3848       if (position !== 'static') return proceed(element);
3849
3850       var offsetParent = element.getOffsetParent();
3851       if (offsetParent && offsetParent.getStyle('position') === 'fixed')
3852         hasLayout(offsetParent);
3853
3854       element.setStyle({ position: 'relative' });
3855       var value = proceed(element);
3856       element.setStyle({ position: position });
3857       return value;
3858     });
3859   } else if (Prototype.Browser.Webkit) {
3860     cumulativeOffset = function(element) {
3861       element = $(element);
3862       var valueT = 0, valueL = 0;
3863       do {
3864         valueT += element.offsetTop  || 0;
3865         valueL += element.offsetLeft || 0;
3866         if (element.offsetParent == document.body)
3867           if (Element.getStyle(element, 'position') == 'absolute') break;
3868
3869         element = element.offsetParent;
3870       } while (element);
3871
3872       return new Element.Offset(valueL, valueT);
3873     };
3874   }
3875
3876
3877   Element.addMethods({
3878     getLayout:              getLayout,
3879     measure:                measure,
3880     getDimensions:          getDimensions,
3881     getOffsetParent:        getOffsetParent,
3882     cumulativeOffset:       cumulativeOffset,
3883     positionedOffset:       positionedOffset,
3884     cumulativeScrollOffset: cumulativeScrollOffset,
3885     viewportOffset:         viewportOffset,
3886     absolutize:             absolutize,
3887     relativize:             relativize
3888   });
3889
3890   function isBody(element) {
3891     return element.nodeName.toUpperCase() === 'BODY';
3892   }
3893
3894   function isHtml(element) {
3895     return element.nodeName.toUpperCase() === 'HTML';
3896   }
3897
3898   function isDocument(element) {
3899     return element.nodeType === Node.DOCUMENT_NODE;
3900   }
3901
3902   function isDetached(element) {
3903     return element !== document.body &&
3904      !Element.descendantOf(element, document.body);
3905   }
3906
3907   if ('getBoundingClientRect' in document.documentElement) {
3908     Element.addMethods({
3909       viewportOffset: function(element) {
3910         element = $(element);
3911         if (isDetached(element)) return new Element.Offset(0, 0);
3912
3913         var rect = element.getBoundingClientRect(),
3914          docEl = document.documentElement;
3915         return new Element.Offset(rect.left - docEl.clientLeft,
3916          rect.top - docEl.clientTop);
3917       }
3918     });
3919   }
3920 })();
3921 window.$$ = function() {
3922   var expression = $A(arguments).join(', ');
3923   return Prototype.Selector.select(expression, document);