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