9fe6e1243bc0a7cf652985c3edd0d4dc9d8bdfb8
[infodrom/rico3] / ricoClient / js / baselibs / prototype.js
1 /*  Prototype JavaScript framework, version 1.6.1
2  *  (c) 2005-2009 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   Version: '1.6.1',
11
12   Browser: (function(){
13     var ua = navigator.userAgent;
14     var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
15     return {
16       IE:             !!window.attachEvent && !isOpera,
17       Opera:          isOpera,
18       WebKit:         ua.indexOf('AppleWebKit/') > -1,
19       Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
20       MobileSafari:   /Apple.*Mobile.*Safari/.test(ua)
21     }
22   })(),
23
24   BrowserFeatures: {
25     XPath: !!document.evaluate,
26     SelectorsAPI: !!document.querySelector,
27     ElementExtensions: (function() {
28       var constructor = window.Element || window.HTMLElement;
29       return !!(constructor && constructor.prototype);
30     })(),
31     SpecificElementExtensions: (function() {
32       if (typeof window.HTMLDivElement !== 'undefined')
33         return true;
34
35       var div = document.createElement('div');
36       var form = document.createElement('form');
37       var isSupported = false;
38
39       if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
40         isSupported = true;
41       }
42
43       div = form = null;
44
45       return isSupported;
46     })()
47   },
48
49   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
50   JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
51
52   emptyFunction: function() { },
53   K: function(x) { return x }
54 };
55
56 if (Prototype.Browser.MobileSafari)
57   Prototype.BrowserFeatures.SpecificElementExtensions = false;
58
59
60 var Abstract = { };
61
62
63 var Try = {
64   these: function() {
65     var returnValue;
66
67     for (var i = 0, length = arguments.length; i < length; i++) {
68       var lambda = arguments[i];
69       try {
70         returnValue = lambda();
71         break;
72       } catch (e) { }
73     }
74
75     return returnValue;
76   }
77 };
78
79 /* Based on Alex Arnell's inheritance implementation. */
80
81 var Class = (function() {
82   function subclass() {};
83   function create() {
84     var parent = null, properties = $A(arguments);
85     if (Object.isFunction(properties[0]))
86       parent = properties.shift();
87
88     function klass() {
89       this.initialize.apply(this, arguments);
90     }
91
92     Object.extend(klass, Class.Methods);
93     klass.superclass = parent;
94     klass.subclasses = [];
95
96     if (parent) {
97       subclass.prototype = parent.prototype;
98       klass.prototype = new subclass;
99       parent.subclasses.push(klass);
100     }
101
102     for (var i = 0; i < properties.length; i++)
103       klass.addMethods(properties[i]);
104
105     if (!klass.prototype.initialize)
106       klass.prototype.initialize = Prototype.emptyFunction;
107
108     klass.prototype.constructor = klass;
109     return klass;
110   }
111
112   function addMethods(source) {
113     var ancestor   = this.superclass && this.superclass.prototype;
114     var properties = Object.keys(source);
115
116     if (!Object.keys({ toString: true }).length) {
117       if (source.toString != Object.prototype.toString)
118         properties.push("toString");
119       if (source.valueOf != Object.prototype.valueOf)
120         properties.push("valueOf");
121     }
122
123     for (var i = 0, length = properties.length; i < length; i++) {
124       var property = properties[i], value = source[property];
125       if (ancestor && Object.isFunction(value) &&
126           value.argumentNames().first() == "$super") {
127         var method = value;
128         value = (function(m) {
129           return function() { return ancestor[m].apply(this, arguments); };
130         })(property).wrap(method);
131
132         value.valueOf = method.valueOf.bind(method);
133         value.toString = method.toString.bind(method);
134       }
135       this.prototype[property] = value;
136     }
137
138     return this;
139   }
140
141   return {
142     create: create,
143     Methods: {
144       addMethods: addMethods
145     }
146   };
147 })();
148 (function() {
149
150   var _toString = Object.prototype.toString;
151
152   function extend(destination, source) {
153     for (var property in source)
154       destination[property] = source[property];
155     return destination;
156   }
157
158   function inspect(object) {
159     try {
160       if (isUndefined(object)) return 'undefined';
161       if (object === null) return 'null';
162       return object.inspect ? object.inspect() : String(object);
163     } catch (e) {
164       if (e instanceof RangeError) return '...';
165       throw e;
166     }
167   }
168
169   function toJSON(object) {
170     var type = typeof object;
171     switch (type) {
172       case 'undefined':
173       case 'function':
174       case 'unknown': return;
175       case 'boolean': return object.toString();
176     }
177
178     if (object === null) return 'null';
179     if (object.toJSON) return object.toJSON();
180     if (isElement(object)) return;
181
182     var results = [];
183     for (var property in object) {
184       var value = toJSON(object[property]);
185       if (!isUndefined(value))
186         results.push(property.toJSON() + ': ' + value);
187     }
188
189     return '{' + results.join(', ') + '}';
190   }
191
192   function toQueryString(object) {
193     return $H(object).toQueryString();
194   }
195
196   function toHTML(object) {
197     return object && object.toHTML ? object.toHTML() : String.interpret(object);
198   }
199
200   function keys(object) {
201     var results = [];
202     for (var property in object)
203       results.push(property);
204     return results;
205   }
206
207   function values(object) {
208     var results = [];
209     for (var property in object)
210       results.push(object[property]);
211     return results;
212   }
213
214   function clone(object) {
215     return extend({ }, object);
216   }
217
218   function isElement(object) {
219     return !!(object && object.nodeType == 1);
220   }
221
222   function isArray(object) {
223     return _toString.call(object) == "[object Array]";
224   }
225
226
227   function isHash(object) {
228     return object instanceof Hash;
229   }
230
231   function isFunction(object) {
232     return typeof object === "function";
233   }
234
235   function isString(object) {
236     return _toString.call(object) == "[object String]";
237   }
238
239   function isNumber(object) {
240     return _toString.call(object) == "[object Number]";
241   }
242
243   function isUndefined(object) {
244     return typeof object === "undefined";
245   }
246
247   extend(Object, {
248     extend:        extend,
249     inspect:       inspect,
250     toJSON:        toJSON,
251     toQueryString: toQueryString,
252     toHTML:        toHTML,
253     keys:          keys,
254     values:        values,
255     clone:         clone,
256     isElement:     isElement,
257     isArray:       isArray,
258     isHash:        isHash,
259     isFunction:    isFunction,
260     isString:      isString,
261     isNumber:      isNumber,
262     isUndefined:   isUndefined
263   });
264 })();
265 Object.extend(Function.prototype, (function() {
266   var slice = Array.prototype.slice;
267
268   function update(array, args) {
269     var arrayLength = array.length, length = args.length;
270     while (length--) array[arrayLength + length] = args[length];
271     return array;
272   }
273
274   function merge(array, args) {
275     array = slice.call(array, 0);
276     return update(array, args);
277   }
278
279   function argumentNames() {
280     var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
281       .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
282       .replace(/\s+/g, '').split(',');
283     return names.length == 1 && !names[0] ? [] : names;
284   }
285
286   function bind(context) {
287     if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
288     var __method = this, args = slice.call(arguments, 1);
289     return function() {
290       var a = merge(args, arguments);
291       return __method.apply(context, a);
292     }
293   }
294
295   function bindAsEventListener(context) {
296     var __method = this, args = slice.call(arguments, 1);
297     return function(event) {
298       var a = update([event || window.event], args);
299       return __method.apply(context, a);
300     }
301   }
302
303   function curry() {
304     if (!arguments.length) return this;
305     var __method = this, args = slice.call(arguments, 0);
306     return function() {
307       var a = merge(args, arguments);
308       return __method.apply(this, a);
309     }
310   }
311
312   function delay(timeout) {
313     var __method = this, args = slice.call(arguments, 1);
314     timeout = timeout * 1000
315     return window.setTimeout(function() {
316       return __method.apply(__method, args);
317     }, timeout);
318   }
319
320   function defer() {
321     var args = update([0.01], arguments);
322     return this.delay.apply(this, args);
323   }
324
325   function wrap(wrapper) {
326     var __method = this;
327     return function() {
328       var a = update([__method.bind(this)], arguments);
329       return wrapper.apply(this, a);
330     }
331   }
332
333   function methodize() {
334     if (this._methodized) return this._methodized;
335     var __method = this;
336     return this._methodized = function() {
337       var a = update([this], arguments);
338       return __method.apply(null, a);
339     };
340   }
341
342   return {
343     argumentNames:       argumentNames,
344     bind:                bind,
345     bindAsEventListener: bindAsEventListener,
346     curry:               curry,
347     delay:               delay,
348     defer:               defer,
349     wrap:                wrap,
350     methodize:           methodize
351   }
352 })());
353
354
355 Date.prototype.toJSON = function() {
356   return '"' + this.getUTCFullYear() + '-' +
357     (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
358     this.getUTCDate().toPaddedString(2) + 'T' +
359     this.getUTCHours().toPaddedString(2) + ':' +
360     this.getUTCMinutes().toPaddedString(2) + ':' +
361     this.getUTCSeconds().toPaddedString(2) + 'Z"';
362 };
363
364
365 RegExp.prototype.match = RegExp.prototype.test;
366
367 RegExp.escape = function(str) {
368   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
369 };
370 var PeriodicalExecuter = Class.create({
371   initialize: function(callback, frequency) {
372     this.callback = callback;
373     this.frequency = frequency;
374     this.currentlyExecuting = false;
375
376     this.registerCallback();
377   },
378
379   registerCallback: function() {
380     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
381   },
382
383   execute: function() {
384     this.callback(this);
385   },
386
387   stop: function() {
388     if (!this.timer) return;
389     clearInterval(this.timer);
390     this.timer = null;
391   },
392
393   onTimerEvent: function() {
394     if (!this.currentlyExecuting) {
395       try {
396         this.currentlyExecuting = true;
397         this.execute();
398         this.currentlyExecuting = false;
399       } catch(e) {
400         this.currentlyExecuting = false;
401         throw e;
402       }
403     }
404   }
405 });
406 Object.extend(String, {
407   interpret: function(value) {
408     return value == null ? '' : String(value);
409   },
410   specialChar: {
411     '\b': '\\b',
412     '\t': '\\t',
413     '\n': '\\n',
414     '\f': '\\f',
415     '\r': '\\r',
416     '\\': '\\\\'
417   }
418 });
419
420 Object.extend(String.prototype, (function() {
421
422   function prepareReplacement(replacement) {
423     if (Object.isFunction(replacement)) return replacement;
424     var template = new Template(replacement);
425     return function(match) { return template.evaluate(match) };
426   }
427
428   function gsub(pattern, replacement) {
429     var result = '', source = this, match;
430     replacement = prepareReplacement(replacement);
431
432     if (Object.isString(pattern))
433       pattern = RegExp.escape(pattern);
434
435     if (!(pattern.length || pattern.source)) {
436       replacement = replacement('');
437       return replacement + source.split('').join(replacement) + replacement;
438     }
439
440     while (source.length > 0) {
441       if (match = source.match(pattern)) {
442         result += source.slice(0, match.index);
443         result += String.interpret(replacement(match));
444         source  = source.slice(match.index + match[0].length);
445       } else {
446         result += source, source = '';
447       }
448     }
449     return result;
450   }
451
452   function sub(pattern, replacement, count) {
453     replacement = prepareReplacement(replacement);
454     count = Object.isUndefined(count) ? 1 : count;
455
456     return this.gsub(pattern, function(match) {
457       if (--count < 0) return match[0];
458       return replacement(match);
459     });
460   }
461
462   function scan(pattern, iterator) {
463     this.gsub(pattern, iterator);
464     return String(this);
465   }
466
467   function truncate(length, truncation) {
468     length = length || 30;
469     truncation = Object.isUndefined(truncation) ? '...' : truncation;
470     return this.length > length ?
471       this.slice(0, length - truncation.length) + truncation : String(this);
472   }
473
474   function strip() {
475     return this.replace(/^\s+/, '').replace(/\s+$/, '');
476   }
477
478   function stripTags() {
479     return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
480   }
481
482   function stripScripts() {
483     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
484   }
485
486   function extractScripts() {
487     var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
488     var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
489     return (this.match(matchAll) || []).map(function(scriptTag) {
490       return (scriptTag.match(matchOne) || ['', ''])[1];
491     });
492   }
493
494   function evalScripts() {
495     return this.extractScripts().map(function(script) { return eval(script) });
496   }
497
498   function escapeHTML() {
499     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
500   }
501
502   function unescapeHTML() {
503     return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
504   }
505
506
507   function toQueryParams(separator) {
508     var match = this.strip().match(/([^?#]*)(#.*)?$/);
509     if (!match) return { };
510
511     return match[1].split(separator || '&').inject({ }, function(hash, pair) {
512       if ((pair = pair.split('='))[0]) {
513         var key = decodeURIComponent(pair.shift());
514         var value = pair.length > 1 ? pair.join('=') : pair[0];
515         if (value != undefined) value = decodeURIComponent(value);
516
517         if (key in hash) {
518           if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
519           hash[key].push(value);
520         }
521         else hash[key] = value;
522       }
523       return hash;
524     });
525   }
526
527   function toArray() {
528     return this.split('');
529   }
530
531   function succ() {
532     return this.slice(0, this.length - 1) +
533       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
534   }
535
536   function times(count) {
537     return count < 1 ? '' : new Array(count + 1).join(this);
538   }
539
540   function camelize() {
541     var parts = this.split('-'), len = parts.length;
542     if (len == 1) return parts[0];
543
544     var camelized = this.charAt(0) == '-'
545       ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
546       : parts[0];
547
548     for (var i = 1; i < len; i++)
549       camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
550
551     return camelized;
552   }
553
554   function capitalize() {
555     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
556   }
557
558   function underscore() {
559     return this.replace(/::/g, '/')
560                .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
561                .replace(/([a-z\d])([A-Z])/g, '$1_$2')
562                .replace(/-/g, '_')
563                .toLowerCase();
564   }
565
566   function dasherize() {
567     return this.replace(/_/g, '-');
568   }
569
570   function inspect(useDoubleQuotes) {
571     var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
572       if (character in String.specialChar) {
573         return String.specialChar[character];
574       }
575       return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
576     });
577     if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
578     return "'" + escapedString.replace(/'/g, '\\\'') + "'";
579   }
580
581   function toJSON() {
582     return this.inspect(true);
583   }
584
585   function unfilterJSON(filter) {
586     return this.replace(filter || Prototype.JSONFilter, '$1');
587   }
588
589   function isJSON() {
590     var str = this;
591     if (str.blank()) return false;
592     str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
593     return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
594   }
595
596   function evalJSON(sanitize) {
597     var json = this.unfilterJSON();
598     try {
599       if (!sanitize || json.isJSON()) return eval('(' + json + ')');
600     } catch (e) { }
601     throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
602   }
603
604   function include(pattern) {
605     return this.indexOf(pattern) > -1;
606   }
607
608   function startsWith(pattern) {
609     return this.indexOf(pattern) === 0;
610   }
611
612   function endsWith(pattern) {
613     var d = this.length - pattern.length;
614     return d >= 0 && this.lastIndexOf(pattern) === d;
615   }
616
617   function empty() {
618     return this == '';
619   }
620
621   function blank() {
622     return /^\s*$/.test(this);
623   }
624
625   function interpolate(object, pattern) {
626     return new Template(this, pattern).evaluate(object);
627   }
628
629   return {
630     gsub:           gsub,
631     sub:            sub,
632     scan:           scan,
633     truncate:       truncate,
634     strip:          String.prototype.trim ? String.prototype.trim : strip,
635     stripTags:      stripTags,
636     stripScripts:   stripScripts,
637     extractScripts: extractScripts,
638     evalScripts:    evalScripts,
639     escapeHTML:     escapeHTML,
640     unescapeHTML:   unescapeHTML,
641     toQueryParams:  toQueryParams,
642     parseQuery:     toQueryParams,
643     toArray:        toArray,
644     succ:           succ,
645     times:          times,
646     camelize:       camelize,
647     capitalize:     capitalize,
648     underscore:     underscore,
649     dasherize:      dasherize,
650     inspect:        inspect,
651     toJSON:         toJSON,
652     unfilterJSON:   unfilterJSON,
653     isJSON:         isJSON,
654     evalJSON:       evalJSON,
655     include:        include,
656     startsWith:     startsWith,
657     endsWith:       endsWith,
658     empty:          empty,
659     blank:          blank,
660     interpolate:    interpolate
661   };
662 })());
663
664 var Template = Class.create({
665   initialize: function(template, pattern) {
666     this.template = template.toString();
667     this.pattern = pattern || Template.Pattern;
668   },
669
670   evaluate: function(object) {
671     if (object && Object.isFunction(object.toTemplateReplacements))
672       object = object.toTemplateReplacements();
673
674     return this.template.gsub(this.pattern, function(match) {
675       if (object == null) return (match[1] + '');
676
677       var before = match[1] || '';
678       if (before == '\\') return match[2];
679
680       var ctx = object, expr = match[3];
681       var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
682       match = pattern.exec(expr);
683       if (match == null) return before;
684
685       while (match != null) {
686         var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
687         ctx = ctx[comp];
688         if (null == ctx || '' == match[3]) break;
689         expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
690         match = pattern.exec(expr);
691       }
692
693       return before + String.interpret(ctx);
694     });
695   }
696 });
697 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
698
699 var $break = { };
700
701 var Enumerable = (function() {
702   function each(iterator, context) {
703     var index = 0;
704     try {
705       this._each(function(value) {
706         iterator.call(context, value, index++);
707       });
708     } catch (e) {
709       if (e != $break) throw e;
710     }
711     return this;
712   }
713
714   function eachSlice(number, iterator, context) {
715     var index = -number, slices = [], array = this.toArray();
716     if (number < 1) return array;
717     while ((index += number) < array.length)
718       slices.push(array.slice(index, index+number));
719     return slices.collect(iterator, context);
720   }
721
722   function all(iterator, context) {
723     iterator = iterator || Prototype.K;
724     var result = true;
725     this.each(function(value, index) {
726       result = result && !!iterator.call(context, value, index);
727       if (!result) throw $break;
728     });
729     return result;
730   }
731
732   function any(iterator, context) {
733     iterator = iterator || Prototype.K;
734     var result = false;
735     this.each(function(value, index) {
736       if (result = !!iterator.call(context, value, index))
737         throw $break;
738     });
739     return result;
740   }
741
742   function collect(iterator, context) {
743     iterator = iterator || Prototype.K;
744     var results = [];
745     this.each(function(value, index) {
746       results.push(iterator.call(context, value, index));
747     });
748     return results;
749   }
750
751   function detect(iterator, context) {
752     var result;
753     this.each(function(value, index) {
754       if (iterator.call(context, value, index)) {
755         result = value;
756         throw $break;
757       }
758     });
759     return result;
760   }
761
762   function findAll(iterator, context) {
763     var results = [];
764     this.each(function(value, index) {
765       if (iterator.call(context, value, index))
766         results.push(value);
767     });
768     return results;
769   }
770
771   function grep(filter, iterator, context) {
772     iterator = iterator || Prototype.K;
773     var results = [];
774
775     if (Object.isString(filter))
776       filter = new RegExp(RegExp.escape(filter));
777
778     this.each(function(value, index) {
779       if (filter.match(value))
780         results.push(iterator.call(context, value, index));
781     });
782     return results;
783   }
784
785   function include(object) {
786     if (Object.isFunction(this.indexOf))
787       if (this.indexOf(object) != -1) return true;
788
789     var found = false;
790     this.each(function(value) {
791       if (value == object) {
792         found = true;
793         throw $break;
794       }
795     });
796     return found;
797   }
798
799   function inGroupsOf(number, fillWith) {
800     fillWith = Object.isUndefined(fillWith) ? null : fillWith;
801     return this.eachSlice(number, function(slice) {
802       while(slice.length < number) slice.push(fillWith);
803       return slice;
804     });
805   }
806
807   function inject(memo, iterator, context) {
808     this.each(function(value, index) {
809       memo = iterator.call(context, memo, value, index);
810     });
811     return memo;
812   }
813
814   function invoke(method) {
815     var args = $A(arguments).slice(1);
816     return this.map(function(value) {
817       return value[method].apply(value, args);
818     });
819   }
820
821   function max(iterator, context) {
822     iterator = iterator || Prototype.K;
823     var result;
824     this.each(function(value, index) {
825       value = iterator.call(context, value, index);
826       if (result == null || value >= result)
827         result = value;
828     });
829     return result;
830   }
831
832   function min(iterator, context) {
833     iterator = iterator || Prototype.K;
834     var result;
835     this.each(function(value, index) {
836       value = iterator.call(context, value, index);
837       if (result == null || value < result)
838         result = value;
839     });
840     return result;
841   }
842
843   function partition(iterator, context) {
844     iterator = iterator || Prototype.K;
845     var trues = [], falses = [];
846     this.each(function(value, index) {
847       (iterator.call(context, value, index) ?
848         trues : falses).push(value);
849     });
850     return [trues, falses];
851   }
852
853   function pluck(property) {
854     var results = [];
855     this.each(function(value) {
856       results.push(value[property]);
857     });
858     return results;
859   }
860
861   function reject(iterator, context) {
862     var results = [];
863     this.each(function(value, index) {
864       if (!iterator.call(context, value, index))
865         results.push(value);
866     });
867     return results;
868   }
869
870   function sortBy(iterator, context) {
871     return this.map(function(value, index) {
872       return {
873         value: value,
874         criteria: iterator.call(context, value, index)
875       };
876     }).sort(function(left, right) {
877       var a = left.criteria, b = right.criteria;
878       return a < b ? -1 : a > b ? 1 : 0;
879     }).pluck('value');
880   }
881
882   function toArray() {
883     return this.map();
884   }
885
886   function zip() {
887     var iterator = Prototype.K, args = $A(arguments);
888     if (Object.isFunction(args.last()))
889       iterator = args.pop();
890
891     var collections = [this].concat(args).map($A);
892     return this.map(function(value, index) {
893       return iterator(collections.pluck(index));
894     });
895   }
896
897   function size() {
898     return this.toArray().length;
899   }
900
901   function inspect() {
902     return '#<Enumerable:' + this.toArray().inspect() + '>';
903   }
904
905
906
907
908
909
910
911
912
913   return {
914     each:       each,
915     eachSlice:  eachSlice,
916     all:        all,
917     every:      all,
918     any:        any,
919     some:       any,
920     collect:    collect,
921     map:        collect,
922     detect:     detect,
923     findAll:    findAll,
924     select:     findAll,
925     filter:     findAll,
926     grep:       grep,
927     include:    include,
928     member:     include,
929     inGroupsOf: inGroupsOf,
930     inject:     inject,
931     invoke:     invoke,
932     max:        max,
933     min:        min,
934     partition:  partition,
935     pluck:      pluck,
936     reject:     reject,
937     sortBy:     sortBy,
938     toArray:    toArray,
939     entries:    toArray,
940     zip:        zip,
941     size:       size,
942     inspect:    inspect,
943     find:       detect
944   };
945 })();
946 function $A(iterable) {
947   if (!iterable) return [];
948   if ('toArray' in Object(iterable)) return iterable.toArray();
949   var length = iterable.length || 0, results = new Array(length);
950   while (length--) results[length] = iterable[length];
951   return results;
952 }
953
954 function $w(string) {
955   if (!Object.isString(string)) return [];
956   string = string.strip();
957   return string ? string.split(/\s+/) : [];
958 }
959
960 Array.from = $A;
961
962
963 (function() {
964   var arrayProto = Array.prototype,
965       slice = arrayProto.slice,
966       _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
967
968   function each(iterator) {
969     for (var i = 0, length = this.length; i < length; i++)
970       iterator(this[i]);
971   }
972   if (!_each) _each = each;
973
974   function clear() {
975     this.length = 0;
976     return this;
977   }
978
979   function first() {
980     return this[0];
981   }
982
983   function last() {
984     return this[this.length - 1];
985   }
986
987   function compact() {
988     return this.select(function(value) {
989       return value != null;
990     });
991   }
992
993   function flatten() {
994     return this.inject([], function(array, value) {
995       if (Object.isArray(value))
996         return array.concat(value.flatten());
997       array.push(value);
998       return array;
999     });
1000   }
1001
1002   function without() {
1003     var values = slice.call(arguments, 0);
1004     return this.select(function(value) {
1005       return !values.include(value);
1006     });
1007   }
1008
1009   function reverse(inline) {
1010     return (inline !== false ? this : this.toArray())._reverse();
1011   }
1012
1013   function uniq(sorted) {
1014     return this.inject([], function(array, value, index) {
1015       if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1016         array.push(value);
1017       return array;
1018     });
1019   }
1020
1021   function intersect(array) {
1022     return this.uniq().findAll(function(item) {
1023       return array.detect(function(value) { return item === value });
1024     });
1025   }
1026
1027
1028   function clone() {
1029     return slice.call(this, 0);
1030   }
1031
1032   function size() {
1033     return this.length;
1034   }
1035
1036   function inspect() {
1037     return '[' + this.map(Object.inspect).join(', ') + ']';
1038   }
1039
1040   function toJSON() {
1041     var results = [];
1042     this.each(function(object) {
1043       var value = Object.toJSON(object);
1044       if (!Object.isUndefined(value)) results.push(value);
1045     });
1046     return '[' + results.join(', ') + ']';
1047   }
1048
1049   function indexOf(item, i) {
1050     i || (i = 0);
1051     var length = this.length;
1052     if (i < 0) i = length + i;
1053     for (; i < length; i++)
1054       if (this[i] === item) return i;
1055     return -1;
1056   }
1057
1058   function lastIndexOf(item, i) {
1059     i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
1060     var n = this.slice(0, i).reverse().indexOf(item);
1061     return (n < 0) ? n : i - n - 1;
1062   }
1063
1064   function concat() {
1065     var array = slice.call(this, 0), item;
1066     for (var i = 0, length = arguments.length; i < length; i++) {
1067       item = arguments[i];
1068       if (Object.isArray(item) && !('callee' in item)) {
1069         for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
1070           array.push(item[j]);
1071       } else {
1072         array.push(item);
1073       }
1074     }
1075     return array;
1076   }
1077
1078   Object.extend(arrayProto, Enumerable);
1079
1080   if (!arrayProto._reverse)
1081     arrayProto._reverse = arrayProto.reverse;
1082
1083   Object.extend(arrayProto, {
1084     _each:     _each,
1085     clear:     clear,
1086     first:     first,
1087     last:      last,
1088     compact:   compact,
1089     flatten:   flatten,
1090     without:   without,
1091     reverse:   reverse,
1092     uniq:      uniq,
1093     intersect: intersect,
1094     clone:     clone,
1095     toArray:   clone,
1096     size:      size,
1097     inspect:   inspect,
1098     toJSON:    toJSON
1099   });
1100
1101   var CONCAT_ARGUMENTS_BUGGY = (function() {
1102     return [].concat(arguments)[0][0] !== 1;
1103   })(1,2)
1104
1105   if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1106
1107   if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1108   if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1109 })();
1110 function $H(object) {
1111   return new Hash(object);
1112 };
1113
1114 var Hash = Class.create(Enumerable, (function() {
1115   function initialize(object) {
1116     this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1117   }
1118
1119   function _each(iterator) {
1120     for (var key in this._object) {
1121       var value = this._object[key], pair = [key, value];
1122       pair.key = key;
1123       pair.value = value;
1124       iterator(pair);
1125     }
1126   }
1127
1128   function set(key, value) {
1129     return this._object[key] = value;
1130   }
1131
1132   function get(key) {
1133     if (this._object[key] !== Object.prototype[key])
1134       return this._object[key];
1135   }
1136
1137   function unset(key) {
1138     var value = this._object[key];
1139     delete this._object[key];
1140     return value;
1141   }
1142
1143   function toObject() {
1144     return Object.clone(this._object);
1145   }
1146
1147   function keys() {
1148     return this.pluck('key');
1149   }
1150
1151   function values() {
1152     return this.pluck('value');
1153   }
1154
1155   function index(value) {
1156     var match = this.detect(function(pair) {
1157       return pair.value === value;
1158     });
1159     return match && match.key;
1160   }
1161
1162   function merge(object) {
1163     return this.clone().update(object);
1164   }
1165
1166   function update(object) {
1167     return new Hash(object).inject(this, function(result, pair) {
1168       result.set(pair.key, pair.value);
1169       return result;
1170     });
1171   }
1172
1173   function toQueryPair(key, value) {
1174     if (Object.isUndefined(value)) return key;
1175     return key + '=' + encodeURIComponent(String.interpret(value));
1176   }
1177
1178   function toQueryString() {
1179     return this.inject([], function(results, pair) {
1180       var key = encodeURIComponent(pair.key), values = pair.value;
1181
1182       if (values && typeof values == 'object') {
1183         if (Object.isArray(values))
1184           return results.concat(values.map(toQueryPair.curry(key)));
1185       } else results.push(toQueryPair(key, values));
1186       return results;
1187     }).join('&');
1188   }
1189
1190   function inspect() {
1191     return '#<Hash:{' + this.map(function(pair) {
1192       return pair.map(Object.inspect).join(': ');
1193     }).join(', ') + '}>';
1194   }
1195
1196   function toJSON() {
1197     return Object.toJSON(this.toObject());
1198   }
1199
1200   function clone() {
1201     return new Hash(this);
1202   }
1203
1204   return {
1205     initialize:             initialize,
1206     _each:                  _each,
1207     set:                    set,
1208     get:                    get,
1209     unset:                  unset,
1210     toObject:               toObject,
1211     toTemplateReplacements: toObject,
1212     keys:                   keys,
1213     values:                 values,
1214     index:                  index,
1215     merge:                  merge,
1216     update:                 update,
1217     toQueryString:          toQueryString,
1218     inspect:                inspect,
1219     toJSON:                 toJSON,
1220     clone:                  clone
1221   };
1222 })());
1223
1224 Hash.from = $H;
1225 Object.extend(Number.prototype, (function() {
1226   function toColorPart() {
1227     return this.toPaddedString(2, 16);
1228   }
1229
1230   function succ() {
1231     return this + 1;
1232   }
1233
1234   function times(iterator, context) {
1235     $R(0, this, true).each(iterator, context);
1236     return this;
1237   }
1238
1239   function toPaddedString(length, radix) {
1240     var string = this.toString(radix || 10);
1241     return '0'.times(length - string.length) + string;
1242   }
1243
1244   function toJSON() {
1245     return isFinite(this) ? this.toString() : 'null';
1246   }
1247
1248   function abs() {
1249     return Math.abs(this);
1250   }
1251
1252   function round() {
1253     return Math.round(this);
1254   }
1255
1256   function ceil() {
1257     return Math.ceil(this);
1258   }
1259
1260   function floor() {
1261     return Math.floor(this);
1262   }
1263
1264   return {
1265     toColorPart:    toColorPart,
1266     succ:           succ,
1267     times:          times,
1268     toPaddedString: toPaddedString,
1269     toJSON:         toJSON,
1270     abs:            abs,
1271     round:          round,
1272     ceil:           ceil,
1273     floor:          floor
1274   };
1275 })());
1276
1277 function $R(start, end, exclusive) {
1278   return new ObjectRange(start, end, exclusive);
1279 }
1280
1281 var ObjectRange = Class.create(Enumerable, (function() {
1282   function initialize(start, end, exclusive) {
1283     this.start = start;
1284     this.end = end;
1285     this.exclusive = exclusive;
1286   }
1287
1288   function _each(iterator) {
1289     var value = this.start;
1290     while (this.include(value)) {
1291       iterator(value);
1292       value = value.succ();
1293     }
1294   }
1295
1296   function include(value) {
1297     if (value < this.start)
1298       return false;
1299     if (this.exclusive)
1300       return value < this.end;
1301     return value <= this.end;
1302   }
1303
1304   return {
1305     initialize: initialize,
1306     _each:      _each,
1307     include:    include
1308   };
1309 })());
1310
1311
1312
1313 var Ajax = {
1314   getTransport: function() {
1315     return Try.these(
1316       function() {return new XMLHttpRequest()},
1317       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1318       function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1319     ) || false;
1320   },
1321
1322   activeRequestCount: 0
1323 };
1324
1325 Ajax.Responders = {
1326   responders: [],
1327
1328   _each: function(iterator) {
1329     this.responders._each(iterator);
1330   },
1331
1332   register: function(responder) {
1333     if (!this.include(responder))
1334       this.responders.push(responder);
1335   },
1336
1337   unregister: function(responder) {
1338     this.responders = this.responders.without(responder);
1339   },
1340
1341   dispatch: function(callback, request, transport, json) {
1342     this.each(function(responder) {
1343       if (Object.isFunction(responder[callback])) {
1344         try {
1345           responder[callback].apply(responder, [request, transport, json]);
1346         } catch (e) { }
1347       }
1348     });
1349   }
1350 };
1351
1352 Object.extend(Ajax.Responders, Enumerable);
1353
1354 Ajax.Responders.register({
1355   onCreate:   function() { Ajax.activeRequestCount++ },
1356   onComplete: function() { Ajax.activeRequestCount-- }
1357 });
1358 Ajax.Base = Class.create({
1359   initialize: function(options) {
1360     this.options = {
1361       method:       'post',
1362       asynchronous: true,
1363       contentType:  'application/x-www-form-urlencoded',
1364       encoding:     'UTF-8',
1365       parameters:   '',
1366       evalJSON:     true,
1367       evalJS:       true
1368     };
1369     Object.extend(this.options, options || { });
1370
1371     this.options.method = this.options.method.toLowerCase();
1372
1373     if (Object.isString(this.options.parameters))
1374       this.options.parameters = this.options.parameters.toQueryParams();
1375     else if (Object.isHash(this.options.parameters))
1376       this.options.parameters = this.options.parameters.toObject();
1377   }
1378 });
1379 Ajax.Request = Class.create(Ajax.Base, {
1380   _complete: false,
1381
1382   initialize: function($super, url, options) {
1383     $super(options);
1384     this.transport = Ajax.getTransport();
1385     this.request(url);
1386   },
1387
1388   request: function(url) {
1389     this.url = url;
1390     this.method = this.options.method;
1391     var params = Object.clone(this.options.parameters);
1392
1393     if (!['get', 'post'].include(this.method)) {
1394       params['_method'] = this.method;
1395       this.method = 'post';
1396     }
1397
1398     this.parameters = params;
1399
1400     if (params = Object.toQueryString(params)) {
1401       if (this.method == 'get')
1402         this.url += (this.url.include('?') ? '&' : '?') + params;
1403       else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1404         params += '&_=';
1405     }
1406
1407     try {
1408       var response = new Ajax.Response(this);
1409       if (this.options.onCreate) this.options.onCreate(response);
1410       Ajax.Responders.dispatch('onCreate', this, response);
1411
1412       this.transport.open(this.method.toUpperCase(), this.url,
1413         this.options.asynchronous);
1414
1415       if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1416
1417       this.transport.onreadystatechange = this.onStateChange.bind(this);
1418       this.setRequestHeaders();
1419
1420       this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1421       this.transport.send(this.body);
1422
1423       /* Force Firefox to handle ready state 4 for synchronous requests */
1424       if (!this.options.asynchronous && this.transport.overrideMimeType)
1425         this.onStateChange();
1426
1427     }
1428     catch (e) {
1429       this.dispatchException(e);
1430     }
1431   },
1432
1433   onStateChange: function() {
1434     var readyState = this.transport.readyState;
1435     if (readyState > 1 && !((readyState == 4) && this._complete))
1436       this.respondToReadyState(this.transport.readyState);
1437   },
1438
1439   setRequestHeaders: function() {
1440     var headers = {
1441       'X-Requested-With': 'XMLHttpRequest',
1442       'X-Prototype-Version': Prototype.Version,
1443       'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1444     };
1445
1446     if (this.method == 'post') {
1447       headers['Content-type'] = this.options.contentType +
1448         (this.options.encoding ? '; charset=' + this.options.encoding : '');
1449
1450       /* Force "Connection: close" for older Mozilla browsers to work
1451        * around a bug where XMLHttpRequest sends an incorrect
1452        * Content-length header. See Mozilla Bugzilla #246651.
1453        */
1454       if (this.transport.overrideMimeType &&
1455           (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1456             headers['Connection'] = 'close';
1457     }
1458
1459     if (typeof this.options.requestHeaders == 'object') {
1460       var extras = this.options.requestHeaders;
1461
1462       if (Object.isFunction(extras.push))
1463         for (var i = 0, length = extras.length; i < length; i += 2)
1464           headers[extras[i]] = extras[i+1];
1465       else
1466         $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1467     }
1468
1469     for (var name in headers)
1470       this.transport.setRequestHeader(name, headers[name]);
1471   },
1472
1473   success: function() {
1474     var status = this.getStatus();
1475     return !status || (status >= 200 && status < 300);
1476   },
1477
1478   getStatus: function() {
1479     try {
1480       return this.transport.status || 0;
1481     } catch (e) { return 0 }
1482   },
1483
1484   respondToReadyState: function(readyState) {
1485     var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1486
1487     if (state == 'Complete') {
1488       try {
1489         this._complete = true;
1490         (this.options['on' + response.status]
1491          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1492          || Prototype.emptyFunction)(response, response.headerJSON);
1493       } catch (e) {
1494         this.dispatchException(e);
1495       }
1496
1497       var contentType = response.getHeader('Content-type');
1498       if (this.options.evalJS == 'force'
1499           || (this.options.evalJS && this.isSameOrigin() && contentType
1500           && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1501         this.evalResponse();
1502     }
1503
1504     try {
1505       (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1506       Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1507     } catch (e) {
1508       this.dispatchException(e);
1509     }
1510
1511     if (state == 'Complete') {
1512       this.transport.onreadystatechange = Prototype.emptyFunction;
1513     }
1514   },
1515
1516   isSameOrigin: function() {
1517     var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1518     return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1519       protocol: location.protocol,
1520       domain: document.domain,
1521       port: location.port ? ':' + location.port : ''
1522     }));
1523   },
1524
1525   getHeader: function(name) {
1526     try {
1527       return this.transport.getResponseHeader(name) || null;
1528     } catch (e) { return null; }
1529   },
1530
1531   evalResponse: function() {
1532     try {
1533       return eval((this.transport.responseText || '').unfilterJSON());
1534     } catch (e) {
1535       this.dispatchException(e);
1536     }
1537   },
1538
1539   dispatchException: function(exception) {
1540     (this.options.onException || Prototype.emptyFunction)(this, exception);
1541     Ajax.Responders.dispatch('onException', this, exception);
1542   }
1543 });
1544
1545 Ajax.Request.Events =
1546   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1547
1548
1549
1550
1551
1552
1553
1554
1555 Ajax.Response = Class.create({
1556   initialize: function(request){
1557     this.request = request;
1558     var transport  = this.transport  = request.transport,
1559         readyState = this.readyState = transport.readyState;
1560
1561     if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1562       this.status       = this.getStatus();
1563       this.statusText   = this.getStatusText();
1564       this.responseText = String.interpret(transport.responseText);
1565       this.headerJSON   = this._getHeaderJSON();
1566     }
1567
1568     if(readyState == 4) {
1569       var xml = transport.responseXML;
1570       this.responseXML  = Object.isUndefined(xml) ? null : xml;
1571       this.responseJSON = this._getResponseJSON();
1572     }
1573   },
1574
1575   status:      0,
1576
1577   statusText: '',
1578
1579   getStatus: Ajax.Request.prototype.getStatus,
1580
1581   getStatusText: function() {
1582     try {
1583       return this.transport.statusText || '';
1584     } catch (e) { return '' }
1585   },
1586
1587   getHeader: Ajax.Request.prototype.getHeader,
1588
1589   getAllHeaders: function() {
1590     try {
1591       return this.getAllResponseHeaders();
1592     } catch (e) { return null }
1593   },
1594
1595   getResponseHeader: function(name) {
1596     return this.transport.getResponseHeader(name);
1597   },
1598
1599   getAllResponseHeaders: function() {
1600     return this.transport.getAllResponseHeaders();
1601   },
1602
1603   _getHeaderJSON: function() {
1604     var json = this.getHeader('X-JSON');
1605     if (!json) return null;
1606     json = decodeURIComponent(escape(json));
1607     try {
1608       return json.evalJSON(this.request.options.sanitizeJSON ||
1609         !this.request.isSameOrigin());
1610     } catch (e) {
1611       this.request.dispatchException(e);
1612     }
1613   },
1614
1615   _getResponseJSON: function() {
1616     var options = this.request.options;
1617     if (!options.evalJSON || (options.evalJSON != 'force' &&
1618       !(this.getHeader('Content-type') || '').include('application/json')) ||
1619         this.responseText.blank())
1620           return null;
1621     try {
1622       return this.responseText.evalJSON(options.sanitizeJSON ||
1623         !this.request.isSameOrigin());
1624     } catch (e) {
1625       this.request.dispatchException(e);
1626     }
1627   }
1628 });
1629
1630 Ajax.Updater = Class.create(Ajax.Request, {
1631   initialize: function($super, container, url, options) {
1632     this.container = {
1633       success: (container.success || container),
1634       failure: (container.failure || (container.success ? null : container))
1635     };
1636
1637     options = Object.clone(options);
1638     var onComplete = options.onComplete;
1639     options.onComplete = (function(response, json) {
1640       this.updateContent(response.responseText);
1641       if (Object.isFunction(onComplete)) onComplete(response, json);
1642     }).bind(this);
1643
1644     $super(url, options);
1645   },
1646
1647   updateContent: function(responseText) {
1648     var receiver = this.container[this.success() ? 'success' : 'failure'],
1649         options = this.options;
1650
1651     if (!options.evalScripts) responseText = responseText.stripScripts();
1652
1653     if (receiver = $(receiver)) {
1654       if (options.insertion) {
1655         if (Object.isString(options.insertion)) {
1656           var insertion = { }; insertion[options.insertion] = responseText;
1657           receiver.insert(insertion);
1658         }
1659         else options.insertion(receiver, responseText);
1660       }
1661       else receiver.update(responseText);
1662     }
1663   }
1664 });
1665
1666 Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1667   initialize: function($super, container, url, options) {
1668     $super(options);
1669     this.onComplete = this.options.onComplete;
1670
1671     this.frequency = (this.options.frequency || 2);
1672     this.decay = (this.options.decay || 1);
1673
1674     this.updater = { };
1675     this.container = container;
1676     this.url = url;
1677
1678     this.start();
1679   },
1680
1681   start: function() {
1682     this.options.onComplete = this.updateComplete.bind(this);
1683     this.onTimerEvent();
1684   },
1685
1686   stop: function() {
1687     this.updater.options.onComplete = undefined;
1688     clearTimeout(this.timer);
1689     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1690   },
1691
1692   updateComplete: function(response) {
1693     if (this.options.decay) {
1694       this.decay = (response.responseText == this.lastText ?
1695         this.decay * this.options.decay : 1);
1696
1697       this.lastText = response.responseText;
1698     }
1699     this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1700   },
1701
1702   onTimerEvent: function() {
1703     this.updater = new Ajax.Updater(this.container, this.url, this.options);
1704   }
1705 });
1706
1707
1708
1709 function $(element) {
1710   if (arguments.length > 1) {
1711     for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1712       elements.push($(arguments[i]));
1713     return elements;
1714   }
1715   if (Object.isString(element))
1716     element = document.getElementById(element);
1717   return Element.extend(element);
1718 }
1719
1720 if (Prototype.BrowserFeatures.XPath) {
1721   document._getElementsByXPath = function(expression, parentElement) {
1722     var results = [];
1723     var query = document.evaluate(expression, $(parentElement) || document,
1724       null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1725     for (var i = 0, length = query.snapshotLength; i < length; i++)
1726       results.push(Element.extend(query.snapshotItem(i)));
1727     return results;
1728   };
1729 }
1730
1731 /*--------------------------------------------------------------------------*/
1732
1733 if (!window.Node) var Node = { };
1734
1735 if (!Node.ELEMENT_NODE) {
1736   Object.extend(Node, {
1737     ELEMENT_NODE: 1,
1738     ATTRIBUTE_NODE: 2,
1739     TEXT_NODE: 3,
1740     CDATA_SECTION_NODE: 4,
1741     ENTITY_REFERENCE_NODE: 5,
1742     ENTITY_NODE: 6,
1743     PROCESSING_INSTRUCTION_NODE: 7,
1744     COMMENT_NODE: 8,
1745     DOCUMENT_NODE: 9,
1746     DOCUMENT_TYPE_NODE: 10,
1747     DOCUMENT_FRAGMENT_NODE: 11,
1748     NOTATION_NODE: 12
1749   });
1750 }
1751
1752
1753 (function(global) {
1754
1755   var SETATTRIBUTE_IGNORES_NAME = (function(){
1756     var elForm = document.createElement("form");
1757     var elInput = document.createElement("input");
1758     var root = document.documentElement;
1759     elInput.setAttribute("name", "test");
1760     elForm.appendChild(elInput);
1761     root.appendChild(elForm);
1762     var isBuggy = elForm.elements
1763       ? (typeof elForm.elements.test == "undefined")
1764       : null;
1765     root.removeChild(elForm);
1766     elForm = elInput = null;
1767     return isBuggy;
1768   })();
1769
1770   var element = global.Element;
1771   global.Element = function(tagName, attributes) {
1772     attributes = attributes || { };
1773     tagName = tagName.toLowerCase();
1774     var cache = Element.cache;
1775     if (SETATTRIBUTE_IGNORES_NAME && attributes.name) {
1776       tagName = '<' + tagName + ' name="' + attributes.name + '">';
1777       delete attributes.name;
1778       return Element.writeAttribute(document.createElement(tagName), attributes);
1779     }
1780     if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1781     return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1782   };
1783   Object.extend(global.Element, element || { });
1784   if (element) global.Element.prototype = element.prototype;
1785 })(this);
1786
1787 Element.cache = { };
1788 Element.idCounter = 1;
1789
1790 Element.Methods = {
1791   visible: function(element) {
1792     return $(element).style.display != 'none';
1793   },
1794
1795   toggle: function(element) {
1796     element = $(element);
1797     Element[Element.visible(element) ? 'hide' : 'show'](element);
1798     return element;
1799   },
1800
1801
1802   hide: function(element) {
1803     element = $(element);
1804     element.style.display = 'none';
1805     return element;
1806   },
1807
1808   show: function(element) {
1809     element = $(element);
1810     element.style.display = '';
1811     return element;
1812   },
1813
1814   remove: function(element) {
1815     element = $(element);
1816     element.parentNode.removeChild(element);
1817     return element;
1818   },
1819
1820   update: (function(){
1821
1822     var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
1823       var el = document.createElement("select"),
1824           isBuggy = true;
1825       el.innerHTML = "<option value=\"test\">test</option>";
1826       if (el.options && el.options[0]) {
1827         isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
1828       }
1829       el = null;
1830       return isBuggy;
1831     })();
1832
1833     var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
1834       try {
1835         var el = document.createElement("table");
1836         if (el && el.tBodies) {
1837           el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
1838           var isBuggy = typeof el.tBodies[0] == "undefined";
1839           el = null;
1840           return isBuggy;
1841         }
1842       } catch (e) {
1843         return true;
1844       }
1845     })();
1846
1847     var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
1848       var s = document.createElement("script"),
1849           isBuggy = false;
1850       try {
1851         s.appendChild(document.createTextNode(""));
1852         isBuggy = !s.firstChild ||
1853           s.firstChild && s.firstChild.nodeType !== 3;
1854       } catch (e) {
1855         isBuggy = true;
1856       }
1857       s = null;
1858       return isBuggy;
1859     })();
1860
1861     function update(element, content) {
1862       element = $(element);
1863
1864       if (content && content.toElement)
1865         content = content.toElement();
1866
1867       if (Object.isElement(content))
1868         return element.update().insert(content);
1869
1870       content = Object.toHTML(content);
1871
1872       var tagName = element.tagName.toUpperCase();
1873
1874       if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
1875         element.text = content;
1876         return element;
1877       }
1878
1879       if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
1880         if (tagName in Element._insertionTranslations.tags) {
1881           while (element.firstChild) {
1882             element.removeChild(element.firstChild);
1883           }
1884           Element._getContentFromAnonymousElement(tagName, content.stripScripts())
1885             .each(function(node) {
1886               element.appendChild(node)
1887             });
1888         }
1889         else {
1890           element.innerHTML = content.stripScripts();
1891         }
1892       }
1893       else {
1894         element.innerHTML = content.stripScripts();
1895       }
1896
1897       content.evalScripts.bind(content).defer();
1898       return element;
1899     }
1900
1901     return update;
1902   })(),
1903
1904   replace: function(element, content) {
1905     element = $(element);
1906     if (content && content.toElement) content = content.toElement();
1907     else if (!Object.isElement(content)) {
1908       content = Object.toHTML(content);
1909       var range = element.ownerDocument.createRange();
1910       range.selectNode(element);
1911       content.evalScripts.bind(content).defer();
1912       content = range.createContextualFragment(content.stripScripts());
1913     }
1914     element.parentNode.replaceChild(content, element);
1915     return element;
1916   },
1917
1918   insert: function(element, insertions) {
1919     element = $(element);
1920
1921     if (Object.isString(insertions) || Object.isNumber(insertions) ||
1922         Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1923           insertions = {bottom:insertions};
1924
1925     var content, insert, tagName, childNodes;
1926
1927     for (var position in insertions) {
1928       content  = insertions[position];
1929       position = position.toLowerCase();
1930       insert = Element._insertionTranslations[position];
1931
1932       if (content && content.toElement) content = content.toElement();
1933       if (Object.isElement(content)) {
1934         insert(element, content);
1935         continue;
1936       }
1937
1938       content = Object.toHTML(content);
1939
1940       tagName = ((position == 'before' || position == 'after')
1941         ? element.parentNode : element).tagName.toUpperCase();
1942
1943       childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
1944
1945       if (position == 'top' || position == 'after') childNodes.reverse();
1946       childNodes.each(insert.curry(element));
1947
1948       content.evalScripts.bind(content).defer();
1949     }
1950
1951     return element;
1952   },
1953
1954   wrap: function(element, wrapper, attributes) {
1955     element = $(element);
1956     if (Object.isElement(wrapper))
1957       $(wrapper).writeAttribute(attributes || { });
1958     else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1959     else wrapper = new Element('div', wrapper);
1960     if (element.parentNode)
1961       element.parentNode.replaceChild(wrapper, element);
1962     wrapper.appendChild(element);
1963     return wrapper;
1964   },
1965
1966   inspect: function(element) {
1967     element = $(element);
1968     var result = '<' + element.tagName.toLowerCase();
1969     $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1970       var property = pair.first(), attribute = pair.last();
1971       var value = (element[property] || '').toString();
1972       if (value) result += ' ' + attribute + '=' + value.inspect(true);
1973     });
1974     return result + '>';
1975   },
1976
1977   recursivelyCollect: function(element, property) {
1978     element = $(element);
1979     var elements = [];
1980     while (element = element[property])
1981       if (element.nodeType == 1)
1982         elements.push(Element.extend(element));
1983     return elements;
1984   },
1985
1986   ancestors: function(element) {
1987     return Element.recursivelyCollect(element, 'parentNode');
1988   },
1989
1990   descendants: function(element) {
1991     return Element.select(element, "*");
1992   },
1993
1994   firstDescendant: function(element) {
1995     element = $(element).firstChild;
1996     while (element && element.nodeType != 1) element = element.nextSibling;
1997     return $(element);
1998   },
1999
2000   immediateDescendants: function(element) {
2001     if (!(element = $(element).firstChild)) return [];
2002     while (element && element.nodeType != 1) element = element.nextSibling;
2003     if (element) return [element].concat($(element).nextSiblings());
2004     return [];
2005   },
2006
2007   previousSiblings: function(element) {
2008     return Element.recursivelyCollect(element, 'previousSibling');
2009   },
2010
2011   nextSiblings: function(element) {
2012     return Element.recursivelyCollect(element, 'nextSibling');
2013   },
2014
2015   siblings: function(element) {
2016     element = $(element);
2017     return Element.previousSiblings(element).reverse()
2018       .concat(Element.nextSiblings(element));
2019   },
2020
2021   match: function(element, selector) {
2022     if (Object.isString(selector))
2023       selector = new Selector(selector);
2024     return selector.match($(element));
2025   },
2026
2027   up: function(element, expression, index) {
2028     element = $(element);
2029     if (arguments.length == 1) return $(element.parentNode);
2030     var ancestors = Element.ancestors(element);
2031     return Object.isNumber(expression) ? ancestors[expression] :
2032       Selector.findElement(ancestors, expression, index);
2033   },
2034
2035   down: function(element, expression, index) {
2036     element = $(element);
2037     if (arguments.length == 1) return Element.firstDescendant(element);
2038     return Object.isNumber(expression) ? Element.descendants(element)[expression] :
2039       Element.select(element, expression)[index || 0];
2040   },
2041
2042   previous: function(element, expression, index) {
2043     element = $(element);
2044     if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
2045     var previousSiblings = Element.previousSiblings(element);
2046     return Object.isNumber(expression) ? previousSiblings[expression] :
2047       Selector.findElement(previousSiblings, expression, index);
2048   },
2049
2050   next: function(element, expression, index) {
2051     element = $(element);
2052     if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
2053     var nextSiblings = Element.nextSiblings(element);
2054     return Object.isNumber(expression) ? nextSiblings[expression] :
2055       Selector.findElement(nextSiblings, expression, index);
2056   },
2057
2058
2059   select: function(element) {
2060     var args = Array.prototype.slice.call(arguments, 1);
2061     return Selector.findChildElements(element, args);
2062   },
2063
2064   adjacent: function(element) {
2065     var args = Array.prototype.slice.call(arguments, 1);
2066     return Selector.findChildElements(element.parentNode, args).without(element);
2067   },
2068
2069   identify: function(element) {
2070     element = $(element);
2071     var id = Element.readAttribute(element, 'id');
2072     if (id) return id;
2073     do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id));
2074     Element.writeAttribute(element, 'id', id);
2075     return id;
2076   },
2077
2078   readAttribute: function(element, name) {
2079     element = $(element);
2080     if (Prototype.Browser.IE) {
2081       var t = Element._attributeTranslations.read;
2082       if (t.values[name]) return t.values[name](element, name);
2083       if (t.names[name]) name = t.names[name];
2084       if (name.include(':')) {
2085         return (!element.attributes || !element.attributes[name]) ? null :
2086          element.attributes[name].value;
2087       }
2088     }
2089     return element.getAttribute(name);
2090   },
2091
2092   writeAttribute: function(element, name, value) {
2093     element = $(element);
2094     var attributes = { }, t = Element._attributeTranslations.write;
2095
2096     if (typeof name == 'object') attributes = name;
2097     else attributes[name] = Object.isUndefined(value) ? true : value;
2098
2099     for (var attr in attributes) {
2100       name = t.names[attr] || attr;
2101       value = attributes[attr];
2102       if (t.values[attr]) name = t.values[attr](element, value);
2103       if (value === false || value === null)
2104         element.removeAttribute(name);
2105       else if (value === true)
2106         element.setAttribute(name, name);
2107       else element.setAttribute(name, value);
2108     }
2109     return element;
2110   },
2111
2112   getHeight: function(element) {
2113     return Element.getDimensions(element).height;
2114   },
2115
2116   getWidth: function(element) {
2117     return Element.getDimensions(element).width;
2118   },
2119
2120   classNames: function(element) {
2121     return new Element.ClassNames(element);
2122   },
2123
2124   hasClassName: function(element, className) {
2125     if (!(element = $(element))) return;
2126     var elementClassName = element.className;
2127     return (elementClassName.length > 0 && (elementClassName == className ||
2128       new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
2129   },
2130
2131   addClassName: function(element, className) {
2132     if (!(element = $(element))) return;
2133     if (!Element.hasClassName(element, className))
2134       element.className += (element.className ? ' ' : '') + className;
2135     return element;
2136   },
2137
2138   removeClassName: function(element, className) {
2139     if (!(element = $(element))) return;
2140     element.className = element.className.replace(
2141       new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
2142     return element;
2143   },
2144
2145   toggleClassName: function(element, className) {
2146     if (!(element = $(element))) return;
2147     return Element[Element.hasClassName(element, className) ?
2148       'removeClassName' : 'addClassName'](element, className);
2149   },
2150
2151   cleanWhitespace: function(element) {
2152     element = $(element);
2153     var node = element.firstChild;
2154     while (node) {
2155       var nextNode = node.nextSibling;
2156       if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
2157         element.removeChild(node);
2158       node = nextNode;
2159     }
2160     return element;
2161   },
2162
2163   empty: function(element) {
2164     return $(element).innerHTML.blank();
2165   },
2166
2167   descendantOf: function(element, ancestor) {
2168     element = $(element), ancestor = $(ancestor);
2169
2170     if (element.compareDocumentPosition)
2171       return (element.compareDocumentPosition(ancestor) & 8) === 8;
2172
2173     if (ancestor.contains)
2174       return ancestor.contains(element) && ancestor !== element;
2175
2176     while (element = element.parentNode)
2177       if (element == ancestor) return true;
2178
2179     return false;
2180   },
2181
2182   scrollTo: function(element) {
2183     element = $(element);
2184     var pos = Element.cumulativeOffset(element);
2185     window.scrollTo(pos[0], pos[1]);
2186     return element;
2187   },
2188
2189   getStyle: function(element, style) {
2190     element = $(element);
2191     style = style == 'float' ? 'cssFloat' : style.camelize();
2192     var value = element.style[style];
2193     if (!value || value == 'auto') {
2194       var css = document.defaultView.getComputedStyle(element, null);
2195       value = css ? css[style] : null;
2196     }
2197     if (style == 'opacity') return value ? parseFloat(value) : 1.0;
2198     return value == 'auto' ? null : value;
2199   },
2200
2201   getOpacity: function(element) {
2202     return $(element).getStyle('opacity');
2203   },
2204
2205   setStyle: function(element, styles) {
2206     element = $(element);
2207     var elementStyle = element.style, match;
2208     if (Object.isString(styles)) {
2209       element.style.cssText += ';' + styles;
2210       return styles.include('opacity') ?
2211         element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
2212     }
2213     for (var property in styles)
2214       if (property == 'opacity') element.setOpacity(styles[property]);
2215       else
2216         elementStyle[(property == 'float' || property == 'cssFloat') ?
2217           (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
2218             property] = styles[property];
2219
2220     return element;
2221   },
2222
2223   setOpacity: function(element, value) {
2224     element = $(element);
2225     element.style.opacity = (value == 1 || value === '') ? '' :
2226       (value < 0.00001) ? 0 : value;
2227     return element;
2228   },
2229
2230   getDimensions: function(element) {
2231     element = $(element);
2232     var display = Element.getStyle(element, 'display');
2233     if (display != 'none' && display != null) // Safari bug
2234       return {width: element.offsetWidth, height: element.offsetHeight};
2235
2236     var els = element.style;
2237     var originalVisibility = els.visibility;
2238     var originalPosition = els.position;
2239     var originalDisplay = els.display;
2240     els.visibility = 'hidden';
2241     if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
2242       els.position = 'absolute';
2243     els.display = 'block';
2244     var originalWidth = element.clientWidth;
2245     var originalHeight = element.clientHeight;
2246     els.display = originalDisplay;
2247     els.position = originalPosition;
2248     els.visibility = originalVisibility;
2249     return {width: originalWidth, height: originalHeight};
2250   },
2251
2252   makePositioned: function(element) {
2253     element = $(element);
2254     var pos = Element.getStyle(element, 'position');
2255     if (pos == 'static' || !pos) {
2256       element._madePositioned = true;
2257       element.style.position = 'relative';
2258       if (Prototype.Browser.Opera) {
2259         element.style.top = 0;
2260         element.style.left = 0;
2261       }
2262     }
2263     return element;
2264   },
2265
2266   undoPositioned: function(element) {
2267     element = $(element);
2268     if (element._madePositioned) {
2269       element._madePositioned = undefined;
2270       element.style.position =
2271         element.style.top =
2272         element.style.left =
2273         element.style.bottom =
2274         element.style.right = '';
2275     }
2276     return element;
2277   },
2278
2279   makeClipping: function(element) {
2280     element = $(element);
2281     if (element._overflow) return element;
2282     element._overflow = Element.getStyle(element, 'overflow') || 'auto';
2283     if (element._overflow !== 'hidden')
2284       element.style.overflow = 'hidden';
2285     return element;
2286   },
2287
2288   undoClipping: function(element) {
2289     element = $(element);
2290     if (!element._overflow) return element;
2291     element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2292     element._overflow = null;
2293     return element;
2294   },
2295
2296   cumulativeOffset: function(element) {
2297     var valueT = 0, valueL = 0;
2298     do {
2299       valueT += element.offsetTop  || 0;
2300       valueL += element.offsetLeft || 0;
2301       element = element.offsetParent;
2302     } while (element);
2303     return Element._returnOffset(valueL, valueT);
2304   },
2305
2306   positionedOffset: function(element) {
2307     var valueT = 0, valueL = 0;
2308     do {
2309       valueT += element.offsetTop  || 0;
2310       valueL += element.offsetLeft || 0;
2311       element = element.offsetParent;
2312       if (element) {
2313         if (element.tagName.toUpperCase() == 'BODY') break;
2314         var p = Element.getStyle(element, 'position');
2315         if (p !== 'static') break;
2316       }
2317     } while (element);
2318     return Element._returnOffset(valueL, valueT);
2319   },
2320
2321   absolutize: function(element) {
2322     element = $(element);
2323     if (Element.getStyle(element, 'position') == 'absolute') return element;
2324
2325     var offsets = Element.positionedOffset(element);
2326     var top     = offsets[1];
2327     var left    = offsets[0];
2328     var width   = element.clientWidth;
2329     var height  = element.clientHeight;
2330
2331     element._originalLeft   = left - parseFloat(element.style.left  || 0);
2332     element._originalTop    = top  - parseFloat(element.style.top || 0);
2333     element._originalWidth  = element.style.width;
2334     element._originalHeight = element.style.height;
2335
2336     element.style.position = 'absolute';
2337     element.style.top    = top + 'px';
2338     element.style.left   = left + 'px';
2339     element.style.width  = width + 'px';
2340     element.style.height = height + 'px';
2341     return element;
2342   },
2343
2344   relativize: function(element) {
2345     element = $(element);
2346     if (Element.getStyle(element, 'position') == 'relative') return element;
2347
2348     element.style.position = 'relative';
2349     var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
2350     var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2351
2352     element.style.top    = top + 'px';
2353     element.style.left   = left + 'px';
2354     element.style.height = element._originalHeight;
2355     element.style.width  = element._originalWidth;
2356     return element;
2357   },
2358
2359   cumulativeScrollOffset: function(element) {
2360     var valueT = 0, valueL = 0;
2361     do {
2362       valueT += element.scrollTop  || 0;
2363       valueL += element.scrollLeft || 0;
2364       element = element.parentNode;
2365     } while (element);
2366     return Element._returnOffset(valueL, valueT);
2367   },
2368
2369   getOffsetParent: function(element) {
2370     if (element.offsetParent) return $(element.offsetParent);
2371     if (element == document.body) return $(element);
2372
2373     while ((element = element.parentNode) && element != document.body)
2374       if (Element.getStyle(element, 'position') != 'static')
2375         return $(element);
2376
2377     return $(document.body);
2378   },
2379
2380   viewportOffset: function(forElement) {
2381     var valueT = 0, valueL = 0;
2382
2383     var element = forElement;
2384     do {
2385       valueT += element.offsetTop  || 0;
2386       valueL += element.offsetLeft || 0;
2387
2388       if (element.offsetParent == document.body &&
2389         Element.getStyle(element, 'position') == 'absolute') break;
2390
2391     } while (element = element.offsetParent);
2392
2393     element = forElement;
2394     do {
2395       if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
2396         valueT -= element.scrollTop  || 0;
2397         valueL -= element.scrollLeft || 0;
2398       }
2399     } while (element = element.parentNode);
2400
2401     return Element._returnOffset(valueL, valueT);
2402   },
2403
2404   clonePosition: function(element, source) {
2405     var options = Object.extend({
2406       setLeft:    true,
2407       setTop:     true,
2408       setWidth:   true,
2409       setHeight:  true,
2410       offsetTop:  0,
2411       offsetLeft: 0
2412     }, arguments[2] || { });
2413
2414     source = $(source);
2415     var p = Element.viewportOffset(source);
2416
2417     element = $(element);
2418     var delta = [0, 0];
2419     var parent = null;
2420     if (Element.getStyle(element, 'position') == 'absolute') {
2421       parent = Element.getOffsetParent(element);
2422       delta = Element.viewportOffset(parent);
2423     }
2424
2425     if (parent == document.body) {
2426       delta[0] -= document.body.offsetLeft;
2427       delta[1] -= document.body.offsetTop;
2428     }
2429
2430     if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2431     if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2432     if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
2433     if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2434     return element;
2435   }
2436 };
2437
2438 Object.extend(Element.Methods, {
2439   getElementsBySelector: Element.Methods.select,
2440
2441   childElements: Element.Methods.immediateDescendants
2442 });
2443
2444 Element._attributeTranslations = {
2445   write: {
2446     names: {
2447       className: 'class',
2448       htmlFor:   'for'
2449     },
2450     values: { }
2451   }
2452 };
2453
2454 if (Prototype.Browser.Opera) {
2455   Element.Methods.getStyle = Element.Methods.getStyle.wrap(
2456     function(proceed, element, style) {
2457       switch (style) {
2458         case 'left': case 'top': case 'right': case 'bottom':
2459           if (proceed(element, 'position') === 'static') return null;
2460         case 'height': case 'width':
2461           if (!Element.visible(element)) return null;
2462
2463           var dim = parseInt(proceed(element, style), 10);
2464
2465           if (dim !== element['offset' + style.capitalize()])
2466             return dim + 'px';
2467
2468           var properties;
2469           if (style === 'height') {
2470             properties = ['border-top-width', 'padding-top',
2471              'padding-bottom', 'border-bottom-width'];
2472           }
2473           else {
2474             properties = ['border-left-width', 'padding-left',
2475              'padding-right', 'border-right-width'];
2476           }
2477           return properties.inject(dim, function(memo, property) {
2478             var val = proceed(element, property);
2479             return val === null ? memo : memo - parseInt(val, 10);
2480           }) + 'px';
2481         default: return proceed(element, style);
2482       }
2483     }
2484   );
2485
2486   Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
2487     function(proceed, element, attribute) {
2488       if (attribute === 'title') return element.title;
2489       return proceed(element, attribute);
2490     }
2491   );
2492 }
2493
2494 else if (Prototype.Browser.IE) {
2495   Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
2496     function(proceed, element) {
2497       element = $(element);
2498       try { element.offsetParent }
2499       catch(e) { return $(document.body) }
2500       var position = element.getStyle('position');
2501       if (position !== 'static') return proceed(element);
2502       element.setStyle({ position: 'relative' });
2503       var value = proceed(element);
2504       element.setStyle({ position: position });
2505       return value;
2506     }
2507   );
2508
2509   $w('positionedOffset viewportOffset').each(function(method) {
2510     Element.Methods[method] = Element.Methods[method].wrap(
2511       function(proceed, element) {
2512         element = $(element);
2513         try { element.offsetParent }
2514         catch(e) { return Element._returnOffset(0,0) }
2515         var position = element.getStyle('position');
2516         if (position !== 'static') return proceed(element);
2517         var offsetParent = element.getOffsetParent();
2518         if (offsetParent && offsetParent.getStyle('position') === 'fixed')
2519           offsetParent.setStyle({ zoom: 1 });
2520         element.setStyle({ position: 'relative' });
2521         var value = proceed(element);
2522         element.setStyle({ position: position });
2523         return value;
2524       }
2525     );
2526   });
2527
2528   Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
2529     function(proceed, element) {
2530       try { element.offsetParent }
2531       catch(e) { return Element._returnOffset(0,0) }
2532       return proceed(element);
2533     }
2534   );
2535
2536   Element.Methods.getStyle = function(element, style) {
2537     element = $(element);
2538     style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2539     var value = element.style[style];
2540     if (!value && element.currentStyle) value = element.currentStyle[style];
2541
2542     if (style == 'opacity') {
2543       if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2544         if (value[1]) return parseFloat(value[1]) / 100;
2545       return 1.0;
2546     }
2547
2548     if (value == 'auto') {
2549       if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2550         return element['offset' + style.capitalize()] + 'px';
2551       return null;
2552     }
2553     return value;
2554   };
2555
2556   Element.Methods.setOpacity = function(element, value) {
2557     function stripAlpha(filter){
2558       return filter.replace(/alpha\([^\)]*\)/gi,'');
2559     }
2560     element = $(element);
2561     var currentStyle = element.currentStyle;
2562     if ((currentStyle && !currentStyle.hasLayout) ||
2563       (!currentStyle && element.style.zoom == 'normal'))
2564         element.style.zoom = 1;
2565
2566     var filter = element.getStyle('filter'), style = element.style;
2567     if (value == 1 || value === '') {
2568       (filter = stripAlpha(filter)) ?
2569         style.filter = filter : style.removeAttribute('filter');
2570       return element;
2571     } else if (value < 0.00001) value = 0;
2572     style.filter = stripAlpha(filter) +
2573       'alpha(opacity=' + (value * 100) + ')';
2574     return element;
2575   };
2576
2577   Element._attributeTranslations = (function(){
2578
2579     var classProp = 'className';
2580     var forProp = 'for';
2581
2582     var el = document.createElement('div');
2583
2584     el.setAttribute(classProp, 'x');
2585
2586     if (el.className !== 'x') {
2587       el.setAttribute('class', 'x');
2588       if (el.className === 'x') {
2589         classProp = 'class';
2590       }
2591     }
2592     el = null;
2593
2594     el = document.createElement('label');
2595     el.setAttribute(forProp, 'x');
2596     if (el.htmlFor !== 'x') {
2597       el.setAttribute('htmlFor', 'x');
2598       if (el.htmlFor === 'x') {
2599         forProp = 'htmlFor';
2600       }
2601     }
2602     el = null;
2603
2604     return {
2605       read: {
2606         names: {
2607           'class':      classProp,
2608           'className':  classProp,
2609           'for':        forProp,
2610           'htmlFor':    forProp
2611         },
2612         values: {
2613           _getAttr: function(element, attribute) {
2614             return element.getAttribute(attribute);
2615           },
2616           _getAttr2: function(element, attribute) {
2617             return element.getAttribute(attribute, 2);
2618           },
2619           _getAttrNode: function(element, attribute) {
2620             var node = element.getAttributeNode(attribute);
2621             return node ? node.value : "";
2622           },
2623           _getEv: (function(){
2624
2625             var el = document.createElement('div');
2626             el.onclick = Prototype.emptyFunction;
2627             var value = el.getAttribute('onclick');
2628             var f;
2629
2630             if (String(value).indexOf('{') > -1) {
2631               f = function(element, attribute) {
2632                 attribute = element.getAttribute(attribute);
2633                 if (!attribute) return null;
2634                 attribute = attribute.toString();
2635                 attribute = attribute.split('{')[1];
2636                 attribute = attribute.split('}')[0];
2637                 return attribute.strip();
2638               };
2639             }
2640             else if (value === '') {
2641               f = function(element, attribute) {
2642                 attribute = element.getAttribute(attribute);
2643                 if (!attribute) return null;
2644                 return attribute.strip();
2645               };
2646             }
2647             el = null;
2648             return f;
2649           })(),
2650           _flag: function(element, attribute) {
2651             return $(element).hasAttribute(attribute) ? attribute : null;
2652           },
2653           style: function(element) {
2654             return element.style.cssText.toLowerCase();
2655           },
2656           title: function(element) {
2657             return element.title;
2658           }
2659         }
2660       }
2661     }
2662   })();
2663
2664   Element._attributeTranslations.write = {
2665     names: Object.extend({
2666       cellpadding: 'cellPadding',
2667       cellspacing: 'cellSpacing'
2668     }, Element._attributeTranslations.read.names),
2669     values: {
2670       checked: function(element, value) {
2671         element.checked = !!value;
2672       },
2673
2674       style: function(element, value) {
2675         element.style.cssText = value ? value : '';
2676       }
2677     }
2678   };
2679
2680   Element._attributeTranslations.has = {};
2681
2682   $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2683       'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
2684     Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2685     Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2686   });
2687
2688   (function(v) {
2689     Object.extend(v, {
2690       href:        v._getAttr2,
2691       src:         v._getAttr2,
2692       type:        v._getAttr,
2693       action:      v._getAttrNode,
2694       disabled:    v._flag,
2695       checked:     v._flag,
2696       readonly:    v._flag,
2697       multiple:    v._flag,
2698       onload:      v._getEv,
2699       onunload:    v._getEv,
2700       onclick:     v._getEv,
2701       ondblclick:  v._getEv,
2702       onmousedown: v._getEv,
2703       onmouseup:   v._getEv,
2704       onmouseover: v._getEv,
2705       onmousemove: v._getEv,
2706       onmouseout:  v._getEv,
2707       onfocus:     v._getEv,
2708       onblur:      v._getEv,
2709       onkeypress:  v._getEv,
2710       onkeydown:   v._getEv,
2711       onkeyup:     v._getEv,
2712       onsubmit:    v._getEv,
2713       onreset:     v._getEv,
2714       onselect:    v._getEv,
2715       onchange:    v._getEv
2716     });
2717   })(Element._attributeTranslations.read.values);
2718
2719   if (Prototype.BrowserFeatures.ElementExtensions) {
2720     (function() {
2721       function _descendants(element) {
2722         var nodes = element.getElementsByTagName('*'), results = [];
2723         for (var i = 0, node; node = nodes[i]; i++)
2724           if (node.tagName !== "!") // Filter out comment nodes.
2725             results.push(node);
2726         return results;
2727       }
2728
2729       Element.Methods.down = function(element, expression, index) {
2730         element = $(element);
2731         if (arguments.length == 1) return element.firstDescendant();
2732         return Object.isNumber(expression) ? _descendants(element)[expression] :
2733           Element.select(element, expression)[index || 0];
2734       }
2735     })();
2736   }
2737
2738 }
2739
2740 else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2741   Element.Methods.setOpacity = function(element, value) {
2742     element = $(element);
2743     element.style.opacity = (value == 1) ? 0.999999 :
2744       (value === '') ? '' : (value < 0.00001) ? 0 : value;
2745     return element;
2746   };
2747 }
2748
2749 else if (Prototype.Browser.WebKit) {
2750   Element.Methods.setOpacity = function(element, value) {
2751     element = $(element);
2752     element.style.opacity = (value == 1 || value === '') ? '' :
2753       (value < 0.00001) ? 0 : value;
2754
2755     if (value == 1)
2756       if(element.tagName.toUpperCase() == 'IMG' && element.width) {
2757         element.width++; element.width--;
2758       } else try {
2759         var n = document.createTextNode(' ');
2760         element.appendChild(n);
2761         element.removeChild(n);
2762       } catch (e) { }
2763
2764     return element;
2765   };
2766
2767   Element.Methods.cumulativeOffset = function(element) {
2768     var valueT = 0, valueL = 0;
2769     do {
2770       valueT += element.offsetTop  || 0;
2771       valueL += element.offsetLeft || 0;
2772       if (element.offsetParent == document.body)
2773         if (Element.getStyle(element, 'position') == 'absolute') break;
2774
2775       element = element.offsetParent;
2776     } while (element);
2777
2778     return Element._returnOffset(valueL, valueT);
2779   };
2780 }
2781
2782 if ('outerHTML' in document.documentElement) {
2783   Element.Methods.replace = function(element, content) {
2784     element = $(element);
2785
2786     if (content && content.toElement) content = content.toElement();
2787     if (Object.isElement(content)) {
2788       element.parentNode.replaceChild(content, element);
2789       return element;
2790     }
2791
2792     content = Object.toHTML(content);
2793     var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2794
2795     if (Element._insertionTranslations.tags[tagName]) {
2796       var nextSibling = element.next();
2797       var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2798       parent.removeChild(element);
2799       if (nextSibling)
2800         fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2801       else
2802         fragments.each(function(node) { parent.appendChild(node) });
2803     }
2804     else element.outerHTML = content.stripScripts();
2805
2806     content.evalScripts.bind(content).defer();
2807     return element;
2808   };
2809 }
2810
2811 Element._returnOffset = function(l, t) {
2812   var result = [l, t];
2813   result.left = l;
2814   result.top = t;
2815   return result;
2816 };
2817
2818 Element._getContentFromAnonymousElement = function(tagName, html) {
2819   var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
2820   if (t) {
2821     div.innerHTML = t[0] + html + t[1];
2822     t[2].times(function() { div = div.firstChild });
2823   } else div.innerHTML = html;
2824   return $A(div.childNodes);
2825 };
2826
2827 Element._insertionTranslations = {
2828   before: function(element, node) {
2829     element.parentNode.insertBefore(node, element);
2830   },
2831   top: function(element, node) {
2832     element.insertBefore(node, element.firstChild);
2833   },
2834   bottom: function(element, node) {
2835     element.appendChild(node);
2836   },
2837   after: function(element, node) {
2838     element.parentNode.insertBefore(node, element.nextSibling);
2839   },
2840   tags: {
2841     TABLE:  ['<table>',                '</table>',                   1],
2842     TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
2843     TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
2844     TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2845     SELECT: ['<select>',               '</select>',                  1]
2846   }
2847 };
2848
2849 (function() {
2850   var tags = Element._insertionTranslations.tags;
2851   Object.extend(tags, {
2852     THEAD: tags.TBODY,
2853     TFOOT: tags.TBODY,
2854     TH:    tags.TD
2855   });
2856 })();
2857
2858 Element.Methods.Simulated = {
2859   hasAttribute: function(element, attribute) {
2860     attribute = Element._attributeTranslations.has[attribute] || attribute;
2861     var node = $(element).getAttributeNode(attribute);
2862     return !!(node && node.specified);
2863   }
2864 };
2865
2866 Element.Methods.ByTag = { };
2867
2868 Object.extend(Element, Element.Methods);
2869
2870 (function(div) {
2871
2872   if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
2873     window.HTMLElement = { };
2874     window.HTMLElement.prototype = div['__proto__'];
2875     Prototype.BrowserFeatures.ElementExtensions = true;
2876   }
2877
2878   div = null;
2879
2880 })(document.createElement('div'))
2881
2882 Element.extend = (function() {
2883
2884   function checkDeficiency(tagName) {
2885     if (typeof window.Element != 'undefined') {
2886       var proto = window.Element.prototype;
2887       if (proto) {
2888         var id = '_' + (Math.random()+'').slice(2);
2889         var el = document.createElement(tagName);
2890         proto[id] = 'x';
2891         var isBuggy = (el[id] !== 'x');
2892         delete proto[id];
2893         el = null;
2894         return isBuggy;
2895       }
2896     }
2897     return false;
2898   }
2899
2900   function extendElementWith(element, methods) {
2901     for (var property in methods) {
2902       var value = methods[property];
2903       if (Object.isFunction(value) && !(property in element))
2904         element[property] = value.methodize();
2905     }
2906   }
2907
2908   var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');
2909
2910   if (Prototype.BrowserFeatures.SpecificElementExtensions) {
2911     if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
2912       return function(element) {
2913         if (element && typeof element._extendedByPrototype == 'undefined') {
2914           var t = element.tagName;
2915           if (t && (/^(?:object|applet|embed)$/i.test(t))) {
2916             extendElementWith(element, Element.Methods);
2917             extendElementWith(element, Element.Methods.Simulated);
2918             extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
2919           }
2920         }
2921         return element;
2922       }
2923     }
2924     return Prototype.K;
2925   }
2926
2927   var Methods = { }, ByTag = Element.Methods.ByTag;
2928
2929   var extend = Object.extend(function(element) {
2930     if (!element || typeof element._extendedByPrototype != 'undefined' ||
2931         element.nodeType != 1 || element == window) return element;
2932
2933     var methods = Object.clone(Methods),
2934         tagName = element.tagName.toUpperCase();
2935
2936     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2937
2938     extendElementWith(element, methods);
2939
2940     element._extendedByPrototype = Prototype.emptyFunction;
2941     return element;
2942
2943   }, {
2944     refresh: function() {
2945       if (!Prototype.BrowserFeatures.ElementExtensions) {
2946         Object.extend(Methods, Element.Methods);
2947         Object.extend(Methods, Element.Methods.Simulated);
2948       }
2949     }
2950   });
2951
2952   extend.refresh();
2953   return extend;
2954 })();
2955
2956 Element.hasAttribute = function(element, attribute) {
2957   if (element.hasAttribute) return element.hasAttribute(attribute);
2958   return Element.Methods.Simulated.hasAttribute(element, attribute);
2959 };
2960
2961 Element.addMethods = function(methods) {
2962   var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2963
2964   if (!methods) {
2965     Object.extend(Form, Form.Methods);
2966     Object.extend(Form.Element, Form.Element.Methods);
2967     Object.extend(Element.Methods.ByTag, {
2968       "FORM":     Object.clone(Form.Methods),
2969       "INPUT":    Object.clone(Form.Element.Methods),
2970       "SELECT":   Object.clone(Form.Element.Methods),
2971       "TEXTAREA": Object.clone(Form.Element.Methods)
2972     });
2973   }
2974
2975   if (arguments.length == 2) {
2976     var tagName = methods;
2977     methods = arguments[1];
2978   }
2979
2980   if (!tagName) Object.extend(Element.Methods, methods || { });
2981   else {
2982     if (Object.isArray(tagName)) tagName.each(extend);
2983     else extend(tagName);
2984   }
2985
2986   function extend(tagName) {
2987     tagName = tagName.toUpperCase();
2988     if (!Element.Methods.ByTag[tagName])
2989       Element.Methods.ByTag[tagName] = { };
2990     Object.extend(Element.Methods.ByTag[tagName], methods);
2991   }
2992
2993   function copy(methods, destination, onlyIfAbsent) {
2994     onlyIfAbsent = onlyIfAbsent || false;
2995     for (var property in methods) {
2996       var value = methods[property];
2997       if (!Object.isFunction(value)) continue;
2998       if (!onlyIfAbsent || !(property in destination))
2999         destination[property] = value.methodize();
3000     }
3001   }
3002
3003   function findDOMClass(tagName) {
3004     var klass;
3005     var trans = {
3006       "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3007       "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3008       "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3009       "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3010       "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3011       "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3012       "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3013       "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3014       "FrameSet", "IFRAME": "IFrame"
3015     };
3016     if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3017     if (window[klass]) return window[klass];
3018     klass = 'HTML' + tagName + 'Element';
3019     if (window[klass]) return window[klass];
3020     klass = 'HTML' + tagName.capitalize() + 'Element';
3021     if (window[klass]) return window[klass];
3022
3023     var element = document.createElement(tagName);
3024     var proto = element['__proto__'] || element.constructor.prototype;
3025     element = null;
3026     return proto;
3027   }
3028
3029   var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
3030    Element.prototype;
3031
3032   if (F.ElementExtensions) {
3033     copy(Element.Methods, elementPrototype);
3034     copy(Element.Methods.Simulated, elementPrototype, true);
3035   }
3036
3037   if (F.SpecificElementExtensions) {
3038     for (var tag in Element.Methods.ByTag) {
3039       var klass = findDOMClass(tag);
3040       if (Object.isUndefined(klass)) continue;
3041       copy(T[tag], klass.prototype);
3042     }
3043   }
3044
3045   Object.extend(Element, Element.Methods);
3046   delete Element.ByTag;
3047
3048   if (Element.extend.refresh) Element.extend.refresh();
3049   Element.cache = { };
3050 };
3051
3052
3053 document.viewport = {
3054
3055   getDimensions: function() {
3056     return { width: this.getWidth(), height: this.getHeight() };
3057   },
3058
3059   getScrollOffsets: function() {
3060     return Element._returnOffset(
3061       window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
3062       window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
3063   }
3064 };
3065
3066 (function(viewport) {
3067   var B = Prototype.Browser, doc = document, element, property = {};
3068
3069   function getRootElement() {
3070     if (B.WebKit && !doc.evaluate)
3071       return document;
3072
3073     if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
3074       return document.body;
3075
3076     return document.documentElement;
3077   }
3078
3079   function define(D) {
3080     if (!element) element = getRootElement();
3081
3082     property[D] = 'client' + D;
3083
3084     viewport['get' + D] = function() { return element[property[D]] };
3085     return viewport['get' + D]();
3086   }
3087
3088   viewport.getWidth  = define.curry('Width');
3089
3090   viewport.getHeight = define.curry('Height');
3091 })(document.viewport);
3092
3093
3094 Element.Storage = {
3095   UID: 1
3096 };
3097
3098 Element.addMethods({
3099   getStorage: function(element) {
3100     if (!(element = $(element))) return;
3101
3102     var uid;
3103     if (element === window) {
3104       uid = 0;
3105     } else {
3106       if (typeof element._prototypeUID === "undefined")
3107         element._prototypeUID = [Element.Storage.UID++];
3108       uid = element._prototypeUID[0];
3109     }
3110
3111     if (!Element.Storage[uid])
3112       Element.Storage[uid] = $H();
3113
3114     return Element.Storage[uid];
3115   },
3116
3117   store: function(element, key, value) {
3118     if (!(element = $(element))) return;
3119
3120     if (arguments.length === 2) {
3121       Element.getStorage(element).update(key);
3122     } else {
3123       Element.getStorage(element).set(key, value);
3124     }
3125
3126     return element;
3127   },
3128
3129   retrieve: function(element, key, defaultValue) {
3130     if (!(element = $(element))) return;
3131     var hash = Element.getStorage(element), value = hash.get(key);
3132
3133     if (Object.isUndefined(value)) {
3134       hash.set(key, defaultValue);
3135       value = defaultValue;
3136     }
3137
3138     return value;
3139   },
3140
3141   clone: function(element, deep) {
3142     if (!(element = $(element))) return;
3143     var clone = element.cloneNode(deep);
3144     clone._prototypeUID = void 0;
3145     if (deep) {
3146       var descendants = Element.select(clone, '*'),
3147           i = descendants.length;
3148       while (i--) {
3149         descendants[i]._prototypeUID = void 0;
3150       }
3151     }
3152     return Element.extend(clone);
3153   }
3154 });
3155 /* Portions of the Selector class are derived from Jack Slocum's DomQuery,
3156  * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
3157  * license.  Please see http://www.yui-ext.com/ for more information. */
3158
3159 var Selector = Class.create({
3160   initialize: function(expression) {
3161     this.expression = expression.strip();
3162
3163     if (this.shouldUseSelectorsAPI()) {
3164       this.mode = 'selectorsAPI';
3165     } else if (this.shouldUseXPath()) {
3166       this.mode = 'xpath';
3167       this.compileXPathMatcher();
3168     } else {
3169       this.mode = "normal";
3170       this.compileMatcher();
3171     }
3172
3173   },
3174
3175   shouldUseXPath: (function() {
3176
3177     var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
3178       var isBuggy = false;
3179       if (document.evaluate && window.XPathResult) {
3180         var el = document.createElement('div');
3181         el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';
3182
3183         var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
3184           "//*[local-name()='li' or local-name()='LI']";
3185
3186         var result = document.evaluate(xpath, el, null,
3187           XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
3188
3189         isBuggy = (result.snapshotLength !== 2);
3190         el = null;
3191       }
3192       return isBuggy;
3193     })();
3194
3195     return function() {
3196       if (!Prototype.BrowserFeatures.XPath) return false;
3197
3198       var e = this.expression;
3199
3200       if (Prototype.Browser.WebKit &&
3201        (e.include("-of-type") || e.include(":empty")))
3202         return false;
3203
3204       if ((/(\[[\w-]*?:|:checked)/).test(e))
3205         return false;
3206
3207       if (IS_DESCENDANT_SELECTOR_BUGGY) return false;
3208
3209       return true;
3210     }
3211
3212   })(),
3213
3214   shouldUseSelectorsAPI: function() {
3215     if (!Prototype.BrowserFeatures.SelectorsAPI) return false;
3216
3217     if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;
3218
3219     if (!Selector._div) Selector._div = new Element('div');
3220
3221     try {
3222       Selector._div.querySelector(this.expression);
3223     } catch(e) {
3224       return false;
3225     }
3226
3227     return true;
3228   },
3229
3230   compileMatcher: function() {
3231     var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
3232         c = Selector.criteria, le, p, m, len = ps.length, name;
3233
3234     if (Selector._cache[e]) {
3235       this.matcher = Selector._cache[e];
3236       return;
3237     }
3238
3239     this.matcher = ["this.matcher = function(root) {",
3240                     "var r = root, h = Selector.handlers, c = false, n;"];
3241
3242     while (e && le != e && (/\S/).test(e)) {
3243       le = e;
3244       for (var i = 0; i<len; i++) {
3245         p = ps[i].re;
3246         name = ps[i].name;
3247         if (m = e.match(p)) {
3248           this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :
3249             new Template(c[name]).evaluate(m));
3250           e = e.replace(m[0], '');
3251           break;
3252         }
3253       }
3254     }
3255
3256     this.matcher.push("return h.unique(n);\n}");
3257     eval(this.matcher.join('\n'));
3258     Selector._cache[this.expression] = this.matcher;
3259   },
3260
3261   compileXPathMatcher: function() {
3262     var e = this.expression, ps = Selector.patterns,
3263         x = Selector.xpath, le, m, len = ps.length, name;
3264
3265     if (Selector._cache[e]) {
3266       this.xpath = Selector._cache[e]; return;
3267     }
3268
3269     this.matcher = ['.//*'];
3270     while (e && le != e && (/\S/).test(e)) {
3271       le = e;
3272       for (var i = 0; i<len; i++) {
3273         name = ps[i].name;
3274         if (m = e.match(ps[i].re)) {
3275           this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
3276             new Template(x[name]).evaluate(m));
3277           e = e.replace(m[0], '');
3278           break;
3279         }
3280       }
3281     }
3282
3283     this.xpath = this.matcher.join('');
3284     Selector._cache[this.expression] = this.xpath;
3285   },
3286
3287   findElements: function(root) {
3288     root = root || document;
3289     var e = this.expression, results;
3290
3291     switch (this.mode) {
3292       case 'selectorsAPI':
3293         if (root !== document) {
3294           var oldId = root.id, id = $(root).identify();
3295           id = id.replace(/([\.:])/g, "\\$1");
3296           e = "#" + id + " " + e;
3297         }
3298
3299         results = $A(root.querySelectorAll(e)).map(Element.extend);
3300         root.id = oldId;
3301
3302         return results;
3303       case 'xpath':
3304         return document._getElementsByXPath(this.xpath, root);
3305       default:
3306        return this.matcher(root);
3307     }
3308   },
3309
3310   match: function(element) {
3311     this.tokens = [];
3312
3313     var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
3314     var le, p, m, len = ps.length, name;
3315
3316     while (e && le !== e && (/\S/).test(e)) {
3317       le = e;
3318       for (var i = 0; i<len; i++) {
3319         p = ps[i].re;
3320         name = ps[i].name;
3321         if (m = e.match(p)) {
3322           if (as[name]) {
3323             this.tokens.push([name, Object.clone(m)]);
3324             e = e.replace(m[0], '');
3325           } else {
3326             return this.findElements(document).include(element);
3327           }
3328         }
3329       }
3330     }
3331
3332     var match = true, name, matches;
3333     for (var i = 0, token; token = this.tokens[i]; i++) {
3334       name = token[0], matches = token[1];
3335       if (!Selector.assertions[name](element, matches)) {
3336         match = false; break;
3337       }
3338     }
3339
3340     return match;
3341   },
3342
3343   toString: function() {
3344     return this.expression;
3345   },
3346
3347   inspect: function() {
3348     return "#<Selector:" + this.expression.inspect() + ">";
3349   }
3350 });
3351
3352 if (Prototype.BrowserFeatures.SelectorsAPI &&
3353  document.compatMode === 'BackCompat') {
3354   Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
3355     var div = document.createElement('div'),
3356      span = document.createElement('span');
3357
3358     div.id = "prototype_test_id";
3359     span.className = 'Test';
3360     div.appendChild(span);
3361     var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
3362     div = span = null;
3363     return isIgnored;
3364   })();
3365 }
3366
3367 Object.extend(Selector, {
3368   _cache: { },
3369
3370   xpath: {
3371     descendant:   "//*",
3372     child:        "/*",
3373     adjacent:     "/following-sibling::*[1]",
3374     laterSibling: '/following-sibling::*',
3375     tagName:      function(m) {
3376       if (m[1] == '*') return '';
3377       return "[local-name()='" + m[1].toLowerCase() +
3378              "' or local-name()='" + m[1].toUpperCase() + "']";
3379     },
3380     className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
3381     id:           "[@id='#{1}']",
3382     attrPresence: function(m) {
3383       m[1] = m[1].toLowerCase();
3384       return new Template("[@#{1}]").evaluate(m);
3385     },
3386     attr: function(m) {
3387       m[1] = m[1].toLowerCase();
3388       m[3] = m[5] || m[6];
3389       return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
3390     },
3391     pseudo: function(m) {
3392       var h = Selector.xpath.pseudos[m[1]];
3393       if (!h) return '';
3394       if (Object.isFunction(h)) return h(m);
3395       return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
3396     },
3397     operators: {
3398       '=':  "[@#{1}='#{3}']",
3399       '!=': "[@#{1}!='#{3}']",
3400       '^=': "[starts-with(@#{1}, '#{3}')]",
3401       '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
3402       '*=': "[contains(@#{1}, '#{3}')]",
3403       '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
3404       '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
3405     },
3406     pseudos: {
3407       'first-child': '[not(preceding-sibling::*)]',
3408       'last-child':  '[not(following-sibling::*)]',
3409       'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
3410       'empty':       "[count(*) = 0 and (count(text()) = 0)]",
3411       'checked':     "[@checked]",
3412       'disabled':    "[(@disabled) and (@type!='hidden')]",
3413       'enabled':     "[not(@disabled) and (@type!='hidden')]",
3414       'not': function(m) {
3415         var e = m[6], p = Selector.patterns,
3416             x = Selector.xpath, le, v, len = p.length, name;
3417
3418         var exclusion = [];
3419         while (e && le != e && (/\S/).test(e)) {
3420           le = e;
3421           for (var i = 0; i<len; i++) {
3422             name = p[i].name
3423             if (m = e.match(p[i].re)) {
3424               v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m);
3425               exclusion.push("(" + v.substring(1, v.length - 1) + ")");
3426               e = e.replace(m[0], '');
3427               break;
3428             }
3429           }
3430         }
3431         return "[not(" + exclusion.join(" and ") + ")]";
3432       },
3433       'nth-child':      function(m) {
3434         return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
3435       },
3436       'nth-last-child': function(m) {
3437         return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
3438       },
3439       'nth-of-type':    function(m) {
3440         return Selector.xpath.pseudos.nth("position() ", m);
3441       },
3442       'nth-last-of-type': function(m) {
3443         return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
3444       },
3445       'first-of-type':  function(m) {
3446         m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
3447       },
3448       'last-of-type':   function(m) {
3449         m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
3450       },
3451       'only-of-type':   function(m) {
3452         var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
3453       },
3454       nth: function(fragment, m) {
3455         var mm, formula = m[6], predicate;
3456         if (formula == 'even') formula = '2n+0';
3457         if (formula == 'odd')  formula = '2n+1';
3458         if (mm = formula.match(/^(\d+)$/)) // digit only
3459           return '[' + fragment + "= " + mm[1] + ']';
3460         if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3461           if (mm[1] == "-") mm[1] = -1;
3462           var a = mm[1] ? Number(mm[1]) : 1;
3463           var b = mm[2] ? Number(mm[2]) : 0;
3464           predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
3465           "((#{fragment} - #{b}) div #{a} >= 0)]";
3466           return new Template(predicate).evaluate({
3467             fragment: fragment, a: a, b: b });
3468         }
3469       }
3470     }
3471   },
3472
3473   criteria: {
3474     tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
3475     className:    'n = h.className(n, r, "#{1}", c);    c = false;',
3476     id:           'n = h.id(n, r, "#{1}", c);           c = false;',
3477     attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
3478     attr: function(m) {
3479       m[3] = (m[5] || m[6]);
3480       return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
3481     },
3482     pseudo: function(m) {
3483       if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
3484       return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
3485     },
3486     descendant:   'c = "descendant";',
3487     child:        'c = "child";',
3488     adjacent:     'c = "adjacent";',
3489     laterSibling: 'c = "laterSibling";'
3490   },
3491
3492   patterns: [
3493     { name: 'laterSibling', re: /^\s*~\s*/ },
3494     { name: 'child',        re: /^\s*>\s*/ },
3495     { name: 'adjacent',     re: /^\s*\+\s*/ },
3496     { name: 'descendant',   re: /^\s/ },
3497
3498     { name: 'tagName',      re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
3499     { name: 'id',           re: /^#([\w\-\*]+)(\b|$)/ },
3500     { name: 'className',    re: /^\.([\w\-\*]+)(\b|$)/ },
3501     { name: 'pseudo',       re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
3502     { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
3503     { name: 'attr',         re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
3504   ],
3505
3506   assertions: {
3507     tagName: function(element, matches) {
3508       return matches[1].toUpperCase() == element.tagName.toUpperCase();
3509     },
3510
3511     className: function(element, matches) {
3512       return Element.hasClassName(element, matches[1]);
3513     },
3514
3515     id: function(element, matches) {
3516       return element.id === matches[1];
3517     },
3518
3519     attrPresence: function(element, matches) {
3520       return Element.hasAttribute(element, matches[1]);
3521     },
3522
3523     attr: function(element, matches) {
3524       var nodeValue = Element.readAttribute(element, matches[1]);
3525       return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
3526     }
3527   },
3528
3529   handlers: {
3530     concat: function(a, b) {
3531       for (var i = 0, node; node = b[i]; i++)
3532         a.push(node);
3533       return a;
3534     },
3535
3536     mark: function(nodes) {
3537       var _true = Prototype.emptyFunction;
3538       for (var i = 0, node; node = nodes[i]; i++)
3539         node._countedByPrototype = _true;
3540       return nodes;
3541     },
3542
3543     unmark: (function(){
3544
3545       var PROPERTIES_ATTRIBUTES_MAP = (function(){
3546         var el = document.createElement('div'),
3547             isBuggy = false,
3548             propName = '_countedByPrototype',
3549             value = 'x'
3550         el[propName] = value;
3551         isBuggy = (el.getAttribute(propName) === value);
3552         el = null;
3553         return isBuggy;
3554       })();
3555
3556       return PROPERTIES_ATTRIBUTES_MAP ?
3557         function(nodes) {
3558           for (var i = 0, node; node = nodes[i]; i++)
3559             node.removeAttribute('_countedByPrototype');
3560           return nodes;
3561         } :
3562         function(nodes) {
3563           for (var i = 0, node; node = nodes[i]; i++)
3564             node._countedByPrototype = void 0;
3565           return nodes;
3566         }
3567     })(),
3568
3569     index: function(parentNode, reverse, ofType) {
3570       parentNode._countedByPrototype = Prototype.emptyFunction;
3571       if (reverse) {
3572         for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3573           var node = nodes[i];
3574           if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3575         }
3576       } else {
3577         for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3578           if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
3579       }
3580     },
3581
3582     unique: function(nodes) {
3583       if (nodes.length == 0) return nodes;
3584       var results = [], n;
3585       for (var i = 0, l = nodes.length; i < l; i++)
3586         if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
3587           n._countedByPrototype = Prototype.emptyFunction;
3588           results.push(Element.extend(n));
3589         }
3590       return Selector.handlers.unmark(results);
3591     },
3592
3593     descendant: function(nodes) {
3594       var h = Selector.handlers;
3595       for (var i = 0, results = [], node; node = nodes[i]; i++)
3596         h.concat(results, node.getElementsByTagName('*'));
3597       return results;
3598     },
3599
3600     child: function(nodes) {
3601       var h = Selector.handlers;
3602       for (var i = 0, results = [], node; node = nodes[i]; i++) {
3603         for (var j = 0, child; child = node.childNodes[j]; j++)
3604           if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3605       }
3606       return results;
3607     },
3608
3609     adjacent: function(nodes) {
3610       for (var i = 0, results = [], node; node = nodes[i]; i++) {
3611         var next = this.nextElementSibling(node);
3612         if (next) results.push(next);
3613       }
3614       return results;
3615     },
3616
3617     laterSibling: function(nodes) {
3618       var h = Selector.handlers;
3619       for (var i = 0, results = [], node; node = nodes[i]; i++)
3620         h.concat(results, Element.nextSiblings(node));
3621       return results;
3622     },
3623
3624     nextElementSibling: function(node) {
3625       while (node = node.nextSibling)
3626         if (node.nodeType == 1) return node;
3627       return null;
3628     },
3629
3630     previousElementSibling: function(node) {
3631       while (node = node.previousSibling)
3632         if (node.nodeType == 1) return node;
3633       return null;
3634     },
3635
3636     tagName: function(nodes, root, tagName, combinator) {
3637       var uTagName = tagName.toUpperCase();
3638       var results = [], h = Selector.handlers;
3639       if (nodes) {
3640         if (combinator) {
3641           if (combinator == "descendant") {
3642             for (var i = 0, node; node = nodes[i]; i++)
3643               h.concat(results, node.getElementsByTagName(tagName));
3644             return results;
3645           } else nodes = this[combinator](nodes);
3646           if (tagName == "*") return nodes;
3647         }
3648         for (var i = 0, node; node = nodes[i]; i++)
3649           if (node.tagName.toUpperCase() === uTagName) results.push(node);
3650         return results;
3651       } else return root.getElementsByTagName(tagName);
3652     },
3653
3654     id: function(nodes, root, id, combinator) {
3655       var targetNode = $(id), h = Selector.handlers;
3656
3657       if (root == document) {
3658         if (!targetNode) return [];
3659         if (!nodes) return [targetNode];
3660       } else {
3661         if (!root.sourceIndex || root.sourceIndex < 1) {
3662           var nodes = root.getElementsByTagName('*');
3663           for (var j = 0, node; node = nodes[j]; j++) {
3664             if (node.id === id) return [node];
3665           }
3666         }
3667       }
3668
3669       if (nodes) {
3670         if (combinator) {
3671           if (combinator == 'child') {
3672             for (var i = 0, node; node = nodes[i]; i++)
3673               if (targetNode.parentNode == node) return [targetNode];
3674           } else if (combinator == 'descendant') {
3675             for (var i = 0, node; node = nodes[i]; i++)
3676               if (Element.descendantOf(targetNode, node)) return [targetNode];
3677           } else if (combinator == 'adjacent') {
3678             for (var i = 0, node; node = nodes[i]; i++)
3679               if (Selector.handlers.previousElementSibling(targetNode) == node)
3680                 return [targetNode];
3681           } else nodes = h[combinator](nodes);
3682         }
3683         for (var i = 0, node; node = nodes[i]; i++)
3684           if (node == targetNode) return [targetNode];
3685         return [];
3686       }
3687       return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3688     },
3689
3690     className: function(nodes, root, className, combinator) {
3691       if (nodes && combinator) nodes = this[combinator](nodes);
3692       return Selector.handlers.byClassName(nodes, root, className);
3693     },
3694
3695     byClassName: function(nodes, root, className) {
3696       if (!nodes) nodes = Selector.handlers.descendant([root]);
3697       var needle = ' ' + className + ' ';
3698       for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
3699         nodeClassName = node.className;
3700         if (nodeClassName.length == 0) continue;
3701         if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
3702           results.push(node);
3703       }
3704       return results;
3705     },
3706
3707     attrPresence: function(nodes, root, attr, combinator) {
3708       if (!nodes) nodes = root.getElementsByTagName("*");
3709       if (nodes && combinator) nodes = this[combinator](nodes);
3710       var results = [];
3711       for (var i = 0, node; node = nodes[i]; i++)
3712         if (Element.hasAttribute(node, attr)) results.push(node);
3713       return results;
3714     },
3715
3716     attr: function(nodes, root, attr, value, operator, combinator) {
3717       if (!nodes) nodes = root.getElementsByTagName("*");
3718       if (nodes && combinator) nodes = this[combinator](nodes);
3719       var handler = Selector.operators[operator], results = [];
3720       for (var i = 0, node; node = nodes[i]; i++) {
3721         var nodeValue = Element.readAttribute(node, attr);
3722         if (nodeValue === null) continue;
3723         if (handler(nodeValue, value)) results.push(node);
3724       }
3725       return results;
3726     },
3727
3728     pseudo: function(nodes, name, value, root, combinator) {
3729       if (nodes && combinator) nodes = this[combinator](nodes);
3730       if (!nodes) nodes = root.getElementsByTagName("*");
3731       return Selector.pseudos[name](nodes, value, root);
3732     }
3733   },
3734
3735   pseudos: {
3736     'first-child': function(nodes, value, root) {
3737       for (var i = 0, results = [], node; node = nodes[i]; i++) {
3738         if (Selector.handlers.previousElementSibling(node)) continue;
3739           results.push(node);
3740       }
3741       return results;
3742     },
3743     'last-child': function(nodes, value, root) {
3744       for (var i = 0, results = [], node; node = nodes[i]; i++) {
3745         if (Selector.handlers.nextElementSibling(node)) continue;
3746           results.push(node);
3747       }
3748       return results;
3749     },
3750     'only-child': function(nodes, value, root) {
3751       var h = Selector.handlers;
3752       for (var i = 0, results = [], node; node = nodes[i]; i++)
3753         if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3754           results.push(node);
3755       return results;
3756     },
3757     'nth-child':        function(nodes, formula, root) {
3758       return Selector.pseudos.nth(nodes, formula, root);
3759     },
3760     'nth-last-child':   function(nodes, formula, root) {
3761       return Selector.pseudos.nth(nodes, formula, root, true);
3762     },
3763     'nth-of-type':      function(nodes, formula, root) {
3764       return Selector.pseudos.nth(nodes, formula, root, false, true);
3765     },
3766     'nth-last-of-type': function(nodes, formula, root) {
3767       return Selector.pseudos.nth(nodes, formula, root, true, true);
3768     },
3769     'first-of-type':    function(nodes, formula, root) {
3770       return Selector.pseudos.nth(nodes, "1", root, false, true);
3771     },
3772     'last-of-type':     function(nodes, formula, root) {
3773       return Selector.pseudos.nth(nodes, "1", root, true, true);
3774     },
3775     'only-of-type':     function(nodes, formula, root) {
3776       var p = Selector.pseudos;
3777       return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
3778     },
3779
3780     getIndices: function(a, b, total) {
3781       if (a == 0) return b > 0 ? [b] : [];
3782       return $R(1, total).inject([], function(memo, i) {
3783         if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3784         return memo;
3785       });
3786     },
3787
3788     nth: function(nodes, formula, root, reverse, ofType) {
3789       if (nodes.length == 0) return [];
3790       if (formula == 'even') formula = '2n+0';
3791       if (formula == 'odd')  formula = '2n+1';
3792       var h = Selector.handlers, results = [], indexed = [], m;
3793       h.mark(nodes);
3794       for (var i = 0, node; node = nodes[i]; i++) {
3795         if (!node.parentNode._countedByPrototype) {
3796           h.index(node.parentNode, reverse, ofType);
3797           indexed.push(node.parentNode);
3798         }
3799       }
3800       if (formula.match(/^\d+$/)) { // just a number
3801         formula = Number(formula);
3802         for (var i = 0, node; node = nodes[i]; i++)
3803           if (node.nodeIndex == formula) results.push(node);
3804       } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3805         if (m[1] == "-") m[1] = -1;
3806         var a = m[1] ? Number(m[1]) : 1;
3807         var b = m[2] ? Number(m[2]) : 0;
3808         var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3809         for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3810           for (var j = 0; j < l; j++)
3811             if (node.nodeIndex == indices[j]) results.push(node);
3812         }
3813       }
3814       h.unmark(nodes);
3815       h.unmark(indexed);
3816       return results;
3817     },
3818
3819     'empty': function(nodes, value, root) {
3820       for (var i = 0, results = [], node; node = nodes[i]; i++) {
3821         if (node.tagName == '!' || node.firstChild) continue;
3822         results.push(node);
3823       }
3824       return results;
3825     },
3826
3827     'not': function(nodes, selector, root) {
3828       var h = Selector.handlers, selectorType, m;
3829       var exclusions = new Selector(selector).findElements(root);
3830       h.mark(exclusions);
3831       for (var i = 0, results = [], node; node = nodes[i]; i++)
3832         if (!node._countedByPrototype) results.push(node);
3833       h.unmark(exclusions);
3834       return results;
3835     },
3836
3837     'enabled': function(nodes, value, root) {
3838       for (var i = 0, results = [], node; node = nodes[i]; i++)
3839         if (!node.disabled && (!node.type || node.type !== 'hidden'))
3840           results.push(node);
3841       return results;
3842     },
3843
3844     'disabled': function(nodes, value, root) {
3845       for (var i = 0, results = [], node; node = nodes[i]; i++)
3846         if (node.disabled) results.push(node);
3847       return results;
3848     },
3849
3850     'checked': function(nodes, value, root) {
3851       for (var i = 0, results = [], node; node = nodes[i]; i++)
3852         if (node.checked) results.push(node);
3853       return results;
3854     }
3855   },
3856
3857   operators: {
3858     '=':  function(nv, v) { return nv == v; },
3859     '!=': function(nv, v) { return nv != v; },
3860     '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
3861     '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
3862     '*=': function(nv, v) { return nv == v || nv && nv.include(v); },
3863     '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3864     '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
3865      '-').include('-' + (v || "").toUpperCase() + '-'); }
3866   },
3867
3868   split: function(expression) {
3869     var expressions = [];
3870     expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
3871       expressions.push(m[1].strip());
3872     });
3873     return expressions;
3874   },
3875
3876   matchElements: function(elements, expression) {
3877     var matches = $$(expression), h = Selector.handlers;
3878     h.mark(matches);
3879     for (var i = 0, results = [], element; element = elements[i]; i++)
3880       if (element._countedByPrototype) results.push(element);
3881     h.unmark(matches);
3882     return results;
3883   },
3884
3885   findElement: function(elements, expression, index) {
3886     if (Object.isNumber(expression)) {
3887       index = expression; expression = false;
3888     }
3889     return Selector.matchElements(elements, expression || '*')[index || 0];
3890   },
3891
3892   findChildElements: function(element, expressions) {
3893     expressions = Selector.split(expressions.join(','));
3894     var results = [], h = Selector.handlers;
3895     for (var i = 0, l = expressions.length, selector; i < l; i++) {
3896       selector = new Selector(expressions[i].strip());
3897       h.concat(results, selector.findElements(element));
3898     }
3899     return (l > 1) ? h.unique(results) : results;
3900   }
3901 });
3902
3903 if (Prototype.Browser.IE) {
3904   Object.extend(Selector.handlers, {
3905     concat: function(a, b) {
3906       for (var i = 0, node; node = b[i]; i++)
3907         if (node.tagName !== "!") a.push(node);
3908       return a;
3909     }
3910   });
3911 }
3912
3913 function $$() {
3914   return Selector.findChildElements(document, $A(arguments));
3915 }
3916
3917 var Form = {
3918   reset: function(form) {
3919     form = $(form);
3920     form.reset();
3921     return form;
3922   },
3923
3924   serializeElements: function(elements, options) {
3925     if (typeof options != 'object') options = { hash: !!options };
3926     else if (Object.isUndefined(options.hash)) options.hash = true;
3927     var key, value, submitted = false, submit = options.submit;
3928
3929     var data = elements.inject({ }, function(result, element) {
3930       if (!element.disabled && element.name) {
3931         key = element.name; value = $(element).getValue();
3932         if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
3933             submit !== false && (!submit || key == submit) && (submitted = true)))) {
3934           if (key in result) {
3935             if (!Object.isArray(result[key])) result[key] = [result[key]];
3936             result[key].push(value);
3937           }
3938           else result[key] = value;
3939         }
3940       }
3941       return result;
3942     });
3943
3944     return options.hash ? data : Object.toQueryString(data);
3945   }
3946 };
3947
3948 Form.Methods = {
3949   serialize: function(form, options) {
3950     return Form.serializeElements(Form.getElements(form), options);
3951   },
3952
3953   getElements: function(form) {
3954     var elements = $(form).getElementsByTagName('*'),
3955         element,
3956         arr = [ ],
3957         serializers = Form.Element.Serializers;
3958     for (var i = 0; element = elements[i]; i++) {
3959       arr.push(element);
3960     }
3961     return arr.inject([], function(elements, child) {
3962       if (serializers[child.tagName.toLowerCase()])
3963         elements.push(Element.extend(child));
3964       return elements;
3965     })
3966   },
3967
3968   getInputs: function(form, typeName, name) {
3969     form = $(form);
3970     var inputs = form.getElementsByTagName('input');
3971
3972     if (!typeName && !name) return $A(inputs).map(Element.extend);
3973
3974     for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
3975       var input = inputs[i];
3976       if ((typeName && input.type != typeName) || (name && input.name != name))
3977         continue;
3978       matchingInputs.push(Element.extend(input));
3979     }
3980
3981     return matchingInputs;
3982   },
3983
3984   disable: function(form) {
3985     form = $(form);
3986     Form.getElements(form).invoke('disable');
3987     return form;
3988   },
3989
3990   enable: function(form) {
3991     form = $(form);
3992     Form.getElements(form).invoke('enable');
3993     return form;
3994   },
3995
3996   findFirstElement: function(form) {
3997     var elements = $(form).getElements().findAll(function(element) {
3998       return 'hidden' != element.type && !element.disabled;
3999     });
4000     var firstByIndex = elements.findAll(function(element) {
4001       return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
4002     }).sortBy(function(element) { return element.tabIndex }).first();
4003
4004     return firstByIndex ? firstByIndex : elements.find(function(element) {
4005       return /^(?:input|select|textarea)$/i.test(element.tagName);
4006     });
4007   },
4008
4009   focusFirstElement: function(form) {
4010     form = $(form);
4011     form.findFirstElement().activate();
4012     return form;
4013   },
4014
4015   request: function(form, options) {
4016     form = $(form), options = Object.clone(options || { });
4017
4018     var params = options.parameters, action = form.readAttribute('action') || '';
4019     if (action.blank()) action = window.location.href;
4020     options.parameters = form.serialize(true);
4021
4022     if (params) {
4023       if (Object.isString(params)) params = params.toQueryParams();
4024       Object.extend(options.parameters, params);
4025     }
4026
4027     if (form.hasAttribute('method') && !options.method)
4028       options.method = form.method;
4029
4030     return new Ajax.Request(action, options);
4031   }
4032 };
4033
4034 /*--------------------------------------------------------------------------*/
4035
4036
4037 Form.Element = {
4038   focus: function(element) {
4039     $(element).focus();
4040     return element;
4041   },
4042
4043   select: function(element) {
4044     $(element).select();
4045     return element;
4046   }
4047 };
4048
4049 Form.Element.Methods = {
4050
4051   serialize: function(element) {
4052     element = $(element);
4053     if (!element.disabled && element.name) {
4054       var value = element.getValue();
4055       if (value != undefined) {
4056         var pair = { };
4057         pair[element.name] = value;
4058         return Object.toQueryString(pair);
4059       }
4060     }
4061     return '';
4062   },
4063
4064   getValue: function(element) {
4065     element = $(element);
4066     var method = element.tagName.toLowerCase();
4067     return Form.Element.Serializers[method](element);
4068   },
4069
4070   setValue: function(element, value) {
4071     element = $(element);
4072     var method = element.tagName.toLowerCase();
4073     Form.Element.Serializers[method](element, value);
4074     return element;
4075   },
4076
4077   clear: function(element) {
4078     $(element).value = '';
4079     return element;
4080   },
4081
4082   present: function(element) {
4083     return $(element).value != '';
4084   },
4085
4086   activate: function(element) {
4087     element = $(element);
4088     try {
4089       element.focus();
4090       if (element.select && (element.tagName.toLowerCase() != 'input' ||
4091           !(/^(?:button|reset|submit)$/i.test(element.type))))
4092         element.select();
4093     } catch (e) { }
4094     return element;
4095   },
4096
4097   disable: function(element) {
4098     element = $(element);
4099     element.disabled = true;
4100     return element;
4101   },
4102
4103   enable: function(element) {
4104     element = $(element);
4105     element.disabled = false;
4106     return element;
4107   }
4108 };
4109
4110 /*--------------------------------------------------------------------------*/
4111
4112 var Field = Form.Element;
4113
4114 var $F = Form.Element.Methods.getValue;
4115
4116 /*--------------------------------------------------------------------------*/
4117
4118 Form.Element.Serializers = {
4119   input: function(element, value) {
4120     switch (element.type.toLowerCase()) {
4121       case 'checkbox':
4122       case 'radio':
4123         return Form.Element.Serializers.inputSelector(element, value);
4124       default:
4125         return Form.Element.Serializers.textarea(element, value);
4126     }
4127   },
4128
4129   inputSelector: function(element, value) {
4130     if (Object.isUndefined(value)) return element.checked ? element.value : null;
4131     else element.checked = !!value;
4132   },
4133
4134   textarea: function(element, value) {
4135     if (Object.isUndefined(value)) return element.value;
4136     else element.value = value;
4137   },
4138
4139   select: function(element, value) {
4140     if (Object.isUndefined(value))
4141       return this[element.type == 'select-one' ?
4142         'selectOne' : 'selectMany'](element);
4143     else {
4144       var opt, currentValue, single = !Object.isArray(value);
4145       for (var i = 0, length = element.length; i < length; i++) {
4146         opt = element.options[i];
4147         currentValue = this.optionValue(opt);
4148         if (single) {
4149           if (currentValue == value) {
4150             opt.selected = true;
4151             return;
4152           }
4153         }
4154         else opt.selected = value.include(currentValue);
4155       }
4156     }
4157   },
4158
4159   selectOne: function(element) {
4160     var index = element.selectedIndex;
4161     return index >= 0 ? this.optionValue(element.options[index]) : null;
4162   },
4163
4164   selectMany: function(element) {
4165     var values, length = element.length;
4166     if (!length) return null;
4167
4168     for (var i = 0, values = []; i < length; i++) {
4169       var opt = element.options[i];
4170       if (opt.selected) values.push(this.optionValue(opt));
4171     }
4172     return values;
4173   },
4174
4175   optionValue: function(opt) {
4176     return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
4177   }
4178 };
4179
4180 /*--------------------------------------------------------------------------*/
4181
4182
4183 Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
4184   initialize: function($super, element, frequency, callback) {
4185     $super(callback, frequency);
4186     this.element   = $(element);
4187     this.lastValue = this.getValue();
4188   },
4189
4190   execute: function() {
4191     var value = this.getValue();
4192     if (Object.isString(this.lastValue) && Object.isString(value) ?
4193         this.lastValue != value : String(this.lastValue) != String(value)) {
4194       this.callback(this.element, value);
4195       this.lastValue = value;
4196     }
4197   }
4198 });
4199
4200 Form.Element.Observer = Class.create(Abstract.TimedObserver, {
4201   getValue: function() {
4202     return Form.Element.getValue(this.element);
4203   }
4204 });
4205
4206 Form.Observer = Class.create(Abstract.TimedObserver, {
4207   getValue: function() {
4208     return Form.serialize(this.element);
4209   }
4210 });
4211
4212 /*--------------------------------------------------------------------------*/
4213
4214 Abstract.EventObserver = Class.create({
4215   initialize: function(element, callback) {
4216     this.element  = $(element);
4217     this.callback = callback;
4218
4219     this.lastValue = this.getValue();
4220     if (this.element.tagName.toLowerCase() == 'form')
4221       this.registerFormCallbacks();
4222     else
4223       this.registerCallback(this.element);
4224   },
4225
4226   onElementEvent: function() {
4227     var value = this.getValue();
4228     if (this.lastValue != value) {
4229       this.callback(this.element, value);
4230       this.lastValue = value;
4231     }
4232   },
4233
4234   registerFormCallbacks: function() {
4235     Form.getElements(this.element).each(this.registerCallback, this);
4236   },
4237
4238   registerCallback: function(element) {
4239     if (element.type) {
4240       switch (element.type.toLowerCase()) {
4241         case 'checkbox':
4242         case 'radio':
4243           Event.observe(element, 'click', this.onElementEvent.bind(this));
4244           break;
4245         default:
4246           Event.observe(element, 'change', this.onElementEvent.bind(this));
4247           break;
4248       }
4249     }
4250   }
4251 });
4252
4253 Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
4254   getValue: function() {
4255     return Form.Element.getValue(this.element);
4256   }
4257 });
4258
4259 Form.EventObserver = Class.create(Abstract.EventObserver, {
4260   getValue: function() {
4261     return Form.serialize(this.element);
4262   }
4263 });
4264 (function() {
4265
4266   var Event = {
4267     KEY_BACKSPACE: 8,
4268     KEY_TAB:       9,
4269     KEY_RETURN:   13,
4270     KEY_ESC:      27,
4271     KEY_LEFT:     37,
4272     KEY_UP:       38,
4273     KEY_RIGHT:    39,
4274     KEY_DOWN:     40,
4275     KEY_DELETE:   46,
4276     KEY_HOME:     36,
4277     KEY_END:      35,
4278     KEY_PAGEUP:   33,
4279     KEY_PAGEDOWN: 34,
4280     KEY_INSERT:   45,
4281
4282     cache: {}
4283   };
4284
4285   var docEl = document.documentElement;
4286   var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
4287     && 'onmouseleave' in docEl;
4288
4289   var _isButton;
4290   if (Prototype.Browser.IE) {
4291     var buttonMap = { 0: 1, 1: 4, 2: 2 };
4292     _isButton = function(event, code) {
4293       return event.button === buttonMap[code];
4294     };
4295   } else if (Prototype.Browser.WebKit) {
4296     _isButton = function(event, code) {
4297       switch (code) {
4298         case 0: return event.which == 1 && !event.metaKey;
4299         case 1: return event.which == 1 && event.metaKey;
4300         default: return false;
4301       }
4302     };
4303   } else {
4304     _isButton = function(event, code) {
4305       return event.which ? (event.which === code + 1) : (event.button === code);
4306     };
4307   }
4308
4309   function isLeftClick(event)   { return _isButton(event, 0) }
4310
4311   function isMiddleClick(event) { return _isButton(event, 1) }
4312
4313   function isRightClick(event)  { return _isButton(event, 2) }
4314
4315   function element(event) {
4316     event = Event.extend(event);
4317
4318     var node = event.target, type = event.type,
4319      currentTarget = event.currentTarget;
4320
4321     if (currentTarget && currentTarget.tagName) {
4322       if (type === 'load' || type === 'error' ||
4323         (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
4324           && currentTarget.type === 'radio'))
4325             node = currentTarget;
4326     }
4327
4328     if (node.nodeType == Node.TEXT_NODE)
4329       node = node.parentNode;
4330
4331     return Element.extend(node);
4332   }
4333
4334   function findElement(event, expression) {
4335     var element = Event.element(event);
4336     if (!expression) return element;
4337     var elements = [element].concat(element.ancestors());
4338     return Selector.findElement(elements, expression, 0);
4339   }
4340
4341   function pointer(event) {
4342     return { x: pointerX(event), y: pointerY(event) };
4343   }
4344
4345   function pointerX(event) {
4346     var docElement = document.documentElement,
4347      body = document.body || { scrollLeft: 0 };
4348
4349     return event.pageX || (event.clientX +
4350       (docElement.scrollLeft || body.scrollLeft) -
4351       (docElement.clientLeft || 0));
4352   }
4353
4354   function pointerY(event) {
4355     var docElement = document.documentElement,
4356      body = document.body || { scrollTop: 0 };
4357
4358     return  event.pageY || (event.clientY +
4359        (docElement.scrollTop || body.scrollTop) -
4360        (docElement.clientTop || 0));
4361   }
4362
4363
4364   function stop(event) {
4365     Event.extend(event);
4366     event.preventDefault();
4367     event.stopPropagation();
4368
4369     event.stopped = true;
4370   }
4371
4372   Event.Methods = {
4373     isLeftClick: isLeftClick,
4374     isMiddleClick: isMiddleClick,
4375     isRightClick: isRightClick,
4376
4377     element: element,
4378     findElement: findElement,
4379
4380     pointer: pointer,
4381     pointerX: pointerX,
4382     pointerY: pointerY,
4383
4384     stop: stop
4385   };
4386
4387
4388   var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
4389     m[name] = Event.Methods[name].methodize();
4390     return m;
4391   });
4392
4393   if (Prototype.Browser.IE) {
4394     function _relatedTarget(event) {
4395       var element;
4396       switch (event.type) {
4397         case 'mouseover': element = event.fromElement; break;
4398         case 'mouseout':  element = event.toElement;   break;
4399         default: return null;
4400       }
4401       return Element.extend(element);
4402     }
4403
4404     Object.extend(methods, {
4405       stopPropagation: function() { this.cancelBubble = true },
4406       preventDefault:  function() { this.returnValue = false },
4407       inspect: function() { return '[object Event]' }
4408     });
4409
4410     Event.extend = function(event, element) {
4411       if (!event) return false;
4412       if (event._extendedByPrototype) return event;
4413
4414       event._extendedByPrototype = Prototype.emptyFunction;
4415       var pointer = Event.pointer(event);
4416
4417       Object.extend(event, {
4418         target: event.srcElement || element,
4419         relatedTarget: _relatedTarget(event),
4420         pageX:  pointer.x,
4421         pageY:  pointer.y
4422       });
4423
4424       return Object.extend(event, methods);
4425     };
4426   } else {
4427     Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
4428     Object.extend(Event.prototype, methods);
4429     Event.extend = Prototype.K;
4430   }
4431
4432   function _createResponder(element, eventName, handler) {
4433     var registry = Element.retrieve(element, 'prototype_event_registry');
4434
4435     if (Object.isUndefined(registry)) {
4436       CACHE.push(element);
4437       registry = Element.retrieve(element, 'prototype_event_registry', $H());
4438     }
4439
4440     var respondersForEvent = registry.get(eventName);
4441     if (Object.isUndefined(respondersForEvent)) {
4442       respondersForEvent = [];
4443       registry.set(eventName, respondersForEvent);
4444     }
4445
4446     if (respondersForEvent.pluck('handler').include(handler)) return false;
4447
4448     var responder;
4449     if (eventName.include(":")) {
4450       responder = function(event) {
4451         if (Object.isUndefined(event.eventName))
4452           return false;
4453
4454         if (event.eventName !== eventName)
4455           return false;
4456
4457         Event.extend(event, element);
4458         handler.call(element, event);
4459       };
4460     } else {
4461       if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
4462        (eventName === "mouseenter" || eventName === "mouseleave")) {
4463         if (eventName === "mouseenter" || eventName === "mouseleave") {
4464           responder = function(event) {
4465             Event.extend(event, element);
4466
4467             var parent = event.relatedTarget;
4468             while (parent && parent !== element) {
4469               try { parent = parent.parentNode; }
4470               catch(e) { parent = element; }
4471             }
4472
4473             if (parent === element) return;
4474
4475             handler.call(element, event);
4476           };
4477         }
4478       } else {
4479         responder = function(event) {
4480           Event.extend(event, element);
4481           handler.call(element, event);
4482         };
4483       }
4484     }
4485
4486     responder.handler = handler;
4487     respondersForEvent.push(responder);
4488     return responder;
4489   }
4490
4491   function _destroyCache() {
4492     for (var i = 0, length = CACHE.length; i < length; i++) {
4493       Event.stopObserving(CACHE[i]);
4494       CACHE[i] = null;
4495     }
4496   }
4497
4498   var CACHE = [];
4499
4500   if (Prototype.Browser.IE)
4501     window.attachEvent('onunload', _destroyCache);
4502
4503   if (Prototype.Browser.WebKit)
4504     window.addEventListener('unload', Prototype.emptyFunction, false);
4505
4506
4507   var _getDOMEventName = Prototype.K;
4508
4509   if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
4510     _getDOMEventName = function(eventName) {
4511       var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
4512       return eventName in translations ? translations[eventName] : eventName;
4513     };
4514   }
4515
4516   function observe(element, eventName, handler) {
4517     element = $(element);
4518
4519     var responder = _createResponder(element, eventName, handler);
4520
4521     if (!responder) return element;
4522
4523     if (eventName.include(':')) {
4524       if (element.addEventListener)
4525         element.addEventListener("dataavailable", responder, false);
4526       else {
4527         element.attachEvent("ondataavailable", responder);
4528         element.attachEvent("onfilterchange", responder);
4529       }
4530     } else {
4531       var actualEventName = _getDOMEventName(eventName);
4532
4533       if (element.addEventListener)
4534         element.addEventListener(actualEventName, responder, false);
4535       else
4536         element.attachEvent("on" + actualEventName, responder);
4537     }
4538
4539     return element;
4540   }
4541
4542   function stopObserving(element, eventName, handler) {
4543     element = $(element);
4544
4545     var registry = Element.retrieve(element, 'prototype_event_registry');
4546
4547     if (Object.isUndefined(registry)) return element;
4548
4549     if (eventName && !handler) {
4550       var responders = registry.get(eventName);
4551
4552       if (Object.isUndefined(responders)) return element;
4553
4554       responders.each( function(r) {
4555         Element.stopObserving(element, eventName, r.handler);
4556       });
4557       return element;
4558     } else if (!eventName) {
4559       registry.each( function(pair) {
4560         var eventName = pair.key, responders = pair.value;
4561
4562         responders.each( function(r) {
4563           Element.stopObserving(element, eventName, r.handler);
4564         });
4565       });
4566       return element;
4567     }
4568
4569     var responders = registry.get(eventName);
4570
4571     if (!responders) return;
4572
4573     var responder = responders.find( function(r) { return r.handler === handler; });
4574     if (!responder) return element;
4575
4576     var actualEventName = _getDOMEventName(eventName);
4577
4578     if (eventName.include(':')) {
4579       if (element.removeEventListener)
4580         element.removeEventListener("dataavailable", responder, false);
4581       else {
4582         element.detachEvent("ondataavailable", responder);
4583         element.detachEvent("onfilterchange",  responder);
4584       }
4585     } else {
4586       if (element.removeEventListener)
4587         element.removeEventListener(actualEventName, responder, false);
4588       else
4589         element.detachEvent('on' + actualEventName, responder);
4590     }
4591
4592     registry.set(eventName, responders.without(responder));
4593
4594     return element;
4595   }
4596
4597   function fire(element, eventName, memo, bubble) {
4598     element = $(element);
4599
4600     if (Object.isUndefined(bubble))
4601       bubble = true;
4602
4603     if (element == document && document.createEvent && !element.dispatchEvent)
4604       element = document.documentElement;
4605
4606     var event;
4607     if (document.createEvent) {
4608       event = document.createEvent('HTMLEvents');
4609       event.initEvent('dataavailable', true, true);
4610     } else {
4611       event = document.createEventObject();
4612       event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
4613     }
4614
4615     event.eventName = eventName;
4616     event.memo = memo || { };
4617
4618     if (document.createEvent)
4619       element.dispatchEvent(event);
4620     else
4621       element.fireEvent(event.eventType, event);
4622
4623     return Event.extend(event);
4624   }
4625
4626
4627   Object.extend(Event, Event.Methods);
4628
4629   Object.extend(Event, {
4630     fire:          fire,
4631     observe:       observe,
4632     stopObserving: stopObserving
4633   });
4634
4635   Element.addMethods({
4636     fire:          fire,
4637
4638     observe:       observe,
4639
4640     stopObserving: stopObserving
4641   });
4642
4643   Object.extend(document, {
4644     fire:          fire.methodize(),
4645
4646     observe:       observe.methodize(),
4647
4648     stopObserving: stopObserving.methodize(),
4649
4650     loaded:        false
4651   });
4652
4653   if (window.Event) Object.extend(window.Event, Event);
4654   else window.Event = Event;
4655 })();
4656
4657 (function() {
4658   /* Support for the DOMContentLoaded event is based on work by Dan Webb,
4659      Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
4660
4661   var timer;
4662
4663   function fireContentLoadedEvent() {
4664     if (document.loaded) return;
4665     if (timer) window.clearTimeout(timer);
4666     document.loaded = true;
4667     document.fire('dom:loaded');
4668   }
4669
4670   function checkReadyState() {
4671     if (document.readyState === 'complete') {
4672       document.stopObserving('readystatechange', checkReadyState);
4673       fireContentLoadedEvent();
4674     }
4675   }
4676
4677   function pollDoScroll() {
4678     try { document.documentElement.doScroll('left'); }
4679     catch(e) {
4680       timer = pollDoScroll.defer();
4681       return;
4682     }
4683     fireContentLoadedEvent();
4684   }
4685
4686   if (document.addEventListener) {
4687     document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
4688   } else {
4689     document.observe('readystatechange', checkReadyState);
4690     if (window == top)
4691       timer = pollDoScroll.defer();
4692   }
4693
4694   Event.observe(window, 'load', fireContentLoadedEvent);
4695 })();
4696
4697 Element.addMethods();
4698
4699 /*------------------------------- DEPRECATED -------------------------------*/
4700
4701 Hash.toQueryString = Object.toQueryString;
4702
4703 var Toggle = { display: Element.toggle };
4704
4705 Element.Methods.childOf = Element.Methods.descendantOf;
4706
4707 var Insertion = {
4708   Before: function(element, content) {
4709     return Element.insert(element, {before:content});
4710   },
4711
4712   Top: function(element, content) {
4713     return Element.insert(element, {top:content});
4714   },
4715
4716   Bottom: function(element, content) {
4717     return Element.insert(element, {bottom:content});
4718   },
4719
4720   After: function(element, content) {
4721     return Element.insert(element, {after:content});
4722   }
4723 };
4724
4725 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4726
4727 var Position = {
4728   includeScrollOffsets: false,
4729
4730   prepare: function() {
4731     this.deltaX =  window.pageXOffset
4732                 || document.documentElement.scrollLeft
4733                 || document.body.scrollLeft
4734                 || 0;
4735     this.deltaY =  window.pageYOffset
4736                 || document.documentElement.scrollTop
4737                 || document.body.scrollTop
4738                 || 0;
4739   },
4740
4741   within: function(element, x, y) {
4742     if (this.includeScrollOffsets)
4743       return this.withinIncludingScrolloffsets(element, x, y);
4744     this.xcomp = x;
4745     this.ycomp = y;
4746     this.offset = Element.cumulativeOffset(element);
4747
4748     return (y >= this.offset[1] &&
4749             y <  this.offset[1] + element.offsetHeight &&
4750             x >= this.offset[0] &&
4751             x <  this.offset[0] + element.offsetWidth);
4752   },
4753
4754   withinIncludingScrolloffsets: function(element, x, y) {
4755     var offsetcache = Element.cumulativeScrollOffset(element);
4756
4757     this.xcomp = x + offsetcache[0] - this.deltaX;
4758     this.ycomp = y + offsetcache[1] - this.deltaY;
4759     this.offset = Element.cumulativeOffset(element);
4760
4761     return (this.ycomp >= this.offset[1] &&
4762             this.ycomp <  this.offset[1] + element.offsetHeight &&
4763             this.xcomp >= this.offset[0] &&
4764             this.xcomp <  this.offset[0] + element.offsetWidth);
4765   },
4766
4767   overlap: function(mode, element) {
4768     if (!mode) return 0;
4769     if (mode == 'vertical')
4770       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
4771         element.offsetHeight;
4772     if (mode == 'horizontal')
4773       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
4774         element.offsetWidth;
4775   },
4776
4777
4778   cumulativeOffset: Element.Methods.cumulativeOffset,
4779
4780   positionedOffset: Element.Methods.positionedOffset,
4781
4782   absolutize: function(element) {
4783     Position.prepare();
4784     return Element.absolutize(element);
4785   },
4786
4787   relativize: function(element) {
4788     Position.prepare();
4789     return Element.relativize(element);
4790   },
4791
4792   realOffset: Element.Methods.cumulativeScrollOffset,
4793
4794   offsetParent: Element.Methods.getOffsetParent,
4795
4796   page: Element.Methods.viewportOffset,
4797
4798   clone: function(source, target, options) {
4799     options = options || { };
4800     return Element.clonePosition(target, source, options);
4801   }
4802 };
4803
4804 /*--------------------------------------------------------------------------*/
4805
4806 if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
4807   function iter(name) {
4808     return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
4809   }
4810
4811   instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
4812   function(element, className) {
4813     className = className.toString().strip();
4814     var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
4815     return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
4816   } : function(element, className) {
4817     className = className.toString().strip();
4818     var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
4819     if (!classNames && !className) return elements;
4820
4821     var nodes = $(element).getElementsByTagName('*');
4822     className = ' ' + className + ' ';
4823
4824     for (var i = 0, child, cn; child = nodes[i]; i++) {
4825       if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
4826           (classNames && classNames.all(function(name) {
4827             return !name.toString().blank() && cn.include(' ' + name + ' ');
4828           }))))
4829         elements.push(Element.extend(child));
4830     }
4831     return elements;
4832   };
4833
4834   return function(className, parentElement) {
4835     return $(parentElement || document.body).getElementsByClassName(className);
4836   };
4837 }(Element.Methods);
4838
4839 /*--------------------------------------------------------------------------*/
4840
4841 Element.ClassNames = Class.create();
4842 Element.ClassNames.prototype = {
4843   initialize: function(element) {
4844     this.element = $(element);
4845   },
4846
4847   _each: function(iterator) {
4848     this.element.className.split(/\s+/).select(function(name) {
4849       return name.length > 0;
4850     })._each(iterator);
4851   },
4852
4853   set: function(className) {
4854     this.element.className = className;
4855   },
4856
4857   add: function(classNameToAdd) {
4858     if (this.include(classNameToAdd)) return;
4859     this.set($A(this).concat(classNameToAdd).join(' '));
4860   },
4861
4862   remove: function(classNameToRemove) {
4863     if (!this.include(classNameToRemove)) return;
4864     this.set($A(this).without(classNameToRemove).join(' '));
4865   },
4866
4867   toString: function() {
4868     return $A(this).join(' ');
4869   }
4870 };
4871
4872 Object.extend(Element.ClassNames.prototype, Enumerable);
4873
4874 /*--------------------------------------------------------------------------*/