]> git.wh0rd.org Git - tt-rss.git/blob - lib/prototype.js
pngcrush.sh
[tt-rss.git] / lib / prototype.js
1 // Modified document.on() to modified.p_on() to fix compatibility with Dojo -fox
2
3 /*  Prototype JavaScript framework, version 1.7.3
4  *  (c) 2005-2010 Sam Stephenson
5  *
6  *  Prototype is freely distributable under the terms of an MIT-style license.
7  *  For details, see the Prototype web site: http://www.prototypejs.org/
8  *
9  *--------------------------------------------------------------------------*/
10
11 var Prototype = {
12
13   Version: '1.7.3',
14
15   Browser: (function(){
16     var ua = navigator.userAgent;
17     var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
18     return {
19       IE:             !!window.attachEvent && !isOpera,
20       Opera:          isOpera,
21       WebKit:         ua.indexOf('AppleWebKit/') > -1,
22       Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
23       MobileSafari:   /Apple.*Mobile/.test(ua)
24     }
25   })(),
26
27   BrowserFeatures: {
28     XPath: !!document.evaluate,
29
30     SelectorsAPI: !!document.querySelector,
31
32     ElementExtensions: (function() {
33       var constructor = window.Element || window.HTMLElement;
34       return !!(constructor && constructor.prototype);
35     })(),
36     SpecificElementExtensions: (function() {
37       if (typeof window.HTMLDivElement !== 'undefined')
38         return true;
39
40       var div = document.createElement('div'),
41           form = document.createElement('form'),
42           isSupported = false;
43
44       if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
45         isSupported = true;
46       }
47
48       div = form = null;
49
50       return isSupported;
51     })()
52   },
53
54   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script\\s*>',
55   JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
56
57   emptyFunction: function() { },
58
59   K: function(x) { return x }
60 };
61
62 if (Prototype.Browser.MobileSafari)
63   Prototype.BrowserFeatures.SpecificElementExtensions = false;
64 /* Based on Alex Arnell's inheritance implementation. */
65
66 var Class = (function() {
67
68   var IS_DONTENUM_BUGGY = (function(){
69     for (var p in { toString: 1 }) {
70       if (p === 'toString') return false;
71     }
72     return true;
73   })();
74
75   function subclass() {};
76   function create() {
77     var parent = null, properties = $A(arguments);
78     if (Object.isFunction(properties[0]))
79       parent = properties.shift();
80
81     function klass() {
82       this.initialize.apply(this, arguments);
83     }
84
85     Object.extend(klass, Class.Methods);
86     klass.superclass = parent;
87     klass.subclasses = [];
88
89     if (parent) {
90       subclass.prototype = parent.prototype;
91       klass.prototype = new subclass;
92       parent.subclasses.push(klass);
93     }
94
95     for (var i = 0, length = properties.length; i < length; i++)
96       klass.addMethods(properties[i]);
97
98     if (!klass.prototype.initialize)
99       klass.prototype.initialize = Prototype.emptyFunction;
100
101     klass.prototype.constructor = klass;
102     return klass;
103   }
104
105   function addMethods(source) {
106     var ancestor   = this.superclass && this.superclass.prototype,
107         properties = Object.keys(source);
108
109     if (IS_DONTENUM_BUGGY) {
110       if (source.toString != Object.prototype.toString)
111         properties.push("toString");
112       if (source.valueOf != Object.prototype.valueOf)
113         properties.push("valueOf");
114     }
115
116     for (var i = 0, length = properties.length; i < length; i++) {
117       var property = properties[i], value = source[property];
118       if (ancestor && Object.isFunction(value) &&
119           value.argumentNames()[0] == "$super") {
120         var method = value;
121         value = (function(m) {
122           return function() { return ancestor[m].apply(this, arguments); };
123         })(property).wrap(method);
124
125         value.valueOf = (function(method) {
126           return function() { return method.valueOf.call(method); };
127         })(method);
128
129         value.toString = (function(method) {
130           return function() { return method.toString.call(method); };
131         })(method);
132       }
133       this.prototype[property] = value;
134     }
135
136     return this;
137   }
138
139   return {
140     create: create,
141     Methods: {
142       addMethods: addMethods
143     }
144   };
145 })();
146 (function() {
147
148   var _toString = Object.prototype.toString,
149       _hasOwnProperty = Object.prototype.hasOwnProperty,
150       NULL_TYPE = 'Null',
151       UNDEFINED_TYPE = 'Undefined',
152       BOOLEAN_TYPE = 'Boolean',
153       NUMBER_TYPE = 'Number',
154       STRING_TYPE = 'String',
155       OBJECT_TYPE = 'Object',
156       FUNCTION_CLASS = '[object Function]',
157       BOOLEAN_CLASS = '[object Boolean]',
158       NUMBER_CLASS = '[object Number]',
159       STRING_CLASS = '[object String]',
160       ARRAY_CLASS = '[object Array]',
161       DATE_CLASS = '[object Date]',
162       NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
163         typeof JSON.stringify === 'function' &&
164         JSON.stringify(0) === '0' &&
165         typeof JSON.stringify(Prototype.K) === 'undefined';
166
167
168
169   var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf',
170    'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'];
171
172   var IS_DONTENUM_BUGGY = (function(){
173     for (var p in { toString: 1 }) {
174       if (p === 'toString') return false;
175     }
176     return true;
177   })();
178
179   function Type(o) {
180     switch(o) {
181       case null: return NULL_TYPE;
182       case (void 0): return UNDEFINED_TYPE;
183     }
184     var type = typeof o;
185     switch(type) {
186       case 'boolean': return BOOLEAN_TYPE;
187       case 'number':  return NUMBER_TYPE;
188       case 'string':  return STRING_TYPE;
189     }
190     return OBJECT_TYPE;
191   }
192
193   function extend(destination, source) {
194     for (var property in source)
195       destination[property] = source[property];
196     return destination;
197   }
198
199   function inspect(object) {
200     try {
201       if (isUndefined(object)) return 'undefined';
202       if (object === null) return 'null';
203       return object.inspect ? object.inspect() : String(object);
204     } catch (e) {
205       if (e instanceof RangeError) return '...';
206       throw e;
207     }
208   }
209
210   function toJSON(value) {
211     return Str('', { '': value }, []);
212   }
213
214   function Str(key, holder, stack) {
215     var value = holder[key];
216     if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
217       value = value.toJSON(key);
218     }
219
220     var _class = _toString.call(value);
221
222     switch (_class) {
223       case NUMBER_CLASS:
224       case BOOLEAN_CLASS:
225       case STRING_CLASS:
226         value = value.valueOf();
227     }
228
229     switch (value) {
230       case null: return 'null';
231       case true: return 'true';
232       case false: return 'false';
233     }
234
235     var type = typeof value;
236     switch (type) {
237       case 'string':
238         return value.inspect(true);
239       case 'number':
240         return isFinite(value) ? String(value) : 'null';
241       case 'object':
242
243         for (var i = 0, length = stack.length; i < length; i++) {
244           if (stack[i] === value) {
245             throw new TypeError("Cyclic reference to '" + value + "' in object");
246           }
247         }
248         stack.push(value);
249
250         var partial = [];
251         if (_class === ARRAY_CLASS) {
252           for (var i = 0, length = value.length; i < length; i++) {
253             var str = Str(i, value, stack);
254             partial.push(typeof str === 'undefined' ? 'null' : str);
255           }
256           partial = '[' + partial.join(',') + ']';
257         } else {
258           var keys = Object.keys(value);
259           for (var i = 0, length = keys.length; i < length; i++) {
260             var key = keys[i], str = Str(key, value, stack);
261             if (typeof str !== "undefined") {
262                partial.push(key.inspect(true)+ ':' + str);
263              }
264           }
265           partial = '{' + partial.join(',') + '}';
266         }
267         stack.pop();
268         return partial;
269     }
270   }
271
272   function stringify(object) {
273     return JSON.stringify(object);
274   }
275
276   function toQueryString(object) {
277     return $H(object).toQueryString();
278   }
279
280   function toHTML(object) {
281     return object && object.toHTML ? object.toHTML() : String.interpret(object);
282   }
283
284   function keys(object) {
285     if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
286     var results = [];
287     for (var property in object) {
288       if (_hasOwnProperty.call(object, property))
289         results.push(property);
290     }
291
292     if (IS_DONTENUM_BUGGY) {
293       for (var i = 0; property = DONT_ENUMS[i]; i++) {
294         if (_hasOwnProperty.call(object, property))
295           results.push(property);
296       }
297     }
298
299     return results;
300   }
301
302   function values(object) {
303     var results = [];
304     for (var property in object)
305       results.push(object[property]);
306     return results;
307   }
308
309   function clone(object) {
310     return extend({ }, object);
311   }
312
313   function isElement(object) {
314     return !!(object && object.nodeType == 1);
315   }
316
317   function isArray(object) {
318     return _toString.call(object) === ARRAY_CLASS;
319   }
320
321   var hasNativeIsArray = (typeof Array.isArray == 'function')
322     && Array.isArray([]) && !Array.isArray({});
323
324   if (hasNativeIsArray) {
325     isArray = Array.isArray;
326   }
327
328   function isHash(object) {
329     return object instanceof Hash;
330   }
331
332   function isFunction(object) {
333     return _toString.call(object) === FUNCTION_CLASS;
334   }
335
336   function isString(object) {
337     return _toString.call(object) === STRING_CLASS;
338   }
339
340   function isNumber(object) {
341     return _toString.call(object) === NUMBER_CLASS;
342   }
343
344   function isDate(object) {
345     return _toString.call(object) === DATE_CLASS;
346   }
347
348   function isUndefined(object) {
349     return typeof object === "undefined";
350   }
351
352   extend(Object, {
353     extend:        extend,
354     inspect:       inspect,
355     toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
356     toQueryString: toQueryString,
357     toHTML:        toHTML,
358     keys:          Object.keys || keys,
359     values:        values,
360     clone:         clone,
361     isElement:     isElement,
362     isArray:       isArray,
363     isHash:        isHash,
364     isFunction:    isFunction,
365     isString:      isString,
366     isNumber:      isNumber,
367     isDate:        isDate,
368     isUndefined:   isUndefined
369   });
370 })();
371 Object.extend(Function.prototype, (function() {
372   var slice = Array.prototype.slice;
373
374   function update(array, args) {
375     var arrayLength = array.length, length = args.length;
376     while (length--) array[arrayLength + length] = args[length];
377     return array;
378   }
379
380   function merge(array, args) {
381     array = slice.call(array, 0);
382     return update(array, args);
383   }
384
385   function argumentNames() {
386     var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
387       .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
388       .replace(/\s+/g, '').split(',');
389     return names.length == 1 && !names[0] ? [] : names;
390   }
391
392
393   function bind(context) {
394     if (arguments.length < 2 && Object.isUndefined(arguments[0]))
395       return this;
396
397     if (!Object.isFunction(this))
398       throw new TypeError("The object is not callable.");
399
400     var nop = function() {};
401     var __method = this, args = slice.call(arguments, 1);
402
403     var bound = function() {
404       var a = merge(args, arguments);
405       var c = this instanceof bound ? this : context;
406       return __method.apply(c, a);
407     };
408
409     nop.prototype   = this.prototype;
410     bound.prototype = new nop();
411
412     return bound;
413   }
414
415   function bindAsEventListener(context) {
416     var __method = this, args = slice.call(arguments, 1);
417     return function(event) {
418       var a = update([event || window.event], args);
419       return __method.apply(context, a);
420     }
421   }
422
423   function curry() {
424     if (!arguments.length) return this;
425     var __method = this, args = slice.call(arguments, 0);
426     return function() {
427       var a = merge(args, arguments);
428       return __method.apply(this, a);
429     }
430   }
431
432   function delay(timeout) {
433     var __method = this, args = slice.call(arguments, 1);
434     timeout = timeout * 1000;
435     return window.setTimeout(function() {
436       return __method.apply(__method, args);
437     }, timeout);
438   }
439
440   function defer() {
441     var args = update([0.01], arguments);
442     return this.delay.apply(this, args);
443   }
444
445   function wrap(wrapper) {
446     var __method = this;
447     return function() {
448       var a = update([__method.bind(this)], arguments);
449       return wrapper.apply(this, a);
450     }
451   }
452
453   function methodize() {
454     if (this._methodized) return this._methodized;
455     var __method = this;
456     return this._methodized = function() {
457       var a = update([this], arguments);
458       return __method.apply(null, a);
459     };
460   }
461
462   var extensions = {
463     argumentNames:       argumentNames,
464     bindAsEventListener: bindAsEventListener,
465     curry:               curry,
466     delay:               delay,
467     defer:               defer,
468     wrap:                wrap,
469     methodize:           methodize
470   };
471
472   if (!Function.prototype.bind)
473     extensions.bind = bind;
474
475   return extensions;
476 })());
477
478
479
480 (function(proto) {
481
482
483   function toISOString() {
484     return this.getUTCFullYear() + '-' +
485       (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
486       this.getUTCDate().toPaddedString(2) + 'T' +
487       this.getUTCHours().toPaddedString(2) + ':' +
488       this.getUTCMinutes().toPaddedString(2) + ':' +
489       this.getUTCSeconds().toPaddedString(2) + 'Z';
490   }
491
492
493   function toJSON() {
494     return this.toISOString();
495   }
496
497   if (!proto.toISOString) proto.toISOString = toISOString;
498   if (!proto.toJSON) proto.toJSON = toJSON;
499
500 })(Date.prototype);
501
502
503 RegExp.prototype.match = RegExp.prototype.test;
504
505 RegExp.escape = function(str) {
506   return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
507 };
508 var PeriodicalExecuter = Class.create({
509   initialize: function(callback, frequency) {
510     this.callback = callback;
511     this.frequency = frequency;
512     this.currentlyExecuting = false;
513
514     this.registerCallback();
515   },
516
517   registerCallback: function() {
518     this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
519   },
520
521   execute: function() {
522     this.callback(this);
523   },
524
525   stop: function() {
526     if (!this.timer) return;
527     clearInterval(this.timer);
528     this.timer = null;
529   },
530
531   onTimerEvent: function() {
532     if (!this.currentlyExecuting) {
533       try {
534         this.currentlyExecuting = true;
535         this.execute();
536         this.currentlyExecuting = false;
537       } catch(e) {
538         this.currentlyExecuting = false;
539         throw e;
540       }
541     }
542   }
543 });
544 Object.extend(String, {
545   interpret: function(value) {
546     return value == null ? '' : String(value);
547   },
548   specialChar: {
549     '\b': '\\b',
550     '\t': '\\t',
551     '\n': '\\n',
552     '\f': '\\f',
553     '\r': '\\r',
554     '\\': '\\\\'
555   }
556 });
557
558 Object.extend(String.prototype, (function() {
559   var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
560     typeof JSON.parse === 'function' &&
561     JSON.parse('{"test": true}').test;
562
563   function prepareReplacement(replacement) {
564     if (Object.isFunction(replacement)) return replacement;
565     var template = new Template(replacement);
566     return function(match) { return template.evaluate(match) };
567   }
568
569   function isNonEmptyRegExp(regexp) {
570     return regexp.source && regexp.source !== '(?:)';
571   }
572
573
574   function gsub(pattern, replacement) {
575     var result = '', source = this, match;
576     replacement = prepareReplacement(replacement);
577
578     if (Object.isString(pattern))
579       pattern = RegExp.escape(pattern);
580
581     if (!(pattern.length || isNonEmptyRegExp(pattern))) {
582       replacement = replacement('');
583       return replacement + source.split('').join(replacement) + replacement;
584     }
585
586     while (source.length > 0) {
587       match = source.match(pattern)
588       if (match && match[0].length > 0) {
589         result += source.slice(0, match.index);
590         result += String.interpret(replacement(match));
591         source  = source.slice(match.index + match[0].length);
592       } else {
593         result += source, source = '';
594       }
595     }
596     return result;
597   }
598
599   function sub(pattern, replacement, count) {
600     replacement = prepareReplacement(replacement);
601     count = Object.isUndefined(count) ? 1 : count;
602
603     return this.gsub(pattern, function(match) {
604       if (--count < 0) return match[0];
605       return replacement(match);
606     });
607   }
608
609   function scan(pattern, iterator) {
610     this.gsub(pattern, iterator);
611     return String(this);
612   }
613
614   function truncate(length, truncation) {
615     length = length || 30;
616     truncation = Object.isUndefined(truncation) ? '...' : truncation;
617     return this.length > length ?
618       this.slice(0, length - truncation.length) + truncation : String(this);
619   }
620
621   function strip() {
622     return this.replace(/^\s+/, '').replace(/\s+$/, '');
623   }
624
625   function stripTags() {
626     return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, '');
627   }
628
629   function stripScripts() {
630     return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
631   }
632
633   function extractScripts() {
634     var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
635         matchOne = new RegExp(Prototype.ScriptFragment, 'im');
636     return (this.match(matchAll) || []).map(function(scriptTag) {
637       return (scriptTag.match(matchOne) || ['', ''])[1];
638     });
639   }
640
641   function evalScripts() {
642     return this.extractScripts().map(function(script) { return eval(script); });
643   }
644
645   function escapeHTML() {
646     return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
647   }
648
649   function unescapeHTML() {
650     return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
651   }
652
653
654   function toQueryParams(separator) {
655     var match = this.strip().match(/([^?#]*)(#.*)?$/);
656     if (!match) return { };
657
658     return match[1].split(separator || '&').inject({ }, function(hash, pair) {
659       if ((pair = pair.split('='))[0]) {
660         var key = decodeURIComponent(pair.shift()),
661             value = pair.length > 1 ? pair.join('=') : pair[0];
662
663         if (value != undefined) {
664           value = value.gsub('+', ' ');
665           value = decodeURIComponent(value);
666         }
667
668         if (key in hash) {
669           if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
670           hash[key].push(value);
671         }
672         else hash[key] = value;
673       }
674       return hash;
675     });
676   }
677
678   function toArray() {
679     return this.split('');
680   }
681
682   function succ() {
683     return this.slice(0, this.length - 1) +
684       String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
685   }
686
687   function times(count) {
688     return count < 1 ? '' : new Array(count + 1).join(this);
689   }
690
691   function camelize() {
692     return this.replace(/-+(.)?/g, function(match, chr) {
693       return chr ? chr.toUpperCase() : '';
694     });
695   }
696
697   function capitalize() {
698     return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
699   }
700
701   function underscore() {
702     return this.replace(/::/g, '/')
703                .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
704                .replace(/([a-z\d])([A-Z])/g, '$1_$2')
705                .replace(/-/g, '_')
706                .toLowerCase();
707   }
708
709   function dasherize() {
710     return this.replace(/_/g, '-');
711   }
712
713   function inspect(useDoubleQuotes) {
714     var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
715       if (character in String.specialChar) {
716         return String.specialChar[character];
717       }
718       return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
719     });
720     if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
721     return "'" + escapedString.replace(/'/g, '\\\'') + "'";
722   }
723
724   function unfilterJSON(filter) {
725     return this.replace(filter || Prototype.JSONFilter, '$1');
726   }
727
728   function isJSON() {
729     var str = this;
730     if (str.blank()) return false;
731     str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
732     str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
733     str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
734     return (/^[\],:{}\s]*$/).test(str);
735   }
736
737   function evalJSON(sanitize) {
738     var json = this.unfilterJSON(),
739         cx = /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\u0000]/g;
740     if (cx.test(json)) {
741       json = json.replace(cx, function (a) {
742         return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
743       });
744     }
745     try {
746       if (!sanitize || json.isJSON()) return eval('(' + json + ')');
747     } catch (e) { }
748     throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
749   }
750
751   function parseJSON() {
752     var json = this.unfilterJSON();
753     return JSON.parse(json);
754   }
755
756   function include(pattern) {
757     return this.indexOf(pattern) > -1;
758   }
759
760   function startsWith(pattern, position) {
761     position = Object.isNumber(position) ? position : 0;
762     return this.lastIndexOf(pattern, position) === position;
763   }
764
765   function endsWith(pattern, position) {
766     pattern = String(pattern);
767     position = Object.isNumber(position) ? position : this.length;
768     if (position < 0) position = 0;
769     if (position > this.length) position = this.length;
770     var d = position - pattern.length;
771     return d >= 0 && this.indexOf(pattern, d) === d;
772   }
773
774   function empty() {
775     return this == '';
776   }
777
778   function blank() {
779     return /^\s*$/.test(this);
780   }
781
782   function interpolate(object, pattern) {
783     return new Template(this, pattern).evaluate(object);
784   }
785
786   return {
787     gsub:           gsub,
788     sub:            sub,
789     scan:           scan,
790     truncate:       truncate,
791     strip:          String.prototype.trim || strip,
792     stripTags:      stripTags,
793     stripScripts:   stripScripts,
794     extractScripts: extractScripts,
795     evalScripts:    evalScripts,
796     escapeHTML:     escapeHTML,
797     unescapeHTML:   unescapeHTML,
798     toQueryParams:  toQueryParams,
799     parseQuery:     toQueryParams,
800     toArray:        toArray,
801     succ:           succ,
802     times:          times,
803     camelize:       camelize,
804     capitalize:     capitalize,
805     underscore:     underscore,
806     dasherize:      dasherize,
807     inspect:        inspect,
808     unfilterJSON:   unfilterJSON,
809     isJSON:         isJSON,
810     evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
811     include:        include,
812     startsWith:     String.prototype.startsWith || startsWith,
813     endsWith:       String.prototype.endsWith || endsWith,
814     empty:          empty,
815     blank:          blank,
816     interpolate:    interpolate
817   };
818 })());
819
820 var Template = Class.create({
821   initialize: function(template, pattern) {
822     this.template = template.toString();
823     this.pattern = pattern || Template.Pattern;
824   },
825
826   evaluate: function(object) {
827     if (object && Object.isFunction(object.toTemplateReplacements))
828       object = object.toTemplateReplacements();
829
830     return this.template.gsub(this.pattern, function(match) {
831       if (object == null) return (match[1] + '');
832
833       var before = match[1] || '';
834       if (before == '\\') return match[2];
835
836       var ctx = object, expr = match[3],
837           pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
838
839       match = pattern.exec(expr);
840       if (match == null) return before;
841
842       while (match != null) {
843         var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
844         ctx = ctx[comp];
845         if (null == ctx || '' == match[3]) break;
846         expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
847         match = pattern.exec(expr);
848       }
849
850       return before + String.interpret(ctx);
851     });
852   }
853 });
854 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
855
856 var $break = { };
857
858 var Enumerable = (function() {
859   function each(iterator, context) {
860     try {
861       this._each(iterator, context);
862     } catch (e) {
863       if (e != $break) throw e;
864     }
865     return this;
866   }
867
868   function eachSlice(number, iterator, context) {
869     var index = -number, slices = [], array = this.toArray();
870     if (number < 1) return array;
871     while ((index += number) < array.length)
872       slices.push(array.slice(index, index+number));
873     return slices.collect(iterator, context);
874   }
875
876   function all(iterator, context) {
877     iterator = iterator || Prototype.K;
878     var result = true;
879     this.each(function(value, index) {
880       result = result && !!iterator.call(context, value, index, this);
881       if (!result) throw $break;
882     }, this);
883     return result;
884   }
885
886   function any(iterator, context) {
887     iterator = iterator || Prototype.K;
888     var result = false;
889     this.each(function(value, index) {
890       if (result = !!iterator.call(context, value, index, this))
891         throw $break;
892     }, this);
893     return result;
894   }
895
896   function collect(iterator, context) {
897     iterator = iterator || Prototype.K;
898     var results = [];
899     this.each(function(value, index) {
900       results.push(iterator.call(context, value, index, this));
901     }, this);
902     return results;
903   }
904
905   function detect(iterator, context) {
906     var result;
907     this.each(function(value, index) {
908       if (iterator.call(context, value, index, this)) {
909         result = value;
910         throw $break;
911       }
912     }, this);
913     return result;
914   }
915
916   function findAll(iterator, context) {
917     var results = [];
918     this.each(function(value, index) {
919       if (iterator.call(context, value, index, this))
920         results.push(value);
921     }, this);
922     return results;
923   }
924
925   function grep(filter, iterator, context) {
926     iterator = iterator || Prototype.K;
927     var results = [];
928
929     if (Object.isString(filter))
930       filter = new RegExp(RegExp.escape(filter));
931
932     this.each(function(value, index) {
933       if (filter.match(value))
934         results.push(iterator.call(context, value, index, this));
935     }, this);
936     return results;
937   }
938
939   function include(object) {
940     if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1)
941       return true;
942
943     var found = false;
944     this.each(function(value) {
945       if (value == object) {
946         found = true;
947         throw $break;
948       }
949     });
950     return found;
951   }
952
953   function inGroupsOf(number, fillWith) {
954     fillWith = Object.isUndefined(fillWith) ? null : fillWith;
955     return this.eachSlice(number, function(slice) {
956       while(slice.length < number) slice.push(fillWith);
957       return slice;
958     });
959   }
960
961   function inject(memo, iterator, context) {
962     this.each(function(value, index) {
963       memo = iterator.call(context, memo, value, index, this);
964     }, this);
965     return memo;
966   }
967
968   function invoke(method) {
969     var args = $A(arguments).slice(1);
970     return this.map(function(value) {
971       return value[method].apply(value, args);
972     });
973   }
974
975   function max(iterator, context) {
976     iterator = iterator || Prototype.K;
977     var result;
978     this.each(function(value, index) {
979       value = iterator.call(context, value, index, this);
980       if (result == null || value >= result)
981         result = value;
982     }, this);
983     return result;
984   }
985
986   function min(iterator, context) {
987     iterator = iterator || Prototype.K;
988     var result;
989     this.each(function(value, index) {
990       value = iterator.call(context, value, index, this);
991       if (result == null || value < result)
992         result = value;
993     }, this);
994     return result;
995   }
996
997   function partition(iterator, context) {
998     iterator = iterator || Prototype.K;
999     var trues = [], falses = [];
1000     this.each(function(value, index) {
1001       (iterator.call(context, value, index, this) ?
1002         trues : falses).push(value);
1003     }, this);
1004     return [trues, falses];
1005   }
1006
1007   function pluck(property) {
1008     var results = [];
1009     this.each(function(value) {
1010       results.push(value[property]);
1011     });
1012     return results;
1013   }
1014
1015   function reject(iterator, context) {
1016     var results = [];
1017     this.each(function(value, index) {
1018       if (!iterator.call(context, value, index, this))
1019         results.push(value);
1020     }, this);
1021     return results;
1022   }
1023
1024   function sortBy(iterator, context) {
1025     return this.map(function(value, index) {
1026       return {
1027         value: value,
1028         criteria: iterator.call(context, value, index, this)
1029       };
1030     }, this).sort(function(left, right) {
1031       var a = left.criteria, b = right.criteria;
1032       return a < b ? -1 : a > b ? 1 : 0;
1033     }).pluck('value');
1034   }
1035
1036   function toArray() {
1037     return this.map();
1038   }
1039
1040   function zip() {
1041     var iterator = Prototype.K, args = $A(arguments);
1042     if (Object.isFunction(args.last()))
1043       iterator = args.pop();
1044
1045     var collections = [this].concat(args).map($A);
1046     return this.map(function(value, index) {
1047       return iterator(collections.pluck(index));
1048     });
1049   }
1050
1051   function size() {
1052     return this.toArray().length;
1053   }
1054
1055   function inspect() {
1056     return '#<Enumerable:' + this.toArray().inspect() + '>';
1057   }
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067   return {
1068     each:       each,
1069     eachSlice:  eachSlice,
1070     all:        all,
1071     every:      all,
1072     any:        any,
1073     some:       any,
1074     collect:    collect,
1075     map:        collect,
1076     detect:     detect,
1077     findAll:    findAll,
1078     select:     findAll,
1079     filter:     findAll,
1080     grep:       grep,
1081     include:    include,
1082     member:     include,
1083     inGroupsOf: inGroupsOf,
1084     inject:     inject,
1085     invoke:     invoke,
1086     max:        max,
1087     min:        min,
1088     partition:  partition,
1089     pluck:      pluck,
1090     reject:     reject,
1091     sortBy:     sortBy,
1092     toArray:    toArray,
1093     entries:    toArray,
1094     zip:        zip,
1095     size:       size,
1096     inspect:    inspect,
1097     find:       detect
1098   };
1099 })();
1100
1101 function $A(iterable) {
1102   if (!iterable) return [];
1103   if ('toArray' in Object(iterable)) return iterable.toArray();
1104   var length = iterable.length || 0, results = new Array(length);
1105   while (length--) results[length] = iterable[length];
1106   return results;
1107 }
1108
1109
1110 function $w(string) {
1111   if (!Object.isString(string)) return [];
1112   string = string.strip();
1113   return string ? string.split(/\s+/) : [];
1114 }
1115
1116 Array.from = $A;
1117
1118
1119 (function() {
1120   var arrayProto = Array.prototype,
1121       slice = arrayProto.slice,
1122       _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1123
1124   function each(iterator, context) {
1125     for (var i = 0, length = this.length >>> 0; i < length; i++) {
1126       if (i in this) iterator.call(context, this[i], i, this);
1127     }
1128   }
1129   if (!_each) _each = each;
1130
1131   function clear() {
1132     this.length = 0;
1133     return this;
1134   }
1135
1136   function first() {
1137     return this[0];
1138   }
1139
1140   function last() {
1141     return this[this.length - 1];
1142   }
1143
1144   function compact() {
1145     return this.select(function(value) {
1146       return value != null;
1147     });
1148   }
1149
1150   function flatten() {
1151     return this.inject([], function(array, value) {
1152       if (Object.isArray(value))
1153         return array.concat(value.flatten());
1154       array.push(value);
1155       return array;
1156     });
1157   }
1158
1159   function without() {
1160     var values = slice.call(arguments, 0);
1161     return this.select(function(value) {
1162       return !values.include(value);
1163     });
1164   }
1165
1166   function reverse(inline) {
1167     return (inline === false ? this.toArray() : this)._reverse();
1168   }
1169
1170   function uniq(sorted) {
1171     return this.inject([], function(array, value, index) {
1172       if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1173         array.push(value);
1174       return array;
1175     });
1176   }
1177
1178   function intersect(array) {
1179     return this.uniq().findAll(function(item) {
1180       return array.indexOf(item) !== -1;
1181     });
1182   }
1183
1184
1185   function clone() {
1186     return slice.call(this, 0);
1187   }
1188
1189   function size() {
1190     return this.length;
1191   }
1192
1193   function inspect() {
1194     return '[' + this.map(Object.inspect).join(', ') + ']';
1195   }
1196
1197   function indexOf(item, i) {
1198     if (this == null) throw new TypeError();
1199
1200     var array = Object(this), length = array.length >>> 0;
1201     if (length === 0) return -1;
1202
1203     i = Number(i);
1204     if (isNaN(i)) {
1205       i = 0;
1206     } else if (i !== 0 && isFinite(i)) {
1207       i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
1208     }
1209
1210     if (i > length) return -1;
1211
1212     var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0);
1213     for (; k < length; k++)
1214       if (k in array && array[k] === item) return k;
1215     return -1;
1216   }
1217
1218
1219   function lastIndexOf(item, i) {
1220     if (this == null) throw new TypeError();
1221
1222     var array = Object(this), length = array.length >>> 0;
1223     if (length === 0) return -1;
1224
1225     if (!Object.isUndefined(i)) {
1226       i = Number(i);
1227       if (isNaN(i)) {
1228         i = 0;
1229       } else if (i !== 0 && isFinite(i)) {
1230         i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
1231       }
1232     } else {
1233       i = length;
1234     }
1235
1236     var k = i >= 0 ? Math.min(i, length - 1) :
1237      length - Math.abs(i);
1238
1239     for (; k >= 0; k--)
1240       if (k in array && array[k] === item) return k;
1241     return -1;
1242   }
1243
1244   function concat(_) {
1245     var array = [], items = slice.call(arguments, 0), item, n = 0;
1246     items.unshift(this);
1247     for (var i = 0, length = items.length; i < length; i++) {
1248       item = items[i];
1249       if (Object.isArray(item) && !('callee' in item)) {
1250         for (var j = 0, arrayLength = item.length; j < arrayLength; j++) {
1251           if (j in item) array[n] = item[j];
1252           n++;
1253         }
1254       } else {
1255         array[n++] = item;
1256       }
1257     }
1258     array.length = n;
1259     return array;
1260   }
1261
1262
1263   function wrapNative(method) {
1264     return function() {
1265       if (arguments.length === 0) {
1266         return method.call(this, Prototype.K);
1267       } else if (arguments[0] === undefined) {
1268         var args = slice.call(arguments, 1);
1269         args.unshift(Prototype.K);
1270         return method.apply(this, args);
1271       } else {
1272         return method.apply(this, arguments);
1273       }
1274     };
1275   }
1276
1277
1278   function map(iterator) {
1279     if (this == null) throw new TypeError();
1280     iterator = iterator || Prototype.K;
1281
1282     var object = Object(this);
1283     var results = [], context = arguments[1], n = 0;
1284
1285     for (var i = 0, length = object.length >>> 0; i < length; i++) {
1286       if (i in object) {
1287         results[n] = iterator.call(context, object[i], i, object);
1288       }
1289       n++;
1290     }
1291     results.length = n;
1292     return results;
1293   }
1294
1295   if (arrayProto.map) {
1296     map = wrapNative(Array.prototype.map);
1297   }
1298
1299   function filter(iterator) {
1300     if (this == null || !Object.isFunction(iterator))
1301       throw new TypeError();
1302
1303     var object = Object(this);
1304     var results = [], context = arguments[1], value;
1305
1306     for (var i = 0, length = object.length >>> 0; i < length; i++) {
1307       if (i in object) {
1308         value = object[i];
1309         if (iterator.call(context, value, i, object)) {
1310           results.push(value);
1311         }
1312       }
1313     }
1314     return results;
1315   }
1316
1317   if (arrayProto.filter) {
1318     filter = Array.prototype.filter;
1319   }
1320
1321   function some(iterator) {
1322     if (this == null) throw new TypeError();
1323     iterator = iterator || Prototype.K;
1324     var context = arguments[1];
1325
1326     var object = Object(this);
1327     for (var i = 0, length = object.length >>> 0; i < length; i++) {
1328       if (i in object && iterator.call(context, object[i], i, object)) {
1329         return true;
1330       }
1331     }
1332
1333     return false;
1334   }
1335
1336   if (arrayProto.some) {
1337     some = wrapNative(Array.prototype.some);
1338   }
1339
1340   function every(iterator) {
1341     if (this == null) throw new TypeError();
1342     iterator = iterator || Prototype.K;
1343     var context = arguments[1];
1344
1345     var object = Object(this);
1346     for (var i = 0, length = object.length >>> 0; i < length; i++) {
1347       if (i in object && !iterator.call(context, object[i], i, object)) {
1348         return false;
1349       }
1350     }
1351
1352     return true;
1353   }
1354
1355   if (arrayProto.every) {
1356     every = wrapNative(Array.prototype.every);
1357   }
1358
1359
1360   Object.extend(arrayProto, Enumerable);
1361
1362   if (arrayProto.entries === Enumerable.entries) {
1363     delete arrayProto.entries;
1364   }
1365
1366   if (!arrayProto._reverse)
1367     arrayProto._reverse = arrayProto.reverse;
1368
1369   Object.extend(arrayProto, {
1370     _each:     _each,
1371
1372     map:       map,
1373     collect:   map,
1374     select:    filter,
1375     filter:    filter,
1376     findAll:   filter,
1377     some:      some,
1378     any:       some,
1379     every:     every,
1380     all:       every,
1381
1382     clear:     clear,
1383     first:     first,
1384     last:      last,
1385     compact:   compact,
1386     flatten:   flatten,
1387     without:   without,
1388     reverse:   reverse,
1389     uniq:      uniq,
1390     intersect: intersect,
1391     clone:     clone,
1392     toArray:   clone,
1393     size:      size,
1394     inspect:   inspect
1395   });
1396
1397   var CONCAT_ARGUMENTS_BUGGY = (function() {
1398     return [].concat(arguments)[0][0] !== 1;
1399   })(1,2);
1400
1401   if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1402
1403   if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1404   if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1405 })();
1406 function $H(object) {
1407   return new Hash(object);
1408 };
1409
1410 var Hash = Class.create(Enumerable, (function() {
1411   function initialize(object) {
1412     this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1413   }
1414
1415
1416   function _each(iterator, context) {
1417     var i = 0;
1418     for (var key in this._object) {
1419       var value = this._object[key], pair = [key, value];
1420       pair.key = key;
1421       pair.value = value;
1422       iterator.call(context, pair, i);
1423       i++;
1424     }
1425   }
1426
1427   function set(key, value) {
1428     return this._object[key] = value;
1429   }
1430
1431   function get(key) {
1432     if (this._object[key] !== Object.prototype[key])
1433       return this._object[key];
1434   }
1435
1436   function unset(key) {
1437     var value = this._object[key];
1438     delete this._object[key];
1439     return value;
1440   }
1441
1442   function toObject() {
1443     return Object.clone(this._object);
1444   }
1445
1446
1447
1448   function keys() {
1449     return this.pluck('key');
1450   }
1451
1452   function values() {
1453     return this.pluck('value');
1454   }
1455
1456   function index(value) {
1457     var match = this.detect(function(pair) {
1458       return pair.value === value;
1459     });
1460     return match && match.key;
1461   }
1462
1463   function merge(object) {
1464     return this.clone().update(object);
1465   }
1466
1467   function update(object) {
1468     return new Hash(object).inject(this, function(result, pair) {
1469       result.set(pair.key, pair.value);
1470       return result;
1471     });
1472   }
1473
1474   function toQueryPair(key, value) {
1475     if (Object.isUndefined(value)) return key;
1476
1477     value = String.interpret(value);
1478
1479     value = value.gsub(/(\r)?\n/, '\r\n');
1480     value = encodeURIComponent(value);
1481     value = value.gsub(/%20/, '+');
1482     return key + '=' + value;
1483   }
1484
1485   function toQueryString() {
1486     return this.inject([], function(results, pair) {
1487       var key = encodeURIComponent(pair.key), values = pair.value;
1488
1489       if (values && typeof values == 'object') {
1490         if (Object.isArray(values)) {
1491           var queryValues = [];
1492           for (var i = 0, len = values.length, value; i < len; i++) {
1493             value = values[i];
1494             queryValues.push(toQueryPair(key, value));
1495           }
1496           return results.concat(queryValues);
1497         }
1498       } else results.push(toQueryPair(key, values));
1499       return results;
1500     }).join('&');
1501   }
1502
1503   function inspect() {
1504     return '#<Hash:{' + this.map(function(pair) {
1505       return pair.map(Object.inspect).join(': ');
1506     }).join(', ') + '}>';
1507   }
1508
1509   function clone() {
1510     return new Hash(this);
1511   }
1512
1513   return {
1514     initialize:             initialize,
1515     _each:                  _each,
1516     set:                    set,
1517     get:                    get,
1518     unset:                  unset,
1519     toObject:               toObject,
1520     toTemplateReplacements: toObject,
1521     keys:                   keys,
1522     values:                 values,
1523     index:                  index,
1524     merge:                  merge,
1525     update:                 update,
1526     toQueryString:          toQueryString,
1527     inspect:                inspect,
1528     toJSON:                 toObject,
1529     clone:                  clone
1530   };
1531 })());
1532
1533 Hash.from = $H;
1534 Object.extend(Number.prototype, (function() {
1535   function toColorPart() {
1536     return this.toPaddedString(2, 16);
1537   }
1538
1539   function succ() {
1540     return this + 1;
1541   }
1542
1543   function times(iterator, context) {
1544     $R(0, this, true).each(iterator, context);
1545     return this;
1546   }
1547
1548   function toPaddedString(length, radix) {
1549     var string = this.toString(radix || 10);
1550     return '0'.times(length - string.length) + string;
1551   }
1552
1553   function abs() {
1554     return Math.abs(this);
1555   }
1556
1557   function round() {
1558     return Math.round(this);
1559   }
1560
1561   function ceil() {
1562     return Math.ceil(this);
1563   }
1564
1565   function floor() {
1566     return Math.floor(this);
1567   }
1568
1569   return {
1570     toColorPart:    toColorPart,
1571     succ:           succ,
1572     times:          times,
1573     toPaddedString: toPaddedString,
1574     abs:            abs,
1575     round:          round,
1576     ceil:           ceil,
1577     floor:          floor
1578   };
1579 })());
1580
1581 function $R(start, end, exclusive) {
1582   return new ObjectRange(start, end, exclusive);
1583 }
1584
1585 var ObjectRange = Class.create(Enumerable, (function() {
1586   function initialize(start, end, exclusive) {
1587     this.start = start;
1588     this.end = end;
1589     this.exclusive = exclusive;
1590   }
1591
1592   function _each(iterator, context) {
1593     var value = this.start, i;
1594     for (i = 0; this.include(value); i++) {
1595       iterator.call(context, value, i);
1596       value = value.succ();
1597     }
1598   }
1599
1600   function include(value) {
1601     if (value < this.start)
1602       return false;
1603     if (this.exclusive)
1604       return value < this.end;
1605     return value <= this.end;
1606   }
1607
1608   return {
1609     initialize: initialize,
1610     _each:      _each,
1611     include:    include
1612   };
1613 })());
1614
1615
1616
1617 var Abstract = { };
1618
1619
1620 var Try = {
1621   these: function() {
1622     var returnValue;
1623
1624     for (var i = 0, length = arguments.length; i < length; i++) {
1625       var lambda = arguments[i];
1626       try {
1627         returnValue = lambda();
1628         break;
1629       } catch (e) { }
1630     }
1631
1632     return returnValue;
1633   }
1634 };
1635
1636 var Ajax = {
1637   getTransport: function() {
1638     return Try.these(
1639       function() {return new XMLHttpRequest()},
1640       function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1641       function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1642     ) || false;
1643   },
1644
1645   activeRequestCount: 0
1646 };
1647
1648 Ajax.Responders = {
1649   responders: [],
1650
1651   _each: function(iterator, context) {
1652     this.responders._each(iterator, context);
1653   },
1654
1655   register: function(responder) {
1656     if (!this.include(responder))
1657       this.responders.push(responder);
1658   },
1659
1660   unregister: function(responder) {
1661     this.responders = this.responders.without(responder);
1662   },
1663
1664   dispatch: function(callback, request, transport, json) {
1665     this.each(function(responder) {
1666       if (Object.isFunction(responder[callback])) {
1667         try {
1668           responder[callback].apply(responder, [request, transport, json]);
1669         } catch (e) { }
1670       }
1671     });
1672   }
1673 };
1674
1675 Object.extend(Ajax.Responders, Enumerable);
1676
1677 Ajax.Responders.register({
1678   onCreate:   function() { Ajax.activeRequestCount++ },
1679   onComplete: function() { Ajax.activeRequestCount-- }
1680 });
1681 Ajax.Base = Class.create({
1682   initialize: function(options) {
1683     this.options = {
1684       method:       'post',
1685       asynchronous: true,
1686       contentType:  'application/x-www-form-urlencoded',
1687       encoding:     'UTF-8',
1688       parameters:   '',
1689       evalJSON:     true,
1690       evalJS:       true
1691     };
1692     Object.extend(this.options, options || { });
1693
1694     this.options.method = this.options.method.toLowerCase();
1695
1696     if (Object.isHash(this.options.parameters))
1697       this.options.parameters = this.options.parameters.toObject();
1698   }
1699 });
1700 Ajax.Request = Class.create(Ajax.Base, {
1701   _complete: false,
1702
1703   initialize: function($super, url, options) {
1704     $super(options);
1705     this.transport = Ajax.getTransport();
1706     this.request(url);
1707   },
1708
1709   request: function(url) {
1710     this.url = url;
1711     this.method = this.options.method;
1712     var params = Object.isString(this.options.parameters) ?
1713           this.options.parameters :
1714           Object.toQueryString(this.options.parameters);
1715
1716     if (!['get', 'post'].include(this.method)) {
1717       params += (params ? '&' : '') + "_method=" + this.method;
1718       this.method = 'post';
1719     }
1720
1721     if (params && this.method === 'get') {
1722       this.url += (this.url.include('?') ? '&' : '?') + params;
1723     }
1724
1725     this.parameters = params.toQueryParams();
1726
1727     try {
1728       var response = new Ajax.Response(this);
1729       if (this.options.onCreate) this.options.onCreate(response);
1730       Ajax.Responders.dispatch('onCreate', this, response);
1731
1732       this.transport.open(this.method.toUpperCase(), this.url,
1733         this.options.asynchronous);
1734
1735       if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1736
1737       this.transport.onreadystatechange = this.onStateChange.bind(this);
1738       this.setRequestHeaders();
1739
1740       this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1741       this.transport.send(this.body);
1742
1743       /* Force Firefox to handle ready state 4 for synchronous requests */
1744       if (!this.options.asynchronous && this.transport.overrideMimeType)
1745         this.onStateChange();
1746
1747     }
1748     catch (e) {
1749       this.dispatchException(e);
1750     }
1751   },
1752
1753   onStateChange: function() {
1754     var readyState = this.transport.readyState;
1755     if (readyState > 1 && !((readyState == 4) && this._complete))
1756       this.respondToReadyState(this.transport.readyState);
1757   },
1758
1759   setRequestHeaders: function() {
1760     var headers = {
1761       'X-Requested-With': 'XMLHttpRequest',
1762       'X-Prototype-Version': Prototype.Version,
1763       'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1764     };
1765
1766     if (this.method == 'post') {
1767       headers['Content-type'] = this.options.contentType +
1768         (this.options.encoding ? '; charset=' + this.options.encoding : '');
1769
1770       /* Force "Connection: close" for older Mozilla browsers to work
1771        * around a bug where XMLHttpRequest sends an incorrect
1772        * Content-length header. See Mozilla Bugzilla #246651.
1773        */
1774       if (this.transport.overrideMimeType &&
1775           (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1776             headers['Connection'] = 'close';
1777     }
1778
1779     if (typeof this.options.requestHeaders == 'object') {
1780       var extras = this.options.requestHeaders;
1781
1782       if (Object.isFunction(extras.push))
1783         for (var i = 0, length = extras.length; i < length; i += 2)
1784           headers[extras[i]] = extras[i+1];
1785       else
1786         $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1787     }
1788
1789     for (var name in headers)
1790       if (headers[name] != null)
1791         this.transport.setRequestHeader(name, headers[name]);
1792   },
1793
1794   success: function() {
1795     var status = this.getStatus();
1796     return !status || (status >= 200 && status < 300) || status == 304;
1797   },
1798
1799   getStatus: function() {
1800     try {
1801       if (this.transport.status === 1223) return 204;
1802       return this.transport.status || 0;
1803     } catch (e) { return 0 }
1804   },
1805
1806   respondToReadyState: function(readyState) {
1807     var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1808
1809     if (state == 'Complete') {
1810       try {
1811         this._complete = true;
1812         (this.options['on' + response.status]
1813          || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1814          || Prototype.emptyFunction)(response, response.headerJSON);
1815       } catch (e) {
1816         this.dispatchException(e);
1817       }
1818
1819       var contentType = response.getHeader('Content-type');
1820       if (this.options.evalJS == 'force'
1821           || (this.options.evalJS && this.isSameOrigin() && contentType
1822           && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1823         this.evalResponse();
1824     }
1825
1826     try {
1827       (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1828       Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1829     } catch (e) {
1830       this.dispatchException(e);
1831     }
1832
1833     if (state == 'Complete') {
1834       this.transport.onreadystatechange = Prototype.emptyFunction;
1835     }
1836   },
1837
1838   isSameOrigin: function() {
1839     var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1840     return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1841       protocol: location.protocol,
1842       domain: document.domain,
1843       port: location.port ? ':' + location.port : ''
1844     }));
1845   },
1846
1847   getHeader: function(name) {
1848     try {
1849       return this.transport.getResponseHeader(name) || null;
1850     } catch (e) { return null; }
1851   },
1852
1853   evalResponse: function() {
1854     try {
1855       return eval((this.transport.responseText || '').unfilterJSON());
1856     } catch (e) {
1857       this.dispatchException(e);
1858     }
1859   },
1860
1861   dispatchException: function(exception) {
1862     (this.options.onException || Prototype.emptyFunction)(this, exception);
1863     Ajax.Responders.dispatch('onException', this, exception);
1864   }
1865 });
1866
1867 Ajax.Request.Events =
1868   ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1869
1870
1871
1872
1873
1874
1875
1876
1877 Ajax.Response = Class.create({
1878   initialize: function(request){
1879     this.request = request;
1880     var transport  = this.transport  = request.transport,
1881         readyState = this.readyState = transport.readyState;
1882
1883     if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1884       this.status       = this.getStatus();
1885       this.statusText   = this.getStatusText();
1886       this.responseText = String.interpret(transport.responseText);
1887       this.headerJSON   = this._getHeaderJSON();
1888     }
1889
1890     if (readyState == 4) {
1891       var xml = transport.responseXML;
1892       this.responseXML  = Object.isUndefined(xml) ? null : xml;
1893       this.responseJSON = this._getResponseJSON();
1894     }
1895   },
1896
1897   status:      0,
1898
1899   statusText: '',
1900
1901   getStatus: Ajax.Request.prototype.getStatus,
1902
1903   getStatusText: function() {
1904     try {
1905       return this.transport.statusText || '';
1906     } catch (e) { return '' }
1907   },
1908
1909   getHeader: Ajax.Request.prototype.getHeader,
1910
1911   getAllHeaders: function() {
1912     try {
1913       return this.getAllResponseHeaders();
1914     } catch (e) { return null }
1915   },
1916
1917   getResponseHeader: function(name) {
1918     return this.transport.getResponseHeader(name);
1919   },
1920
1921   getAllResponseHeaders: function() {
1922     return this.transport.getAllResponseHeaders();
1923   },
1924
1925   _getHeaderJSON: function() {
1926     var json = this.getHeader('X-JSON');
1927     if (!json) return null;
1928
1929     try {
1930       json = decodeURIComponent(escape(json));
1931     } catch(e) {
1932     }
1933
1934     try {
1935       return json.evalJSON(this.request.options.sanitizeJSON ||
1936         !this.request.isSameOrigin());
1937     } catch (e) {
1938       this.request.dispatchException(e);
1939     }
1940   },
1941
1942   _getResponseJSON: function() {
1943     var options = this.request.options;
1944     if (!options.evalJSON || (options.evalJSON != 'force' &&
1945       !(this.getHeader('Content-type') || '').include('application/json')) ||
1946         this.responseText.blank())
1947           return null;
1948     try {
1949       return this.responseText.evalJSON(options.sanitizeJSON ||
1950         !this.request.isSameOrigin());
1951     } catch (e) {
1952       this.request.dispatchException(e);
1953     }
1954   }
1955 });
1956
1957 Ajax.Updater = Class.create(Ajax.Request, {
1958   initialize: function($super, container, url, options) {
1959     this.container = {
1960       success: (container.success || container),
1961       failure: (container.failure || (container.success ? null : container))
1962     };
1963
1964     options = Object.clone(options);
1965     var onComplete = options.onComplete;
1966     options.onComplete = (function(response, json) {
1967       this.updateContent(response.responseText);
1968       if (Object.isFunction(onComplete)) onComplete(response, json);
1969     }).bind(this);
1970
1971     $super(url, options);
1972   },
1973
1974   updateContent: function(responseText) {
1975     var receiver = this.container[this.success() ? 'success' : 'failure'],
1976         options = this.options;
1977
1978     if (!options.evalScripts) responseText = responseText.stripScripts();
1979
1980     if (receiver = $(receiver)) {
1981       if (options.insertion) {
1982         if (Object.isString(options.insertion)) {
1983           var insertion = { }; insertion[options.insertion] = responseText;
1984           receiver.insert(insertion);
1985         }
1986         else options.insertion(receiver, responseText);
1987       }
1988       else receiver.update(responseText);
1989     }
1990   }
1991 });
1992
1993 Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1994   initialize: function($super, container, url, options) {
1995     $super(options);
1996     this.onComplete = this.options.onComplete;
1997
1998     this.frequency = (this.options.frequency || 2);
1999     this.decay = (this.options.decay || 1);
2000
2001     this.updater = { };
2002     this.container = container;
2003     this.url = url;
2004
2005     this.start();
2006   },
2007
2008   start: function() {
2009     this.options.onComplete = this.updateComplete.bind(this);
2010     this.onTimerEvent();
2011   },
2012
2013   stop: function() {
2014     this.updater.options.onComplete = undefined;
2015     clearTimeout(this.timer);
2016     (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
2017   },
2018
2019   updateComplete: function(response) {
2020     if (this.options.decay) {
2021       this.decay = (response.responseText == this.lastText ?
2022         this.decay * this.options.decay : 1);
2023
2024       this.lastText = response.responseText;
2025     }
2026     this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
2027   },
2028
2029   onTimerEvent: function() {
2030     this.updater = new Ajax.Updater(this.container, this.url, this.options);
2031   }
2032 });
2033
2034 (function(GLOBAL) {
2035
2036   var UNDEFINED;
2037   var SLICE = Array.prototype.slice;
2038
2039   var DIV = document.createElement('div');
2040
2041
2042   function $(element) {
2043     if (arguments.length > 1) {
2044       for (var i = 0, elements = [], length = arguments.length; i < length; i++)
2045         elements.push($(arguments[i]));
2046       return elements;
2047     }
2048
2049     if (Object.isString(element))
2050       element = document.getElementById(element);
2051     return Element.extend(element);
2052   }
2053
2054   GLOBAL.$ = $;
2055
2056
2057   if (!GLOBAL.Node) GLOBAL.Node = {};
2058
2059   if (!GLOBAL.Node.ELEMENT_NODE) {
2060     Object.extend(GLOBAL.Node, {
2061       ELEMENT_NODE:                1,
2062       ATTRIBUTE_NODE:              2,
2063       TEXT_NODE:                   3,
2064       CDATA_SECTION_NODE:          4,
2065       ENTITY_REFERENCE_NODE:       5,
2066       ENTITY_NODE:                 6,
2067       PROCESSING_INSTRUCTION_NODE: 7,
2068       COMMENT_NODE:                8,
2069       DOCUMENT_NODE:               9,
2070       DOCUMENT_TYPE_NODE:         10,
2071       DOCUMENT_FRAGMENT_NODE:     11,
2072       NOTATION_NODE:              12
2073     });
2074   }
2075
2076   var ELEMENT_CACHE = {};
2077
2078   function shouldUseCreationCache(tagName, attributes) {
2079     if (tagName === 'select') return false;
2080     if ('type' in attributes) return false;
2081     return true;
2082   }
2083
2084   var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
2085     try {
2086       var el = document.createElement('<input name="x">');
2087       return el.tagName.toLowerCase() === 'input' && el.name === 'x';
2088     }
2089     catch(err) {
2090       return false;
2091     }
2092   })();
2093
2094
2095   var oldElement = GLOBAL.Element;
2096   function Element(tagName, attributes) {
2097     attributes = attributes || {};
2098     tagName = tagName.toLowerCase();
2099
2100     if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
2101       tagName = '<' + tagName + ' name="' + attributes.name + '">';
2102       delete attributes.name;
2103       return Element.writeAttribute(document.createElement(tagName), attributes);
2104     }
2105
2106     if (!ELEMENT_CACHE[tagName])
2107       ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName));
2108
2109     var node = shouldUseCreationCache(tagName, attributes) ?
2110      ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName);
2111
2112     return Element.writeAttribute(node, attributes);
2113   }
2114
2115   GLOBAL.Element = Element;
2116
2117   Object.extend(GLOBAL.Element, oldElement || {});
2118   if (oldElement) GLOBAL.Element.prototype = oldElement.prototype;
2119
2120   Element.Methods = { ByTag: {}, Simulated: {} };
2121
2122   var methods = {};
2123
2124   var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' };
2125   function inspect(element) {
2126     element = $(element);
2127     var result = '<' + element.tagName.toLowerCase();
2128
2129     var attribute, value;
2130     for (var property in INSPECT_ATTRIBUTES) {
2131       attribute = INSPECT_ATTRIBUTES[property];
2132       value = (element[property] || '').toString();
2133       if (value) result += ' ' + attribute + '=' + value.inspect(true);
2134     }
2135
2136     return result + '>';
2137   }
2138
2139   methods.inspect = inspect;
2140
2141
2142   function visible(element) {
2143     return $(element).getStyle('display') !== 'none';
2144   }
2145
2146   function toggle(element, bool) {
2147     element = $(element);
2148     if (typeof bool !== 'boolean')
2149       bool = !Element.visible(element);
2150     Element[bool ? 'show' : 'hide'](element);
2151
2152     return element;
2153   }
2154
2155   function hide(element) {
2156     element = $(element);
2157     element.style.display = 'none';
2158     return element;
2159   }
2160
2161   function show(element) {
2162     element = $(element);
2163     element.style.display = '';
2164     return element;
2165   }
2166
2167
2168   Object.extend(methods, {
2169     visible: visible,
2170     toggle:  toggle,
2171     hide:    hide,
2172     show:    show
2173   });
2174
2175
2176   function remove(element) {
2177     element = $(element);
2178     element.parentNode.removeChild(element);
2179     return element;
2180   }
2181
2182   var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
2183     var el = document.createElement("select"),
2184         isBuggy = true;
2185     el.innerHTML = "<option value=\"test\">test</option>";
2186     if (el.options && el.options[0]) {
2187       isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
2188     }
2189     el = null;
2190     return isBuggy;
2191   })();
2192
2193   var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
2194     try {
2195       var el = document.createElement("table");
2196       if (el && el.tBodies) {
2197         el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
2198         var isBuggy = typeof el.tBodies[0] == "undefined";
2199         el = null;
2200         return isBuggy;
2201       }
2202     } catch (e) {
2203       return true;
2204     }
2205   })();
2206
2207   var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
2208     try {
2209       var el = document.createElement('div');
2210       el.innerHTML = "<link />";
2211       var isBuggy = (el.childNodes.length === 0);
2212       el = null;
2213       return isBuggy;
2214     } catch(e) {
2215       return true;
2216     }
2217   })();
2218
2219   var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
2220    TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
2221
2222   var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
2223     var s = document.createElement("script"),
2224         isBuggy = false;
2225     try {
2226       s.appendChild(document.createTextNode(""));
2227       isBuggy = !s.firstChild ||
2228         s.firstChild && s.firstChild.nodeType !== 3;
2229     } catch (e) {
2230       isBuggy = true;
2231     }
2232     s = null;
2233     return isBuggy;
2234   })();
2235
2236   function update(element, content) {
2237     element = $(element);
2238
2239     var descendants = element.getElementsByTagName('*'),
2240      i = descendants.length;
2241     while (i--) purgeElement(descendants[i]);
2242
2243     if (content && content.toElement)
2244       content = content.toElement();
2245
2246     if (Object.isElement(content))
2247       return element.update().insert(content);
2248
2249
2250     content = Object.toHTML(content);
2251     var tagName = element.tagName.toUpperCase();
2252
2253     if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
2254       element.text = content;
2255       return element;
2256     }
2257
2258     if (ANY_INNERHTML_BUGGY) {
2259       if (tagName in INSERTION_TRANSLATIONS.tags) {
2260         while (element.firstChild)
2261           element.removeChild(element.firstChild);
2262
2263         var nodes = getContentFromAnonymousElement(tagName, content.stripScripts());
2264         for (var i = 0, node; node = nodes[i]; i++)
2265           element.appendChild(node);
2266
2267       } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
2268         while (element.firstChild)
2269           element.removeChild(element.firstChild);
2270
2271         var nodes = getContentFromAnonymousElement(tagName,
2272          content.stripScripts(), true);
2273
2274         for (var i = 0, node; node = nodes[i]; i++)
2275           element.appendChild(node);
2276       } else {
2277         element.innerHTML = content.stripScripts();
2278       }
2279     } else {
2280       element.innerHTML = content.stripScripts();
2281     }
2282
2283     content.evalScripts.bind(content).defer();
2284     return element;
2285   }
2286
2287   function replace(element, content) {
2288     element = $(element);
2289
2290     if (content && content.toElement) {
2291       content = content.toElement();
2292     } else if (!Object.isElement(content)) {
2293       content = Object.toHTML(content);
2294       var range = element.ownerDocument.createRange();
2295       range.selectNode(element);
2296       content.evalScripts.bind(content).defer();
2297       content = range.createContextualFragment(content.stripScripts());
2298     }
2299
2300     element.parentNode.replaceChild(content, element);
2301     return element;
2302   }
2303
2304   var INSERTION_TRANSLATIONS = {
2305     before: function(element, node) {
2306       element.parentNode.insertBefore(node, element);
2307     },
2308     top: function(element, node) {
2309       element.insertBefore(node, element.firstChild);
2310     },
2311     bottom: function(element, node) {
2312       element.appendChild(node);
2313     },
2314     after: function(element, node) {
2315       element.parentNode.insertBefore(node, element.nextSibling);
2316     },
2317
2318     tags: {
2319       TABLE:  ['<table>',                '</table>',                   1],
2320       TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
2321       TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
2322       TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2323       SELECT: ['<select>',               '</select>',                  1]
2324     }
2325   };
2326
2327   var tags = INSERTION_TRANSLATIONS.tags;
2328
2329   Object.extend(tags, {
2330     THEAD: tags.TBODY,
2331     TFOOT: tags.TBODY,
2332     TH:    tags.TD
2333   });
2334
2335   function replace_IE(element, content) {
2336     element = $(element);
2337     if (content && content.toElement)
2338       content = content.toElement();
2339     if (Object.isElement(content)) {
2340       element.parentNode.replaceChild(content, element);
2341       return element;
2342     }
2343
2344     content = Object.toHTML(content);
2345     var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2346
2347     if (tagName in INSERTION_TRANSLATIONS.tags) {
2348       var nextSibling = Element.next(element);
2349       var fragments = getContentFromAnonymousElement(
2350        tagName, content.stripScripts());
2351
2352       parent.removeChild(element);
2353
2354       var iterator;
2355       if (nextSibling)
2356         iterator = function(node) { parent.insertBefore(node, nextSibling) };
2357       else
2358         iterator = function(node) { parent.appendChild(node); }
2359
2360       fragments.each(iterator);
2361     } else {
2362       element.outerHTML = content.stripScripts();
2363     }
2364
2365     content.evalScripts.bind(content).defer();
2366     return element;
2367   }
2368
2369   if ('outerHTML' in document.documentElement)
2370     replace = replace_IE;
2371
2372   function isContent(content) {
2373     if (Object.isUndefined(content) || content === null) return false;
2374
2375     if (Object.isString(content) || Object.isNumber(content)) return true;
2376     if (Object.isElement(content)) return true;
2377     if (content.toElement || content.toHTML) return true;
2378
2379     return false;
2380   }
2381
2382   function insertContentAt(element, content, position) {
2383     position   = position.toLowerCase();
2384     var method = INSERTION_TRANSLATIONS[position];
2385
2386     if (content && content.toElement) content = content.toElement();
2387     if (Object.isElement(content)) {
2388       method(element, content);
2389       return element;
2390     }
2391
2392     content = Object.toHTML(content);
2393     var tagName = ((position === 'before' || position === 'after') ?
2394      element.parentNode : element).tagName.toUpperCase();
2395
2396     var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts());
2397
2398     if (position === 'top' || position === 'after') childNodes.reverse();
2399
2400     for (var i = 0, node; node = childNodes[i]; i++)
2401       method(element, node);
2402
2403     content.evalScripts.bind(content).defer();
2404   }
2405
2406   function insert(element, insertions) {
2407     element = $(element);
2408
2409     if (isContent(insertions))
2410       insertions = { bottom: insertions };
2411
2412     for (var position in insertions)
2413       insertContentAt(element, insertions[position], position);
2414
2415     return element;
2416   }
2417
2418   function wrap(element, wrapper, attributes) {
2419     element = $(element);
2420
2421     if (Object.isElement(wrapper)) {
2422       $(wrapper).writeAttribute(attributes || {});
2423     } else if (Object.isString(wrapper)) {
2424       wrapper = new Element(wrapper, attributes);
2425     } else {
2426       wrapper = new Element('div', wrapper);
2427     }
2428
2429     if (element.parentNode)
2430       element.parentNode.replaceChild(wrapper, element);
2431
2432     wrapper.appendChild(element);
2433
2434     return wrapper;
2435   }
2436
2437   function cleanWhitespace(element) {
2438     element = $(element);
2439     var node = element.firstChild;
2440
2441     while (node) {
2442       var nextNode = node.nextSibling;
2443       if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue))
2444         element.removeChild(node);
2445       node = nextNode;
2446     }
2447     return element;
2448   }
2449
2450   function empty(element) {
2451     return $(element).innerHTML.blank();
2452   }
2453
2454   function getContentFromAnonymousElement(tagName, html, force) {
2455     var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV;
2456
2457     var workaround = !!t;
2458     if (!workaround && force) {
2459       workaround = true;
2460       t = ['', '', 0];
2461     }
2462
2463     if (workaround) {
2464       div.innerHTML = '&#160;' + t[0] + html + t[1];
2465       div.removeChild(div.firstChild);
2466       for (var i = t[2]; i--; )
2467         div = div.firstChild;
2468     } else {
2469       div.innerHTML = html;
2470     }
2471
2472     return $A(div.childNodes);
2473   }
2474
2475   function clone(element, deep) {
2476     if (!(element = $(element))) return;
2477     var clone = element.cloneNode(deep);
2478     if (!HAS_UNIQUE_ID_PROPERTY) {
2479       clone._prototypeUID = UNDEFINED;
2480       if (deep) {
2481         var descendants = Element.select(clone, '*'),
2482          i = descendants.length;
2483         while (i--)
2484           descendants[i]._prototypeUID = UNDEFINED;
2485       }
2486     }
2487     return Element.extend(clone);
2488   }
2489
2490   function purgeElement(element) {
2491     var uid = getUniqueElementID(element);
2492     if (uid) {
2493       Element.stopObserving(element);
2494       if (!HAS_UNIQUE_ID_PROPERTY)
2495         element._prototypeUID = UNDEFINED;
2496       delete Element.Storage[uid];
2497     }
2498   }
2499
2500   function purgeCollection(elements) {
2501     var i = elements.length;
2502     while (i--)
2503       purgeElement(elements[i]);
2504   }
2505
2506   function purgeCollection_IE(elements) {
2507     var i = elements.length, element, uid;
2508     while (i--) {
2509       element = elements[i];
2510       uid = getUniqueElementID(element);
2511       delete Element.Storage[uid];
2512       delete Event.cache[uid];
2513     }
2514   }
2515
2516   if (HAS_UNIQUE_ID_PROPERTY) {
2517     purgeCollection = purgeCollection_IE;
2518   }
2519
2520
2521   function purge(element) {
2522     if (!(element = $(element))) return;
2523     purgeElement(element);
2524
2525     var descendants = element.getElementsByTagName('*'),
2526      i = descendants.length;
2527
2528     while (i--) purgeElement(descendants[i]);
2529
2530     return null;
2531   }
2532
2533   Object.extend(methods, {
2534     remove:  remove,
2535     update:  update,
2536     replace: replace,
2537     insert:  insert,
2538     wrap:    wrap,
2539     cleanWhitespace: cleanWhitespace,
2540     empty:   empty,
2541     clone:   clone,
2542     purge:   purge
2543   });
2544
2545
2546
2547   function recursivelyCollect(element, property, maximumLength) {
2548     element = $(element);
2549     maximumLength = maximumLength || -1;
2550     var elements = [];
2551
2552     while (element = element[property]) {
2553       if (element.nodeType === Node.ELEMENT_NODE)
2554         elements.push(Element.extend(element));
2555
2556       if (elements.length === maximumLength) break;
2557     }
2558
2559     return elements;
2560   }
2561
2562
2563   function ancestors(element) {
2564     return recursivelyCollect(element, 'parentNode');
2565   }
2566
2567   function descendants(element) {
2568     return Element.select(element, '*');
2569   }
2570
2571   function firstDescendant(element) {
2572     element = $(element).firstChild;
2573     while (element && element.nodeType !== Node.ELEMENT_NODE)
2574       element = element.nextSibling;
2575
2576     return $(element);
2577   }
2578
2579   function immediateDescendants(element) {
2580     var results = [], child = $(element).firstChild;
2581
2582     while (child) {
2583       if (child.nodeType === Node.ELEMENT_NODE)
2584         results.push(Element.extend(child));
2585
2586       child = child.nextSibling;
2587     }
2588
2589     return results;
2590   }
2591
2592   function previousSiblings(element) {
2593     return recursivelyCollect(element, 'previousSibling');
2594   }
2595
2596   function nextSiblings(element) {
2597     return recursivelyCollect(element, 'nextSibling');
2598   }
2599
2600   function siblings(element) {
2601     element = $(element);
2602     var previous = previousSiblings(element),
2603      next = nextSiblings(element);
2604     return previous.reverse().concat(next);
2605   }
2606
2607   function match(element, selector) {
2608     element = $(element);
2609
2610     if (Object.isString(selector))
2611       return Prototype.Selector.match(element, selector);
2612
2613     return selector.match(element);
2614   }
2615
2616
2617   function _recursivelyFind(element, property, expression, index) {
2618     element = $(element), expression = expression || 0, index = index || 0;
2619     if (Object.isNumber(expression)) {
2620       index = expression, expression = null;
2621     }
2622
2623     while (element = element[property]) {
2624       if (element.nodeType !== 1) continue;
2625       if (expression && !Prototype.Selector.match(element, expression))
2626         continue;
2627       if (--index >= 0) continue;
2628
2629       return Element.extend(element);
2630     }
2631   }
2632
2633
2634   function up(element, expression, index) {
2635     element = $(element);
2636
2637     if (arguments.length === 1) return $(element.parentNode);
2638     return _recursivelyFind(element, 'parentNode', expression, index);
2639   }
2640
2641   function down(element, expression, index) {
2642     if (arguments.length === 1) return firstDescendant(element);
2643     element = $(element), expression = expression || 0, index = index || 0;
2644
2645     if (Object.isNumber(expression))
2646       index = expression, expression = '*';
2647
2648     var node = Prototype.Selector.select(expression, element)[index];
2649     return Element.extend(node);
2650   }
2651
2652   function previous(element, expression, index) {
2653     return _recursivelyFind(element, 'previousSibling', expression, index);
2654   }
2655
2656   function next(element, expression, index) {
2657     return _recursivelyFind(element, 'nextSibling', expression, index);
2658   }
2659
2660   function select(element) {
2661     element = $(element);
2662     var expressions = SLICE.call(arguments, 1).join(', ');
2663     return Prototype.Selector.select(expressions, element);
2664   }
2665
2666   function adjacent(element) {
2667     element = $(element);
2668     var expressions = SLICE.call(arguments, 1).join(', ');
2669     var siblings = Element.siblings(element), results = [];
2670     for (var i = 0, sibling; sibling = siblings[i]; i++) {
2671       if (Prototype.Selector.match(sibling, expressions))
2672         results.push(sibling);
2673     }
2674
2675     return results;
2676   }
2677
2678   function descendantOf_DOM(element, ancestor) {
2679     element = $(element), ancestor = $(ancestor);
2680     if (!element || !ancestor) return false;
2681     while (element = element.parentNode)
2682       if (element === ancestor) return true;
2683     return false;
2684   }
2685
2686   function descendantOf_contains(element, ancestor) {
2687     element = $(element), ancestor = $(ancestor);
2688     if (!element || !ancestor) return false;
2689     if (!ancestor.contains) return descendantOf_DOM(element, ancestor);
2690     return ancestor.contains(element) && ancestor !== element;
2691   }
2692
2693   function descendantOf_compareDocumentPosition(element, ancestor) {
2694     element = $(element), ancestor = $(ancestor);
2695     if (!element || !ancestor) return false;
2696     return (element.compareDocumentPosition(ancestor) & 8) === 8;
2697   }
2698
2699   var descendantOf;
2700   if (DIV.compareDocumentPosition) {
2701     descendantOf = descendantOf_compareDocumentPosition;
2702   } else if (DIV.contains) {
2703     descendantOf = descendantOf_contains;
2704   } else {
2705     descendantOf = descendantOf_DOM;
2706   }
2707
2708
2709   Object.extend(methods, {
2710     recursivelyCollect:   recursivelyCollect,
2711     ancestors:            ancestors,
2712     descendants:          descendants,
2713     firstDescendant:      firstDescendant,
2714     immediateDescendants: immediateDescendants,
2715     previousSiblings:     previousSiblings,
2716     nextSiblings:         nextSiblings,
2717     siblings:             siblings,
2718     match:                match,
2719     up:                   up,
2720     down:                 down,
2721     previous:             previous,
2722     next:                 next,
2723     select:               select,
2724     adjacent:             adjacent,
2725     descendantOf:         descendantOf,
2726
2727     getElementsBySelector: select,
2728
2729     childElements:         immediateDescendants
2730   });
2731
2732
2733   var idCounter = 1;
2734   function identify(element) {
2735     element = $(element);
2736     var id = Element.readAttribute(element, 'id');
2737     if (id) return id;
2738
2739     do { id = 'anonymous_element_' + idCounter++ } while ($(id));
2740
2741     Element.writeAttribute(element, 'id', id);
2742     return id;
2743   }
2744
2745
2746   function readAttribute(element, name) {
2747     return $(element).getAttribute(name);
2748   }
2749
2750   function readAttribute_IE(element, name) {
2751     element = $(element);
2752
2753     var table = ATTRIBUTE_TRANSLATIONS.read;
2754     if (table.values[name])
2755       return table.values[name](element, name);
2756
2757     if (table.names[name]) name = table.names[name];
2758
2759     if (name.include(':')) {
2760       if (!element.attributes || !element.attributes[name]) return null;
2761       return element.attributes[name].value;
2762     }
2763
2764     return element.getAttribute(name);
2765   }
2766
2767   function readAttribute_Opera(element, name) {
2768     if (name === 'title') return element.title;
2769     return element.getAttribute(name);
2770   }
2771
2772   var PROBLEMATIC_ATTRIBUTE_READING = (function() {
2773     DIV.setAttribute('onclick', []);
2774     var value = DIV.getAttribute('onclick');
2775     var isFunction = Object.isArray(value);
2776     DIV.removeAttribute('onclick');
2777     return isFunction;
2778   })();
2779
2780   if (PROBLEMATIC_ATTRIBUTE_READING) {
2781     readAttribute = readAttribute_IE;
2782   } else if (Prototype.Browser.Opera) {
2783     readAttribute = readAttribute_Opera;
2784   }
2785
2786
2787   function writeAttribute(element, name, value) {
2788     element = $(element);
2789     var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write;
2790
2791     if (typeof name === 'object') {
2792       attributes = name;
2793     } else {
2794       attributes[name] = Object.isUndefined(value) ? true : value;
2795     }
2796
2797     for (var attr in attributes) {
2798       name = table.names[attr] || attr;
2799       value = attributes[attr];
2800       if (table.values[attr]) {
2801         value = table.values[attr](element, value);
2802         if (Object.isUndefined(value)) continue;
2803       }
2804       if (value === false || value === null)
2805         element.removeAttribute(name);
2806       else if (value === true)
2807         element.setAttribute(name, name);
2808       else element.setAttribute(name, value);
2809     }
2810
2811     return element;
2812   }
2813
2814   var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () {
2815     if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) {
2816       return false;
2817     }
2818     var checkbox = document.createElement('<input type="checkbox">');
2819     checkbox.checked = true;
2820     var node = checkbox.getAttributeNode('checked');
2821     return !node || !node.specified;
2822   })();
2823
2824   function hasAttribute(element, attribute) {
2825     attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute;
2826     var node = $(element).getAttributeNode(attribute);
2827     return !!(node && node.specified);
2828   }
2829
2830   function hasAttribute_IE(element, attribute) {
2831     if (attribute === 'checked') {
2832       return element.checked;
2833     }
2834     return hasAttribute(element, attribute);
2835   }
2836
2837   GLOBAL.Element.Methods.Simulated.hasAttribute =
2838    PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ?
2839    hasAttribute_IE : hasAttribute;
2840
2841   function classNames(element) {
2842     return new Element.ClassNames(element);
2843   }
2844
2845   var regExpCache = {};
2846   function getRegExpForClassName(className) {
2847     if (regExpCache[className]) return regExpCache[className];
2848
2849     var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)");
2850     regExpCache[className] = re;
2851     return re;
2852   }
2853
2854   function hasClassName(element, className) {
2855     if (!(element = $(element))) return;
2856
2857     var elementClassName = element.className;
2858
2859     if (elementClassName.length === 0) return false;
2860     if (elementClassName === className) return true;
2861
2862     return getRegExpForClassName(className).test(elementClassName);
2863   }
2864
2865   function addClassName(element, className) {
2866     if (!(element = $(element))) return;
2867
2868     if (!hasClassName(element, className))
2869       element.className += (element.className ? ' ' : '') + className;
2870
2871     return element;
2872   }
2873
2874   function removeClassName(element, className) {
2875     if (!(element = $(element))) return;
2876
2877     element.className = element.className.replace(
2878      getRegExpForClassName(className), ' ').strip();
2879
2880     return element;
2881   }
2882
2883   function toggleClassName(element, className, bool) {
2884     if (!(element = $(element))) return;
2885
2886     if (Object.isUndefined(bool))
2887       bool = !hasClassName(element, className);
2888
2889     var method = Element[bool ? 'addClassName' : 'removeClassName'];
2890     return method(element, className);
2891   }
2892
2893   var ATTRIBUTE_TRANSLATIONS = {};
2894
2895   var classProp = 'className', forProp = 'for';
2896
2897   DIV.setAttribute(classProp, 'x');
2898   if (DIV.className !== 'x') {
2899     DIV.setAttribute('class', 'x');
2900     if (DIV.className === 'x')
2901       classProp = 'class';
2902   }
2903
2904   var LABEL = document.createElement('label');
2905   LABEL.setAttribute(forProp, 'x');
2906   if (LABEL.htmlFor !== 'x') {
2907     LABEL.setAttribute('htmlFor', 'x');
2908     if (LABEL.htmlFor === 'x')
2909       forProp = 'htmlFor';
2910   }
2911   LABEL = null;
2912
2913   function _getAttr(element, attribute) {
2914     return element.getAttribute(attribute);
2915   }
2916
2917   function _getAttr2(element, attribute) {
2918     return element.getAttribute(attribute, 2);
2919   }
2920
2921   function _getAttrNode(element, attribute) {
2922     var node = element.getAttributeNode(attribute);
2923     return node ? node.value : '';
2924   }
2925
2926   function _getFlag(element, attribute) {
2927     return $(element).hasAttribute(attribute) ? attribute : null;
2928   }
2929
2930   DIV.onclick = Prototype.emptyFunction;
2931   var onclickValue = DIV.getAttribute('onclick');
2932
2933   var _getEv;
2934
2935   if (String(onclickValue).indexOf('{') > -1) {
2936     _getEv = function(element, attribute) {
2937       var value = element.getAttribute(attribute);
2938       if (!value) return null;
2939       value = value.toString();
2940       value = value.split('{')[1];
2941       value = value.split('}')[0];
2942       return value.strip();
2943     };
2944   }
2945   else if (onclickValue === '') {
2946     _getEv = function(element, attribute) {
2947       var value = element.getAttribute(attribute);
2948       if (!value) return null;
2949       return value.strip();
2950     };
2951   }
2952
2953   ATTRIBUTE_TRANSLATIONS.read = {
2954     names: {
2955       'class':     classProp,
2956       'className': classProp,
2957       'for':       forProp,
2958       'htmlFor':   forProp
2959     },
2960
2961     values: {
2962       style: function(element) {
2963         return element.style.cssText.toLowerCase();
2964       },
2965       title: function(element) {
2966         return element.title;
2967       }
2968     }
2969   };
2970
2971   ATTRIBUTE_TRANSLATIONS.write = {
2972     names: {
2973       className:   'class',
2974       htmlFor:     'for',
2975       cellpadding: 'cellPadding',
2976       cellspacing: 'cellSpacing'
2977     },
2978
2979     values: {
2980       checked: function(element, value) {
2981         value = !!value;
2982         element.checked = value;
2983         return value ? 'checked' : null;
2984       },
2985
2986       style: function(element, value) {
2987         element.style.cssText = value ? value : '';
2988       }
2989     }
2990   };
2991
2992   ATTRIBUTE_TRANSLATIONS.has = { names: {} };
2993
2994   Object.extend(ATTRIBUTE_TRANSLATIONS.write.names,
2995    ATTRIBUTE_TRANSLATIONS.read.names);
2996
2997   var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' +
2998    'accessKey tabIndex encType maxLength readOnly longDesc frameBorder');
2999
3000   for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) {
3001     ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr;
3002     ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()]   = attr;
3003   }
3004
3005   Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, {
3006     href:        _getAttr2,
3007     src:         _getAttr2,
3008     type:        _getAttr,
3009     action:      _getAttrNode,
3010     disabled:    _getFlag,
3011     checked:     _getFlag,
3012     readonly:    _getFlag,
3013     multiple:    _getFlag,
3014     onload:      _getEv,
3015     onunload:    _getEv,
3016     onclick:     _getEv,
3017     ondblclick:  _getEv,
3018     onmousedown: _getEv,
3019     onmouseup:   _getEv,
3020     onmouseover: _getEv,
3021     onmousemove: _getEv,
3022     onmouseout:  _getEv,
3023     onfocus:     _getEv,
3024     onblur:      _getEv,
3025     onkeypress:  _getEv,
3026     onkeydown:   _getEv,
3027     onkeyup:     _getEv,
3028     onsubmit:    _getEv,
3029     onreset:     _getEv,
3030     onselect:    _getEv,
3031     onchange:    _getEv
3032   });
3033
3034
3035   Object.extend(methods, {
3036     identify:        identify,
3037     readAttribute:   readAttribute,
3038     writeAttribute:  writeAttribute,
3039     classNames:      classNames,
3040     hasClassName:    hasClassName,
3041     addClassName:    addClassName,
3042     removeClassName: removeClassName,
3043     toggleClassName: toggleClassName
3044   });
3045
3046
3047   function normalizeStyleName(style) {
3048     if (style === 'float' || style === 'styleFloat')
3049       return 'cssFloat';
3050     return style.camelize();
3051   }
3052
3053   function normalizeStyleName_IE(style) {
3054     if (style === 'float' || style === 'cssFloat')
3055       return 'styleFloat';
3056     return style.camelize();
3057   }
3058
3059   function setStyle(element, styles) {
3060     element = $(element);
3061     var elementStyle = element.style, match;
3062
3063     if (Object.isString(styles)) {
3064       elementStyle.cssText += ';' + styles;
3065       if (styles.include('opacity')) {
3066         var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1];
3067         Element.setOpacity(element, opacity);
3068       }
3069       return element;
3070     }
3071
3072     for (var property in styles) {
3073       if (property === 'opacity') {
3074         Element.setOpacity(element, styles[property]);
3075       } else {
3076         var value = styles[property];
3077         if (property === 'float' || property === 'cssFloat') {
3078           property = Object.isUndefined(elementStyle.styleFloat) ?
3079            'cssFloat' : 'styleFloat';
3080         }
3081         elementStyle[property] = value;
3082       }
3083     }
3084
3085     return element;
3086   }
3087
3088
3089   function getStyle(element, style) {
3090     element = $(element);
3091     style = normalizeStyleName(style);
3092
3093     var value = element.style[style];
3094     if (!value || value === 'auto') {
3095       var css = document.defaultView.getComputedStyle(element, null);
3096       value = css ? css[style] : null;
3097     }
3098
3099     if (style === 'opacity') return value ? parseFloat(value) : 1.0;
3100     return value === 'auto' ? null : value;
3101   }
3102
3103   function getStyle_Opera(element, style) {
3104     switch (style) {
3105       case 'height': case 'width':
3106         if (!Element.visible(element)) return null;
3107
3108         var dim = parseInt(getStyle(element, style), 10);
3109
3110         if (dim !== element['offset' + style.capitalize()])
3111           return dim + 'px';
3112
3113         return Element.measure(element, style);
3114
3115       default: return getStyle(element, style);
3116     }
3117   }
3118
3119   function getStyle_IE(element, style) {
3120     element = $(element);
3121     style = normalizeStyleName_IE(style);
3122
3123     var value = element.style[style];
3124     if (!value && element.currentStyle) {
3125       value = element.currentStyle[style];
3126     }
3127
3128     if (style === 'opacity') {
3129       if (!STANDARD_CSS_OPACITY_SUPPORTED)
3130         return getOpacity_IE(element);
3131       else return value ? parseFloat(value) : 1.0;
3132     }
3133
3134     if (value === 'auto') {
3135       if ((style === 'width' || style === 'height') && Element.visible(element))
3136         return Element.measure(element, style) + 'px';
3137       return null;
3138     }
3139
3140     return value;
3141   }
3142
3143   function stripAlphaFromFilter_IE(filter) {
3144     return (filter || '').replace(/alpha\([^\)]*\)/gi, '');
3145   }
3146
3147   function hasLayout_IE(element) {
3148     if (!element.currentStyle || !element.currentStyle.hasLayout)
3149       element.style.zoom = 1;
3150     return element;
3151   }
3152
3153   var STANDARD_CSS_OPACITY_SUPPORTED = (function() {
3154     DIV.style.cssText = "opacity:.55";
3155     return /^0.55/.test(DIV.style.opacity);
3156   })();
3157
3158   function setOpacity(element, value) {
3159     element = $(element);
3160     if (value == 1 || value === '') value = '';
3161     else if (value < 0.00001) value = 0;
3162     element.style.opacity = value;
3163     return element;
3164   }
3165
3166   function setOpacity_IE(element, value) {
3167     if (STANDARD_CSS_OPACITY_SUPPORTED)
3168       return setOpacity(element, value);
3169
3170     element = hasLayout_IE($(element));
3171     var filter = Element.getStyle(element, 'filter'),
3172      style = element.style;
3173
3174     if (value == 1 || value === '') {
3175       filter = stripAlphaFromFilter_IE(filter);
3176       if (filter) style.filter = filter;
3177       else style.removeAttribute('filter');
3178       return element;
3179     }
3180
3181     if (value < 0.00001) value = 0;
3182
3183     style.filter = stripAlphaFromFilter_IE(filter) +
3184      ' alpha(opacity=' + (value * 100) + ')';
3185
3186     return element;
3187   }
3188
3189
3190   function getOpacity(element) {
3191     return Element.getStyle(element, 'opacity');
3192   }
3193
3194   function getOpacity_IE(element) {
3195     if (STANDARD_CSS_OPACITY_SUPPORTED)
3196       return getOpacity(element);
3197
3198     var filter = Element.getStyle(element, 'filter');
3199     if (filter.length === 0) return 1.0;
3200     var match = (filter || '').match(/alpha\(opacity=(.*)\)/i);
3201     if (match && match[1]) return parseFloat(match[1]) / 100;
3202     return 1.0;
3203   }
3204
3205
3206   Object.extend(methods, {
3207     setStyle:   setStyle,
3208     getStyle:   getStyle,
3209     setOpacity: setOpacity,
3210     getOpacity: getOpacity
3211   });
3212
3213   if ('styleFloat' in DIV.style) {
3214     methods.getStyle = getStyle_IE;
3215     methods.setOpacity = setOpacity_IE;
3216     methods.getOpacity = getOpacity_IE;
3217   }
3218
3219   var UID = 0;
3220
3221   GLOBAL.Element.Storage = { UID: 1 };
3222
3223   function getUniqueElementID(element) {
3224     if (element === window) return 0;
3225
3226     if (typeof element._prototypeUID === 'undefined')
3227       element._prototypeUID = Element.Storage.UID++;
3228     return element._prototypeUID;
3229   }
3230
3231   function getUniqueElementID_IE(element) {
3232     if (element === window) return 0;
3233     if (element == document) return 1;
3234     return element.uniqueID;
3235   }
3236
3237   var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV);
3238   if (HAS_UNIQUE_ID_PROPERTY)
3239     getUniqueElementID = getUniqueElementID_IE;
3240
3241   function getStorage(element) {
3242     if (!(element = $(element))) return;
3243
3244     var uid = getUniqueElementID(element);
3245
3246     if (!Element.Storage[uid])
3247       Element.Storage[uid] = $H();
3248
3249     return Element.Storage[uid];
3250   }
3251
3252   function store(element, key, value) {
3253     if (!(element = $(element))) return;
3254     var storage = getStorage(element);
3255     if (arguments.length === 2) {
3256       storage.update(key);
3257     } else {
3258       storage.set(key, value);
3259     }
3260     return element;
3261   }
3262
3263   function retrieve(element, key, defaultValue) {
3264     if (!(element = $(element))) return;
3265     var storage = getStorage(element), value = storage.get(key);
3266
3267     if (Object.isUndefined(value)) {
3268       storage.set(key, defaultValue);
3269       value = defaultValue;
3270     }
3271
3272     return value;
3273   }
3274
3275
3276   Object.extend(methods, {
3277     getStorage: getStorage,
3278     store:      store,
3279     retrieve:   retrieve
3280   });
3281
3282
3283   var Methods = {}, ByTag = Element.Methods.ByTag,
3284    F = Prototype.BrowserFeatures;
3285
3286   if (!F.ElementExtensions && ('__proto__' in DIV)) {
3287     GLOBAL.HTMLElement = {};
3288     GLOBAL.HTMLElement.prototype = DIV['__proto__'];
3289     F.ElementExtensions = true;
3290   }
3291
3292   function checkElementPrototypeDeficiency(tagName) {
3293     if (typeof window.Element === 'undefined') return false;
3294     if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false;
3295     var proto = window.Element.prototype;
3296     if (proto) {
3297       var id = '_' + (Math.random() + '').slice(2),
3298        el = document.createElement(tagName);
3299       proto[id] = 'x';
3300       var isBuggy = (el[id] !== 'x');
3301       delete proto[id];
3302       el = null;
3303       return isBuggy;
3304     }
3305
3306     return false;
3307   }
3308
3309   var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY =
3310    checkElementPrototypeDeficiency('object');
3311
3312   function extendElementWith(element, methods) {
3313     for (var property in methods) {
3314       var value = methods[property];
3315       if (Object.isFunction(value) && !(property in element))
3316         element[property] = value.methodize();
3317     }
3318   }
3319
3320   var EXTENDED = {};
3321   function elementIsExtended(element) {
3322     var uid = getUniqueElementID(element);
3323     return (uid in EXTENDED);
3324   }
3325
3326   function extend(element) {
3327     if (!element || elementIsExtended(element)) return element;
3328     if (element.nodeType !== Node.ELEMENT_NODE || element == window)
3329       return element;
3330
3331     var methods = Object.clone(Methods),
3332      tagName = element.tagName.toUpperCase();
3333
3334     if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
3335
3336     extendElementWith(element, methods);
3337     EXTENDED[getUniqueElementID(element)] = true;
3338     return element;
3339   }
3340
3341   function extend_IE8(element) {
3342     if (!element || elementIsExtended(element)) return element;
3343
3344     var t = element.tagName;
3345     if (t && (/^(?:object|applet|embed)$/i.test(t))) {
3346       extendElementWith(element, Element.Methods);
3347       extendElementWith(element, Element.Methods.Simulated);
3348       extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
3349     }
3350
3351     return element;
3352   }
3353
3354   if (F.SpecificElementExtensions) {
3355     extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K;
3356   }
3357
3358   function addMethodsToTagName(tagName, methods) {
3359     tagName = tagName.toUpperCase();
3360     if (!ByTag[tagName]) ByTag[tagName] = {};
3361     Object.extend(ByTag[tagName], methods);
3362   }
3363
3364   function mergeMethods(destination, methods, onlyIfAbsent) {
3365     if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false;
3366     for (var property in methods) {
3367       var value = methods[property];
3368       if (!Object.isFunction(value)) continue;
3369       if (!onlyIfAbsent || !(property in destination))
3370         destination[property] = value.methodize();
3371     }
3372   }
3373
3374   function findDOMClass(tagName) {
3375     var klass;
3376     var trans = {
3377       "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3378       "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3379       "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3380       "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3381       "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3382       "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3383       "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3384       "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3385       "FrameSet", "IFRAME": "IFrame"
3386     };
3387     if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3388     if (window[klass]) return window[klass];
3389     klass = 'HTML' + tagName + 'Element';
3390     if (window[klass]) return window[klass];
3391     klass = 'HTML' + tagName.capitalize() + 'Element';
3392     if (window[klass]) return window[klass];
3393
3394     var element = document.createElement(tagName),
3395      proto = element['__proto__'] || element.constructor.prototype;
3396
3397     element = null;
3398     return proto;
3399   }
3400
3401   function addMethods(methods) {
3402     if (arguments.length === 0) addFormMethods();
3403
3404     if (arguments.length === 2) {
3405       var tagName = methods;
3406       methods = arguments[1];
3407     }
3408
3409     if (!tagName) {
3410       Object.extend(Element.Methods, methods || {});
3411     } else {
3412       if (Object.isArray(tagName)) {
3413         for (var i = 0, tag; tag = tagName[i]; i++)
3414           addMethodsToTagName(tag, methods);
3415       } else {
3416         addMethodsToTagName(tagName, methods);
3417       }
3418     }
3419
3420     var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype :
3421      Element.prototype;
3422
3423     if (F.ElementExtensions) {
3424       mergeMethods(ELEMENT_PROTOTYPE, Element.Methods);
3425       mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true);
3426     }
3427
3428     if (F.SpecificElementExtensions) {
3429       for (var tag in Element.Methods.ByTag) {
3430         var klass = findDOMClass(tag);
3431         if (Object.isUndefined(klass)) continue;
3432         mergeMethods(klass.prototype, ByTag[tag]);
3433       }
3434     }
3435
3436     Object.extend(Element, Element.Methods);
3437     Object.extend(Element, Element.Methods.Simulated);
3438     delete Element.ByTag;
3439     delete Element.Simulated;
3440
3441     Element.extend.refresh();
3442
3443     ELEMENT_CACHE = {};
3444   }
3445
3446   Object.extend(GLOBAL.Element, {
3447     extend:     extend,
3448     addMethods: addMethods
3449   });
3450
3451   if (extend === Prototype.K) {
3452     GLOBAL.Element.extend.refresh = Prototype.emptyFunction;
3453   } else {
3454     GLOBAL.Element.extend.refresh = function() {
3455       if (Prototype.BrowserFeatures.ElementExtensions) return;
3456       Object.extend(Methods, Element.Methods);
3457       Object.extend(Methods, Element.Methods.Simulated);
3458
3459       EXTENDED = {};
3460     };
3461   }
3462
3463   function addFormMethods() {
3464     Object.extend(Form, Form.Methods);
3465     Object.extend(Form.Element, Form.Element.Methods);
3466     Object.extend(Element.Methods.ByTag, {
3467       "FORM":     Object.clone(Form.Methods),
3468       "INPUT":    Object.clone(Form.Element.Methods),
3469       "SELECT":   Object.clone(Form.Element.Methods),
3470       "TEXTAREA": Object.clone(Form.Element.Methods),
3471       "BUTTON":   Object.clone(Form.Element.Methods)
3472     });
3473   }
3474
3475   Element.addMethods(methods);
3476
3477   function destroyCache_IE() {
3478     DIV = null;
3479     ELEMENT_CACHE = null;
3480   }
3481
3482   if (window.attachEvent)
3483     window.attachEvent('onunload', destroyCache_IE);
3484
3485 })(this);
3486 (function() {
3487
3488   function toDecimal(pctString) {
3489     var match = pctString.match(/^(\d+)%?$/i);
3490     if (!match) return null;
3491     return (Number(match[1]) / 100);
3492   }
3493
3494   function getRawStyle(element, style) {
3495     element = $(element);
3496
3497     var value = element.style[style];
3498     if (!value || value === 'auto') {
3499       var css = document.defaultView.getComputedStyle(element, null);
3500       value = css ? css[style] : null;
3501     }
3502
3503     if (style === 'opacity') return value ? parseFloat(value) : 1.0;
3504     return value === 'auto' ? null : value;
3505   }
3506
3507   function getRawStyle_IE(element, style) {
3508     var value = element.style[style];
3509     if (!value && element.currentStyle) {
3510       value = element.currentStyle[style];
3511     }
3512     return value;
3513   }
3514
3515   function getContentWidth(element, context) {
3516     var boxWidth = element.offsetWidth;
3517
3518     var bl = getPixelValue(element, 'borderLeftWidth',  context) || 0;
3519     var br = getPixelValue(element, 'borderRightWidth', context) || 0;
3520     var pl = getPixelValue(element, 'paddingLeft',      context) || 0;
3521     var pr = getPixelValue(element, 'paddingRight',     context) || 0;
3522
3523     return boxWidth - bl - br - pl - pr;
3524   }
3525
3526   if (!Object.isUndefined(document.documentElement.currentStyle) && !Prototype.Browser.Opera) {
3527     getRawStyle = getRawStyle_IE;
3528   }
3529
3530
3531   function getPixelValue(value, property, context) {
3532     var element = null;
3533     if (Object.isElement(value)) {
3534       element = value;
3535       value = getRawStyle(element, property);
3536     }
3537
3538     if (value === null || Object.isUndefined(value)) {
3539       return null;
3540     }
3541
3542     if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3543       return window.parseFloat(value);
3544     }
3545
3546     var isPercentage = value.include('%'), isViewport = (context === document.viewport);
3547
3548     if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
3549       var style = element.style.left, rStyle = element.runtimeStyle.left;
3550       element.runtimeStyle.left = element.currentStyle.left;
3551       element.style.left = value || 0;
3552       value = element.style.pixelLeft;
3553       element.style.left = style;
3554       element.runtimeStyle.left = rStyle;
3555
3556       return value;
3557     }
3558
3559     if (element && isPercentage) {
3560       context = context || element.parentNode;
3561       var decimal = toDecimal(value), whole = null;
3562
3563       var isHorizontal = property.include('left') || property.include('right') ||
3564        property.include('width');
3565
3566       var isVertical   = property.include('top') || property.include('bottom') ||
3567         property.include('height');
3568
3569       if (context === document.viewport) {
3570         if (isHorizontal) {
3571           whole = document.viewport.getWidth();
3572         } else if (isVertical) {
3573           whole = document.viewport.getHeight();
3574         }
3575       } else {
3576         if (isHorizontal) {
3577           whole = $(context).measure('width');
3578         } else if (isVertical) {
3579           whole = $(context).measure('height');
3580         }
3581       }
3582
3583       return (whole === null) ? 0 : whole * decimal;
3584     }
3585
3586     return 0;
3587   }
3588
3589   function toCSSPixels(number) {
3590     if (Object.isString(number) && number.endsWith('px'))
3591       return number;
3592     return number + 'px';
3593   }
3594
3595   function isDisplayed(element) {
3596     while (element && element.parentNode) {
3597       var display = element.getStyle('display');
3598       if (display === 'none') {
3599         return false;
3600       }
3601       element = $(element.parentNode);
3602     }
3603     return true;
3604   }
3605
3606   var hasLayout = Prototype.K;
3607   if ('currentStyle' in document.documentElement) {
3608     hasLayout = function(element) {
3609       if (!element.currentStyle.hasLayout) {
3610         element.style.zoom = 1;
3611       }
3612       return element;
3613     };
3614   }
3615
3616   function cssNameFor(key) {
3617     if (key.include('border')) key = key + '-width';
3618     return key.camelize();
3619   }
3620
3621   Element.Layout = Class.create(Hash, {
3622     initialize: function($super, element, preCompute) {
3623       $super();
3624       this.element = $(element);
3625
3626       Element.Layout.PROPERTIES.each( function(property) {
3627         this._set(property, null);
3628       }, this);
3629
3630       if (preCompute) {
3631         this._preComputing = true;
3632         this._begin();
3633         Element.Layout.PROPERTIES.each( this._compute, this );
3634         this._end();
3635         this._preComputing = false;
3636       }
3637     },
3638
3639     _set: function(property, value) {
3640       return Hash.prototype.set.call(this, property, value);
3641     },
3642
3643     set: function(property, value) {
3644       throw "Properties of Element.Layout are read-only.";
3645     },
3646
3647     get: function($super, property) {
3648       var value = $super(property);
3649       return value === null ? this._compute(property) : value;
3650     },
3651
3652     _begin: function() {
3653       if (this._isPrepared()) return;
3654
3655       var element = this.element;
3656       if (isDisplayed(element)) {
3657         this._setPrepared(true);
3658         return;
3659       }
3660
3661
3662       var originalStyles = {
3663         position:   element.style.position   || '',
3664         width:      element.style.width      || '',
3665         visibility: element.style.visibility || '',
3666         display:    element.style.display    || ''
3667       };
3668
3669       element.store('prototype_original_styles', originalStyles);
3670
3671       var position = getRawStyle(element, 'position'), width = element.offsetWidth;
3672
3673       if (width === 0 || width === null) {
3674         element.style.display = 'block';
3675         width = element.offsetWidth;
3676       }
3677
3678       var context = (position === 'fixed') ? document.viewport :
3679        element.parentNode;
3680
3681       var tempStyles = {
3682         visibility: 'hidden',
3683         display:    'block'
3684       };
3685
3686       if (position !== 'fixed') tempStyles.position = 'absolute';
3687
3688       element.setStyle(tempStyles);
3689
3690       var positionedWidth = element.offsetWidth, newWidth;
3691       if (width && (positionedWidth === width)) {
3692         newWidth = getContentWidth(element, context);
3693       } else if (position === 'absolute' || position === 'fixed') {
3694         newWidth = getContentWidth(element, context);
3695       } else {
3696         var parent = element.parentNode, pLayout = $(parent).getLayout();
3697
3698         newWidth = pLayout.get('width') -
3699          this.get('margin-left') -
3700          this.get('border-left') -
3701          this.get('padding-left') -
3702          this.get('padding-right') -
3703          this.get('border-right') -
3704          this.get('margin-right');
3705       }
3706
3707       element.setStyle({ width: newWidth + 'px' });
3708
3709       this._setPrepared(true);
3710     },
3711
3712     _end: function() {
3713       var element = this.element;
3714       var originalStyles = element.retrieve('prototype_original_styles');
3715       element.store('prototype_original_styles', null);
3716       element.setStyle(originalStyles);
3717       this._setPrepared(false);
3718     },
3719
3720     _compute: function(property) {
3721       var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3722       if (!(property in COMPUTATIONS)) {
3723         throw "Property not found.";
3724       }
3725
3726       return this._set(property, COMPUTATIONS[property].call(this, this.element));
3727     },
3728
3729     _isPrepared: function() {
3730       return this.element.retrieve('prototype_element_layout_prepared', false);
3731     },
3732
3733     _setPrepared: function(bool) {
3734       return this.element.store('prototype_element_layout_prepared', bool);
3735     },
3736
3737     toObject: function() {
3738       var args = $A(arguments);
3739       var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3740        args.join(' ').split(' ');
3741       var obj = {};
3742       keys.each( function(key) {
3743         if (!Element.Layout.PROPERTIES.include(key)) return;
3744         var value = this.get(key);
3745         if (value != null) obj[key] = value;
3746       }, this);
3747       return obj;
3748     },
3749
3750     toHash: function() {
3751       var obj = this.toObject.apply(this, arguments);
3752       return new Hash(obj);
3753     },
3754
3755     toCSS: function() {
3756       var args = $A(arguments);
3757       var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3758        args.join(' ').split(' ');
3759       var css = {};
3760
3761       keys.each( function(key) {
3762         if (!Element.Layout.PROPERTIES.include(key)) return;
3763         if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3764
3765         var value = this.get(key);
3766         if (value != null) css[cssNameFor(key)] = value + 'px';
3767       }, this);
3768       return css;
3769     },
3770
3771     inspect: function() {
3772       return "#<Element.Layout>";
3773     }
3774   });
3775
3776   Object.extend(Element.Layout, {
3777     PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3778
3779     COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3780
3781     COMPUTATIONS: {
3782       'height': function(element) {
3783         if (!this._preComputing) this._begin();
3784
3785         var bHeight = this.get('border-box-height');
3786         if (bHeight <= 0) {
3787           if (!this._preComputing) this._end();
3788           return 0;
3789         }
3790
3791         var bTop = this.get('border-top'),
3792          bBottom = this.get('border-bottom');
3793
3794         var pTop = this.get('padding-top'),
3795          pBottom = this.get('padding-bottom');
3796
3797         if (!this._preComputing) this._end();
3798
3799         return bHeight - bTop - bBottom - pTop - pBottom;
3800       },
3801
3802       'width': function(element) {
3803         if (!this._preComputing) this._begin();
3804
3805         var bWidth = this.get('border-box-width');
3806         if (bWidth <= 0) {
3807           if (!this._preComputing) this._end();
3808           return 0;
3809         }
3810
3811         var bLeft = this.get('border-left'),
3812          bRight = this.get('border-right');
3813
3814         var pLeft = this.get('padding-left'),
3815          pRight = this.get('padding-right');
3816
3817         if (!this._preComputing) this._end();
3818         return bWidth - bLeft - bRight - pLeft - pRight;
3819       },
3820
3821       'padding-box-height': function(element) {
3822         var height = this.get('height'),
3823          pTop = this.get('padding-top'),
3824          pBottom = this.get('padding-bottom');
3825
3826         return height + pTop + pBottom;
3827       },
3828
3829       'padding-box-width': function(element) {
3830         var width = this.get('width'),
3831          pLeft = this.get('padding-left'),
3832          pRight = this.get('padding-right');
3833
3834         return width + pLeft + pRight;
3835       },
3836
3837       'border-box-height': function(element) {
3838         if (!this._preComputing) this._begin();
3839         var height = element.offsetHeight;
3840         if (!this._preComputing) this._end();
3841         return height;
3842       },
3843
3844       'border-box-width': function(element) {
3845         if (!this._preComputing) this._begin();
3846         var width = element.offsetWidth;
3847         if (!this._preComputing) this._end();
3848         return width;
3849       },
3850
3851       'margin-box-height': function(element) {
3852         var bHeight = this.get('border-box-height'),
3853          mTop = this.get('margin-top'),
3854          mBottom = this.get('margin-bottom');
3855
3856         if (bHeight <= 0) return 0;
3857
3858         return bHeight + mTop + mBottom;
3859       },
3860
3861       'margin-box-width': function(element) {
3862         var bWidth = this.get('border-box-width'),
3863          mLeft = this.get('margin-left'),
3864          mRight = this.get('margin-right');
3865
3866         if (bWidth <= 0) return 0;
3867
3868         return bWidth + mLeft + mRight;
3869       },
3870
3871       'top': function(element) {
3872         var offset = element.positionedOffset();
3873         return offset.top;
3874       },
3875
3876       'bottom': function(element) {
3877         var offset = element.positionedOffset(),
3878          parent = element.getOffsetParent(),
3879          pHeight = parent.measure('height');
3880
3881         var mHeight = this.get('border-box-height');
3882
3883         return pHeight - mHeight - offset.top;
3884       },
3885
3886       'left': function(element) {
3887         var offset = element.positionedOffset();
3888         return offset.left;
3889       },
3890
3891       'right': function(element) {
3892         var offset = element.positionedOffset(),
3893          parent = element.getOffsetParent(),
3894          pWidth = parent.measure('width');
3895
3896         var mWidth = this.get('border-box-width');
3897
3898         return pWidth - mWidth - offset.left;
3899       },
3900
3901       'padding-top': function(element) {
3902         return getPixelValue(element, 'paddingTop');
3903       },
3904
3905       'padding-bottom': function(element) {
3906         return getPixelValue(element, 'paddingBottom');
3907       },
3908
3909       'padding-left': function(element) {
3910         return getPixelValue(element, 'paddingLeft');
3911       },
3912
3913       'padding-right': function(element) {
3914         return getPixelValue(element, 'paddingRight');
3915       },
3916
3917       'border-top': function(element) {
3918         return getPixelValue(element, 'borderTopWidth');
3919       },
3920
3921       'border-bottom': function(element) {
3922         return getPixelValue(element, 'borderBottomWidth');
3923       },
3924
3925       'border-left': function(element) {
3926         return getPixelValue(element, 'borderLeftWidth');
3927       },
3928
3929       'border-right': function(element) {
3930         return getPixelValue(element, 'borderRightWidth');
3931       },
3932
3933       'margin-top': function(element) {
3934         return getPixelValue(element, 'marginTop');
3935       },
3936
3937       'margin-bottom': function(element) {
3938         return getPixelValue(element, 'marginBottom');
3939       },
3940
3941       'margin-left': function(element) {
3942         return getPixelValue(element, 'marginLeft');
3943       },
3944
3945       'margin-right': function(element) {
3946         return getPixelValue(element, 'marginRight');
3947       }
3948     }
3949   });
3950
3951   if ('getBoundingClientRect' in document.documentElement) {
3952     Object.extend(Element.Layout.COMPUTATIONS, {
3953       'right': function(element) {
3954         var parent = hasLayout(element.getOffsetParent());
3955         var rect = element.getBoundingClientRect(),
3956          pRect = parent.getBoundingClientRect();
3957
3958         return (pRect.right - rect.right).round();
3959       },
3960
3961       'bottom': function(element) {
3962         var parent = hasLayout(element.getOffsetParent());
3963         var rect = element.getBoundingClientRect(),
3964          pRect = parent.getBoundingClientRect();
3965
3966         return (pRect.bottom - rect.bottom).round();
3967       }
3968     });
3969   }
3970
3971   Element.Offset = Class.create({
3972     initialize: function(left, top) {
3973       this.left = left.round();
3974       this.top  = top.round();
3975
3976       this[0] = this.left;
3977       this[1] = this.top;
3978     },
3979
3980     relativeTo: function(offset) {
3981       return new Element.Offset(
3982         this.left - offset.left,
3983         this.top  - offset.top
3984       );
3985     },
3986
3987     inspect: function() {
3988       return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3989     },
3990
3991     toString: function() {
3992       return "[#{left}, #{top}]".interpolate(this);
3993     },
3994
3995     toArray: function() {
3996       return [this.left, this.top];
3997     }
3998   });
3999
4000   function getLayout(element, preCompute) {
4001     return new Element.Layout(element, preCompute);
4002   }
4003
4004   function measure(element, property) {
4005     return $(element).getLayout().get(property);
4006   }
4007
4008   function getHeight(element) {
4009     return Element.getDimensions(element).height;
4010   }
4011
4012   function getWidth(element) {
4013     return Element.getDimensions(element).width;
4014   }
4015
4016   function getDimensions(element) {
4017     element = $(element);
4018     var display = Element.getStyle(element, 'display');
4019
4020     if (display && display !== 'none') {
4021       return { width: element.offsetWidth, height: element.offsetHeight };
4022     }
4023
4024     var style = element.style;
4025     var originalStyles = {
4026       visibility: style.visibility,
4027       position:   style.position,
4028       display:    style.display
4029     };
4030
4031     var newStyles = {
4032       visibility: 'hidden',
4033       display:    'block'
4034     };
4035
4036     if (originalStyles.position !== 'fixed')
4037       newStyles.position = 'absolute';
4038
4039     Element.setStyle(element, newStyles);
4040
4041     var dimensions = {
4042       width:  element.offsetWidth,
4043       height: element.offsetHeight
4044     };
4045
4046     Element.setStyle(element, originalStyles);
4047
4048     return dimensions;
4049   }
4050
4051   function getOffsetParent(element) {
4052     element = $(element);
4053
4054     function selfOrBody(element) {
4055       return isHtml(element) ? $(document.body) : $(element);
4056     }
4057
4058     if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
4059       return $(document.body);
4060
4061     var isInline = (Element.getStyle(element, 'display') === 'inline');
4062     if (!isInline && element.offsetParent) return selfOrBody(element.offsetParent);
4063
4064     while ((element = element.parentNode) && element !== document.body) {
4065       if (Element.getStyle(element, 'position') !== 'static') {
4066         return selfOrBody(element);
4067       }
4068     }
4069
4070     return $(document.body);
4071   }
4072
4073
4074   function cumulativeOffset(element) {
4075     element = $(element);
4076     var valueT = 0, valueL = 0;
4077     if (element.parentNode) {
4078       do {
4079         valueT += element.offsetTop  || 0;
4080         valueL += element.offsetLeft || 0;
4081         element = element.offsetParent;
4082       } while (element);
4083     }
4084     return new Element.Offset(valueL, valueT);
4085   }
4086
4087   function positionedOffset(element) {
4088     element = $(element);
4089
4090     var layout = element.getLayout();
4091
4092     var valueT = 0, valueL = 0;
4093     do {
4094       valueT += element.offsetTop  || 0;
4095       valueL += element.offsetLeft || 0;
4096       element = element.offsetParent;
4097       if (element) {
4098         if (isBody(element)) break;
4099         var p = Element.getStyle(element, 'position');
4100         if (p !== 'static') break;
4101       }
4102     } while (element);
4103
4104     valueL -= layout.get('margin-left');
4105     valueT -= layout.get('margin-top');
4106
4107     return new Element.Offset(valueL, valueT);
4108   }
4109
4110   function cumulativeScrollOffset(element) {
4111     var valueT = 0, valueL = 0;
4112     do {
4113       if (element === document.body) {
4114         var bodyScrollNode = document.documentElement || document.body.parentNode || document.body;
4115         valueT += !Object.isUndefined(window.pageYOffset) ? window.pageYOffset : bodyScrollNode.scrollTop || 0;
4116         valueL += !Object.isUndefined(window.pageXOffset) ? window.pageXOffset : bodyScrollNode.scrollLeft || 0;
4117         break;
4118       } else {
4119         valueT += element.scrollTop  || 0;
4120         valueL += element.scrollLeft || 0;
4121         element = element.parentNode;
4122       }
4123     } while (element);
4124     return new Element.Offset(valueL, valueT);
4125   }
4126
4127   function viewportOffset(forElement) {
4128     var valueT = 0, valueL = 0, docBody = document.body;
4129
4130     forElement = $(forElement);
4131     var element = forElement;
4132     do {
4133       valueT += element.offsetTop  || 0;
4134       valueL += element.offsetLeft || 0;
4135       if (element.offsetParent == docBody &&
4136         Element.getStyle(element, 'position') == 'absolute') break;
4137     } while (element = element.offsetParent);
4138
4139     element = forElement;
4140     do {
4141       if (element != docBody) {
4142         valueT -= element.scrollTop  || 0;
4143         valueL -= element.scrollLeft || 0;
4144       }
4145     } while (element = element.parentNode);
4146     return new Element.Offset(valueL, valueT);
4147   }
4148
4149   function absolutize(element) {
4150     element = $(element);
4151
4152     if (Element.getStyle(element, 'position') === 'absolute') {
4153       return element;
4154     }
4155
4156     var offsetParent = getOffsetParent(element);
4157     var eOffset = element.viewportOffset(),
4158      pOffset = offsetParent.viewportOffset();
4159
4160     var offset = eOffset.relativeTo(pOffset);
4161     var layout = element.getLayout();
4162
4163     element.store('prototype_absolutize_original_styles', {
4164       position: element.getStyle('position'),
4165       left:     element.getStyle('left'),
4166       top:      element.getStyle('top'),
4167       width:    element.getStyle('width'),
4168       height:   element.getStyle('height')
4169     });
4170
4171     element.setStyle({
4172       position: 'absolute',
4173       top:    offset.top + 'px',
4174       left:   offset.left + 'px',
4175       width:  layout.get('width') + 'px',
4176       height: layout.get('height') + 'px'
4177     });
4178
4179     return element;
4180   }
4181
4182   function relativize(element) {
4183     element = $(element);
4184     if (Element.getStyle(element, 'position') === 'relative') {
4185       return element;
4186     }
4187
4188     var originalStyles =
4189      element.retrieve('prototype_absolutize_original_styles');
4190
4191     if (originalStyles) element.setStyle(originalStyles);
4192     return element;
4193   }
4194
4195
4196   function scrollTo(element) {
4197     element = $(element);
4198     var pos = Element.cumulativeOffset(element);
4199     window.scrollTo(pos.left, pos.top);
4200     return element;
4201   }
4202
4203
4204   function makePositioned(element) {
4205     element = $(element);
4206     var position = Element.getStyle(element, 'position'), styles = {};
4207     if (position === 'static' || !position) {
4208       styles.position = 'relative';
4209       if (Prototype.Browser.Opera) {
4210         styles.top  = 0;
4211         styles.left = 0;
4212       }
4213       Element.setStyle(element, styles);
4214       Element.store(element, 'prototype_made_positioned', true);
4215     }
4216     return element;
4217   }
4218
4219   function undoPositioned(element) {
4220     element = $(element);
4221     var storage = Element.getStorage(element),
4222      madePositioned = storage.get('prototype_made_positioned');
4223
4224     if (madePositioned) {
4225       storage.unset('prototype_made_positioned');
4226       Element.setStyle(element, {
4227         position: '',
4228         top:      '',
4229         bottom:   '',
4230         left:     '',
4231         right:    ''
4232       });
4233     }
4234     return element;
4235   }
4236
4237   function makeClipping(element) {
4238     element = $(element);
4239
4240     var storage = Element.getStorage(element),
4241      madeClipping = storage.get('prototype_made_clipping');
4242
4243     if (Object.isUndefined(madeClipping)) {
4244       var overflow = Element.getStyle(element, 'overflow');
4245       storage.set('prototype_made_clipping', overflow);
4246       if (overflow !== 'hidden')
4247         element.style.overflow = 'hidden';
4248     }
4249
4250     return element;
4251   }
4252
4253   function undoClipping(element) {
4254     element = $(element);
4255     var storage = Element.getStorage(element),
4256      overflow = storage.get('prototype_made_clipping');
4257
4258     if (!Object.isUndefined(overflow)) {
4259       storage.unset('prototype_made_clipping');
4260       element.style.overflow = overflow || '';
4261     }
4262
4263     return element;
4264   }
4265
4266   function clonePosition(element, source, options) {
4267     options = Object.extend({
4268       setLeft:    true,
4269       setTop:     true,
4270       setWidth:   true,
4271       setHeight:  true,
4272       offsetTop:  0,
4273       offsetLeft: 0
4274     }, options || {});
4275
4276     var docEl = document.documentElement;
4277
4278     source  = $(source);
4279     element = $(element);
4280     var p, delta, layout, styles = {};
4281
4282     if (options.setLeft || options.setTop) {
4283       p = Element.viewportOffset(source);
4284       delta = [0, 0];
4285       if (Element.getStyle(element, 'position') === 'absolute') {
4286         var parent = Element.getOffsetParent(element);
4287         if (parent !== document.body) delta = Element.viewportOffset(parent);
4288       }
4289     }
4290
4291     function pageScrollXY() {
4292       var x = 0, y = 0;
4293       if (Object.isNumber(window.pageXOffset)) {
4294         x = window.pageXOffset;
4295         y = window.pageYOffset;
4296       } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
4297         x = document.body.scrollLeft;
4298         y = document.body.scrollTop;
4299       } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) {
4300         x = docEl.scrollLeft;
4301         y = docEl.scrollTop;
4302       }
4303       return { x: x, y: y };
4304     }
4305
4306     var pageXY = pageScrollXY();
4307
4308
4309     if (options.setWidth || options.setHeight) {
4310       layout = Element.getLayout(source);
4311     }
4312
4313     if (options.setLeft)
4314       styles.left = (p[0] + pageXY.x - delta[0] + options.offsetLeft) + 'px';
4315     if (options.setTop)
4316       styles.top  = (p[1] + pageXY.y - delta[1] + options.offsetTop)  + 'px';
4317
4318     var currentLayout = element.getLayout();
4319
4320     if (options.setWidth) {
4321       styles.width = layout.get('width')  + 'px';
4322     }
4323     if (options.setHeight) {
4324       styles.height = layout.get('height') + 'px';
4325     }
4326
4327     return Element.setStyle(element, styles);
4328   }
4329
4330
4331   if (Prototype.Browser.IE) {
4332     getOffsetParent = getOffsetParent.wrap(
4333       function(proceed, element) {
4334         element = $(element);
4335
4336         if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
4337           return $(document.body);
4338
4339         var position = element.getStyle('position');
4340         if (position !== 'static') return proceed(element);
4341
4342         element.setStyle({ position: 'relative' });
4343         var value = proceed(element);
4344         element.setStyle({ position: position });
4345         return value;
4346       }
4347     );
4348
4349     positionedOffset = positionedOffset.wrap(function(proceed, element) {
4350       element = $(element);
4351       if (!element.parentNode) return new Element.Offset(0, 0);
4352       var position = element.getStyle('position');
4353       if (position !== 'static') return proceed(element);
4354
4355       var offsetParent = element.getOffsetParent();
4356       if (offsetParent && offsetParent.getStyle('position') === 'fixed')
4357         hasLayout(offsetParent);
4358
4359       element.setStyle({ position: 'relative' });
4360       var value = proceed(element);
4361       element.setStyle({ position: position });
4362       return value;
4363     });
4364   } else if (Prototype.Browser.Webkit) {
4365     cumulativeOffset = function(element) {
4366       element = $(element);
4367       var valueT = 0, valueL = 0;
4368       do {
4369         valueT += element.offsetTop  || 0;
4370         valueL += element.offsetLeft || 0;
4371         if (element.offsetParent == document.body) {
4372           if (Element.getStyle(element, 'position') == 'absolute') break;
4373         }
4374
4375         element = element.offsetParent;
4376       } while (element);
4377
4378       return new Element.Offset(valueL, valueT);
4379     };
4380   }
4381
4382
4383   Element.addMethods({
4384     getLayout:              getLayout,
4385     measure:                measure,
4386     getWidth:               getWidth,
4387     getHeight:              getHeight,
4388     getDimensions:          getDimensions,
4389     getOffsetParent:        getOffsetParent,
4390     cumulativeOffset:       cumulativeOffset,
4391     positionedOffset:       positionedOffset,
4392     cumulativeScrollOffset: cumulativeScrollOffset,
4393     viewportOffset:         viewportOffset,
4394     absolutize:             absolutize,
4395     relativize:             relativize,
4396     scrollTo:               scrollTo,
4397     makePositioned:         makePositioned,
4398     undoPositioned:         undoPositioned,
4399     makeClipping:           makeClipping,
4400     undoClipping:           undoClipping,
4401     clonePosition:          clonePosition
4402   });
4403
4404   function isBody(element) {
4405     return element.nodeName.toUpperCase() === 'BODY';
4406   }
4407
4408   function isHtml(element) {
4409     return element.nodeName.toUpperCase() === 'HTML';
4410   }
4411
4412   function isDocument(element) {
4413     return element.nodeType === Node.DOCUMENT_NODE;
4414   }
4415
4416   function isDetached(element) {
4417     return element !== document.body &&
4418      !Element.descendantOf(element, document.body);
4419   }
4420
4421   if ('getBoundingClientRect' in document.documentElement) {
4422     Element.addMethods({
4423       viewportOffset: function(element) {
4424         element = $(element);
4425         if (isDetached(element)) return new Element.Offset(0, 0);
4426
4427         var rect = element.getBoundingClientRect(),
4428          docEl = document.documentElement;
4429         return new Element.Offset(rect.left - docEl.clientLeft,
4430          rect.top - docEl.clientTop);
4431       }
4432     });
4433   }
4434
4435
4436 })();
4437
4438 (function() {
4439
4440   var IS_OLD_OPERA = Prototype.Browser.Opera &&
4441    (window.parseFloat(window.opera.version()) < 9.5);
4442   var ROOT = null;
4443   function getRootElement() {
4444     if (ROOT) return ROOT;
4445     ROOT = IS_OLD_OPERA ? document.body : document.documentElement;
4446     return ROOT;
4447   }
4448
4449   function getDimensions() {
4450     return { width: this.getWidth(), height: this.getHeight() };
4451   }
4452
4453   function getWidth() {
4454     return getRootElement().clientWidth;
4455   }
4456
4457   function getHeight() {
4458     return getRootElement().clientHeight;
4459   }
4460
4461   function getScrollOffsets() {
4462     var x = window.pageXOffset || document.documentElement.scrollLeft ||
4463      document.body.scrollLeft;
4464     var y = window.pageYOffset || document.documentElement.scrollTop ||
4465      document.body.scrollTop;
4466
4467     return new Element.Offset(x, y);
4468   }
4469
4470   document.viewport = {
4471     getDimensions:    getDimensions,
4472     getWidth:         getWidth,
4473     getHeight:        getHeight,
4474     getScrollOffsets: getScrollOffsets
4475   };
4476
4477 })();
4478 window.$$ = function() {
4479   var expression = $A(arguments).join(', ');
4480   return Prototype.Selector.select(expression, document);
4481 };
4482
4483 Prototype.Selector = (function() {
4484
4485   function select() {
4486     throw new Error('Method "Prototype.Selector.select" must be defined.');
4487   }
4488
4489   function match() {
4490     throw new Error('Method "Prototype.Selector.match" must be defined.');
4491   }
4492
4493   function find(elements, expression, index) {
4494     index = index || 0;
4495     var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
4496
4497     for (i = 0; i < length; i++) {
4498       if (match(elements[i], expression) && index == matchIndex++) {
4499         return Element.extend(elements[i]);
4500       }
4501     }
4502   }
4503
4504   function extendElements(elements) {
4505     for (var i = 0, length = elements.length; i < length; i++) {
4506       Element.extend(elements[i]);
4507     }
4508     return elements;
4509   }
4510
4511
4512   var K = Prototype.K;
4513
4514   return {
4515     select: select,
4516     match: match,
4517     find: find,
4518     extendElements: (Element.extend === K) ? K : extendElements,
4519     extendElement: Element.extend
4520   };
4521 })();
4522 Prototype._original_property = window.Sizzle;
4523
4524 ;(function () {
4525   function fakeDefine(fn) {
4526     Prototype._actual_sizzle = fn();
4527   }
4528   fakeDefine.amd = true;
4529
4530   if (typeof define !== 'undefined' && define.amd) {
4531     Prototype._original_define = define;
4532     Prototype._actual_sizzle = null;
4533     window.define = fakeDefine;
4534   }
4535 })();
4536
4537 /*!
4538  * Sizzle CSS Selector Engine v1.10.18
4539  * http://sizzlejs.com/
4540  *
4541  * Copyright 2013 jQuery Foundation, Inc. and other contributors
4542  * Released under the MIT license
4543  * http://jquery.org/license
4544  *
4545  * Date: 2014-02-05
4546  */
4547 (function( window ) {
4548
4549 var i,
4550         support,
4551         Expr,
4552         getText,
4553         isXML,
4554         compile,
4555         select,
4556         outermostContext,
4557         sortInput,
4558         hasDuplicate,
4559
4560         setDocument,
4561         document,
4562         docElem,
4563         documentIsHTML,
4564         rbuggyQSA,
4565         rbuggyMatches,
4566         matches,
4567         contains,
4568
4569         expando = "sizzle" + -(new Date()),
4570         preferredDoc = window.document,
4571         dirruns = 0,
4572         done = 0,
4573         classCache = createCache(),
4574         tokenCache = createCache(),
4575         compilerCache = createCache(),
4576         sortOrder = function( a, b ) {
4577                 if ( a === b ) {
4578                         hasDuplicate = true;
4579                 }
4580                 return 0;
4581         },
4582
4583         strundefined = typeof undefined,
4584         MAX_NEGATIVE = 1 << 31,
4585
4586         hasOwn = ({}).hasOwnProperty,
4587         arr = [],
4588         pop = arr.pop,
4589         push_native = arr.push,
4590         push = arr.push,
4591         slice = arr.slice,
4592         indexOf = arr.indexOf || function( elem ) {
4593                 var i = 0,
4594                         len = this.length;
4595                 for ( ; i < len; i++ ) {
4596                         if ( this[i] === elem ) {
4597                                 return i;
4598                         }
4599                 }
4600                 return -1;
4601         },
4602
4603         booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
4604
4605
4606         whitespace = "[\\x20\\t\\r\\n\\f]",
4607         characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
4608
4609         identifier = characterEncoding.replace( "w", "w#" ),
4610
4611         attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
4612                 "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
4613
4614         pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
4615
4616         rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
4617
4618         rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
4619         rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
4620
4621         rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
4622
4623         rpseudo = new RegExp( pseudos ),
4624         ridentifier = new RegExp( "^" + identifier + "$" ),
4625
4626         matchExpr = {
4627                 "ID": new RegExp( "^#(" + characterEncoding + ")" ),
4628                 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
4629                 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
4630                 "ATTR": new RegExp( "^" + attributes ),
4631                 "PSEUDO": new RegExp( "^" + pseudos ),
4632                 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
4633                         "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
4634                         "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
4635                 "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
4636                 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
4637                         whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
4638         },
4639
4640         rinputs = /^(?:input|select|textarea|button)$/i,
4641         rheader = /^h\d$/i,
4642
4643         rnative = /^[^{]+\{\s*\[native \w/,
4644
4645         rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
4646
4647         rsibling = /[+~]/,
4648         rescape = /'|\\/g,
4649
4650         runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
4651         funescape = function( _, escaped, escapedWhitespace ) {
4652                 var high = "0x" + escaped - 0x10000;
4653                 return high !== high || escapedWhitespace ?
4654                         escaped :
4655                         high < 0 ?
4656                                 String.fromCharCode( high + 0x10000 ) :
4657                                 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
4658         };
4659
4660 try {
4661         push.apply(
4662                 (arr = slice.call( preferredDoc.childNodes )),
4663                 preferredDoc.childNodes
4664         );
4665         arr[ preferredDoc.childNodes.length ].nodeType;
4666 } catch ( e ) {
4667         push = { apply: arr.length ?
4668
4669                 function( target, els ) {
4670                         push_native.apply( target, slice.call(els) );
4671                 } :
4672
4673                 function( target, els ) {
4674                         var j = target.length,
4675                                 i = 0;
4676                         while ( (target[j++] = els[i++]) ) {}
4677                         target.length = j - 1;
4678                 }
4679         };
4680 }
4681
4682 function Sizzle( selector, context, results, seed ) {
4683         var match, elem, m, nodeType,
4684                 i, groups, old, nid, newContext, newSelector;
4685
4686         if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
4687                 setDocument( context );
4688         }
4689
4690         context = context || document;
4691         results = results || [];
4692
4693         if ( !selector || typeof selector !== "string" ) {
4694                 return results;
4695         }
4696
4697         if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
4698                 return [];
4699         }
4700
4701         if ( documentIsHTML && !seed ) {
4702
4703                 if ( (match = rquickExpr.exec( selector )) ) {
4704                         if ( (m = match[1]) ) {
4705                                 if ( nodeType === 9 ) {
4706                                         elem = context.getElementById( m );
4707                                         if ( elem && elem.parentNode ) {
4708                                                 if ( elem.id === m ) {
4709                                                         results.push( elem );
4710                                                         return results;
4711                                                 }
4712                                         } else {
4713                                                 return results;
4714                                         }
4715                                 } else {
4716                                         if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
4717                                                 contains( context, elem ) && elem.id === m ) {
4718                                                 results.push( elem );
4719                                                 return results;
4720                                         }
4721                                 }
4722
4723                         } else if ( match[2] ) {
4724                                 push.apply( results, context.getElementsByTagName( selector ) );
4725                                 return results;
4726
4727                         } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
4728                                 push.apply( results, context.getElementsByClassName( m ) );
4729                                 return results;
4730                         }
4731                 }
4732
4733                 if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
4734                         nid = old = expando;
4735                         newContext = context;
4736                         newSelector = nodeType === 9 && selector;
4737
4738                         if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
4739                                 groups = tokenize( selector );
4740
4741                                 if ( (old = context.getAttribute("id")) ) {
4742                                         nid = old.replace( rescape, "\\$&" );
4743                                 } else {
4744                                         context.setAttribute( "id", nid );
4745                                 }
4746                                 nid = "[id='" + nid + "'] ";
4747
4748                                 i = groups.length;
4749                                 while ( i-- ) {
4750                                         groups[i] = nid + toSelector( groups[i] );
4751                                 }
4752                                 newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
4753                                 newSelector = groups.join(",");
4754                         }
4755
4756                         if ( newSelector ) {
4757                                 try {
4758                                         push.apply( results,
4759                                                 newContext.querySelectorAll( newSelector )
4760                                         );
4761                                         return results;
4762                                 } catch(qsaError) {
4763                                 } finally {
4764                                         if ( !old ) {
4765                                                 context.removeAttribute("id");
4766                                         }
4767                                 }
4768                         }
4769                 }
4770         }
4771
4772         return select( selector.replace( rtrim, "$1" ), context, results, seed );
4773 }
4774
4775 /**
4776  * Create key-value caches of limited size
4777  * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
4778  *      property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
4779  *      deleting the oldest entry
4780  */
4781 function createCache() {
4782         var keys = [];
4783
4784         function cache( key, value ) {
4785                 if ( keys.push( key + " " ) > Expr.cacheLength ) {
4786                         delete cache[ keys.shift() ];
4787                 }
4788                 return (cache[ key + " " ] = value);
4789         }
4790         return cache;
4791 }
4792
4793 /**
4794  * Mark a function for special use by Sizzle
4795  * @param {Function} fn The function to mark
4796  */
4797 function markFunction( fn ) {
4798         fn[ expando ] = true;
4799         return fn;
4800 }
4801
4802 /**
4803  * Support testing using an element
4804  * @param {Function} fn Passed the created div and expects a boolean result
4805  */
4806 function assert( fn ) {
4807         var div = document.createElement("div");
4808
4809         try {
4810                 return !!fn( div );
4811         } catch (e) {
4812                 return false;
4813         } finally {
4814                 if ( div.parentNode ) {
4815                         div.parentNode.removeChild( div );
4816                 }
4817                 div = null;
4818         }
4819 }
4820
4821 /**
4822  * Adds the same handler for all of the specified attrs
4823  * @param {String} attrs Pipe-separated list of attributes
4824  * @param {Function} handler The method that will be applied
4825  */
4826 function addHandle( attrs, handler ) {
4827         var arr = attrs.split("|"),
4828                 i = attrs.length;
4829
4830         while ( i-- ) {
4831                 Expr.attrHandle[ arr[i] ] = handler;
4832         }
4833 }
4834
4835 /**
4836  * Checks document order of two siblings
4837  * @param {Element} a
4838  * @param {Element} b
4839  * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
4840  */
4841 function siblingCheck( a, b ) {
4842         var cur = b && a,
4843                 diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
4844                         ( ~b.sourceIndex || MAX_NEGATIVE ) -
4845                         ( ~a.sourceIndex || MAX_NEGATIVE );
4846
4847         if ( diff ) {
4848                 return diff;
4849         }
4850
4851         if ( cur ) {
4852                 while ( (cur = cur.nextSibling) ) {
4853                         if ( cur === b ) {
4854                                 return -1;
4855                         }
4856                 }
4857         }
4858
4859         return a ? 1 : -1;
4860 }
4861
4862 /**
4863  * Returns a function to use in pseudos for input types
4864  * @param {String} type
4865  */
4866 function createInputPseudo( type ) {
4867         return function( elem ) {
4868                 var name = elem.nodeName.toLowerCase();
4869                 return name === "input" && elem.type === type;
4870         };
4871 }
4872
4873 /**
4874  * Returns a function to use in pseudos for buttons
4875  * @param {String} type
4876  */
4877 function createButtonPseudo( type ) {
4878         return function( elem ) {
4879                 var name = elem.nodeName.toLowerCase();
4880                 return (name === "input" || name === "button") && elem.type === type;
4881         };
4882 }
4883
4884 /**
4885  * Returns a function to use in pseudos for positionals
4886  * @param {Function} fn
4887  */
4888 function createPositionalPseudo( fn ) {
4889         return markFunction(function( argument ) {
4890                 argument = +argument;
4891                 return markFunction(function( seed, matches ) {
4892                         var j,
4893                                 matchIndexes = fn( [], seed.length, argument ),
4894                                 i = matchIndexes.length;
4895
4896                         while ( i-- ) {
4897                                 if ( seed[ (j = matchIndexes[i]) ] ) {
4898                                         seed[j] = !(matches[j] = seed[j]);
4899                                 }
4900                         }
4901                 });
4902         });
4903 }
4904
4905 /**
4906  * Checks a node for validity as a Sizzle context
4907  * @param {Element|Object=} context
4908  * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
4909  */
4910 function testContext( context ) {
4911         return context && typeof context.getElementsByTagName !== strundefined && context;
4912 }
4913
4914 support = Sizzle.support = {};
4915
4916 /**
4917  * Detects XML nodes
4918  * @param {Element|Object} elem An element or a document
4919  * @returns {Boolean} True iff elem is a non-HTML XML node
4920  */
4921 isXML = Sizzle.isXML = function( elem ) {
4922         var documentElement = elem && (elem.ownerDocument || elem).documentElement;
4923         return documentElement ? documentElement.nodeName !== "HTML" : false;
4924 };
4925
4926 /**
4927  * Sets document-related variables once based on the current document
4928  * @param {Element|Object} [doc] An element or document object to use to set the document
4929  * @returns {Object} Returns the current document
4930  */
4931 setDocument = Sizzle.setDocument = function( node ) {
4932         var hasCompare,
4933                 doc = node ? node.ownerDocument || node : preferredDoc,
4934                 parent = doc.defaultView;
4935
4936         if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
4937                 return document;
4938         }
4939
4940         document = doc;
4941         docElem = doc.documentElement;
4942
4943         documentIsHTML = !isXML( doc );
4944
4945         if ( parent && parent !== parent.top ) {
4946                 if ( parent.addEventListener ) {
4947                         parent.addEventListener( "unload", function() {
4948                                 setDocument();
4949                         }, false );
4950                 } else if ( parent.attachEvent ) {
4951                         parent.attachEvent( "onunload", function() {
4952                                 setDocument();
4953                         });
4954                 }
4955         }
4956
4957         /* Attributes
4958         ---------------------------------------------------------------------- */
4959
4960         support.attributes = assert(function( div ) {
4961                 div.className = "i";
4962                 return !div.getAttribute("className");
4963         });
4964
4965         /* getElement(s)By*
4966         ---------------------------------------------------------------------- */
4967
4968         support.getElementsByTagName = assert(function( div ) {
4969                 div.appendChild( doc.createComment("") );
4970                 return !div.getElementsByTagName("*").length;
4971         });
4972
4973         support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
4974                 div.innerHTML = "<div class='a'></div><div class='a i'></div>";
4975
4976                 div.firstChild.className = "i";
4977                 return div.getElementsByClassName("i").length === 2;
4978         });
4979
4980         support.getById = assert(function( div ) {
4981                 docElem.appendChild( div ).id = expando;
4982                 return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
4983         });
4984
4985         if ( support.getById ) {
4986                 Expr.find["ID"] = function( id, context ) {
4987                         if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
4988                                 var m = context.getElementById( id );
4989                                 return m && m.parentNode ? [m] : [];
4990                         }
4991                 };
4992                 Expr.filter["ID"] = function( id ) {
4993                         var attrId = id.replace( runescape, funescape );
4994                         return function( elem ) {
4995                                 return elem.getAttribute("id") === attrId;
4996                         };
4997                 };
4998         } else {
4999                 delete Expr.find["ID"];
5000
5001                 Expr.filter["ID"] =  function( id ) {
5002                         var attrId = id.replace( runescape, funescape );
5003                         return function( elem ) {
5004                                 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
5005                                 return node && node.value === attrId;
5006                         };
5007                 };
5008         }
5009
5010         Expr.find["TAG"] = support.getElementsByTagName ?
5011                 function( tag, context ) {
5012                         if ( typeof context.getElementsByTagName !== strundefined ) {
5013                                 return context.getElementsByTagName( tag );
5014                         }
5015                 } :
5016                 function( tag, context ) {
5017                         var elem,
5018                                 tmp = [],
5019                                 i = 0,
5020                                 results = context.getElementsByTagName( tag );
5021
5022                         if ( tag === "*" ) {
5023                                 while ( (elem = results[i++]) ) {
5024                                         if ( elem.nodeType === 1 ) {
5025                                                 tmp.push( elem );
5026                                         }
5027                                 }
5028
5029                                 return tmp;
5030                         }
5031                         return results;
5032                 };
5033
5034         Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
5035                 if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
5036                         return context.getElementsByClassName( className );
5037                 }
5038         };
5039
5040         /* QSA/matchesSelector
5041         ---------------------------------------------------------------------- */
5042
5043
5044         rbuggyMatches = [];
5045
5046         rbuggyQSA = [];
5047
5048         if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
5049                 assert(function( div ) {
5050                         div.innerHTML = "<select t=''><option selected=''></option></select>";
5051
5052                         if ( div.querySelectorAll("[t^='']").length ) {
5053                                 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
5054                         }
5055
5056                         if ( !div.querySelectorAll("[selected]").length ) {
5057                                 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
5058                         }
5059
5060                         if ( !div.querySelectorAll(":checked").length ) {
5061                                 rbuggyQSA.push(":checked");
5062                         }
5063                 });
5064
5065                 assert(function( div ) {
5066                         var input = doc.createElement("input");
5067                         input.setAttribute( "type", "hidden" );
5068                         div.appendChild( input ).setAttribute( "name", "D" );
5069
5070                         if ( div.querySelectorAll("[name=d]").length ) {
5071                                 rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
5072                         }
5073
5074                         if ( !div.querySelectorAll(":enabled").length ) {
5075                                 rbuggyQSA.push( ":enabled", ":disabled" );
5076                         }
5077
5078                         div.querySelectorAll("*,:x");
5079                         rbuggyQSA.push(",.*:");
5080                 });
5081         }
5082
5083         if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
5084                 docElem.mozMatchesSelector ||
5085                 docElem.oMatchesSelector ||
5086                 docElem.msMatchesSelector) )) ) {
5087
5088                 assert(function( div ) {
5089                         support.disconnectedMatch = matches.call( div, "div" );
5090
5091                         matches.call( div, "[s!='']:x" );
5092                         rbuggyMatches.push( "!=", pseudos );
5093                 });
5094         }
5095
5096         rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
5097         rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
5098
5099         /* Contains
5100         ---------------------------------------------------------------------- */
5101         hasCompare = rnative.test( docElem.compareDocumentPosition );
5102
5103         contains = hasCompare || rnative.test( docElem.contains ) ?
5104                 function( a, b ) {
5105                         var adown = a.nodeType === 9 ? a.documentElement : a,
5106                                 bup = b && b.parentNode;
5107                         return a === bup || !!( bup && bup.nodeType === 1 && (
5108                                 adown.contains ?
5109                                         adown.contains( bup ) :
5110                                         a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
5111                         ));
5112                 } :
5113                 function( a, b ) {
5114                         if ( b ) {
5115                                 while ( (b = b.parentNode) ) {
5116                                         if ( b === a ) {
5117                                                 return true;
5118                                         }
5119                                 }
5120                         }
5121                         return false;
5122                 };
5123
5124         /* Sorting
5125         ---------------------------------------------------------------------- */
5126
5127         sortOrder = hasCompare ?
5128         function( a, b ) {
5129
5130                 if ( a === b ) {
5131                         hasDuplicate = true;
5132                         return 0;
5133                 }
5134
5135                 var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
5136                 if ( compare ) {
5137                         return compare;
5138                 }
5139
5140                 compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
5141                         a.compareDocumentPosition( b ) :
5142
5143                         1;
5144
5145                 if ( compare & 1 ||
5146                         (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
5147
5148                         if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
5149                                 return -1;
5150                         }
5151                         if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
5152                                 return 1;
5153                         }
5154
5155                         return sortInput ?
5156                                 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
5157                                 0;
5158                 }
5159
5160                 return compare & 4 ? -1 : 1;
5161         } :
5162         function( a, b ) {
5163                 if ( a === b ) {
5164                         hasDuplicate = true;
5165                         return 0;
5166                 }
5167
5168                 var cur,
5169                         i = 0,
5170                         aup = a.parentNode,
5171                         bup = b.parentNode,
5172                         ap = [ a ],
5173                         bp = [ b ];
5174
5175                 if ( !aup || !bup ) {
5176                         return a === doc ? -1 :
5177                                 b === doc ? 1 :
5178                                 aup ? -1 :
5179                                 bup ? 1 :
5180                                 sortInput ?
5181                                 ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
5182                                 0;
5183
5184                 } else if ( aup === bup ) {
5185                         return siblingCheck( a, b );
5186                 }
5187
5188                 cur = a;
5189                 while ( (cur = cur.parentNode) ) {
5190                         ap.unshift( cur );
5191                 }
5192                 cur = b;
5193                 while ( (cur = cur.parentNode) ) {
5194                         bp.unshift( cur );
5195                 }
5196
5197                 while ( ap[i] === bp[i] ) {
5198                         i++;
5199                 }
5200
5201                 return i ?
5202                         siblingCheck( ap[i], bp[i] ) :
5203
5204                         ap[i] === preferredDoc ? -1 :
5205                         bp[i] === preferredDoc ? 1 :
5206                         0;
5207         };
5208
5209         return doc;
5210 };
5211
5212 Sizzle.matches = function( expr, elements ) {
5213         return Sizzle( expr, null, null, elements );
5214 };
5215
5216 Sizzle.matchesSelector = function( elem, expr ) {
5217         if ( ( elem.ownerDocument || elem ) !== document ) {
5218                 setDocument( elem );
5219         }
5220
5221         expr = expr.replace( rattributeQuotes, "='$1']" );
5222
5223         if ( support.matchesSelector && documentIsHTML &&
5224                 ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
5225                 ( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
5226
5227                 try {
5228                         var ret = matches.call( elem, expr );
5229
5230                         if ( ret || support.disconnectedMatch ||
5231                                         elem.document && elem.document.nodeType !== 11 ) {
5232                                 return ret;
5233                         }
5234                 } catch(e) {}
5235         }
5236
5237         return Sizzle( expr, document, null, [elem] ).length > 0;
5238 };
5239
5240 Sizzle.contains = function( context, elem ) {
5241         if ( ( context.ownerDocument || context ) !== document ) {
5242                 setDocument( context );
5243         }
5244         return contains( context, elem );
5245 };
5246
5247 Sizzle.attr = function( elem, name ) {
5248         if ( ( elem.ownerDocument || elem ) !== document ) {
5249                 setDocument( elem );
5250         }
5251
5252         var fn = Expr.attrHandle[ name.toLowerCase() ],
5253                 val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
5254                         fn( elem, name, !documentIsHTML ) :
5255                         undefined;
5256
5257         return val !== undefined ?
5258                 val :
5259                 support.attributes || !documentIsHTML ?
5260                         elem.getAttribute( name ) :
5261                         (val = elem.getAttributeNode(name)) && val.specified ?
5262                                 val.value :
5263                                 null;
5264 };
5265
5266 Sizzle.error = function( msg ) {
5267         throw new Error( "Syntax error, unrecognized expression: " + msg );
5268 };
5269
5270 /**
5271  * Document sorting and removing duplicates
5272  * @param {ArrayLike} results
5273  */
5274 Sizzle.uniqueSort = function( results ) {
5275         var elem,
5276                 duplicates = [],
5277                 j = 0,
5278                 i = 0;
5279
5280         hasDuplicate = !support.detectDuplicates;
5281         sortInput = !support.sortStable && results.slice( 0 );
5282         results.sort( sortOrder );
5283
5284         if ( hasDuplicate ) {
5285                 while ( (elem = results[i++]) ) {
5286                         if ( elem === results[ i ] ) {
5287                                 j = duplicates.push( i );
5288                         }
5289                 }
5290                 while ( j-- ) {
5291                         results.splice( duplicates[ j ], 1 );
5292                 }
5293         }
5294
5295         sortInput = null;
5296
5297         return results;
5298 };
5299
5300 /**
5301  * Utility function for retrieving the text value of an array of DOM nodes
5302  * @param {Array|Element} elem
5303  */
5304 getText = Sizzle.getText = function( elem ) {
5305         var node,
5306                 ret = "",
5307                 i = 0,
5308                 nodeType = elem.nodeType;
5309
5310         if ( !nodeType ) {
5311                 while ( (node = elem[i++]) ) {
5312                         ret += getText( node );
5313                 }
5314         } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
5315                 if ( typeof elem.textContent === "string" ) {
5316                         return elem.textContent;
5317                 } else {
5318                         for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
5319                                 ret += getText( elem );
5320                         }
5321                 }
5322         } else if ( nodeType === 3 || nodeType === 4 ) {
5323                 return elem.nodeValue;
5324         }
5325
5326         return ret;
5327 };
5328
5329 Expr = Sizzle.selectors = {
5330
5331         cacheLength: 50,
5332
5333         createPseudo: markFunction,
5334
5335         match: matchExpr,
5336
5337         attrHandle: {},
5338
5339         find: {},
5340
5341         relative: {
5342                 ">": { dir: "parentNode", first: true },
5343                 " ": { dir: "parentNode" },
5344                 "+": { dir: "previousSibling", first: true },
5345                 "~": { dir: "previousSibling" }
5346         },
5347
5348         preFilter: {
5349                 "ATTR": function( match ) {
5350                         match[1] = match[1].replace( runescape, funescape );
5351
5352                         match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
5353
5354                         if ( match[2] === "~=" ) {
5355                                 match[3] = " " + match[3] + " ";
5356                         }
5357
5358                         return match.slice( 0, 4 );
5359                 },
5360
5361                 "CHILD": function( match ) {
5362                         /* matches from matchExpr["CHILD"]
5363                                 1 type (only|nth|...)
5364                                 2 what (child|of-type)
5365                                 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
5366                                 4 xn-component of xn+y argument ([+-]?\d*n|)
5367                                 5 sign of xn-component
5368                                 6 x of xn-component
5369                                 7 sign of y-component
5370                                 8 y of y-component
5371                         */
5372                         match[1] = match[1].toLowerCase();
5373
5374                         if ( match[1].slice( 0, 3 ) === "nth" ) {
5375                                 if ( !match[3] ) {
5376                                         Sizzle.error( match[0] );
5377                                 }
5378
5379                                 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
5380                                 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
5381
5382                         } else if ( match[3] ) {
5383                                 Sizzle.error( match[0] );
5384                         }
5385
5386                         return match;
5387                 },
5388
5389                 "PSEUDO": function( match ) {
5390                         var excess,
5391                                 unquoted = !match[5] && match[2];
5392
5393                         if ( matchExpr["CHILD"].test( match[0] ) ) {
5394                                 return null;
5395                         }
5396
5397                         if ( match[3] && match[4] !== undefined ) {
5398                                 match[2] = match[4];
5399
5400                         } else if ( unquoted && rpseudo.test( unquoted ) &&
5401                                 (excess = tokenize( unquoted, true )) &&
5402                                 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
5403
5404                                 match[0] = match[0].slice( 0, excess );
5405                                 match[2] = unquoted.slice( 0, excess );
5406                         }
5407
5408                         return match.slice( 0, 3 );
5409                 }
5410         },
5411
5412         filter: {
5413
5414                 "TAG": function( nodeNameSelector ) {
5415                         var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
5416                         return nodeNameSelector === "*" ?
5417                                 function() { return true; } :
5418                                 function( elem ) {
5419                                         return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
5420                                 };
5421                 },
5422
5423                 "CLASS": function( className ) {
5424                         var pattern = classCache[ className + " " ];
5425
5426                         return pattern ||
5427                                 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
5428                                 classCache( className, function( elem ) {
5429                                         return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
5430                                 });
5431                 },
5432
5433                 "ATTR": function( name, operator, check ) {
5434                         return function( elem ) {
5435                                 var result = Sizzle.attr( elem, name );
5436
5437                                 if ( result == null ) {
5438                                         return operator === "!=";
5439                                 }
5440                                 if ( !operator ) {
5441                                         return true;
5442                                 }
5443
5444                                 result += "";
5445
5446                                 return operator === "=" ? result === check :
5447                                         operator === "!=" ? result !== check :
5448                                         operator === "^=" ? check && result.indexOf( check ) === 0 :
5449                                         operator === "*=" ? check && result.indexOf( check ) > -1 :
5450                                         operator === "$=" ? check && result.slice( -check.length ) === check :
5451                                         operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
5452                                         operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
5453                                         false;
5454                         };
5455                 },
5456
5457                 "CHILD": function( type, what, argument, first, last ) {
5458                         var simple = type.slice( 0, 3 ) !== "nth",
5459                                 forward = type.slice( -4 ) !== "last",
5460                                 ofType = what === "of-type";
5461
5462                         return first === 1 && last === 0 ?
5463
5464                                 function( elem ) {
5465                                         return !!elem.parentNode;
5466                                 } :
5467
5468                                 function( elem, context, xml ) {
5469                                         var cache, outerCache, node, diff, nodeIndex, start,
5470                                                 dir = simple !== forward ? "nextSibling" : "previousSibling",
5471                                                 parent = elem.parentNode,
5472                                                 name = ofType && elem.nodeName.toLowerCase(),
5473                                                 useCache = !xml && !ofType;
5474
5475                                         if ( parent ) {
5476
5477                                                 if ( simple ) {
5478                                                         while ( dir ) {
5479                                                                 node = elem;
5480                                                                 while ( (node = node[ dir ]) ) {
5481                                                                         if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
5482                                                                                 return false;
5483                                                                         }
5484                                                                 }
5485                                                                 start = dir = type === "only" && !start && "nextSibling";
5486                                                         }
5487                                                         return true;
5488                                                 }
5489
5490                                                 start = [ forward ? parent.firstChild : parent.lastChild ];
5491
5492                                                 if ( forward && useCache ) {
5493                                                         outerCache = parent[ expando ] || (parent[ expando ] = {});
5494                                                         cache = outerCache[ type ] || [];
5495                                                         nodeIndex = cache[0] === dirruns && cache[1];
5496                                                         diff = cache[0] === dirruns && cache[2];
5497                                                         node = nodeIndex && parent.childNodes[ nodeIndex ];
5498
5499                                                         while ( (node = ++nodeIndex && node && node[ dir ] ||
5500
5501                                                                 (diff = nodeIndex = 0) || start.pop()) ) {
5502
5503                                                                 if ( node.nodeType === 1 && ++diff && node === elem ) {
5504                                                                         outerCache[ type ] = [ dirruns, nodeIndex, diff ];
5505                                                                         break;
5506                                                                 }
5507                                                         }
5508
5509                                                 } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
5510                                                         diff = cache[1];
5511
5512                                                 } else {
5513                                                         while ( (node = ++nodeIndex && node && node[ dir ] ||
5514                                                                 (diff = nodeIndex = 0) || start.pop()) ) {
5515
5516                                                                 if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
5517                                                                         if ( useCache ) {
5518                                                                                 (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
5519                                                                         }
5520
5521                                                                         if ( node === elem ) {
5522                                                                                 break;
5523                                                                         }
5524                                                                 }
5525                                                         }
5526                                                 }
5527
5528                                                 diff -= last;
5529                                                 return diff === first || ( diff % first === 0 && diff / first >= 0 );
5530                                         }
5531                                 };
5532                 },
5533
5534                 "PSEUDO": function( pseudo, argument ) {
5535                         var args,
5536                                 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
5537                                         Sizzle.error( "unsupported pseudo: " + pseudo );
5538
5539                         if ( fn[ expando ] ) {
5540                                 return fn( argument );
5541                         }
5542
5543                         if ( fn.length > 1 ) {
5544                                 args = [ pseudo, pseudo, "", argument ];
5545                                 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
5546                                         markFunction(function( seed, matches ) {
5547                                                 var idx,
5548                                                         matched = fn( seed, argument ),
5549                                                         i = matched.length;
5550                                                 while ( i-- ) {
5551                                                         idx = indexOf.call( seed, matched[i] );
5552                                                         seed[ idx ] = !( matches[ idx ] = matched[i] );
5553                                                 }
5554                                         }) :
5555                                         function( elem ) {
5556                                                 return fn( elem, 0, args );
5557                                         };
5558                         }
5559
5560                         return fn;
5561                 }
5562         },
5563
5564         pseudos: {
5565                 "not": markFunction(function( selector ) {
5566                         var input = [],
5567                                 results = [],
5568                                 matcher = compile( selector.replace( rtrim, "$1" ) );
5569
5570                         return matcher[ expando ] ?
5571                                 markFunction(function( seed, matches, context, xml ) {
5572                                         var elem,
5573                                                 unmatched = matcher( seed, null, xml, [] ),
5574                                                 i = seed.length;
5575
5576                                         while ( i-- ) {
5577                                                 if ( (elem = unmatched[i]) ) {
5578                                                         seed[i] = !(matches[i] = elem);
5579                                                 }
5580                                         }
5581                                 }) :
5582                                 function( elem, context, xml ) {
5583                                         input[0] = elem;
5584                                         matcher( input, null, xml, results );
5585                                         return !results.pop();
5586                                 };
5587                 }),
5588
5589                 "has": markFunction(function( selector ) {
5590                         return function( elem ) {
5591                                 return Sizzle( selector, elem ).length > 0;
5592                         };
5593                 }),
5594
5595                 "contains": markFunction(function( text ) {
5596                         return function( elem ) {
5597                                 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
5598                         };
5599                 }),
5600
5601                 "lang": markFunction( function( lang ) {
5602                         if ( !ridentifier.test(lang || "") ) {
5603                                 Sizzle.error( "unsupported lang: " + lang );
5604                         }
5605                         lang = lang.replace( runescape, funescape ).toLowerCase();
5606                         return function( elem ) {
5607                                 var elemLang;
5608                                 do {
5609                                         if ( (elemLang = documentIsHTML ?
5610                                                 elem.lang :
5611                                                 elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
5612
5613                                                 elemLang = elemLang.toLowerCase();
5614                                                 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
5615                                         }
5616                                 } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
5617                                 return false;
5618                         };
5619                 }),
5620
5621                 "target": function( elem ) {
5622                         var hash = window.location && window.location.hash;
5623                         return hash && hash.slice( 1 ) === elem.id;
5624                 },
5625
5626                 "root": function( elem ) {
5627                         return elem === docElem;
5628                 },
5629
5630                 "focus": function( elem ) {
5631                         return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
5632                 },
5633
5634                 "enabled": function( elem ) {
5635                         return elem.disabled === false;
5636                 },
5637
5638                 "disabled": function( elem ) {
5639                         return elem.disabled === true;
5640                 },
5641
5642                 "checked": function( elem ) {
5643                         var nodeName = elem.nodeName.toLowerCase();
5644                         return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
5645                 },
5646
5647                 "selected": function( elem ) {
5648                         if ( elem.parentNode ) {
5649                                 elem.parentNode.selectedIndex;
5650                         }
5651
5652                         return elem.selected === true;
5653                 },
5654
5655                 "empty": function( elem ) {
5656                         for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
5657                                 if ( elem.nodeType < 6 ) {
5658                                         return false;
5659                                 }
5660                         }
5661                         return true;
5662                 },
5663
5664                 "parent": function( elem ) {
5665                         return !Expr.pseudos["empty"]( elem );
5666                 },
5667
5668                 "header": function( elem ) {
5669                         return rheader.test( elem.nodeName );
5670                 },
5671
5672                 "input": function( elem ) {
5673                         return rinputs.test( elem.nodeName );
5674                 },
5675
5676                 "button": function( elem ) {
5677                         var name = elem.nodeName.toLowerCase();
5678                         return name === "input" && elem.type === "button" || name === "button";
5679                 },
5680
5681                 "text": function( elem ) {
5682                         var attr;
5683                         return elem.nodeName.toLowerCase() === "input" &&
5684                                 elem.type === "text" &&
5685
5686                                 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
5687                 },
5688
5689                 "first": createPositionalPseudo(function() {
5690                         return [ 0 ];
5691                 }),
5692
5693                 "last": createPositionalPseudo(function( matchIndexes, length ) {
5694                         return [ length - 1 ];
5695                 }),
5696
5697                 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
5698                         return [ argument < 0 ? argument + length : argument ];
5699                 }),
5700
5701                 "even": createPositionalPseudo(function( matchIndexes, length ) {
5702                         var i = 0;
5703                         for ( ; i < length; i += 2 ) {
5704                                 matchIndexes.push( i );
5705                         }
5706                         return matchIndexes;
5707                 }),
5708
5709                 "odd": createPositionalPseudo(function( matchIndexes, length ) {
5710                         var i = 1;
5711                         for ( ; i < length; i += 2 ) {
5712                                 matchIndexes.push( i );
5713                         }
5714                         return matchIndexes;
5715                 }),
5716
5717                 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
5718                         var i = argument < 0 ? argument + length : argument;
5719                         for ( ; --i >= 0; ) {
5720                                 matchIndexes.push( i );
5721                         }
5722                         return matchIndexes;
5723                 }),
5724
5725                 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
5726                         var i = argument < 0 ? argument + length : argument;
5727                         for ( ; ++i < length; ) {
5728                                 matchIndexes.push( i );
5729                         }
5730                         return matchIndexes;
5731                 })
5732         }
5733 };
5734
5735 Expr.pseudos["nth"] = Expr.pseudos["eq"];
5736
5737 for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
5738         Expr.pseudos[ i ] = createInputPseudo( i );
5739 }
5740 for ( i in { submit: true, reset: true } ) {
5741         Expr.pseudos[ i ] = createButtonPseudo( i );
5742 }
5743
5744 function setFilters() {}
5745 setFilters.prototype = Expr.filters = Expr.pseudos;
5746 Expr.setFilters = new setFilters();
5747
5748 function tokenize( selector, parseOnly ) {
5749         var matched, match, tokens, type,
5750                 soFar, groups, preFilters,
5751                 cached = tokenCache[ selector + " " ];
5752
5753         if ( cached ) {
5754                 return parseOnly ? 0 : cached.slice( 0 );
5755         }
5756
5757         soFar = selector;
5758         groups = [];
5759         preFilters = Expr.preFilter;
5760
5761         while ( soFar ) {
5762
5763                 if ( !matched || (match = rcomma.exec( soFar )) ) {
5764                         if ( match ) {
5765                                 soFar = soFar.slice( match[0].length ) || soFar;
5766                         }
5767                         groups.push( (tokens = []) );
5768                 }
5769
5770                 matched = false;
5771
5772                 if ( (match = rcombinators.exec( soFar )) ) {
5773                         matched = match.shift();
5774                         tokens.push({
5775                                 value: matched,
5776                                 type: match[0].replace( rtrim, " " )
5777                         });
5778                         soFar = soFar.slice( matched.length );
5779                 }
5780
5781                 for ( type in Expr.filter ) {
5782                         if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
5783                                 (match = preFilters[ type ]( match ))) ) {
5784                                 matched = match.shift();
5785                                 tokens.push({
5786                                         value: matched,
5787                                         type: type,
5788                                         matches: match
5789                                 });
5790                                 soFar = soFar.slice( matched.length );
5791                         }
5792                 }
5793
5794                 if ( !matched ) {
5795                         break;
5796                 }
5797         }
5798
5799         return parseOnly ?
5800                 soFar.length :
5801                 soFar ?
5802                         Sizzle.error( selector ) :
5803                         tokenCache( selector, groups ).slice( 0 );
5804 }
5805
5806 function toSelector( tokens ) {
5807         var i = 0,
5808                 len = tokens.length,
5809                 selector = "";
5810         for ( ; i < len; i++ ) {
5811                 selector += tokens[i].value;
5812         }
5813         return selector;
5814 }
5815
5816 function addCombinator( matcher, combinator, base ) {
5817         var dir = combinator.dir,
5818                 checkNonElements = base && dir === "parentNode",
5819                 doneName = done++;
5820
5821         return combinator.first ?
5822                 function( elem, context, xml ) {
5823                         while ( (elem = elem[ dir ]) ) {
5824                                 if ( elem.nodeType === 1 || checkNonElements ) {
5825                                         return matcher( elem, context, xml );
5826                                 }
5827                         }
5828                 } :
5829
5830                 function( elem, context, xml ) {
5831                         var oldCache, outerCache,
5832                                 newCache = [ dirruns, doneName ];
5833
5834                         if ( xml ) {
5835                                 while ( (elem = elem[ dir ]) ) {
5836                                         if ( elem.nodeType === 1 || checkNonElements ) {
5837                                                 if ( matcher( elem, context, xml ) ) {
5838                                                         return true;
5839                                                 }
5840                                         }
5841                                 }
5842                         } else {
5843                                 while ( (elem = elem[ dir ]) ) {
5844                                         if ( elem.nodeType === 1 || checkNonElements ) {
5845                                                 outerCache = elem[ expando ] || (elem[ expando ] = {});
5846                                                 if ( (oldCache = outerCache[ dir ]) &&
5847                                                         oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
5848
5849                                                         return (newCache[ 2 ] = oldCache[ 2 ]);
5850                                                 } else {
5851                                                         outerCache[ dir ] = newCache;
5852
5853                                                         if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
5854                                                                 return true;
5855                                                         }
5856                                                 }
5857                                         }
5858                                 }
5859                         }
5860                 };
5861 }
5862
5863 function elementMatcher( matchers ) {
5864         return matchers.length > 1 ?
5865                 function( elem, context, xml ) {
5866                         var i = matchers.length;
5867                         while ( i-- ) {
5868                                 if ( !matchers[i]( elem, context, xml ) ) {
5869                                         return false;
5870                                 }
5871                         }
5872                         return true;
5873                 } :
5874                 matchers[0];
5875 }
5876
5877 function multipleContexts( selector, contexts, results ) {
5878         var i = 0,
5879                 len = contexts.length;
5880         for ( ; i < len; i++ ) {
5881                 Sizzle( selector, contexts[i], results );
5882         }
5883         return results;
5884 }
5885
5886 function condense( unmatched, map, filter, context, xml ) {
5887         var elem,
5888                 newUnmatched = [],
5889                 i = 0,
5890                 len = unmatched.length,
5891                 mapped = map != null;
5892
5893         for ( ; i < len; i++ ) {
5894                 if ( (elem = unmatched[i]) ) {
5895                         if ( !filter || filter( elem, context, xml ) ) {
5896                                 newUnmatched.push( elem );
5897                                 if ( mapped ) {
5898                                         map.push( i );
5899                                 }
5900                         }
5901                 }
5902         }
5903
5904         return newUnmatched;
5905 }
5906
5907 function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
5908         if ( postFilter && !postFilter[ expando ] ) {
5909                 postFilter = setMatcher( postFilter );
5910         }
5911         if ( postFinder && !postFinder[ expando ] ) {
5912                 postFinder = setMatcher( postFinder, postSelector );
5913         }
5914         return markFunction(function( seed, results, context, xml ) {
5915                 var temp, i, elem,
5916                         preMap = [],
5917                         postMap = [],
5918                         preexisting = results.length,
5919
5920                         elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
5921
5922                         matcherIn = preFilter && ( seed || !selector ) ?
5923                                 condense( elems, preMap, preFilter, context, xml ) :
5924                                 elems,
5925
5926                         matcherOut = matcher ?
5927                                 postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
5928
5929                                         [] :
5930
5931                                         results :
5932                                 matcherIn;
5933
5934                 if ( matcher ) {
5935                         matcher( matcherIn, matcherOut, context, xml );
5936                 }
5937
5938                 if ( postFilter ) {
5939                         temp = condense( matcherOut, postMap );
5940                         postFilter( temp, [], context, xml );
5941
5942                         i = temp.length;
5943                         while ( i-- ) {
5944                                 if ( (elem = temp[i]) ) {
5945                                         matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
5946                                 }
5947                         }
5948                 }
5949
5950                 if ( seed ) {
5951                         if ( postFinder || preFilter ) {
5952                                 if ( postFinder ) {
5953                                         temp = [];
5954                                         i = matcherOut.length;
5955                                         while ( i-- ) {
5956                                                 if ( (elem = matcherOut[i]) ) {
5957                                                         temp.push( (matcherIn[i] = elem) );
5958                                                 }
5959                                         }
5960                                         postFinder( null, (matcherOut = []), temp, xml );
5961                                 }
5962
5963                                 i = matcherOut.length;
5964                                 while ( i-- ) {
5965                                         if ( (elem = matcherOut[i]) &&
5966                                                 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
5967
5968                                                 seed[temp] = !(results[temp] = elem);
5969                                         }
5970                                 }
5971                         }
5972
5973                 } else {
5974                         matcherOut = condense(
5975                                 matcherOut === results ?
5976                                         matcherOut.splice( preexisting, matcherOut.length ) :
5977                                         matcherOut
5978                         );
5979                         if ( postFinder ) {
5980                                 postFinder( null, results, matcherOut, xml );
5981                         } else {
5982                                 push.apply( results, matcherOut );
5983                         }
5984                 }
5985         });
5986 }
5987
5988 function matcherFromTokens( tokens ) {
5989         var checkContext, matcher, j,
5990                 len = tokens.length,
5991                 leadingRelative = Expr.relative[ tokens[0].type ],
5992                 implicitRelative = leadingRelative || Expr.relative[" "],
5993                 i = leadingRelative ? 1 : 0,
5994
5995                 matchContext = addCombinator( function( elem ) {
5996                         return elem === checkContext;
5997                 }, implicitRelative, true ),
5998                 matchAnyContext = addCombinator( function( elem ) {
5999                         return indexOf.call( checkContext, elem ) > -1;
6000                 }, implicitRelative, true ),
6001                 matchers = [ function( elem, context, xml ) {
6002                         return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
6003                                 (checkContext = context).nodeType ?
6004                                         matchContext( elem, context, xml ) :
6005                                         matchAnyContext( elem, context, xml ) );
6006                 } ];
6007
6008         for ( ; i < len; i++ ) {
6009                 if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
6010                         matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
6011                 } else {
6012                         matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
6013
6014                         if ( matcher[ expando ] ) {
6015                                 j = ++i;
6016                                 for ( ; j < len; j++ ) {
6017                                         if ( Expr.relative[ tokens[j].type ] ) {
6018                                                 break;
6019                                         }
6020                                 }
6021                                 return setMatcher(
6022                                         i > 1 && elementMatcher( matchers ),
6023                                         i > 1 && toSelector(
6024                                                 tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
6025                                         ).replace( rtrim, "$1" ),
6026                                         matcher,
6027                                         i < j && matcherFromTokens( tokens.slice( i, j ) ),
6028                                         j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
6029                                         j < len && toSelector( tokens )
6030                                 );
6031                         }
6032                         matchers.push( matcher );
6033                 }
6034         }
6035
6036         return elementMatcher( matchers );
6037 }
6038
6039 function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
6040         var bySet = setMatchers.length > 0,
6041                 byElement = elementMatchers.length > 0,
6042                 superMatcher = function( seed, context, xml, results, outermost ) {
6043                         var elem, j, matcher,
6044                                 matchedCount = 0,
6045                                 i = "0",
6046                                 unmatched = seed && [],
6047                                 setMatched = [],
6048                                 contextBackup = outermostContext,
6049                                 elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
6050                                 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
6051                                 len = elems.length;
6052
6053                         if ( outermost ) {
6054                                 outermostContext = context !== document && context;
6055                         }
6056
6057                         for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
6058                                 if ( byElement && elem ) {
6059                                         j = 0;
6060                                         while ( (matcher = elementMatchers[j++]) ) {
6061                                                 if ( matcher( elem, context, xml ) ) {
6062                                                         results.push( elem );
6063                                                         break;
6064                                                 }
6065                                         }
6066                                         if ( outermost ) {
6067                                                 dirruns = dirrunsUnique;
6068                                         }
6069                                 }
6070
6071                                 if ( bySet ) {
6072                                         if ( (elem = !matcher && elem) ) {
6073                                                 matchedCount--;
6074                                         }
6075
6076                                         if ( seed ) {
6077                                                 unmatched.push( elem );
6078                                         }
6079                                 }
6080                         }
6081
6082                         matchedCount += i;
6083                         if ( bySet && i !== matchedCount ) {
6084                                 j = 0;
6085                                 while ( (matcher = setMatchers[j++]) ) {
6086                                         matcher( unmatched, setMatched, context, xml );
6087                                 }
6088
6089                                 if ( seed ) {
6090                                         if ( matchedCount > 0 ) {
6091                                                 while ( i-- ) {
6092                                                         if ( !(unmatched[i] || setMatched[i]) ) {
6093                                                                 setMatched[i] = pop.call( results );
6094                                                         }
6095                                                 }
6096                                         }
6097
6098                                         setMatched = condense( setMatched );
6099                                 }
6100
6101                                 push.apply( results, setMatched );
6102
6103                                 if ( outermost && !seed && setMatched.length > 0 &&
6104                                         ( matchedCount + setMatchers.length ) > 1 ) {
6105
6106                                         Sizzle.uniqueSort( results );
6107                                 }
6108                         }
6109
6110                         if ( outermost ) {
6111                                 dirruns = dirrunsUnique;
6112                                 outermostContext = contextBackup;
6113                         }
6114
6115                         return unmatched;
6116                 };
6117
6118         return bySet ?
6119                 markFunction( superMatcher ) :
6120                 superMatcher;
6121 }
6122
6123 compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
6124         var i,
6125                 setMatchers = [],
6126                 elementMatchers = [],
6127                 cached = compilerCache[ selector + " " ];
6128
6129         if ( !cached ) {
6130                 if ( !match ) {
6131                         match = tokenize( selector );
6132                 }
6133                 i = match.length;
6134                 while ( i-- ) {
6135                         cached = matcherFromTokens( match[i] );
6136                         if ( cached[ expando ] ) {
6137                                 setMatchers.push( cached );
6138                         } else {
6139                                 elementMatchers.push( cached );
6140                         }
6141                 }
6142
6143                 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
6144
6145                 cached.selector = selector;
6146         }
6147         return cached;
6148 };
6149
6150 /**
6151  * A low-level selection function that works with Sizzle's compiled
6152  *  selector functions
6153  * @param {String|Function} selector A selector or a pre-compiled
6154  *  selector function built with Sizzle.compile
6155  * @param {Element} context
6156  * @param {Array} [results]
6157  * @param {Array} [seed] A set of elements to match against
6158  */
6159 select = Sizzle.select = function( selector, context, results, seed ) {
6160         var i, tokens, token, type, find,
6161                 compiled = typeof selector === "function" && selector,
6162                 match = !seed && tokenize( (selector = compiled.selector || selector) );
6163
6164         results = results || [];
6165
6166         if ( match.length === 1 ) {
6167
6168                 tokens = match[0] = match[0].slice( 0 );
6169                 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
6170                                 support.getById && context.nodeType === 9 && documentIsHTML &&
6171                                 Expr.relative[ tokens[1].type ] ) {
6172
6173                         context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
6174                         if ( !context ) {
6175                                 return results;
6176
6177                         } else if ( compiled ) {
6178                                 context = context.parentNode;
6179                         }
6180
6181                         selector = selector.slice( tokens.shift().value.length );
6182                 }
6183
6184                 i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
6185                 while ( i-- ) {
6186                         token = tokens[i];
6187
6188                         if ( Expr.relative[ (type = token.type) ] ) {
6189                                 break;
6190                         }
6191                         if ( (find = Expr.find[ type ]) ) {
6192                                 if ( (seed = find(
6193                                         token.matches[0].replace( runescape, funescape ),
6194                                         rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
6195                                 )) ) {
6196
6197                                         tokens.splice( i, 1 );
6198                                         selector = seed.length && toSelector( tokens );
6199                                         if ( !selector ) {
6200                                                 push.apply( results, seed );
6201                                                 return results;
6202                                         }
6203
6204                                         break;
6205                                 }
6206                         }
6207                 }
6208         }
6209
6210         ( compiled || compile( selector, match ) )(
6211                 seed,
6212                 context,
6213                 !documentIsHTML,
6214                 results,
6215                 rsibling.test( selector ) && testContext( context.parentNode ) || context
6216         );
6217         return results;
6218 };
6219
6220
6221 support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
6222
6223 support.detectDuplicates = !!hasDuplicate;
6224
6225 setDocument();
6226
6227 support.sortDetached = assert(function( div1 ) {
6228         return div1.compareDocumentPosition( document.createElement("div") ) & 1;
6229 });
6230
6231 if ( !assert(function( div ) {
6232         div.innerHTML = "<a href='#'></a>";
6233         return div.firstChild.getAttribute("href") === "#" ;
6234 }) ) {
6235         addHandle( "type|href|height|width", function( elem, name, isXML ) {
6236                 if ( !isXML ) {
6237                         return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
6238                 }
6239         });
6240 }
6241
6242 if ( !support.attributes || !assert(function( div ) {
6243         div.innerHTML = "<input/>";
6244         div.firstChild.setAttribute( "value", "" );
6245         return div.firstChild.getAttribute( "value" ) === "";
6246 }) ) {
6247         addHandle( "value", function( elem, name, isXML ) {
6248                 if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
6249                         return elem.defaultValue;
6250                 }
6251         });
6252 }
6253
6254 if ( !assert(function( div ) {
6255         return div.getAttribute("disabled") == null;
6256 }) ) {
6257         addHandle( booleans, function( elem, name, isXML ) {
6258                 var val;
6259                 if ( !isXML ) {
6260                         return elem[ name ] === true ? name.toLowerCase() :
6261                                         (val = elem.getAttributeNode( name )) && val.specified ?
6262                                         val.value :
6263                                 null;
6264                 }
6265         });
6266 }
6267
6268 if ( typeof define === "function" && define.amd ) {
6269         define(function() { return Sizzle; });
6270 } else if ( typeof module !== "undefined" && module.exports ) {
6271         module.exports = Sizzle;
6272 } else {
6273         window.Sizzle = Sizzle;
6274 }
6275
6276 })( window );
6277
6278 ;(function() {
6279   if (typeof Sizzle !== 'undefined') {
6280     return;
6281   }
6282
6283   if (typeof define !== 'undefined' && define.amd) {
6284     window.Sizzle = Prototype._actual_sizzle;
6285     window.define = Prototype._original_define;
6286     delete Prototype._actual_sizzle;
6287     delete Prototype._original_define;
6288   } else if (typeof module !== 'undefined' && module.exports) {
6289     window.Sizzle = module.exports;
6290     module.exports = {};
6291   }
6292 })();
6293
6294 ;(function(engine) {
6295   var extendElements = Prototype.Selector.extendElements;
6296
6297   function select(selector, scope) {
6298     return extendElements(engine(selector, scope || document));
6299   }
6300
6301   function match(element, selector) {
6302     return engine.matches(selector, [element]).length == 1;
6303   }
6304
6305   Prototype.Selector.engine = engine;
6306   Prototype.Selector.select = select;
6307   Prototype.Selector.match = match;
6308 })(Sizzle);
6309
6310 window.Sizzle = Prototype._original_property;
6311 delete Prototype._original_property;
6312
6313 var Form = {
6314   reset: function(form) {
6315     form = $(form);
6316     form.reset();
6317     return form;
6318   },
6319
6320   serializeElements: function(elements, options) {
6321     if (typeof options != 'object') options = { hash: !!options };
6322     else if (Object.isUndefined(options.hash)) options.hash = true;
6323     var key, value, submitted = false, submit = options.submit, accumulator, initial;
6324
6325     if (options.hash) {
6326       initial = {};
6327       accumulator = function(result, key, value) {
6328         if (key in result) {
6329           if (!Object.isArray(result[key])) result[key] = [result[key]];
6330           result[key] = result[key].concat(value);
6331         } else result[key] = value;
6332         return result;
6333       };
6334     } else {
6335       initial = '';
6336       accumulator = function(result, key, values) {
6337         if (!Object.isArray(values)) {values = [values];}
6338         if (!values.length) {return result;}
6339         var encodedKey = encodeURIComponent(key).gsub(/%20/, '+');
6340         return result + (result ? "&" : "") + values.map(function (value) {
6341           value = value.gsub(/(\r)?\n/, '\r\n');
6342           value = encodeURIComponent(value);
6343           value = value.gsub(/%20/, '+');
6344           return encodedKey + "=" + value;
6345         }).join("&");
6346       };
6347     }
6348
6349     return elements.inject(initial, function(result, element) {
6350       if (!element.disabled && element.name) {
6351         key = element.name; value = $(element).getValue();
6352         if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
6353             submit !== false && (!submit || key == submit) && (submitted = true)))) {
6354           result = accumulator(result, key, value);
6355         }
6356       }
6357       return result;
6358     });
6359   }
6360 };
6361
6362 Form.Methods = {
6363   serialize: function(form, options) {
6364     return Form.serializeElements(Form.getElements(form), options);
6365   },
6366
6367
6368   getElements: function(form) {
6369     var elements = $(form).getElementsByTagName('*');
6370     var element, results = [], serializers = Form.Element.Serializers;
6371
6372     for (var i = 0; element = elements[i]; i++) {
6373       if (serializers[element.tagName.toLowerCase()])
6374         results.push(Element.extend(element));
6375     }
6376     return results;
6377   },
6378
6379   getInputs: function(form, typeName, name) {
6380     form = $(form);
6381     var inputs = form.getElementsByTagName('input');
6382
6383     if (!typeName && !name) return $A(inputs).map(Element.extend);
6384
6385     for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
6386       var input = inputs[i];
6387       if ((typeName && input.type != typeName) || (name && input.name != name))
6388         continue;
6389       matchingInputs.push(Element.extend(input));
6390     }
6391
6392     return matchingInputs;
6393   },
6394
6395   disable: function(form) {
6396     form = $(form);
6397     Form.getElements(form).invoke('disable');
6398     return form;
6399   },
6400
6401   enable: function(form) {
6402     form = $(form);
6403     Form.getElements(form).invoke('enable');
6404     return form;
6405   },
6406
6407   findFirstElement: function(form) {
6408     var elements = $(form).getElements().findAll(function(element) {
6409       return 'hidden' != element.type && !element.disabled;
6410     });
6411     var firstByIndex = elements.findAll(function(element) {
6412       return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
6413     }).sortBy(function(element) { return element.tabIndex }).first();
6414
6415     return firstByIndex ? firstByIndex : elements.find(function(element) {
6416       return /^(?:input|select|textarea)$/i.test(element.tagName);
6417     });
6418   },
6419
6420   focusFirstElement: function(form) {
6421     form = $(form);
6422     var element = form.findFirstElement();
6423     if (element) element.activate();
6424     return form;
6425   },
6426
6427   request: function(form, options) {
6428     form = $(form), options = Object.clone(options || { });
6429
6430     var params = options.parameters, action = form.readAttribute('action') || '';
6431     if (action.blank()) action = window.location.href;
6432     options.parameters = form.serialize(true);
6433
6434     if (params) {
6435       if (Object.isString(params)) params = params.toQueryParams();
6436       Object.extend(options.parameters, params);
6437     }
6438
6439     if (form.hasAttribute('method') && !options.method)
6440       options.method = form.method;
6441
6442     return new Ajax.Request(action, options);
6443   }
6444 };
6445
6446 /*--------------------------------------------------------------------------*/
6447
6448
6449 Form.Element = {
6450   focus: function(element) {
6451     $(element).focus();
6452     return element;
6453   },
6454
6455   select: function(element) {
6456     $(element).select();
6457     return element;
6458   }
6459 };
6460
6461 Form.Element.Methods = {
6462
6463   serialize: function(element) {
6464     element = $(element);
6465     if (!element.disabled && element.name) {
6466       var value = element.getValue();
6467       if (value != undefined) {
6468         var pair = { };
6469         pair[element.name] = value;
6470         return Object.toQueryString(pair);
6471       }
6472     }
6473     return '';
6474   },
6475
6476   getValue: function(element) {
6477     element = $(element);
6478     var method = element.tagName.toLowerCase();
6479     return Form.Element.Serializers[method](element);
6480   },
6481
6482   setValue: function(element, value) {
6483     element = $(element);
6484     var method = element.tagName.toLowerCase();
6485     Form.Element.Serializers[method](element, value);
6486     return element;
6487   },
6488
6489   clear: function(element) {
6490     $(element).value = '';
6491     return element;
6492   },
6493
6494   present: function(element) {
6495     return $(element).value != '';
6496   },
6497
6498   activate: function(element) {
6499     element = $(element);
6500     try {
6501       element.focus();
6502       if (element.select && (element.tagName.toLowerCase() != 'input' ||
6503           !(/^(?:button|reset|submit)$/i.test(element.type))))
6504         element.select();
6505     } catch (e) { }
6506     return element;
6507   },
6508
6509   disable: function(element) {
6510     element = $(element);
6511     element.disabled = true;
6512     return element;
6513   },
6514
6515   enable: function(element) {
6516     element = $(element);
6517     element.disabled = false;
6518     return element;
6519   }
6520 };
6521
6522 /*--------------------------------------------------------------------------*/
6523
6524 var Field = Form.Element;
6525
6526 var $F = Form.Element.Methods.getValue;
6527
6528 /*--------------------------------------------------------------------------*/
6529
6530 Form.Element.Serializers = (function() {
6531   function input(element, value) {
6532     switch (element.type.toLowerCase()) {
6533       case 'checkbox':
6534       case 'radio':
6535         return inputSelector(element, value);
6536       default:
6537         return valueSelector(element, value);
6538     }
6539   }
6540
6541   function inputSelector(element, value) {
6542     if (Object.isUndefined(value))
6543       return element.checked ? element.value : null;
6544     else element.checked = !!value;
6545   }
6546
6547   function valueSelector(element, value) {
6548     if (Object.isUndefined(value)) return element.value;
6549     else element.value = value;
6550   }
6551
6552   function select(element, value) {
6553     if (Object.isUndefined(value))
6554       return (element.type === 'select-one' ? selectOne : selectMany)(element);
6555
6556     var opt, currentValue, single = !Object.isArray(value);
6557     for (var i = 0, length = element.length; i < length; i++) {
6558       opt = element.options[i];
6559       currentValue = this.optionValue(opt);
6560       if (single) {
6561         if (currentValue == value) {
6562           opt.selected = true;
6563           return;
6564         }
6565       }
6566       else opt.selected = value.include(currentValue);
6567     }
6568   }
6569
6570   function selectOne(element) {
6571     var index = element.selectedIndex;
6572     return index >= 0 ? optionValue(element.options[index]) : null;
6573   }
6574
6575   function selectMany(element) {
6576     var values, length = element.length;
6577     if (!length) return null;
6578
6579     for (var i = 0, values = []; i < length; i++) {
6580       var opt = element.options[i];
6581       if (opt.selected) values.push(optionValue(opt));
6582     }
6583     return values;
6584   }
6585
6586   function optionValue(opt) {
6587     return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
6588   }
6589
6590   return {
6591     input:         input,
6592     inputSelector: inputSelector,
6593     textarea:      valueSelector,
6594     select:        select,
6595     selectOne:     selectOne,
6596     selectMany:    selectMany,
6597     optionValue:   optionValue,
6598     button:        valueSelector
6599   };
6600 })();
6601
6602 /*--------------------------------------------------------------------------*/
6603
6604
6605 Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
6606   initialize: function($super, element, frequency, callback) {
6607     $super(callback, frequency);
6608     this.element   = $(element);
6609     this.lastValue = this.getValue();
6610   },
6611
6612   execute: function() {
6613     var value = this.getValue();
6614     if (Object.isString(this.lastValue) && Object.isString(value) ?
6615         this.lastValue != value : String(this.lastValue) != String(value)) {
6616       this.callback(this.element, value);
6617       this.lastValue = value;
6618     }
6619   }
6620 });
6621
6622 Form.Element.Observer = Class.create(Abstract.TimedObserver, {
6623   getValue: function() {
6624     return Form.Element.getValue(this.element);
6625   }
6626 });
6627
6628 Form.Observer = Class.create(Abstract.TimedObserver, {
6629   getValue: function() {
6630     return Form.serialize(this.element);
6631   }
6632 });
6633
6634 /*--------------------------------------------------------------------------*/
6635
6636 Abstract.EventObserver = Class.create({
6637   initialize: function(element, callback) {
6638     this.element  = $(element);
6639     this.callback = callback;
6640
6641     this.lastValue = this.getValue();
6642     if (this.element.tagName.toLowerCase() == 'form')
6643       this.registerFormCallbacks();
6644     else
6645       this.registerCallback(this.element);
6646   },
6647
6648   onElementEvent: function() {
6649     var value = this.getValue();
6650     if (this.lastValue != value) {
6651       this.callback(this.element, value);
6652       this.lastValue = value;
6653     }
6654   },
6655
6656   registerFormCallbacks: function() {
6657     Form.getElements(this.element).each(this.registerCallback, this);
6658   },
6659
6660   registerCallback: function(element) {
6661     if (element.type) {
6662       switch (element.type.toLowerCase()) {
6663         case 'checkbox':
6664         case 'radio':
6665           Event.observe(element, 'click', this.onElementEvent.bind(this));
6666           break;
6667         default:
6668           Event.observe(element, 'change', this.onElementEvent.bind(this));
6669           break;
6670       }
6671     }
6672   }
6673 });
6674
6675 Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
6676   getValue: function() {
6677     return Form.Element.getValue(this.element);
6678   }
6679 });
6680
6681 Form.EventObserver = Class.create(Abstract.EventObserver, {
6682   getValue: function() {
6683     return Form.serialize(this.element);
6684   }
6685 });
6686 (function(GLOBAL) {
6687   var DIV = document.createElement('div');
6688   var docEl = document.documentElement;
6689   var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
6690    && 'onmouseleave' in docEl;
6691
6692   var Event = {
6693     KEY_BACKSPACE: 8,
6694     KEY_TAB:       9,
6695     KEY_RETURN:   13,
6696     KEY_ESC:      27,
6697     KEY_LEFT:     37,
6698     KEY_UP:       38,
6699     KEY_RIGHT:    39,
6700     KEY_DOWN:     40,
6701     KEY_DELETE:   46,
6702     KEY_HOME:     36,
6703     KEY_END:      35,
6704     KEY_PAGEUP:   33,
6705     KEY_PAGEDOWN: 34,
6706     KEY_INSERT:   45
6707   };
6708
6709
6710   var isIELegacyEvent = function(event) { return false; };
6711
6712   if (window.attachEvent) {
6713     if (window.addEventListener) {
6714       isIELegacyEvent = function(event) {
6715         return !(event instanceof window.Event);
6716       };
6717     } else {
6718       isIELegacyEvent = function(event) { return true; };
6719     }
6720   }
6721
6722   var _isButton;
6723
6724   function _isButtonForDOMEvents(event, code) {
6725     return event.which ? (event.which === code + 1) : (event.button === code);
6726   }
6727
6728   var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
6729   function _isButtonForLegacyEvents(event, code) {
6730     return event.button === legacyButtonMap[code];
6731   }
6732
6733   function _isButtonForWebKit(event, code) {
6734     switch (code) {
6735       case 0: return event.which == 1 && !event.metaKey;
6736       case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
6737       case 2: return event.which == 3;
6738       default: return false;
6739     }
6740   }
6741
6742   if (window.attachEvent) {
6743     if (!window.addEventListener) {
6744       _isButton = _isButtonForLegacyEvents;
6745     } else {
6746       _isButton = function(event, code) {
6747         return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
6748          _isButtonForDOMEvents(event, code);
6749       }
6750     }
6751   } else if (Prototype.Browser.WebKit) {
6752     _isButton = _isButtonForWebKit;
6753   } else {
6754     _isButton = _isButtonForDOMEvents;
6755   }
6756
6757   function isLeftClick(event)   { return _isButton(event, 0) }
6758
6759   function isMiddleClick(event) { return _isButton(event, 1) }
6760
6761   function isRightClick(event)  { return _isButton(event, 2) }
6762
6763   function element(event) {
6764     return Element.extend(_element(event));
6765   }
6766
6767   function _element(event) {
6768     event = Event.extend(event);
6769
6770     var node = event.target, type = event.type,
6771      currentTarget = event.currentTarget;
6772
6773     if (currentTarget && currentTarget.tagName) {
6774       if (type === 'load' || type === 'error' ||
6775         (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
6776           && currentTarget.type === 'radio'))
6777             node = currentTarget;
6778     }
6779
6780     return node.nodeType == Node.TEXT_NODE ? node.parentNode : node;
6781   }
6782
6783   function findElement(event, expression) {
6784     var element = _element(event), selector = Prototype.Selector;
6785     if (!expression) return Element.extend(element);
6786     while (element) {
6787       if (Object.isElement(element) && selector.match(element, expression))
6788         return Element.extend(element);
6789       element = element.parentNode;
6790     }
6791   }
6792
6793   function pointer(event) {
6794     return { x: pointerX(event), y: pointerY(event) };
6795   }
6796
6797   function pointerX(event) {
6798     var docElement = document.documentElement,
6799      body = document.body || { scrollLeft: 0 };
6800
6801     return event.pageX || (event.clientX +
6802       (docElement.scrollLeft || body.scrollLeft) -
6803       (docElement.clientLeft || 0));
6804   }
6805
6806   function pointerY(event) {
6807     var docElement = document.documentElement,
6808      body = document.body || { scrollTop: 0 };
6809
6810     return  event.pageY || (event.clientY +
6811        (docElement.scrollTop || body.scrollTop) -
6812        (docElement.clientTop || 0));
6813   }
6814
6815
6816   function stop(event) {
6817     Event.extend(event);
6818     event.preventDefault();
6819     event.stopPropagation();
6820
6821     event.stopped = true;
6822   }
6823
6824
6825   Event.Methods = {
6826     isLeftClick:   isLeftClick,
6827     isMiddleClick: isMiddleClick,
6828     isRightClick:  isRightClick,
6829
6830     element:     element,
6831     findElement: findElement,
6832
6833     pointer:  pointer,
6834     pointerX: pointerX,
6835     pointerY: pointerY,
6836
6837     stop: stop
6838   };
6839
6840   var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
6841     m[name] = Event.Methods[name].methodize();
6842     return m;
6843   });
6844
6845   if (window.attachEvent) {
6846     function _relatedTarget(event) {
6847       var element;
6848       switch (event.type) {
6849         case 'mouseover':
6850         case 'mouseenter':
6851           element = event.fromElement;
6852           break;
6853         case 'mouseout':
6854         case 'mouseleave':
6855           element = event.toElement;
6856           break;
6857         default:
6858           return null;
6859       }
6860       return Element.extend(element);
6861     }
6862
6863     var additionalMethods = {
6864       stopPropagation: function() { this.cancelBubble = true },
6865       preventDefault:  function() { this.returnValue = false },
6866       inspect: function() { return '[object Event]' }
6867     };
6868
6869     Event.extend = function(event, element) {
6870       if (!event) return false;
6871
6872       if (!isIELegacyEvent(event)) return event;
6873
6874       if (event._extendedByPrototype) return event;
6875       event._extendedByPrototype = Prototype.emptyFunction;
6876
6877       var pointer = Event.pointer(event);
6878
6879       Object.extend(event, {
6880         target: event.srcElement || element,
6881         relatedTarget: _relatedTarget(event),
6882         pageX:  pointer.x,
6883         pageY:  pointer.y
6884       });
6885
6886       Object.extend(event, methods);
6887       Object.extend(event, additionalMethods);
6888
6889       return event;
6890     };
6891   } else {
6892     Event.extend = Prototype.K;
6893   }
6894
6895   if (window.addEventListener) {
6896     Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
6897     Object.extend(Event.prototype, methods);
6898   }
6899
6900   var EVENT_TRANSLATIONS = {
6901     mouseenter: 'mouseover',
6902     mouseleave: 'mouseout'
6903   };
6904
6905   function getDOMEventName(eventName) {
6906     return EVENT_TRANSLATIONS[eventName] || eventName;
6907   }
6908
6909   if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED)
6910     getDOMEventName = Prototype.K;
6911
6912   function getUniqueElementID(element) {
6913     if (element === window) return 0;
6914
6915     if (typeof element._prototypeUID === 'undefined')
6916       element._prototypeUID = Element.Storage.UID++;
6917     return element._prototypeUID;
6918   }
6919
6920   function getUniqueElementID_IE(element) {
6921     if (element === window) return 0;
6922     if (element == document) return 1;
6923     return element.uniqueID;
6924   }
6925
6926   if ('uniqueID' in DIV)
6927     getUniqueElementID = getUniqueElementID_IE;
6928
6929   function isCustomEvent(eventName) {
6930     return eventName.include(':');
6931   }
6932
6933   Event._isCustomEvent = isCustomEvent;
6934
6935   function getOrCreateRegistryFor(element, uid) {
6936     var CACHE = GLOBAL.Event.cache;
6937     if (Object.isUndefined(uid))
6938       uid = getUniqueElementID(element);
6939     if (!CACHE[uid]) CACHE[uid] = { element: element };
6940     return CACHE[uid];
6941   }
6942
6943   function destroyRegistryForElement(element, uid) {
6944     if (Object.isUndefined(uid))
6945       uid = getUniqueElementID(element);
6946     delete GLOBAL.Event.cache[uid];
6947   }
6948
6949
6950   function register(element, eventName, handler) {
6951     var registry = getOrCreateRegistryFor(element);
6952     if (!registry[eventName]) registry[eventName] = [];
6953     var entries = registry[eventName];
6954
6955     var i = entries.length;
6956     while (i--)
6957       if (entries[i].handler === handler) return null;
6958
6959     var uid = getUniqueElementID(element);
6960     var responder = GLOBAL.Event._createResponder(uid, eventName, handler);
6961     var entry = {
6962       responder: responder,
6963       handler:   handler
6964     };
6965
6966     entries.push(entry);
6967     return entry;
6968   }
6969
6970   function unregister(element, eventName, handler) {
6971     var registry = getOrCreateRegistryFor(element);
6972     var entries = registry[eventName] || [];
6973
6974     var i = entries.length, entry;
6975     while (i--) {
6976       if (entries[i].handler === handler) {
6977         entry = entries[i];
6978         break;
6979       }
6980     }
6981
6982     if (entry) {
6983       var index = entries.indexOf(entry);
6984       entries.splice(index, 1);
6985     }
6986
6987     if (entries.length === 0) {
6988       delete registry[eventName];
6989       if (Object.keys(registry).length === 1 && ('element' in registry))
6990         destroyRegistryForElement(element);
6991     }
6992
6993     return entry;
6994   }
6995
6996
6997   function observe(element, eventName, handler) {
6998     element = $(element);
6999     var entry = register(element, eventName, handler);
7000
7001     if (entry === null) return element;
7002
7003     var responder = entry.responder;
7004     if (isCustomEvent(eventName))
7005       observeCustomEvent(element, eventName, responder);
7006     else
7007       observeStandardEvent(element, eventName, responder);
7008
7009     return element;
7010   }
7011
7012   function observeStandardEvent(element, eventName, responder) {
7013     var actualEventName = getDOMEventName(eventName);
7014     if (element.addEventListener) {
7015       element.addEventListener(actualEventName, responder, false);
7016     } else {
7017       element.attachEvent('on' + actualEventName, responder);
7018     }
7019   }
7020
7021   function observeCustomEvent(element, eventName, responder) {
7022     if (element.addEventListener) {
7023       element.addEventListener('dataavailable', responder, false);
7024     } else {
7025       element.attachEvent('ondataavailable', responder);
7026       element.attachEvent('onlosecapture',   responder);
7027     }
7028   }
7029
7030   function stopObserving(element, eventName, handler) {
7031     element = $(element);
7032     var handlerGiven = !Object.isUndefined(handler),
7033      eventNameGiven = !Object.isUndefined(eventName);
7034
7035     if (!eventNameGiven && !handlerGiven) {
7036       stopObservingElement(element);
7037       return element;
7038     }
7039
7040     if (!handlerGiven) {
7041       stopObservingEventName(element, eventName);
7042       return element;
7043     }
7044
7045     var entry = unregister(element, eventName, handler);
7046
7047     if (!entry) return element;
7048     removeEvent(element, eventName, entry.responder);
7049     return element;
7050   }
7051
7052   function stopObservingStandardEvent(element, eventName, responder) {
7053     var actualEventName = getDOMEventName(eventName);
7054     if (element.removeEventListener) {
7055       element.removeEventListener(actualEventName, responder, false);
7056     } else {
7057       element.detachEvent('on' + actualEventName, responder);
7058     }
7059   }
7060
7061   function stopObservingCustomEvent(element, eventName, responder) {
7062     if (element.removeEventListener) {
7063       element.removeEventListener('dataavailable', responder, false);
7064     } else {
7065       element.detachEvent('ondataavailable', responder);
7066       element.detachEvent('onlosecapture',   responder);
7067     }
7068   }
7069
7070
7071
7072   function stopObservingElement(element) {
7073     var uid = getUniqueElementID(element), registry = GLOBAL.Event.cache[uid];
7074     if (!registry) return;
7075
7076     destroyRegistryForElement(element, uid);
7077
7078     var entries, i;
7079     for (var eventName in registry) {
7080       if (eventName === 'element') continue;
7081
7082       entries = registry[eventName];
7083       i = entries.length;
7084       while (i--)
7085         removeEvent(element, eventName, entries[i].responder);
7086     }
7087   }
7088
7089   function stopObservingEventName(element, eventName) {
7090     var registry = getOrCreateRegistryFor(element);
7091     var entries = registry[eventName];
7092     if (entries) {
7093       delete registry[eventName];
7094     }
7095
7096     entries = entries || [];
7097
7098     var i = entries.length;
7099     while (i--)
7100       removeEvent(element, eventName, entries[i].responder);
7101
7102     for (var name in registry) {
7103       if (name === 'element') continue;
7104       return; // There is another registered event
7105     }
7106
7107     destroyRegistryForElement(element);
7108   }
7109
7110
7111   function removeEvent(element, eventName, handler) {
7112     if (isCustomEvent(eventName))
7113       stopObservingCustomEvent(element, eventName, handler);
7114     else
7115       stopObservingStandardEvent(element, eventName, handler);
7116   }
7117
7118
7119
7120   function getFireTarget(element) {
7121     if (element !== document) return element;
7122     if (document.createEvent && !element.dispatchEvent)
7123       return document.documentElement;
7124     return element;
7125   }
7126
7127   function fire(element, eventName, memo, bubble) {
7128     element = getFireTarget($(element));
7129     if (Object.isUndefined(bubble)) bubble = true;
7130     memo = memo || {};
7131
7132     var event = fireEvent(element, eventName, memo, bubble);
7133     return Event.extend(event);
7134   }
7135
7136   function fireEvent_DOM(element, eventName, memo, bubble) {
7137     var event = document.createEvent('HTMLEvents');
7138     event.initEvent('dataavailable', bubble, true);
7139
7140     event.eventName = eventName;
7141     event.memo = memo;
7142
7143     element.dispatchEvent(event);
7144     return event;
7145   }
7146
7147   function fireEvent_IE(element, eventName, memo, bubble) {
7148     var event = document.createEventObject();
7149     event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
7150
7151     event.eventName = eventName;
7152     event.memo = memo;
7153
7154     element.fireEvent(event.eventType, event);
7155     return event;
7156   }
7157
7158   var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE;
7159
7160
7161
7162   Event.Handler = Class.create({
7163     initialize: function(element, eventName, selector, callback) {
7164       this.element   = $(element);
7165       this.eventName = eventName;
7166       this.selector  = selector;
7167       this.callback  = callback;
7168       this.handler   = this.handleEvent.bind(this);
7169     },
7170
7171
7172     start: function() {
7173       Event.observe(this.element, this.eventName, this.handler);
7174       return this;
7175     },
7176
7177     stop: function() {
7178       Event.stopObserving(this.element, this.eventName, this.handler);
7179       return this;
7180     },
7181
7182     handleEvent: function(event) {
7183       var element = Event.findElement(event, this.selector);
7184       if (element) this.callback.call(this.element, event, element);
7185     }
7186   });
7187
7188   function on(element, eventName, selector, callback) {
7189     element = $(element);
7190     if (Object.isFunction(selector) && Object.isUndefined(callback)) {
7191       callback = selector, selector = null;
7192     }
7193
7194     return new Event.Handler(element, eventName, selector, callback).start();
7195   }
7196
7197   Object.extend(Event, Event.Methods);
7198
7199   Object.extend(Event, {
7200     fire:          fire,
7201     observe:       observe,
7202     stopObserving: stopObserving,
7203     p_on:            on
7204   });
7205
7206   Element.addMethods({
7207     fire:          fire,
7208
7209     observe:       observe,
7210
7211     stopObserving: stopObserving,
7212
7213     p_on:            on
7214   });
7215
7216   Object.extend(document, {
7217     fire:          fire.methodize(),
7218
7219     observe:       observe.methodize(),
7220
7221     stopObserving: stopObserving.methodize(),
7222
7223     p_on:            on.methodize(),
7224
7225     loaded:        false
7226   });
7227
7228   if (GLOBAL.Event) Object.extend(window.Event, Event);
7229   else GLOBAL.Event = Event;
7230
7231   GLOBAL.Event.cache = {};
7232
7233   function destroyCache_IE() {
7234     GLOBAL.Event.cache = null;
7235   }
7236
7237   if (window.attachEvent)
7238     window.attachEvent('onunload', destroyCache_IE);
7239
7240   DIV = null;
7241   docEl = null;
7242 })(this);
7243
7244 (function(GLOBAL) {
7245   /* Code for creating leak-free event responders is based on work by
7246    John-David Dalton. */
7247
7248   var docEl = document.documentElement;
7249   var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
7250     && 'onmouseleave' in docEl;
7251
7252   function isSimulatedMouseEnterLeaveEvent(eventName) {
7253     return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
7254      (eventName === 'mouseenter' || eventName === 'mouseleave');
7255   }
7256
7257   function createResponder(uid, eventName, handler) {
7258     if (Event._isCustomEvent(eventName))
7259       return createResponderForCustomEvent(uid, eventName, handler);
7260     if (isSimulatedMouseEnterLeaveEvent(eventName))
7261       return createMouseEnterLeaveResponder(uid, eventName, handler);
7262
7263     return function(event) {
7264       if (!Event.cache) return;
7265
7266       var element = Event.cache[uid].element;
7267       Event.extend(event, element);
7268       handler.call(element, event);
7269     };
7270   }
7271
7272   function createResponderForCustomEvent(uid, eventName, handler) {
7273     return function(event) {
7274       var cache = Event.cache[uid];
7275       var element =  cache && cache.element;
7276
7277       if (Object.isUndefined(event.eventName))
7278         return false;
7279
7280       if (event.eventName !== eventName)
7281         return false;
7282
7283       Event.extend(event, element);
7284       handler.call(element, event);
7285     };
7286   }
7287
7288   function createMouseEnterLeaveResponder(uid, eventName, handler) {
7289     return function(event) {
7290       var element = Event.cache[uid].element;
7291
7292       Event.extend(event, element);
7293       var parent = event.relatedTarget;
7294
7295       while (parent && parent !== element) {
7296         try { parent = parent.parentNode; }
7297         catch(e) { parent = element; }
7298       }
7299
7300       if (parent === element) return;
7301       handler.call(element, event);
7302     }
7303   }
7304
7305   GLOBAL.Event._createResponder = createResponder;
7306   docEl = null;
7307 })(this);
7308
7309 (function(GLOBAL) {
7310   /* Support for the DOMContentLoaded event is based on work by Dan Webb,
7311      Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
7312
7313   var TIMER;
7314
7315   function fireContentLoadedEvent() {
7316     if (document.loaded) return;
7317     if (TIMER) window.clearTimeout(TIMER);
7318     document.loaded = true;
7319     document.fire('dom:loaded');
7320   }
7321
7322   function checkReadyState() {
7323     if (document.readyState === 'complete') {
7324       document.detachEvent('onreadystatechange', checkReadyState);
7325       fireContentLoadedEvent();
7326     }
7327   }
7328
7329   function pollDoScroll() {
7330     try {
7331       document.documentElement.doScroll('left');
7332     } catch (e) {
7333       TIMER = pollDoScroll.defer();
7334       return;
7335     }
7336
7337     fireContentLoadedEvent();
7338   }
7339
7340
7341   if (document.readyState === 'complete') {
7342     fireContentLoadedEvent();
7343     return;
7344   }
7345
7346   if (document.addEventListener) {
7347     document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
7348   } else {
7349     document.attachEvent('onreadystatechange', checkReadyState);
7350     if (window == top) TIMER = pollDoScroll.defer();
7351   }
7352
7353   Event.observe(window, 'load', fireContentLoadedEvent);
7354 })(this);
7355
7356
7357 Element.addMethods();
7358 /*------------------------------- DEPRECATED -------------------------------*/
7359
7360 Hash.toQueryString = Object.toQueryString;
7361
7362 var Toggle = { display: Element.toggle };
7363
7364 Element.addMethods({
7365   childOf: Element.Methods.descendantOf
7366 });
7367
7368 var Insertion = {
7369   Before: function(element, content) {
7370     return Element.insert(element, {before:content});
7371   },
7372
7373   Top: function(element, content) {
7374     return Element.insert(element, {top:content});
7375   },
7376
7377   Bottom: function(element, content) {
7378     return Element.insert(element, {bottom:content});
7379   },
7380
7381   After: function(element, content) {
7382     return Element.insert(element, {after:content});
7383   }
7384 };
7385
7386 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
7387
7388 var Position = {
7389   includeScrollOffsets: false,
7390
7391   prepare: function() {
7392     this.deltaX =  window.pageXOffset
7393                 || document.documentElement.scrollLeft
7394                 || document.body.scrollLeft
7395                 || 0;
7396     this.deltaY =  window.pageYOffset
7397                 || document.documentElement.scrollTop
7398                 || document.body.scrollTop
7399                 || 0;
7400   },
7401
7402   within: function(element, x, y) {
7403     if (this.includeScrollOffsets)
7404       return this.withinIncludingScrolloffsets(element, x, y);
7405     this.xcomp = x;
7406     this.ycomp = y;
7407     this.offset = Element.cumulativeOffset(element);
7408
7409     return (y >= this.offset[1] &&
7410             y <  this.offset[1] + element.offsetHeight &&
7411             x >= this.offset[0] &&
7412             x <  this.offset[0] + element.offsetWidth);
7413   },
7414
7415   withinIncludingScrolloffsets: function(element, x, y) {
7416     var offsetcache = Element.cumulativeScrollOffset(element);
7417
7418     this.xcomp = x + offsetcache[0] - this.deltaX;
7419     this.ycomp = y + offsetcache[1] - this.deltaY;
7420     this.offset = Element.cumulativeOffset(element);
7421
7422     return (this.ycomp >= this.offset[1] &&
7423             this.ycomp <  this.offset[1] + element.offsetHeight &&
7424             this.xcomp >= this.offset[0] &&
7425             this.xcomp <  this.offset[0] + element.offsetWidth);
7426   },
7427
7428   overlap: function(mode, element) {
7429     if (!mode) return 0;
7430     if (mode == 'vertical')
7431       return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
7432         element.offsetHeight;
7433     if (mode == 'horizontal')
7434       return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
7435         element.offsetWidth;
7436   },
7437
7438
7439   cumulativeOffset: Element.Methods.cumulativeOffset,
7440
7441   positionedOffset: Element.Methods.positionedOffset,
7442
7443   absolutize: function(element) {
7444     Position.prepare();
7445     return Element.absolutize(element);
7446   },
7447
7448   relativize: function(element) {
7449     Position.prepare();
7450     return Element.relativize(element);
7451   },
7452
7453   realOffset: Element.Methods.cumulativeScrollOffset,
7454
7455   offsetParent: Element.Methods.getOffsetParent,
7456
7457   page: Element.Methods.viewportOffset,
7458
7459   clone: function(source, target, options) {
7460     options = options || { };
7461     return Element.clonePosition(target, source, options);
7462   }
7463 };
7464
7465 /*--------------------------------------------------------------------------*/
7466
7467 if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
7468   function iter(name) {
7469     return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
7470   }
7471
7472   instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
7473   function(element, className) {
7474     className = className.toString().strip();
7475     var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
7476     return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
7477   } : function(element, className) {
7478     className = className.toString().strip();
7479     var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
7480     if (!classNames && !className) return elements;
7481
7482     var nodes = $(element).getElementsByTagName('*');
7483     className = ' ' + className + ' ';
7484
7485     for (var i = 0, child, cn; child = nodes[i]; i++) {
7486       if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
7487           (classNames && classNames.all(function(name) {
7488             return !name.toString().blank() && cn.include(' ' + name + ' ');
7489           }))))
7490         elements.push(Element.extend(child));
7491     }
7492     return elements;
7493   };
7494
7495   return function(className, parentElement) {
7496     return $(parentElement || document.body).getElementsByClassName(className);
7497   };
7498 }(Element.Methods);
7499
7500 /*--------------------------------------------------------------------------*/
7501
7502 Element.ClassNames = Class.create();
7503 Element.ClassNames.prototype = {
7504   initialize: function(element) {
7505     this.element = $(element);
7506   },
7507
7508   _each: function(iterator, context) {
7509     this.element.className.split(/\s+/).select(function(name) {
7510       return name.length > 0;
7511     })._each(iterator, context);
7512   },
7513
7514   set: function(className) {
7515     this.element.className = className;
7516   },
7517
7518   add: function(classNameToAdd) {
7519     if (this.include(classNameToAdd)) return;
7520     this.set($A(this).concat(classNameToAdd).join(' '));
7521   },
7522
7523   remove: function(classNameToRemove) {
7524     if (!this.include(classNameToRemove)) return;
7525     this.set($A(this).without(classNameToRemove).join(' '));
7526   },
7527
7528   toString: function() {
7529     return $A(this).join(' ');
7530   }
7531 };
7532
7533 Object.extend(Element.ClassNames.prototype, Enumerable);
7534
7535 /*--------------------------------------------------------------------------*/
7536
7537 (function() {
7538   window.Selector = Class.create({
7539     initialize: function(expression) {
7540       this.expression = expression.strip();
7541     },
7542
7543     findElements: function(rootElement) {
7544       return Prototype.Selector.select(this.expression, rootElement);
7545     },
7546
7547     match: function(element) {
7548       return Prototype.Selector.match(element, this.expression);
7549     },
7550
7551     toString: function() {
7552       return this.expression;
7553     },
7554
7555     inspect: function() {
7556       return "#<Selector: " + this.expression + ">";
7557     }
7558   });
7559
7560   Object.extend(Selector, {
7561     matchElements: function(elements, expression) {
7562       var match = Prototype.Selector.match,
7563           results = [];
7564
7565       for (var i = 0, length = elements.length; i < length; i++) {
7566         var element = elements[i];
7567         if (match(element, expression)) {
7568           results.push(Element.extend(element));
7569         }
7570       }
7571       return results;
7572     },
7573
7574     findElement: function(elements, expression, index) {
7575       index = index || 0;
7576       var matchIndex = 0, element;
7577       for (var i = 0, length = elements.length; i < length; i++) {
7578         element = elements[i];
7579         if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
7580           return Element.extend(element);
7581         }
7582       }
7583     },
7584
7585     findChildElements: function(element, expressions) {
7586       var selector = expressions.toArray().join(', ');
7587       return Prototype.Selector.select(selector, element || document);
7588     }
7589   });
7590 })();