1 /* Prototype JavaScript framework, version 1.5.1
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
9 var Prototype = {
10 Version: '1.5.1',
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
34 var Class = {
35 create: function() {
36 return function() {
37 this.initialize.apply(this, arguments);
38 }
39 }
40 }
41
42 var Abstract = new Object();
43
44 Object.extend = function(destination, source) {
45 for (var property in source) {
46 destination[property] = source[property];
47 }
48 return destination;
49 }
50
51 Object.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
102 Function.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
109 Function.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));
113 }
114 }
115
116 Object.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
140 Date.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
149 var 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
167 var PeriodicalExecuter = Class.create();
168 PeriodicalExecuter.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 }
198 Object.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
212 Object.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
398 if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
399 escapeHTML: function() {
400 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
401 },
402 unescapeHTML: function() {
403 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
404 }
405 });
406
407 String.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
413 String.prototype.parseQuery = String.prototype.toQueryParams;
414
415 Object.extend(String.prototype.escapeHTML, {
416 div: document.createElement('div'),
417 text: document.createTextNode('')
418 });
419
420 with (String.prototype.escapeHTML) div.appendChild(text);
421
422 var Template = Class.create();
423 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
424 Template.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
439 var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
440
441 var Enumerable = {
442 each: function(iterator) {
443 var index = 0;
444 try {
445 this._each(function(value) {
446 iterator(value, index++);
447 });
448 } catch (e) {
449 if (e != $break) throw e;
450 }
451 return this;
452 },
453
454 eachSlice: function(number, iterator) {
455 var index = -number, slices = [], array = this.toArray();
456 while ((index += number) < array.length)
457 slices.push(array.slice(index, index+number));
458 return slices.map(iterator);
459 },
460
461 all: function(iterator) {
462 var result = true;
463 this.each(function(value, index) {
464 result = result && !!(iterator || Prototype.K)(value, index);
465 if (!result) throw $break;
466 });
467 return result;
468 },
469
470 any: function(iterator) {
471 var result = false;
472 this.each(function(value, index) {
473 if (result = !!(iterator || Prototype.K)(value, index))
474 throw $break;
475 });
476 return result;
477 },
478
479 collect: function(iterator) {
480 var results = [];
481 this.each(function(value, index) {
482 results.push((iterator || Prototype.K)(value, index));
483 });
484 return results;
485 },
486
487 detect: function(iterator) {
488 var result;
489 this.each(function(value, index) {
490 if (iterator(value, index)) {
491 result = value;
492 throw $break;
493 }
494 });
495 return result;
496 },
497
498 findAll: function(iterator) {
499 var results = [];
500 this.each(function(value, index) {
501 if (iterator(value, index))
502 results.push(value);
503 });
504 return results;
505 },
506
507 grep: function(pattern, iterator) {
508 var results = [];
509 this.each(function(value, index) {
510 var stringValue = value.toString();
511 if (stringValue.match(pattern))
512 results.push((iterator || Prototype.K)(value, index));
513 })
514 return results;
515 },
516
517 include: function(object) {
518 var found = false;
519 this.each(function(value) {
520 if (value == object) {
521 found = true;
522 throw $break;
523 }
524 });
525 return found;
526 },
527
528 inGroupsOf: function(number, fillWith) {
529 fillWith = fillWith === undefined ? null : fillWith;
530 return this.eachSlice(number, function(slice) {
531 while(slice.length < number) slice.push(fillWith);
532 return slice;
533 });
534 },
535
536 inject: function(memo, iterator) {
537 this.each(function(value, index) {
538 memo = iterator(memo, value, index);
539 });
540 return memo;
541 },
542
543 invoke: function(method) {
544 var args = $A(arguments).slice(1);
545 return this.map(function(value) {
546 return value[method].apply(value, args);
547 });
548 },
549
550 max: function(iterator) {
551 var result;
552 this.each(function(value, index) {
553 value = (iterator || Prototype.K)(value, index);
554 if (result == undefined || value >= result)
555 result = value;
556 });
557 return result;
558 },
559
560 min: function(iterator) {
561 var result;
562 this.each(function(value, index) {
563 value = (iterator || Prototype.K)(value, index);
564 if (result == undefined || value < result)
565 result = value;
566 });
567 return result;
568 },
569
570 partition: function(iterator) {
571 var trues = [], falses = [];
572 this.each(function(value, index) {
573 ((iterator || Prototype.K)(value, index) ?
574 trues : falses).push(value);
575 });
576 return [trues, falses];
577 },
578
579 pluck: function(property) {
580 var results = [];
581 this.each(function(value, index) {
582 results.push(value[property]);
583 });
584 return results;
585 },
586
587 reject: function(iterator) {
588 var results = [];
589 this.each(function(value, index) {
590 if (!iterator(value, index))
591 results.push(value);
592 });
593 return results;
594 },
595
596 sortBy: function(iterator) {
597 return this.map(function(value, index) {
598 return {value: value, criteria: iterator(value, index)};
599 }).sort(function(left, right) {
600 var a = left.criteria, b = right.criteria;
601 return a < b ? -1 : a > b ? 1 : 0;
602 }).pluck('value');
603 },
604
605 toArray: function() {
606 return this.map();
607 },
608
609 zip: function() {
610 var iterator = Prototype.K, args = $A(arguments);
611 if (typeof args.last() == 'function')
612 iterator = args.pop();
613
614 var collections = [this].concat(args).map($A);
615 return this.map(function(value, index) {
616 return iterator(collections.pluck(index));
617 });
618 },
619
620 size: function() {
621 return this.toArray().length;
622 },
623
624 inspect: function() {
625 return '#<Enumerable:' + this.toArray().inspect() + '>';
626 }
627 }
628
629 Object.extend(Enumerable, {
630 map: Enumerable.collect,
631 find: Enumerable.detect,
632 select: Enumerable.findAll,
633 member: Enumerable.include,
634 entries: Enumerable.toArray
635 });
636 var $A = Array.from = function(iterable) {
637 if (!iterable) return [];
638 if (iterable.toArray) {
639 return iterable.toArray();
640 } else {
641 var results = [];
642 for (var i = 0, length = iterable.length; i < length; i++)
643 results.push(iterable[i]);
644 return results;
645 }
646 }
647
648 if (Prototype.Browser.WebKit) {
649 $A = Array.from = function(iterable) {
650 if (!iterable) return [];
651 if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
652 iterable.toArray) {
653 return iterable.toArray();
654 } else {
655 var results = [];
656 for (var i = 0, length = iterable.length; i < length; i++)
657 results.push(iterable[i]);
658 return results;
659 }
660 }
661 }
662
663 Object.extend(Array.prototype, Enumerable);
664
665 if (!Array.prototype._reverse)
666 Array.prototype._reverse = Array.prototype.reverse;
667
668 Object.extend(Array.prototype, {
669 _each: function(iterator) {
670 for (var i = 0, length = this.length; i < length; i++)
671 iterator(this[i]);
672 },
673
674 clear: function() {
675 this.length = 0;
676 return this;
677 },
678
679 first: function() {
680 return this[0];
681 },
682
683 last: function() {
684 return this[this.length - 1];
685 },
686
687 compact: function() {
688 return this.select(function(value) {
689 return value != null;
690 });
691 },
692
693 flatten: function() {
694 return this.inject([], function(array, value) {
695 return array.concat(value && value.constructor == Array ?
696 value.flatten() : [value]);
697 });
698 },
699
700 without: function() {
701 var values = $A(arguments);
702 return this.select(function(value) {
703 return !values.include(value);
704 });
705 },
706
707 indexOf: function(object) {
708 for (var i = 0, length = this.length; i < length; i++)
709 if (this[i] == object) return i;
710 return -1;
711 },
712
713 reverse: function(inline) {
714 return (inline !== false ? this : this.toArray())._reverse();
715 },
716
717 reduce: function() {
718 return this.length > 1 ? this : this[0];
719 },
720
721 uniq: function(sorted) {
722 return this.inject([], function(array, value, index) {
723 if (0 == index || (sorted ? array.last() != value : !array.include(value)))
724 array.push(value);
725 return array;
726 });
727 },
728
729 clone: function() {
730 return [].concat(this);
731 },
732
733 size: function() {
734 return this.length;
735 },
736
737 inspect: function() {
738 return '[' + this.map(Object.inspect).join(', ') + ']';
739 },
740
741 toJSON: function() {
742 var results = [];
743 this.each(function(object) {
744 var value = Object.toJSON(object);
745 if (value !== undefined) results.push(value);
746 });
747 return '[' + results.join(', ') + ']';
748 }
749 });
750
751 Array.prototype.toArray = Array.prototype.clone;
752
753 function $w(string) {
754 string = string.strip();
755 return string ? string.split(/\s+/) : [];
756 }
757
758 if (Prototype.Browser.Opera){
759 Array.prototype.concat = function() {
760 var array = [];
761 for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
762 for (var i = 0, length = arguments.length; i < length; i++) {
763 if (arguments[i].constructor == Array) {
764 for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
765 array.push(arguments[i][j]);
766 } else {
767 array.push(arguments[i]);
768 }
769 }
770 return array;
771 }
772 }
773 var Hash = function(object) {
774 if (object instanceof Hash) this.merge(object);
775 else Object.extend(this, object || {});
776 };
777
778 Object.extend(Hash, {
779 toQueryString: function(obj) {
780 var parts = [];
781 parts.add = arguments.callee.addPair;
782
783 this.prototype._each.call(obj, function(pair) {
784 if (!pair.key) return;
785 var value = pair.value;
786
787 if (value && typeof value == 'object') {
788 if (value.constructor == Array) value.each(function(value) {
789 parts.add(pair.key, value);
790 });
791 return;
792 }
793 parts.add(pair.key, value);
794 });
795
796 return parts.join('&');
797 },
798
799 toJSON: function(object) {
800 var results = [];
801 this.prototype._each.call(object, function(pair) {
802 var value = Object.toJSON(pair.value);
803 if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
804 });
805 return '{' + results.join(', ') + '}';
806 }
807 });
808
809 Hash.toQueryString.addPair = function(key, value, prefix) {
810 key = encodeURIComponent(key);
811 if (value === undefined) this.push(key);
812 else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
813 }
814
815 Object.extend(Hash.prototype, Enumerable);
816 Object.extend(Hash.prototype, {
817 _each: function(iterator) {
818 for (var key in this) {
819 var value = this[key];
820 if (value && value == Hash.prototype[key]) continue;
821
822 var pair = [key, value];
823 pair.key = key;
824 pair.value = value;
825 iterator(pair);
826 }
827 },
828
829 keys: function() {
830 return this.pluck('key');
831 },
832
833 values: function() {
834 return this.pluck('value');
835 },
836
837 merge: function(hash) {
838 return $H(hash).inject(this, function(mergedHash, pair) {
839 mergedHash[pair.key] = pair.value;
840 return mergedHash;
841 });
842 },
843
844 remove: function() {
845 var result;
846 for(var i = 0, length = arguments.length; i < length; i++) {
847 var value = this[arguments[i]];
848 if (value !== undefined){
849 if (result === undefined) result = value;
850 else {
851 if (result.constructor != Array) result = [result];
852 result.push(value)
853 }
854 &ens