source: trunk/www.guidonia.net/wp/wp-includes/js/scriptaculous/prototype.js@ 44

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