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