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