1 /* Prototype JavaScript framework, version 1.6.1
2 * (c) 2005-2009 Sam Stephenson
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/
7 *--------------------------------------------------------------------------*/
13 var ua
= navigator
.userAgent
;
14 var isOpera
= Object
.prototype.toString
.call(window
.opera
) == '[object Opera]';
16 IE
: !!window
.attachEvent
&& !isOpera
,
18 WebKit
: ua
.indexOf('AppleWebKit/') > -1,
19 Gecko
: ua
.indexOf('Gecko') > -1 && ua
.indexOf('KHTML') === -1,
20 MobileSafari
: /Apple.*Mobile.*Safari/.test(ua
)
25 XPath
: !!document
.evaluate
,
26 SelectorsAPI
: !!document
.querySelector
,
27 ElementExtensions
: (function() {
28 var constructor = window
.Element
|| window
.HTMLElement
;
29 return !!(constructor && constructor.prototype);
31 SpecificElementExtensions
: (function() {
32 if (typeof window
.HTMLDivElement
!== 'undefined')
35 var div
= document
.createElement('div');
36 var form
= document
.createElement('form');
37 var isSupported
= false;
39 if (div
['__proto__'] && (div
['__proto__'] !== form
['__proto__'])) {
49 ScriptFragment
: '<script[^>]*>([\\S\\s]*?)<\/script>',
50 JSONFilter
: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
52 emptyFunction: function() { },
53 K: function(x
) { return x
}
56 if (Prototype
.Browser
.MobileSafari
)
57 Prototype
.BrowserFeatures
.SpecificElementExtensions
= false;
67 for (var i
= 0, length
= arguments
.length
; i
< length
; i
++) {
68 var lambda
= arguments
[i
];
70 returnValue
= lambda();
79 /* Based on Alex Arnell's inheritance implementation. */
81 var Class
= (function() {
82 function subclass() {};
84 var parent
= null, properties
= $A(arguments
);
85 if (Object
.isFunction(properties
[0]))
86 parent
= properties
.shift();
89 this.initialize
.apply(this, arguments
);
92 Object
.extend(klass
, Class
.Methods
);
93 klass
.superclass
= parent
;
94 klass
.subclasses
= [];
97 subclass
.prototype = parent
.prototype;
98 klass
.prototype = new subclass
;
99 parent
.subclasses
.push(klass
);
102 for (var i
= 0; i
< properties
.length
; i
++)
103 klass
.addMethods(properties
[i
]);
105 if (!klass
.prototype.initialize
)
106 klass
.prototype.initialize
= Prototype
.emptyFunction
;
108 klass
.prototype.constructor = klass
;
112 function addMethods(source
) {
113 var ancestor
= this.superclass
&& this.superclass
.prototype;
114 var properties
= Object
.keys(source
);
116 if (!Object
.keys({ toString
: true }).length
) {
117 if (source
.toString
!= Object
.prototype.toString
)
118 properties
.push("toString");
119 if (source
.valueOf
!= Object
.prototype.valueOf
)
120 properties
.push("valueOf");
123 for (var i
= 0, length
= properties
.length
; i
< length
; i
++) {
124 var property
= properties
[i
], value
= source
[property
];
125 if (ancestor
&& Object
.isFunction(value
) &&
126 value
.argumentNames().first() == "$super") {
128 value
= (function(m
) {
129 return function() { return ancestor
[m
].apply(this, arguments
); };
130 })(property
).wrap(method
);
132 value
.valueOf
= method
.valueOf
.bind(method
);
133 value
.toString
= method
.toString
.bind(method
);
135 this.prototype[property
] = value
;
144 addMethods
: addMethods
150 var _toString
= Object
.prototype.toString
;
152 function extend(destination
, source
) {
153 for (var property
in source
)
154 destination
[property
] = source
[property
];
158 function inspect(object
) {
160 if (isUndefined(object
)) return 'undefined';
161 if (object
=== null) return 'null';
162 return object
.inspect
? object
.inspect() : String(object
);
164 if (e
instanceof RangeError
) return '...';
169 function toJSON(object
) {
170 var type
= typeof object
;
174 case 'unknown': return;
175 case 'boolean': return object
.toString();
178 if (object
=== null) return 'null';
179 if (object
.toJSON
) return object
.toJSON();
180 if (isElement(object
)) return;
183 for (var property
in object
) {
184 var value
= toJSON(object
[property
]);
185 if (!isUndefined(value
))
186 results
.push(property
.toJSON() + ': ' + value
);
189 return '{' + results
.join(', ') + '}';
192 function toQueryString(object
) {
193 return $H(object
).toQueryString();
196 function toHTML(object
) {
197 return object
&& object
.toHTML
? object
.toHTML() : String
.interpret(object
);
200 function keys(object
) {
202 for (var property
in object
)
203 results
.push(property
);
207 function values(object
) {
209 for (var property
in object
)
210 results
.push(object
[property
]);
214 function clone(object
) {
215 return extend({ }, object
);
218 function isElement(object
) {
219 return !!(object
&& object
.nodeType
== 1);
222 function isArray(object
) {
223 return _toString
.call(object
) == "[object Array]";
227 function isHash(object
) {
228 return object
instanceof Hash
;
231 function isFunction(object
) {
232 return typeof object
=== "function";
235 function isString(object
) {
236 return _toString
.call(object
) == "[object String]";
239 function isNumber(object
) {
240 return _toString
.call(object
) == "[object Number]";
243 function isUndefined(object
) {
244 return typeof object
=== "undefined";
251 toQueryString
: toQueryString
,
256 isElement
: isElement
,
259 isFunction
: isFunction
,
262 isUndefined
: isUndefined
265 Object
.extend(Function
.prototype, (function() {
266 var slice
= Array
.prototype.slice
;
268 function update(array
, args
) {
269 var arrayLength
= array
.length
, length
= args
.length
;
270 while (length
--) array
[arrayLength
+ length
] = args
[length
];
274 function merge(array
, args
) {
275 array
= slice
.call(array
, 0);
276 return update(array
, args
);
279 function argumentNames() {
280 var names
= this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
281 .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
282 .replace(/\s+/g, '').split(',');
283 return names
.length
== 1 && !names
[0] ? [] : names
;
286 function bind(context
) {
287 if (arguments
.length
< 2 && Object
.isUndefined(arguments
[0])) return this;
288 var __method
= this, args
= slice
.call(arguments
, 1);
290 var a
= merge(args
, arguments
);
291 return __method
.apply(context
, a
);
295 function bindAsEventListener(context
) {
296 var __method
= this, args
= slice
.call(arguments
, 1);
297 return function(event
) {
298 var a
= update([event
|| window
.event
], args
);
299 return __method
.apply(context
, a
);
304 if (!arguments
.length
) return this;
305 var __method
= this, args
= slice
.call(arguments
, 0);
307 var a
= merge(args
, arguments
);
308 return __method
.apply(this, a
);
312 function delay(timeout
) {
313 var __method
= this, args
= slice
.call(arguments
, 1);
314 timeout
= timeout
* 1000
315 return window
.setTimeout(function() {
316 return __method
.apply(__method
, args
);
321 var args
= update([0.01], arguments
);
322 return this.delay
.apply(this, args
);
325 function wrap(wrapper
) {
328 var a
= update([__method
.bind(this)], arguments
);
329 return wrapper
.apply(this, a
);
333 function methodize() {
334 if (this._methodized
) return this._methodized
;
336 return this._methodized = function() {
337 var a
= update([this], arguments
);
338 return __method
.apply(null, a
);
343 argumentNames
: argumentNames
,
345 bindAsEventListener
: bindAsEventListener
,
355 Date
.prototype.toJSON = function() {
356 return '"' + this.getUTCFullYear() + '-' +
357 (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
358 this.getUTCDate().toPaddedString(2) + 'T' +
359 this.getUTCHours().toPaddedString(2) + ':' +
360 this.getUTCMinutes().toPaddedString(2) + ':' +
361 this.getUTCSeconds().toPaddedString(2) + 'Z"';
365 RegExp
.prototype.match
= RegExp
.prototype.test
;
367 RegExp
.escape = function(str
) {
368 return String(str
).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
370 var PeriodicalExecuter
= Class
.create({
371 initialize: function(callback
, frequency
) {
372 this.callback
= callback
;
373 this.frequency
= frequency
;
374 this.currentlyExecuting
= false;
376 this.registerCallback();
379 registerCallback: function() {
380 this.timer
= setInterval(this.onTimerEvent
.bind(this), this.frequency
* 1000);
383 execute: function() {
388 if (!this.timer
) return;
389 clearInterval(this.timer
);
393 onTimerEvent: function() {
394 if (!this.currentlyExecuting
) {
396 this.currentlyExecuting
= true;
398 this.currentlyExecuting
= false;
400 this.currentlyExecuting
= false;
406 Object
.extend(String
, {
407 interpret: function(value
) {
408 return value
== null ? '' : String(value
);
420 Object
.extend(String
.prototype, (function() {
422 function prepareReplacement(replacement
) {
423 if (Object
.isFunction(replacement
)) return replacement
;
424 var template
= new Template(replacement
);
425 return function(match
) { return template
.evaluate(match
) };
428 function gsub(pattern
, replacement
) {
429 var result
= '', source
= this, match
;
430 replacement
= prepareReplacement(replacement
);
432 if (Object
.isString(pattern
))
433 pattern
= RegExp
.escape(pattern
);
435 if (!(pattern
.length
|| pattern
.source
)) {
436 replacement
= replacement('');
437 return replacement
+ source
.split('').join(replacement
) + replacement
;
440 while (source
.length
> 0) {
441 if (match
= source
.match(pattern
)) {
442 result
+= source
.slice(0, match
.index
);
443 result
+= String
.interpret(replacement(match
));
444 source
= source
.slice(match
.index
+ match
[0].length
);
446 result
+= source
, source
= '';
452 function sub(pattern
, replacement
, count
) {
453 replacement
= prepareReplacement(replacement
);
454 count
= Object
.isUndefined(count
) ? 1 : count
;
456 return this.gsub(pattern
, function(match
) {
457 if (--count
< 0) return match
[0];
458 return replacement(match
);
462 function scan(pattern
, iterator
) {
463 this.gsub(pattern
, iterator
);
467 function truncate(length
, truncation
) {
468 length
= length
|| 30;
469 truncation
= Object
.isUndefined(truncation
) ? '...' : truncation
;
470 return this.length
> length
?
471 this.slice(0, length
- truncation
.length
) + truncation
: String(this);
475 return this.replace(/^\s+/, '').replace(/\s+$/, '');
478 function stripTags() {
479 return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
482 function stripScripts() {
483 return this.replace(new RegExp(Prototype
.ScriptFragment
, 'img'), '');
486 function extractScripts() {
487 var matchAll
= new RegExp(Prototype
.ScriptFragment
, 'img');
488 var matchOne
= new RegExp(Prototype
.ScriptFragment
, 'im');
489 return (this.match(matchAll
) || []).map(function(scriptTag
) {
490 return (scriptTag
.match(matchOne
) || ['', ''])[1];
494 function evalScripts() {
495 return this.extractScripts().map(function(script
) { return eval(script
) });
498 function escapeHTML() {
499 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g
,'>');
502 function unescapeHTML() {
503 return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');
507 function toQueryParams(separator
) {
508 var match
= this.strip().match(/([^?#]*)(#.*)?$/);
509 if (!match
) return { };
511 return match
[1].split(separator
|| '&').inject({ }, function(hash
, pair
) {
512 if ((pair
= pair
.split('='))[0]) {
513 var key
= decodeURIComponent(pair
.shift());
514 var value
= pair
.length
> 1 ? pair
.join('=') : pair
[0];
515 if (value
!= undefined) value
= decodeURIComponent(value
);
518 if (!Object
.isArray(hash
[key
])) hash
[key
] = [hash
[key
]];
519 hash
[key
].push(value
);
521 else hash
[key
] = value
;
528 return this.split('');
532 return this.slice(0, this.length
- 1) +
533 String
.fromCharCode(this.charCodeAt(this.length
- 1) + 1);
536 function times(count
) {
537 return count
< 1 ? '' : new Array(count
+ 1).join(this);
540 function camelize() {
541 var parts
= this.split('-'), len
= parts
.length
;
542 if (len
== 1) return parts
[0];
544 var camelized
= this.charAt(0) == '-'
545 ? parts
[0].charAt(0).toUpperCase() + parts
[0].substring(1)
548 for (var i
= 1; i
< len
; i
++)
549 camelized
+= parts
[i
].charAt(0).toUpperCase() + parts
[i
].substring(1);
554 function capitalize() {
555 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
558 function underscore() {
559 return this.replace(/::/g, '/')
560 .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
561 .replace(/([a-z\d])([A-Z])/g, '$1_$2')
566 function dasherize() {
567 return this.replace(/_
/g
, '-');
570 function inspect(useDoubleQuotes
) {
571 var escapedString
= this.replace(/[\x00-\x1f\\]/g, function(character
) {
572 if (character
in String
.specialChar
) {
573 return String
.specialChar
[character
];
575 return '\\u00' + character
.charCodeAt().toPaddedString(2, 16);
577 if (useDoubleQuotes
) return '"' + escapedString
.replace(/"/g, '\\"') + '"';
578 return "'" + escapedString.replace(/'/g
, '\\\'') + "'";
582 return this.inspect(true);
585 function unfilterJSON(filter
) {
586 return this.replace(filter
|| Prototype
.JSONFilter
, '$1');
591 if (str
.blank()) return false;
592 str
= this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
593 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str
);
596 function evalJSON(sanitize
) {
597 var json
= this.unfilterJSON();
599 if (!sanitize
|| json
.isJSON()) return eval('(' + json
+ ')');
601 throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
604 function include(pattern
) {
605 return this.indexOf(pattern
) > -1;
608 function startsWith(pattern
) {
609 return this.indexOf(pattern
) === 0;
612 function endsWith(pattern
) {
613 var d
= this.length
- pattern
.length
;
614 return d
>= 0 && this.lastIndexOf(pattern
) === d
;
622 return /^\s*$/.test(this);
625 function interpolate(object
, pattern
) {
626 return new Template(this, pattern
).evaluate(object
);
634 strip
: String
.prototype.trim
? String
.prototype.trim
: strip
,
635 stripTags
: stripTags
,
636 stripScripts
: stripScripts
,
637 extractScripts
: extractScripts
,
638 evalScripts
: evalScripts
,
639 escapeHTML
: escapeHTML
,
640 unescapeHTML
: unescapeHTML
,
641 toQueryParams
: toQueryParams
,
642 parseQuery
: toQueryParams
,
647 capitalize
: capitalize
,
648 underscore
: underscore
,
649 dasherize
: dasherize
,
652 unfilterJSON
: unfilterJSON
,
656 startsWith
: startsWith
,
660 interpolate
: interpolate
664 var Template
= Class
.create({
665 initialize: function(template
, pattern
) {
666 this.template
= template
.toString();
667 this.pattern
= pattern
|| Template
.Pattern
;
670 evaluate: function(object
) {
671 if (object
&& Object
.isFunction(object
.toTemplateReplacements
))
672 object
= object
.toTemplateReplacements();
674 return this.template
.gsub(this.pattern
, function(match
) {
675 if (object
== null) return (match
[1] + '');
677 var before
= match
[1] || '';
678 if (before
== '\\') return match
[2];
680 var ctx
= object
, expr
= match
[3];
681 var pattern
= /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
682 match
= pattern
.exec(expr
);
683 if (match
== null) return before
;
685 while (match
!= null) {
686 var comp
= match
[1].startsWith('[') ? match
[2].replace(/\\\\]/g, ']') : match
[1];
688 if (null == ctx
|| '' == match
[3]) break;
689 expr
= expr
.substring('[' == match
[3] ? match
[1].length
: match
[0].length
);
690 match
= pattern
.exec(expr
);
693 return before
+ String
.interpret(ctx
);
697 Template
.Pattern
= /(^|.|\r|\n)(#\{(.*?)\})/;
701 var Enumerable
= (function() {
702 function each(iterator
, context
) {
705 this._each(function(value
) {
706 iterator
.call(context
, value
, index
++);
709 if (e
!= $break) throw e
;
714 function eachSlice(number
, iterator
, context
) {
715 var index
= -number
, slices
= [], array
= this.toArray();
716 if (number
< 1) return array
;
717 while ((index
+= number
) < array
.length
)
718 slices
.push(array
.slice(index
, index
+number
));
719 return slices
.collect(iterator
, context
);
722 function all(iterator
, context
) {
723 iterator
= iterator
|| Prototype
.K
;
725 this.each(function(value
, index
) {
726 result
= result
&& !!iterator
.call(context
, value
, index
);
727 if (!result
) throw $break;
732 function any(iterator
, context
) {
733 iterator
= iterator
|| Prototype
.K
;
735 this.each(function(value
, index
) {
736 if (result
= !!iterator
.call(context
, value
, index
))
742 function collect(iterator
, context
) {
743 iterator
= iterator
|| Prototype
.K
;
745 this.each(function(value
, index
) {
746 results
.push(iterator
.call(context
, value
, index
));
751 function detect(iterator
, context
) {
753 this.each(function(value
, index
) {
754 if (iterator
.call(context
, value
, index
)) {
762 function findAll(iterator
, context
) {
764 this.each(function(value
, index
) {
765 if (iterator
.call(context
, value
, index
))
771 function grep(filter
, iterator
, context
) {
772 iterator
= iterator
|| Prototype
.K
;
775 if (Object
.isString(filter
))
776 filter
= new RegExp(RegExp
.escape(filter
));
778 this.each(function(value
, index
) {
779 if (filter
.match(value
))
780 results
.push(iterator
.call(context
, value
, index
));
785 function include(object
) {
786 if (Object
.isFunction(this.indexOf
))
787 if (this.indexOf(object
) != -1) return true;
790 this.each(function(value
) {
791 if (value
== object
) {
799 function inGroupsOf(number
, fillWith
) {
800 fillWith
= Object
.isUndefined(fillWith
) ? null : fillWith
;
801 return this.eachSlice(number
, function(slice
) {
802 while(slice
.length
< number
) slice
.push(fillWith
);
807 function inject(memo
, iterator
, context
) {
808 this.each(function(value
, index
) {
809 memo
= iterator
.call(context
, memo
, value
, index
);
814 function invoke(method
) {
815 var args
= $A(arguments
).slice(1);
816 return this.map(function(value
) {
817 return value
[method
].apply(value
, args
);
821 function max(iterator
, context
) {
822 iterator
= iterator
|| Prototype
.K
;
824 this.each(function(value
, index
) {
825 value
= iterator
.call(context
, value
, index
);
826 if (result
== null || value
>= result
)
832 function min(iterator
, context
) {
833 iterator
= iterator
|| Prototype
.K
;
835 this.each(function(value
, index
) {
836 value
= iterator
.call(context
, value
, index
);
837 if (result
== null || value
< result
)
843 function partition(iterator
, context
) {
844 iterator
= iterator
|| Prototype
.K
;
845 var trues
= [], falses
= [];
846 this.each(function(value
, index
) {
847 (iterator
.call(context
, value
, index
) ?
848 trues
: falses
).push(value
);
850 return [trues
, falses
];
853 function pluck(property
) {
855 this.each(function(value
) {
856 results
.push(value
[property
]);
861 function reject(iterator
, context
) {
863 this.each(function(value
, index
) {
864 if (!iterator
.call(context
, value
, index
))
870 function sortBy(iterator
, context
) {
871 return this.map(function(value
, index
) {
874 criteria
: iterator
.call(context
, value
, index
)
876 }).sort(function(left
, right
) {
877 var a
= left
.criteria
, b
= right
.criteria
;
878 return a
< b
? -1 : a
> b
? 1 : 0;
887 var iterator
= Prototype
.K
, args
= $A(arguments
);
888 if (Object
.isFunction(args
.last()))
889 iterator
= args
.pop();
891 var collections
= [this].concat(args
).map($A
);
892 return this.map(function(value
, index
) {
893 return iterator(collections
.pluck(index
));
898 return this.toArray().length
;
902 return '#<Enumerable:' + this.toArray().inspect() + '>';
915 eachSlice
: eachSlice
,
929 inGroupsOf
: inGroupsOf
,
934 partition
: partition
,
946 function $A(iterable
) {
947 if (!iterable
) return [];
948 if ('toArray' in Object(iterable
)) return iterable
.toArray();
949 var length
= iterable
.length
|| 0, results
= new Array(length
);
950 while (length
--) results
[length
] = iterable
[length
];
954 function $w(string
) {
955 if (!Object
.isString(string
)) return [];
956 string
= string
.strip();
957 return string
? string
.split(/\s+/) : [];
964 var arrayProto
= Array
.prototype,
965 slice
= arrayProto
.slice
,
966 _each
= arrayProto
.forEach
; // use native browser JS 1.6 implementation if available
968 function each(iterator
) {
969 for (var i
= 0, length
= this.length
; i
< length
; i
++)
972 if (!_each
) _each
= each
;
984 return this[this.length
- 1];
988 return this.select(function(value
) {
989 return value
!= null;
994 return this.inject([], function(array
, value
) {
995 if (Object
.isArray(value
))
996 return array
.concat(value
.flatten());
1002 function without() {
1003 var values
= slice
.call(arguments
, 0);
1004 return this.select(function(value
) {
1005 return !values
.include(value
);
1009 function reverse(inline
) {
1010 return (inline
!== false ? this : this.toArray())._reverse();
1013 function uniq(sorted
) {
1014 return this.inject([], function(array
, value
, index
) {
1015 if (0 == index
|| (sorted
? array
.last() != value
: !array
.include(value
)))
1021 function intersect(array
) {
1022 return this.uniq().findAll(function(item
) {
1023 return array
.detect(function(value
) { return item
=== value
});
1029 return slice
.call(this, 0);
1036 function inspect() {
1037 return '[' + this.map(Object
.inspect
).join(', ') + ']';
1042 this.each(function(object
) {
1043 var value
= Object
.toJSON(object
);
1044 if (!Object
.isUndefined(value
)) results
.push(value
);
1046 return '[' + results
.join(', ') + ']';
1049 function indexOf(item
, i
) {
1051 var length
= this.length
;
1052 if (i
< 0) i
= length
+ i
;
1053 for (; i
< length
; i
++)
1054 if (this[i
] === item
) return i
;
1058 function lastIndexOf(item
, i
) {
1059 i
= isNaN(i
) ? this.length
: (i
< 0 ? this.length
+ i
: i
) + 1;
1060 var n
= this.slice(0, i
).reverse().indexOf(item
);
1061 return (n
< 0) ? n
: i
- n
- 1;
1065 var array
= slice
.call(this, 0), item
;
1066 for (var i
= 0, length
= arguments
.length
; i
< length
; i
++) {
1067 item
= arguments
[i
];
1068 if (Object
.isArray(item
) && !('callee' in item
)) {
1069 for (var j
= 0, arrayLength
= item
.length
; j
< arrayLength
; j
++)
1070 array
.push(item
[j
]);
1078 Object
.extend(arrayProto
, Enumerable
);
1080 if (!arrayProto
._reverse
)
1081 arrayProto
._reverse
= arrayProto
.reverse
;
1083 Object
.extend(arrayProto
, {
1093 intersect
: intersect
,
1101 var CONCAT_ARGUMENTS_BUGGY
= (function() {
1102 return [].concat(arguments
)[0][0] !== 1;
1105 if (CONCAT_ARGUMENTS_BUGGY
) arrayProto
.concat
= concat
;
1107 if (!arrayProto
.indexOf
) arrayProto
.indexOf
= indexOf
;
1108 if (!arrayProto
.lastIndexOf
) arrayProto
.lastIndexOf
= lastIndexOf
;
1110 function $H(object
) {
1111 return new Hash(object
);
1114 var Hash
= Class
.create(Enumerable
, (function() {
1115 function initialize(object
) {
1116 this._object
= Object
.isHash(object
) ? object
.toObject() : Object
.clone(object
);
1119 function _each(iterator
) {
1120 for (var key
in this._object
) {
1121 var value
= this._object
[key
], pair
= [key
, value
];
1128 function set(key
, value
) {
1129 return this._object
[key
] = value
;
1133 if (this._object
[key
] !== Object
.prototype[key
])
1134 return this._object
[key
];
1137 function unset(key
) {
1138 var value
= this._object
[key
];
1139 delete this._object
[key
];
1143 function toObject() {
1144 return Object
.clone(this._object
);
1148 return this.pluck('key');
1152 return this.pluck('value');
1155 function index(value
) {
1156 var match
= this.detect(function(pair
) {
1157 return pair
.value
=== value
;
1159 return match
&& match
.key
;
1162 function merge(object
) {
1163 return this.clone().update(object
);
1166 function update(object
) {
1167 return new Hash(object
).inject(this, function(result
, pair
) {
1168 result
.set(pair
.key
, pair
.value
);
1173 function toQueryPair(key
, value
) {
1174 if (Object
.isUndefined(value
)) return key
;
1175 return key
+ '=' + encodeURIComponent(String
.interpret(value
));
1178 function toQueryString() {
1179 return this.inject([], function(results
, pair
) {
1180 var key
= encodeURIComponent(pair
.key
), values
= pair
.value
;
1182 if (values
&& typeof values
== 'object') {
1183 if (Object
.isArray(values
))
1184 return results
.concat(values
.map(toQueryPair
.curry(key
)));
1185 } else results
.push(toQueryPair(key
, values
));
1190 function inspect() {
1191 return '#<Hash:{' + this.map(function(pair
) {
1192 return pair
.map(Object
.inspect
).join(': ');
1193 }).join(', ') + '}>';
1197 return Object
.toJSON(this.toObject());
1201 return new Hash(this);
1205 initialize
: initialize
,
1211 toTemplateReplacements
: toObject
,
1217 toQueryString
: toQueryString
,
1225 Object
.extend(Number
.prototype, (function() {
1226 function toColorPart() {
1227 return this.toPaddedString(2, 16);
1234 function times(iterator
, context
) {
1235 $R(0, this, true).each(iterator
, context
);
1239 function toPaddedString(length
, radix
) {
1240 var string
= this.toString(radix
|| 10);
1241 return '0'.times(length
- string
.length
) + string
;
1245 return isFinite(this) ? this.toString() : 'null';
1249 return Math
.abs(this);
1253 return Math
.round(this);
1257 return Math
.ceil(this);
1261 return Math
.floor(this);
1265 toColorPart
: toColorPart
,
1268 toPaddedString
: toPaddedString
,
1277 function $R(start
, end
, exclusive
) {
1278 return new ObjectRange(start
, end
, exclusive
);
1281 var ObjectRange
= Class
.create(Enumerable
, (function() {
1282 function initialize(start
, end
, exclusive
) {
1285 this.exclusive
= exclusive
;
1288 function _each(iterator
) {
1289 var value
= this.start
;
1290 while (this.include(value
)) {
1292 value
= value
.succ();
1296 function include(value
) {
1297 if (value
< this.start
)
1300 return value
< this.end
;
1301 return value
<= this.end
;
1305 initialize
: initialize
,
1314 getTransport: function() {
1316 function() {return new XMLHttpRequest()},
1317 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1318 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1322 activeRequestCount
: 0
1328 _each: function(iterator
) {
1329 this.responders
._each(iterator
);
1332 register: function(responder
) {
1333 if (!this.include(responder
))
1334 this.responders
.push(responder
);
1337 unregister: function(responder
) {
1338 this.responders
= this.responders
.without(responder
);
1341 dispatch: function(callback
, request
, transport
, json
) {
1342 this.each(function(responder
) {
1343 if (Object
.isFunction(responder
[callback
])) {
1345 responder
[callback
].apply(responder
, [request
, transport
, json
]);
1352 Object
.extend(Ajax
.Responders
, Enumerable
);
1354 Ajax
.Responders
.register({
1355 onCreate: function() { Ajax
.activeRequestCount
++ },
1356 onComplete: function() { Ajax
.activeRequestCount
-- }
1358 Ajax
.Base
= Class
.create({
1359 initialize: function(options
) {
1363 contentType
: 'application/x-www-form-urlencoded',
1369 Object
.extend(this.options
, options
|| { });
1371 this.options
.method
= this.options
.method
.toLowerCase();
1373 if (Object
.isString(this.options
.parameters
))
1374 this.options
.parameters
= this.options
.parameters
.toQueryParams();
1375 else if (Object
.isHash(this.options
.parameters
))
1376 this.options
.parameters
= this.options
.parameters
.toObject();
1379 Ajax
.Request
= Class
.create(Ajax
.Base
, {
1382 initialize: function($super, url
, options
) {
1384 this.transport
= Ajax
.getTransport();
1388 request: function(url
) {
1390 this.method
= this.options
.method
;
1391 var params
= Object
.clone(this.options
.parameters
);
1393 if (!['get', 'post'].include(this.method
)) {
1394 params
['_method'] = this.method
;
1395 this.method
= 'post';
1398 this.parameters
= params
;
1400 if (params
= Object
.toQueryString(params
)) {
1401 if (this.method
== 'get')
1402 this.url
+= (this.url
.include('?') ? '&' : '?') + params
;
1403 else if (/Konqueror|Safari|KHTML/.test(navigator
.userAgent
))
1408 var response
= new Ajax
.Response(this);
1409 if (this.options
.onCreate
) this.options
.onCreate(response
);
1410 Ajax
.Responders
.dispatch('onCreate', this, response
);
1412 this.transport
.open(this.method
.toUpperCase(), this.url
,
1413 this.options
.asynchronous
);
1415 if (this.options
.asynchronous
) this.respondToReadyState
.bind(this).defer(1);
1417 this.transport
.onreadystatechange
= this.onStateChange
.bind(this);
1418 this.setRequestHeaders();
1420 this.body
= this.method
== 'post' ? (this.options
.postBody
|| params
) : null;
1421 this.transport
.send(this.body
);
1423 /* Force Firefox to handle ready state 4 for synchronous requests */
1424 if (!this.options
.asynchronous
&& this.transport
.overrideMimeType
)
1425 this.onStateChange();
1429 this.dispatchException(e
);
1433 onStateChange: function() {
1434 var readyState
= this.transport
.readyState
;
1435 if (readyState
> 1 && !((readyState
== 4) && this._complete
))
1436 this.respondToReadyState(this.transport
.readyState
);
1439 setRequestHeaders: function() {
1441 'X-Requested-With': 'XMLHttpRequest',
1442 'X-Prototype-Version': Prototype
.Version
,
1443 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1446 if (this.method
== 'post') {
1447 headers
['Content-type'] = this.options
.contentType
+
1448 (this.options
.encoding
? '; charset=' + this.options
.encoding
: '');
1450 /* Force "Connection: close" for older Mozilla browsers to work
1451 * around a bug where XMLHttpRequest sends an incorrect
1452 * Content-length header. See Mozilla Bugzilla #246651.
1454 if (this.transport
.overrideMimeType
&&
1455 (navigator
.userAgent
.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1456 headers
['Connection'] = 'close';
1459 if (typeof this.options
.requestHeaders
== 'object') {
1460 var extras
= this.options
.requestHeaders
;
1462 if (Object
.isFunction(extras
.push
))
1463 for (var i
= 0, length
= extras
.length
; i
< length
; i
+= 2)
1464 headers
[extras
[i
]] = extras
[i
+1];
1466 $H(extras
).each(function(pair
) { headers
[pair
.key
] = pair
.value
});
1469 for (var name
in headers
)
1470 this.transport
.setRequestHeader(name
, headers
[name
]);
1473 success: function() {
1474 var status
= this.getStatus();
1475 return !status
|| (status
>= 200 && status
< 300);
1478 getStatus: function() {
1480 return this.transport
.status
|| 0;
1481 } catch (e
) { return 0 }
1484 respondToReadyState: function(readyState
) {
1485 var state
= Ajax
.Request
.Events
[readyState
], response
= new Ajax
.Response(this);
1487 if (state
== 'Complete') {
1489 this._complete
= true;
1490 (this.options
['on' + response
.status
]
1491 || this.options
['on' + (this.success() ? 'Success' : 'Failure')]
1492 || Prototype
.emptyFunction
)(response
, response
.headerJSON
);
1494 this.dispatchException(e
);
1497 var contentType
= response
.getHeader('Content-type');
1498 if (this.options
.evalJS
== 'force'
1499 || (this.options
.evalJS
&& this.isSameOrigin() && contentType
1500 && contentType
.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1501 this.evalResponse();
1505 (this.options
['on' + state
] || Prototype
.emptyFunction
)(response
, response
.headerJSON
);
1506 Ajax
.Responders
.dispatch('on' + state
, this, response
, response
.headerJSON
);
1508 this.dispatchException(e
);
1511 if (state
== 'Complete') {
1512 this.transport
.onreadystatechange
= Prototype
.emptyFunction
;
1516 isSameOrigin: function() {
1517 var m
= this.url
.match(/^\s*https?:\/\/[^\/]*/);
1518 return !m
|| (m
[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1519 protocol
: location
.protocol
,
1520 domain
: document
.domain
,
1521 port
: location
.port
? ':' + location
.port
: ''
1525 getHeader: function(name
) {
1527 return this.transport
.getResponseHeader(name
) || null;
1528 } catch (e
) { return null; }
1531 evalResponse: function() {
1533 return eval((this.transport
.responseText
|| '').unfilterJSON());
1535 this.dispatchException(e
);
1539 dispatchException: function(exception
) {
1540 (this.options
.onException
|| Prototype
.emptyFunction
)(this, exception
);
1541 Ajax
.Responders
.dispatch('onException', this, exception
);
1545 Ajax
.Request
.Events
=
1546 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1555 Ajax
.Response
= Class
.create({
1556 initialize: function(request
){
1557 this.request
= request
;
1558 var transport
= this.transport
= request
.transport
,
1559 readyState
= this.readyState
= transport
.readyState
;
1561 if((readyState
> 2 && !Prototype
.Browser
.IE
) || readyState
== 4) {
1562 this.status
= this.getStatus();
1563 this.statusText
= this.getStatusText();
1564 this.responseText
= String
.interpret(transport
.responseText
);
1565 this.headerJSON
= this._getHeaderJSON();
1568 if(readyState
== 4) {
1569 var xml
= transport
.responseXML
;
1570 this.responseXML
= Object
.isUndefined(xml
) ? null : xml
;
1571 this.responseJSON
= this._getResponseJSON();
1579 getStatus
: Ajax
.Request
.prototype.getStatus
,
1581 getStatusText: function() {
1583 return this.transport
.statusText
|| '';
1584 } catch (e
) { return '' }
1587 getHeader
: Ajax
.Request
.prototype.getHeader
,
1589 getAllHeaders: function() {
1591 return this.getAllResponseHeaders();
1592 } catch (e
) { return null }
1595 getResponseHeader: function(name
) {
1596 return this.transport
.getResponseHeader(name
);
1599 getAllResponseHeaders: function() {
1600 return this.transport
.getAllResponseHeaders();
1603 _getHeaderJSON: function() {
1604 var json
= this.getHeader('X-JSON');
1605 if (!json
) return null;
1606 json
= decodeURIComponent(escape(json
));
1608 return json
.evalJSON(this.request
.options
.sanitizeJSON
||
1609 !this.request
.isSameOrigin());
1611 this.request
.dispatchException(e
);
1615 _getResponseJSON: function() {
1616 var options
= this.request
.options
;
1617 if (!options
.evalJSON
|| (options
.evalJSON
!= 'force' &&
1618 !(this.getHeader('Content-type') || '').include('application/json')) ||
1619 this.responseText
.blank())
1622 return this.responseText
.evalJSON(options
.sanitizeJSON
||
1623 !this.request
.isSameOrigin());
1625 this.request
.dispatchException(e
);
1630 Ajax
.Updater
= Class
.create(Ajax
.Request
, {
1631 initialize: function($super, container
, url
, options
) {
1633 success
: (container
.success
|| container
),
1634 failure
: (container
.failure
|| (container
.success
? null : container
))
1637 options
= Object
.clone(options
);
1638 var onComplete
= options
.onComplete
;
1639 options
.onComplete
= (function(response
, json
) {
1640 this.updateContent(response
.responseText
);
1641 if (Object
.isFunction(onComplete
)) onComplete(response
, json
);
1644 $super(url
, options
);
1647 updateContent: function(responseText
) {
1648 var receiver
= this.container
[this.success() ? 'success' : 'failure'],
1649 options
= this.options
;
1651 if (!options
.evalScripts
) responseText
= responseText
.stripScripts();
1653 if (receiver
= $(receiver
)) {
1654 if (options
.insertion
) {
1655 if (Object
.isString(options
.insertion
)) {
1656 var insertion
= { }; insertion
[options
.insertion
] = responseText
;
1657 receiver
.insert(insertion
);
1659 else options
.insertion(receiver
, responseText
);
1661 else receiver
.update(responseText
);
1666 Ajax
.PeriodicalUpdater
= Class
.create(Ajax
.Base
, {
1667 initialize: function($super, container
, url
, options
) {
1669 this.onComplete
= this.options
.onComplete
;
1671 this.frequency
= (this.options
.frequency
|| 2);
1672 this.decay
= (this.options
.decay
|| 1);
1675 this.container
= container
;
1682 this.options
.onComplete
= this.updateComplete
.bind(this);
1683 this.onTimerEvent();
1687 this.updater
.options
.onComplete
= undefined;
1688 clearTimeout(this.timer
);
1689 (this.onComplete
|| Prototype
.emptyFunction
).apply(this, arguments
);
1692 updateComplete: function(response
) {
1693 if (this.options
.decay
) {
1694 this.decay
= (response
.responseText
== this.lastText
?
1695 this.decay
* this.options
.decay
: 1);
1697 this.lastText
= response
.responseText
;
1699 this.timer
= this.onTimerEvent
.bind(this).delay(this.decay
* this.frequency
);
1702 onTimerEvent: function() {
1703 this.updater
= new Ajax
.Updater(this.container
, this.url
, this.options
);
1709 function $(element
) {
1710 if (arguments
.length
> 1) {
1711 for (var i
= 0, elements
= [], length
= arguments
.length
; i
< length
; i
++)
1712 elements
.push($(arguments
[i
]));
1715 if (Object
.isString(element
))
1716 element
= document
.getElementById(element
);
1717 return Element
.extend(element
);
1720 if (Prototype
.BrowserFeatures
.XPath
) {
1721 document
._getElementsByXPath = function(expression
, parentElement
) {
1723 var query
= document
.evaluate(expression
, $(parentElement
) || document
,
1724 null, XPathResult
.ORDERED_NODE_SNAPSHOT_TYPE
, null);
1725 for (var i
= 0, length
= query
.snapshotLength
; i
< length
; i
++)
1726 results
.push(Element
.extend(query
.snapshotItem(i
)));
1731 /*--------------------------------------------------------------------------*/
1733 if (!window
.Node
) var Node
= { };
1735 if (!Node
.ELEMENT_NODE
) {
1736 Object
.extend(Node
, {
1740 CDATA_SECTION_NODE
: 4,
1741 ENTITY_REFERENCE_NODE
: 5,
1743 PROCESSING_INSTRUCTION_NODE
: 7,
1746 DOCUMENT_TYPE_NODE
: 10,
1747 DOCUMENT_FRAGMENT_NODE
: 11,
1755 var SETATTRIBUTE_IGNORES_NAME
= (function(){
1756 var elForm
= document
.createElement("form");
1757 var elInput
= document
.createElement("input");
1758 var root
= document
.documentElement
;
1759 elInput
.setAttribute("name", "test");
1760 elForm
.appendChild(elInput
);
1761 root
.appendChild(elForm
);
1762 var isBuggy
= elForm
.elements
1763 ? (typeof elForm
.elements
.test
== "undefined")
1765 root
.removeChild(elForm
);
1766 elForm
= elInput
= null;
1770 var element
= global
.Element
;
1771 global
.Element = function(tagName
, attributes
) {
1772 attributes
= attributes
|| { };
1773 tagName
= tagName
.toLowerCase();
1774 var cache
= Element
.cache
;
1775 if (SETATTRIBUTE_IGNORES_NAME
&& attributes
.name
) {
1776 tagName
= '<' + tagName
+ ' name="' + attributes
.name
+ '">';
1777 delete attributes
.name
;
1778 return Element
.writeAttribute(document
.createElement(tagName
), attributes
);
1780 if (!cache
[tagName
]) cache
[tagName
] = Element
.extend(document
.createElement(tagName
));
1781 return Element
.writeAttribute(cache
[tagName
].cloneNode(false), attributes
);
1783 Object
.extend(global
.Element
, element
|| { });
1784 if (element
) global
.Element
.prototype = element
.prototype;
1787 Element
.cache
= { };
1788 Element
.idCounter
= 1;
1791 visible: function(element
) {
1792 return $(element
).style
.display
!= 'none';
1795 toggle: function(element
) {
1796 element
= $(element
);
1797 Element
[Element
.visible(element
) ? 'hide' : 'show'](element
);
1802 hide: function(element
) {
1803 element
= $(element
);
1804 element
.style
.display
= 'none';
1808 show: function(element
) {
1809 element
= $(element
);
1810 element
.style
.display
= '';
1814 remove: function(element
) {
1815 element
= $(element
);
1816 element
.parentNode
.removeChild(element
);
1820 update
: (function(){
1822 var SELECT_ELEMENT_INNERHTML_BUGGY
= (function(){
1823 var el
= document
.createElement("select"),
1825 el
.innerHTML
= "<option value=\"test\">test</option>";
1826 if (el
.options
&& el
.options
[0]) {
1827 isBuggy
= el
.options
[0].nodeName
.toUpperCase() !== "OPTION";
1833 var TABLE_ELEMENT_INNERHTML_BUGGY
= (function(){
1835 var el
= document
.createElement("table");
1836 if (el
&& el
.tBodies
) {
1837 el
.innerHTML
= "<tbody><tr><td>test</td></tr></tbody>";
1838 var isBuggy
= typeof el
.tBodies
[0] == "undefined";
1847 var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING
= (function () {
1848 var s
= document
.createElement("script"),
1851 s
.appendChild(document
.createTextNode(""));
1852 isBuggy
= !s
.firstChild
||
1853 s
.firstChild
&& s
.firstChild
.nodeType
!== 3;
1861 function update(element
, content
) {
1862 element
= $(element
);
1864 if (content
&& content
.toElement
)
1865 content
= content
.toElement();
1867 if (Object
.isElement(content
))
1868 return element
.update().insert(content
);
1870 content
= Object
.toHTML(content
);
1872 var tagName
= element
.tagName
.toUpperCase();
1874 if (tagName
=== 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING
) {
1875 element
.text
= content
;
1879 if (SELECT_ELEMENT_INNERHTML_BUGGY
|| TABLE_ELEMENT_INNERHTML_BUGGY
) {
1880 if (tagName
in Element
._insertionTranslations
.tags
) {
1881 while (element
.firstChild
) {
1882 element
.removeChild(element
.firstChild
);
1884 Element
._getContentFromAnonymousElement(tagName
, content
.stripScripts())
1885 .each(function(node
) {
1886 element
.appendChild(node
)
1890 element
.innerHTML
= content
.stripScripts();
1894 element
.innerHTML
= content
.stripScripts();
1897 content
.evalScripts
.bind(content
).defer();
1904 replace: function(element
, content
) {
1905 element
= $(element
);
1906 if (content
&& content
.toElement
) content
= content
.toElement();
1907 else if (!Object
.isElement(content
)) {
1908 content
= Object
.toHTML(content
);
1909 var range
= element
.ownerDocument
.createRange();
1910 range
.selectNode(element
);
1911 content
.evalScripts
.bind(content
).defer();
1912 content
= range
.createContextualFragment(content
.stripScripts());
1914 element
.parentNode
.replaceChild(content
, element
);
1918 insert: function(element
, insertions
) {
1919 element
= $(element
);
1921 if (Object
.isString(insertions
) || Object
.isNumber(insertions
) ||
1922 Object
.isElement(insertions
) || (insertions
&& (insertions
.toElement
|| insertions
.toHTML
)))
1923 insertions
= {bottom
:insertions
};
1925 var content
, insert
, tagName
, childNodes
;
1927 for (var position
in insertions
) {
1928 content
= insertions
[position
];
1929 position
= position
.toLowerCase();
1930 insert
= Element
._insertionTranslations
[position
];
1932 if (content
&& content
.toElement
) content
= content
.toElement();
1933 if (Object
.isElement(content
)) {
1934 insert(element
, content
);
1938 content
= Object
.toHTML(content
);
1940 tagName
= ((position
== 'before' || position
== 'after')
1941 ? element
.parentNode
: element
).tagName
.toUpperCase();
1943 childNodes
= Element
._getContentFromAnonymousElement(tagName
, content
.stripScripts());
1945 if (position
== 'top' || position
== 'after') childNodes
.reverse();
1946 childNodes
.each(insert
.curry(element
));
1948 content
.evalScripts
.bind(content
).defer();
1954 wrap: function(element
, wrapper
, attributes
) {
1955 element
= $(element
);
1956 if (Object
.isElement(wrapper
))
1957 $(wrapper
).writeAttribute(attributes
|| { });
1958 else if (Object
.isString(wrapper
)) wrapper
= new Element(wrapper
, attributes
);
1959 else wrapper
= new Element('div', wrapper
);
1960 if (element
.parentNode
)
1961 element
.parentNode
.replaceChild(wrapper
, element
);
1962 wrapper
.appendChild(element
);
1966 inspect: function(element
) {
1967 element
= $(element
);
1968 var result
= '<' + element
.tagName
.toLowerCase();
1969 $H({'id': 'id', 'className': 'class'}).each(function(pair
) {
1970 var property
= pair
.first(), attribute
= pair
.last();
1971 var value
= (element
[property
] || '').toString();
1972 if (value
) result
+= ' ' + attribute
+ '=' + value
.inspect(true);
1974 return result
+ '>';
1977 recursivelyCollect: function(element
, property
) {
1978 element
= $(element
);
1980 while (element
= element
[property
])
1981 if (element
.nodeType
== 1)
1982 elements
.push(Element
.extend(element
));
1986 ancestors: function(element
) {
1987 return Element
.recursivelyCollect(element
, 'parentNode');
1990 descendants: function(element
) {
1991 return Element
.select(element
, "*");
1994 firstDescendant: function(element
) {
1995 element
= $(element
).firstChild
;
1996 while (element
&& element
.nodeType
!= 1) element
= element
.nextSibling
;
2000 immediateDescendants: function(element
) {
2001 if (!(element
= $(element
).firstChild
)) return [];
2002 while (element
&& element
.nodeType
!= 1) element
= element
.nextSibling
;
2003 if (element
) return [element
].concat($(element
).nextSiblings());
2007 previousSiblings: function(element
) {
2008 return Element
.recursivelyCollect(element
, 'previousSibling');
2011 nextSiblings: function(element
) {
2012 return Element
.recursivelyCollect(element
, 'nextSibling');
2015 siblings: function(element
) {
2016 element
= $(element
);
2017 return Element
.previousSiblings(element
).reverse()
2018 .concat(Element
.nextSiblings(element
));
2021 match: function(element
, selector
) {
2022 if (Object
.isString(selector
))
2023 selector
= new Selector(selector
);
2024 return selector
.match($(element
));
2027 up: function(element
, expression
, index
) {
2028 element
= $(element
);
2029 if (arguments
.length
== 1) return $(element
.parentNode
);
2030 var ancestors
= Element
.ancestors(element
);
2031 return Object
.isNumber(expression
) ? ancestors
[expression
] :
2032 Selector
.findElement(ancestors
, expression
, index
);
2035 down: function(element
, expression
, index
) {
2036 element
= $(element
);
2037 if (arguments
.length
== 1) return Element
.firstDescendant(element
);
2038 return Object
.isNumber(expression
) ? Element
.descendants(element
)[expression
] :
2039 Element
.select(element
, expression
)[index
|| 0];
2042 previous: function(element
, expression
, index
) {
2043 element
= $(element
);
2044 if (arguments
.length
== 1) return $(Selector
.handlers
.previousElementSibling(element
));
2045 var previousSiblings
= Element
.previousSiblings(element
);
2046 return Object
.isNumber(expression
) ? previousSiblings
[expression
] :
2047 Selector
.findElement(previousSiblings
, expression
, index
);
2050 next: function(element
, expression
, index
) {
2051 element
= $(element
);
2052 if (arguments
.length
== 1) return $(Selector
.handlers
.nextElementSibling(element
));
2053 var nextSiblings
= Element
.nextSiblings(element
);
2054 return Object
.isNumber(expression
) ? nextSiblings
[expression
] :
2055 Selector
.findElement(nextSiblings
, expression
, index
);
2059 select: function(element
) {
2060 var args
= Array
.prototype.slice
.call(arguments
, 1);
2061 return Selector
.findChildElements(element
, args
);
2064 adjacent: function(element
) {
2065 var args
= Array
.prototype.slice
.call(arguments
, 1);
2066 return Selector
.findChildElements(element
.parentNode
, args
).without(element
);
2069 identify: function(element
) {
2070 element
= $(element
);
2071 var id
= Element
.readAttribute(element
, 'id');
2073 do { id
= 'anonymous_element_' + Element
.idCounter
++ } while ($(id
));
2074 Element
.writeAttribute(element
, 'id', id
);
2078 readAttribute: function(element
, name
) {
2079 element
= $(element
);
2080 if (Prototype
.Browser
.IE
) {
2081 var t
= Element
._attributeTranslations
.read
;
2082 if (t
.values
[name
]) return t
.values
[name
](element
, name
);
2083 if (t
.names
[name
]) name
= t
.names
[name
];
2084 if (name
.include(':')) {
2085 return (!element
.attributes
|| !element
.attributes
[name
]) ? null :
2086 element
.attributes
[name
].value
;
2089 return element
.getAttribute(name
);
2092 writeAttribute: function(element
, name
, value
) {
2093 element
= $(element
);
2094 var attributes
= { }, t
= Element
._attributeTranslations
.write
;
2096 if (typeof name
== 'object') attributes
= name
;
2097 else attributes
[name
] = Object
.isUndefined(value
) ? true : value
;
2099 for (var attr
in attributes
) {
2100 name
= t
.names
[attr
] || attr
;
2101 value
= attributes
[attr
];
2102 if (t
.values
[attr
]) name
= t
.values
[attr
](element
, value
);
2103 if (value
=== false || value
=== null)
2104 element
.removeAttribute(name
);
2105 else if (value
=== true)
2106 element
.setAttribute(name
, name
);
2107 else element
.setAttribute(name
, value
);
2112 getHeight: function(element
) {
2113 return Element
.getDimensions(element
).height
;
2116 getWidth: function(element
) {
2117 return Element
.getDimensions(element
).width
;
2120 classNames: function(element
) {
2121 return new Element
.ClassNames(element
);
2124 hasClassName: function(element
, className
) {
2125 if (!(element
= $(element
))) return;
2126 var elementClassName
= element
.className
;
2127 return (elementClassName
.length
> 0 && (elementClassName
== className
||
2128 new RegExp("(^|\\s)" + className
+ "(\\s|$)").test(elementClassName
)));
2131 addClassName: function(element
, className
) {
2132 if (!(element
= $(element
))) return;
2133 if (!Element
.hasClassName(element
, className
))
2134 element
.className
+= (element
.className
? ' ' : '') + className
;
2138 removeClassName: function(element
, className
) {
2139 if (!(element
= $(element
))) return;
2140 element
.className
= element
.className
.replace(
2141 new RegExp("(^|\\s+)" + className
+ "(\\s+|$)"), ' ').strip();
2145 toggleClassName: function(element
, className
) {
2146 if (!(element
= $(element
))) return;
2147 return Element
[Element
.hasClassName(element
, className
) ?
2148 'removeClassName' : 'addClassName'](element
, className
);
2151 cleanWhitespace: function(element
) {
2152 element
= $(element
);
2153 var node
= element
.firstChild
;
2155 var nextNode
= node
.nextSibling
;
2156 if (node
.nodeType
== 3 && !/\S/.test(node
.nodeValue
))
2157 element
.removeChild(node
);
2163 empty: function(element
) {
2164 return $(element
).innerHTML
.blank();
2167 descendantOf: function(element
, ancestor
) {
2168 element
= $(element
), ancestor
= $(ancestor
);
2170 if (element
.compareDocumentPosition
)
2171 return (element
.compareDocumentPosition(ancestor
) & 8) === 8;
2173 if (ancestor
.contains
)
2174 return ancestor
.contains(element
) && ancestor
!== element
;
2176 while (element
= element
.parentNode
)
2177 if (element
== ancestor
) return true;
2182 scrollTo: function(element
) {
2183 element
= $(element
);
2184 var pos
= Element
.cumulativeOffset(element
);
2185 window
.scrollTo(pos
[0], pos
[1]);
2189 getStyle: function(element
, style
) {
2190 element
= $(element
);
2191 style
= style
== 'float' ? 'cssFloat' : style
.camelize();
2192 var value
= element
.style
[style
];
2193 if (!value
|| value
== 'auto') {
2194 var css
= document
.defaultView
.getComputedStyle(element
, null);
2195 value
= css
? css
[style
] : null;
2197 if (style
== 'opacity') return value
? parseFloat(value
) : 1.0;
2198 return value
== 'auto' ? null : value
;
2201 getOpacity: function(element
) {
2202 return $(element
).getStyle('opacity');
2205 setStyle: function(element
, styles
) {
2206 element
= $(element
);
2207 var elementStyle
= element
.style
, match
;
2208 if (Object
.isString(styles
)) {
2209 element
.style
.cssText
+= ';' + styles
;
2210 return styles
.include('opacity') ?
2211 element
.setOpacity(styles
.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element
;
2213 for (var property
in styles
)
2214 if (property
== 'opacity') element
.setOpacity(styles
[property
]);
2216 elementStyle
[(property
== 'float' || property
== 'cssFloat') ?
2217 (Object
.isUndefined(elementStyle
.styleFloat
) ? 'cssFloat' : 'styleFloat') :
2218 property
] = styles
[property
];
2223 setOpacity: function(element
, value
) {
2224 element
= $(element
);
2225 element
.style
.opacity
= (value
== 1 || value
=== '') ? '' :
2226 (value
< 0.00001) ? 0 : value
;
2230 getDimensions: function(element
) {
2231 element
= $(element
);
2232 var display
= Element
.getStyle(element
, 'display');
2233 if (display
!= 'none' && display
!= null) // Safari bug
2234 return {width
: element
.offsetWidth
, height
: element
.offsetHeight
};
2236 var els
= element
.style
;
2237 var originalVisibility
= els
.visibility
;
2238 var originalPosition
= els
.position
;
2239 var originalDisplay
= els
.display
;
2240 els
.visibility
= 'hidden';
2241 if (originalPosition
!= 'fixed') // Switching fixed to absolute causes issues in Safari
2242 els
.position
= 'absolute';
2243 els
.display
= 'block';
2244 var originalWidth
= element
.clientWidth
;
2245 var originalHeight
= element
.clientHeight
;
2246 els
.display
= originalDisplay
;
2247 els
.position
= originalPosition
;
2248 els
.visibility
= originalVisibility
;
2249 return {width
: originalWidth
, height
: originalHeight
};
2252 makePositioned: function(element
) {
2253 element
= $(element
);
2254 var pos
= Element
.getStyle(element
, 'position');
2255 if (pos
== 'static' || !pos
) {
2256 element
._madePositioned
= true;
2257 element
.style
.position
= 'relative';
2258 if (Prototype
.Browser
.Opera
) {
2259 element
.style
.top
= 0;
2260 element
.style
.left
= 0;
2266 undoPositioned: function(element
) {
2267 element
= $(element
);
2268 if (element
._madePositioned
) {
2269 element
._madePositioned
= undefined;
2270 element
.style
.position
=
2272 element
.style
.left
=
2273 element
.style
.bottom
=
2274 element
.style
.right
= '';
2279 makeClipping: function(element
) {
2280 element
= $(element
);
2281 if (element
._overflow
) return element
;
2282 element
._overflow
= Element
.getStyle(element
, 'overflow') || 'auto';
2283 if (element
._overflow
!== 'hidden')
2284 element
.style
.overflow
= 'hidden';
2288 undoClipping: function(element
) {
2289 element
= $(element
);
2290 if (!element
._overflow
) return element
;
2291 element
.style
.overflow
= element
._overflow
== 'auto' ? '' : element
._overflow
;
2292 element
._overflow
= null;
2296 cumulativeOffset: function(element
) {
2297 var valueT
= 0, valueL
= 0;
2299 valueT
+= element
.offsetTop
|| 0;
2300 valueL
+= element
.offsetLeft
|| 0;
2301 element
= element
.offsetParent
;
2303 return Element
._returnOffset(valueL
, valueT
);
2306 positionedOffset: function(element
) {
2307 var valueT
= 0, valueL
= 0;
2309 valueT
+= element
.offsetTop
|| 0;
2310 valueL
+= element
.offsetLeft
|| 0;
2311 element
= element
.offsetParent
;
2313 if (element
.tagName
.toUpperCase() == 'BODY') break;
2314 var p
= Element
.getStyle(element
, 'position');
2315 if (p
!== 'static') break;
2318 return Element
._returnOffset(valueL
, valueT
);
2321 absolutize: function(element
) {
2322 element
= $(element
);
2323 if (Element
.getStyle(element
, 'position') == 'absolute') return element
;
2325 var offsets
= Element
.positionedOffset(element
);
2326 var top
= offsets
[1];
2327 var left
= offsets
[0];
2328 var width
= element
.clientWidth
;
2329 var height
= element
.clientHeight
;
2331 element
._originalLeft
= left
- parseFloat(element
.style
.left
|| 0);
2332 element
._originalTop
= top
- parseFloat(element
.style
.top
|| 0);
2333 element
._originalWidth
= element
.style
.width
;
2334 element
._originalHeight
= element
.style
.height
;
2336 element
.style
.position
= 'absolute';
2337 element
.style
.top
= top
+ 'px';
2338 element
.style
.left
= left
+ 'px';
2339 element
.style
.width
= width
+ 'px';
2340 element
.style
.height
= height
+ 'px';
2344 relativize: function(element
) {
2345 element
= $(element
);
2346 if (Element
.getStyle(element
, 'position') == 'relative') return element
;
2348 element
.style
.position
= 'relative';
2349 var top
= parseFloat(element
.style
.top
|| 0) - (element
._originalTop
|| 0);
2350 var left
= parseFloat(element
.style
.left
|| 0) - (element
._originalLeft
|| 0);
2352 element
.style
.top
= top
+ 'px';
2353 element
.style
.left
= left
+ 'px';
2354 element
.style
.height
= element
._originalHeight
;
2355 element
.style
.width
= element
._originalWidth
;
2359 cumulativeScrollOffset: function(element
) {
2360 var valueT
= 0, valueL
= 0;
2362 valueT
+= element
.scrollTop
|| 0;
2363 valueL
+= element
.scrollLeft
|| 0;
2364 element
= element
.parentNode
;
2366 return Element
._returnOffset(valueL
, valueT
);
2369 getOffsetParent: function(element
) {
2370 if (element
.offsetParent
) return $(element
.offsetParent
);
2371 if (element
== document
.body
) return $(element
);
2373 while ((element
= element
.parentNode
) && element
!= document
.body
)
2374 if (Element
.getStyle(element
, 'position') != 'static')
2377 return $(document
.body
);
2380 viewportOffset: function(forElement
) {
2381 var valueT
= 0, valueL
= 0;
2383 var element
= forElement
;
2385 valueT
+= element
.offsetTop
|| 0;
2386 valueL
+= element
.offsetLeft
|| 0;
2388 if (element
.offsetParent
== document
.body
&&
2389 Element
.getStyle(element
, 'position') == 'absolute') break;
2391 } while (element
= element
.offsetParent
);
2393 element
= forElement
;
2395 if (!Prototype
.Browser
.Opera
|| (element
.tagName
&& (element
.tagName
.toUpperCase() == 'BODY'))) {
2396 valueT
-= element
.scrollTop
|| 0;
2397 valueL
-= element
.scrollLeft
|| 0;
2399 } while (element
= element
.parentNode
);
2401 return Element
._returnOffset(valueL
, valueT
);
2404 clonePosition: function(element
, source
) {
2405 var options
= Object
.extend({
2412 }, arguments
[2] || { });
2415 var p
= Element
.viewportOffset(source
);
2417 element
= $(element
);
2420 if (Element
.getStyle(element
, 'position') == 'absolute') {
2421 parent
= Element
.getOffsetParent(element
);
2422 delta
= Element
.viewportOffset(parent
);
2425 if (parent
== document
.body
) {
2426 delta
[0] -= document
.body
.offsetLeft
;
2427 delta
[1] -= document
.body
.offsetTop
;
2430 if (options
.setLeft
) element
.style
.left
= (p
[0] - delta
[0] + options
.offsetLeft
) + 'px';
2431 if (options
.setTop
) element
.style
.top
= (p
[1] - delta
[1] + options
.offsetTop
) + 'px';
2432 if (options
.setWidth
) element
.style
.width
= source
.offsetWidth
+ 'px';
2433 if (options
.setHeight
) element
.style
.height
= source
.offsetHeight
+ 'px';
2438 Object
.extend(Element
.Methods
, {
2439 getElementsBySelector
: Element
.Methods
.select
,
2441 childElements
: Element
.Methods
.immediateDescendants
2444 Element
._attributeTranslations
= {
2454 if (Prototype
.Browser
.Opera
) {
2455 Element
.Methods
.getStyle
= Element
.Methods
.getStyle
.wrap(
2456 function(proceed
, element
, style
) {
2458 case 'left': case 'top': case 'right': case 'bottom':
2459 if (proceed(element
, 'position') === 'static') return null;
2460 case 'height': case 'width':
2461 if (!Element
.visible(element
)) return null;
2463 var dim
= parseInt(proceed(element
, style
), 10);
2465 if (dim
!== element
['offset' + style
.capitalize()])
2469 if (style
=== 'height') {
2470 properties
= ['border-top-width', 'padding-top',
2471 'padding-bottom', 'border-bottom-width'];
2474 properties
= ['border-left-width', 'padding-left',
2475 'padding-right', 'border-right-width'];
2477 return properties
.inject(dim
, function(memo
, property
) {
2478 var val
= proceed(element
, property
);
2479 return val
=== null ? memo
: memo
- parseInt(val
, 10);
2481 default: return proceed(element
, style
);
2486 Element
.Methods
.readAttribute
= Element
.Methods
.readAttribute
.wrap(
2487 function(proceed
, element
, attribute
) {
2488 if (attribute
=== 'title') return element
.title
;
2489 return proceed(element
, attribute
);
2494 else if (Prototype
.Browser
.IE
) {
2495 Element
.Methods
.getOffsetParent
= Element
.Methods
.getOffsetParent
.wrap(
2496 function(proceed
, element
) {
2497 element
= $(element
);
2498 try { element
.offsetParent
}
2499 catch(e
) { return $(document
.body
) }
2500 var position
= element
.getStyle('position');
2501 if (position
!== 'static') return proceed(element
);
2502 element
.setStyle({ position
: 'relative' });
2503 var value
= proceed(element
);
2504 element
.setStyle({ position
: position
});
2509 $w('positionedOffset viewportOffset').each(function(method
) {
2510 Element
.Methods
[method
] = Element
.Methods
[method
].wrap(
2511 function(proceed
, element
) {
2512 element
= $(element
);
2513 try { element
.offsetParent
}
2514 catch(e
) { return Element
._returnOffset(0,0) }
2515 var position
= element
.getStyle('position');
2516 if (position
!== 'static') return proceed(element
);
2517 var offsetParent
= element
.getOffsetParent();
2518 if (offsetParent
&& offsetParent
.getStyle('position') === 'fixed')
2519 offsetParent
.setStyle({ zoom
: 1 });
2520 element
.setStyle({ position
: 'relative' });
2521 var value
= proceed(element
);
2522 element
.setStyle({ position
: position
});
2528 Element
.Methods
.cumulativeOffset
= Element
.Methods
.cumulativeOffset
.wrap(
2529 function(proceed
, element
) {
2530 try { element
.offsetParent
}
2531 catch(e
) { return Element
._returnOffset(0,0) }
2532 return proceed(element
);
2536 Element
.Methods
.getStyle = function(element
, style
) {
2537 element
= $(element
);
2538 style
= (style
== 'float' || style
== 'cssFloat') ? 'styleFloat' : style
.camelize();
2539 var value
= element
.style
[style
];
2540 if (!value
&& element
.currentStyle
) value
= element
.currentStyle
[style
];
2542 if (style
== 'opacity') {
2543 if (value
= (element
.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2544 if (value
[1]) return parseFloat(value
[1]) / 100;
2548 if (value
== 'auto') {
2549 if ((style
== 'width' || style
== 'height') && (element
.getStyle('display') != 'none'))
2550 return element
['offset' + style
.capitalize()] + 'px';
2556 Element
.Methods
.setOpacity = function(element
, value
) {
2557 function stripAlpha(filter
){
2558 return filter
.replace(/alpha\([^\)]*\)/gi,'');
2560 element
= $(element
);
2561 var currentStyle
= element
.currentStyle
;
2562 if ((currentStyle
&& !currentStyle
.hasLayout
) ||
2563 (!currentStyle
&& element
.style
.zoom
== 'normal'))
2564 element
.style
.zoom
= 1;
2566 var filter
= element
.getStyle('filter'), style
= element
.style
;
2567 if (value
== 1 || value
=== '') {
2568 (filter
= stripAlpha(filter
)) ?
2569 style
.filter
= filter
: style
.removeAttribute('filter');
2571 } else if (value
< 0.00001) value
= 0;
2572 style
.filter
= stripAlpha(filter
) +
2573 'alpha(opacity=' + (value
* 100) + ')';
2577 Element
._attributeTranslations
= (function(){
2579 var classProp
= 'className';
2580 var forProp
= 'for';
2582 var el
= document
.createElement('div');
2584 el
.setAttribute(classProp
, 'x');
2586 if (el
.className
!== 'x') {
2587 el
.setAttribute('class', 'x');
2588 if (el
.className
=== 'x') {
2589 classProp
= 'class';
2594 el
= document
.createElement('label');
2595 el
.setAttribute(forProp
, 'x');
2596 if (el
.htmlFor
!== 'x') {
2597 el
.setAttribute('htmlFor', 'x');
2598 if (el
.htmlFor
=== 'x') {
2599 forProp
= 'htmlFor';
2608 'className': classProp
,
2613 _getAttr: function(element
, attribute
) {
2614 return element
.getAttribute(attribute
);
2616 _getAttr2: function(element
, attribute
) {
2617 return element
.getAttribute(attribute
, 2);
2619 _getAttrNode: function(element
, attribute
) {
2620 var node
= element
.getAttributeNode(attribute
);
2621 return node
? node
.value
: "";
2623 _getEv
: (function(){
2625 var el
= document
.createElement('div');
2626 el
.onclick
= Prototype
.emptyFunction
;
2627 var value
= el
.getAttribute('onclick');
2630 if (String(value
).indexOf('{') > -1) {
2631 f = function(element
, attribute
) {
2632 attribute
= element
.getAttribute(attribute
);
2633 if (!attribute
) return null;
2634 attribute
= attribute
.toString();
2635 attribute
= attribute
.split('{')[1];
2636 attribute
= attribute
.split('}')[0];
2637 return attribute
.strip();
2640 else if (value
=== '') {
2641 f = function(element
, attribute
) {
2642 attribute
= element
.getAttribute(attribute
);
2643 if (!attribute
) return null;
2644 return attribute
.strip();
2650 _flag: function(element
, attribute
) {
2651 return $(element
).hasAttribute(attribute
) ? attribute
: null;
2653 style: function(element
) {
2654 return element
.style
.cssText
.toLowerCase();
2656 title: function(element
) {
2657 return element
.title
;
2664 Element
._attributeTranslations
.write
= {
2665 names
: Object
.extend({
2666 cellpadding
: 'cellPadding',
2667 cellspacing
: 'cellSpacing'
2668 }, Element
._attributeTranslations
.read
.names
),
2670 checked: function(element
, value
) {
2671 element
.checked
= !!value
;
2674 style: function(element
, value
) {
2675 element
.style
.cssText
= value
? value
: '';
2680 Element
._attributeTranslations
.has
= {};
2682 $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2683 'encType maxLength readOnly longDesc frameBorder').each(function(attr
) {
2684 Element
._attributeTranslations
.write
.names
[attr
.toLowerCase()] = attr
;
2685 Element
._attributeTranslations
.has
[attr
.toLowerCase()] = attr
;
2693 action
: v
._getAttrNode
,
2701 ondblclick
: v
._getEv
,
2702 onmousedown
: v
._getEv
,
2703 onmouseup
: v
._getEv
,
2704 onmouseover
: v
._getEv
,
2705 onmousemove
: v
._getEv
,
2706 onmouseout
: v
._getEv
,
2709 onkeypress
: v
._getEv
,
2710 onkeydown
: v
._getEv
,
2717 })(Element
._attributeTranslations
.read
.values
);
2719 if (Prototype
.BrowserFeatures
.ElementExtensions
) {
2721 function _descendants(element
) {
2722 var nodes
= element
.getElementsByTagName('*'), results
= [];
2723 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
2724 if (node
.tagName
!== "!") // Filter out comment nodes.
2729 Element
.Methods
.down = function(element
, expression
, index
) {
2730 element
= $(element
);
2731 if (arguments
.length
== 1) return element
.firstDescendant();
2732 return Object
.isNumber(expression
) ? _descendants(element
)[expression
] :
2733 Element
.select(element
, expression
)[index
|| 0];
2740 else if (Prototype
.Browser
.Gecko
&& /rv:1\.8\.0/.test(navigator
.userAgent
)) {
2741 Element
.Methods
.setOpacity = function(element
, value
) {
2742 element
= $(element
);
2743 element
.style
.opacity
= (value
== 1) ? 0.999999 :
2744 (value
=== '') ? '' : (value
< 0.00001) ? 0 : value
;
2749 else if (Prototype
.Browser
.WebKit
) {
2750 Element
.Methods
.setOpacity = function(element
, value
) {
2751 element
= $(element
);
2752 element
.style
.opacity
= (value
== 1 || value
=== '') ? '' :
2753 (value
< 0.00001) ? 0 : value
;
2756 if(element
.tagName
.toUpperCase() == 'IMG' && element
.width
) {
2757 element
.width
++; element
.width
--;
2759 var n
= document
.createTextNode(' ');
2760 element
.appendChild(n
);
2761 element
.removeChild(n
);
2767 Element
.Methods
.cumulativeOffset = function(element
) {
2768 var valueT
= 0, valueL
= 0;
2770 valueT
+= element
.offsetTop
|| 0;
2771 valueL
+= element
.offsetLeft
|| 0;
2772 if (element
.offsetParent
== document
.body
)
2773 if (Element
.getStyle(element
, 'position') == 'absolute') break;
2775 element
= element
.offsetParent
;
2778 return Element
._returnOffset(valueL
, valueT
);
2782 if ('outerHTML' in document
.documentElement
) {
2783 Element
.Methods
.replace = function(element
, content
) {
2784 element
= $(element
);
2786 if (content
&& content
.toElement
) content
= content
.toElement();
2787 if (Object
.isElement(content
)) {
2788 element
.parentNode
.replaceChild(content
, element
);
2792 content
= Object
.toHTML(content
);
2793 var parent
= element
.parentNode
, tagName
= parent
.tagName
.toUpperCase();
2795 if (Element
._insertionTranslations
.tags
[tagName
]) {
2796 var nextSibling
= element
.next();
2797 var fragments
= Element
._getContentFromAnonymousElement(tagName
, content
.stripScripts());
2798 parent
.removeChild(element
);
2800 fragments
.each(function(node
) { parent
.insertBefore(node
, nextSibling
) });
2802 fragments
.each(function(node
) { parent
.appendChild(node
) });
2804 else element
.outerHTML
= content
.stripScripts();
2806 content
.evalScripts
.bind(content
).defer();
2811 Element
._returnOffset = function(l
, t
) {
2812 var result
= [l
, t
];
2818 Element
._getContentFromAnonymousElement = function(tagName
, html
) {
2819 var div
= new Element('div'), t
= Element
._insertionTranslations
.tags
[tagName
];
2821 div
.innerHTML
= t
[0] + html
+ t
[1];
2822 t
[2].times(function() { div
= div
.firstChild
});
2823 } else div
.innerHTML
= html
;
2824 return $A(div
.childNodes
);
2827 Element
._insertionTranslations
= {
2828 before: function(element
, node
) {
2829 element
.parentNode
.insertBefore(node
, element
);
2831 top: function(element
, node
) {
2832 element
.insertBefore(node
, element
.firstChild
);
2834 bottom: function(element
, node
) {
2835 element
.appendChild(node
);
2837 after: function(element
, node
) {
2838 element
.parentNode
.insertBefore(node
, element
.nextSibling
);
2841 TABLE
: ['<table>', '</table>', 1],
2842 TBODY
: ['<table><tbody>', '</tbody></table>', 2],
2843 TR
: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2844 TD
: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2845 SELECT
: ['<select>', '</select>', 1]
2850 var tags
= Element
._insertionTranslations
.tags
;
2851 Object
.extend(tags
, {
2858 Element
.Methods
.Simulated
= {
2859 hasAttribute: function(element
, attribute
) {
2860 attribute
= Element
._attributeTranslations
.has
[attribute
] || attribute
;
2861 var node
= $(element
).getAttributeNode(attribute
);
2862 return !!(node
&& node
.specified
);
2866 Element
.Methods
.ByTag
= { };
2868 Object
.extend(Element
, Element
.Methods
);
2872 if (!Prototype
.BrowserFeatures
.ElementExtensions
&& div
['__proto__']) {
2873 window
.HTMLElement
= { };
2874 window
.HTMLElement
.prototype = div
['__proto__'];
2875 Prototype
.BrowserFeatures
.ElementExtensions
= true;
2880 })(document
.createElement('div'))
2882 Element
.extend
= (function() {
2884 function checkDeficiency(tagName
) {
2885 if (typeof window
.Element
!= 'undefined') {
2886 var proto
= window
.Element
.prototype;
2888 var id
= '_' + (Math
.random()+'').slice(2);
2889 var el
= document
.createElement(tagName
);
2891 var isBuggy
= (el
[id
] !== 'x');
2900 function extendElementWith(element
, methods
) {
2901 for (var property
in methods
) {
2902 var value
= methods
[property
];
2903 if (Object
.isFunction(value
) && !(property
in element
))
2904 element
[property
] = value
.methodize();
2908 var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY
= checkDeficiency('object');
2910 if (Prototype
.BrowserFeatures
.SpecificElementExtensions
) {
2911 if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY
) {
2912 return function(element
) {
2913 if (element
&& typeof element
._extendedByPrototype
== 'undefined') {
2914 var t
= element
.tagName
;
2915 if (t
&& (/^(?:object|applet|embed)$/i.test(t
))) {
2916 extendElementWith(element
, Element
.Methods
);
2917 extendElementWith(element
, Element
.Methods
.Simulated
);
2918 extendElementWith(element
, Element
.Methods
.ByTag
[t
.toUpperCase()]);
2927 var Methods
= { }, ByTag
= Element
.Methods
.ByTag
;
2929 var extend
= Object
.extend(function(element
) {
2930 if (!element
|| typeof element
._extendedByPrototype
!= 'undefined' ||
2931 element
.nodeType
!= 1 || element
== window
) return element
;
2933 var methods
= Object
.clone(Methods
),
2934 tagName
= element
.tagName
.toUpperCase();
2936 if (ByTag
[tagName
]) Object
.extend(methods
, ByTag
[tagName
]);
2938 extendElementWith(element
, methods
);
2940 element
._extendedByPrototype
= Prototype
.emptyFunction
;
2944 refresh: function() {
2945 if (!Prototype
.BrowserFeatures
.ElementExtensions
) {
2946 Object
.extend(Methods
, Element
.Methods
);
2947 Object
.extend(Methods
, Element
.Methods
.Simulated
);
2956 Element
.hasAttribute = function(element
, attribute
) {
2957 if (element
.hasAttribute
) return element
.hasAttribute(attribute
);
2958 return Element
.Methods
.Simulated
.hasAttribute(element
, attribute
);
2961 Element
.addMethods = function(methods
) {
2962 var F
= Prototype
.BrowserFeatures
, T
= Element
.Methods
.ByTag
;
2965 Object
.extend(Form
, Form
.Methods
);
2966 Object
.extend(Form
.Element
, Form
.Element
.Methods
);
2967 Object
.extend(Element
.Methods
.ByTag
, {
2968 "FORM": Object
.clone(Form
.Methods
),
2969 "INPUT": Object
.clone(Form
.Element
.Methods
),
2970 "SELECT": Object
.clone(Form
.Element
.Methods
),
2971 "TEXTAREA": Object
.clone(Form
.Element
.Methods
)
2975 if (arguments
.length
== 2) {
2976 var tagName
= methods
;
2977 methods
= arguments
[1];
2980 if (!tagName
) Object
.extend(Element
.Methods
, methods
|| { });
2982 if (Object
.isArray(tagName
)) tagName
.each(extend
);
2983 else extend(tagName
);
2986 function extend(tagName
) {
2987 tagName
= tagName
.toUpperCase();
2988 if (!Element
.Methods
.ByTag
[tagName
])
2989 Element
.Methods
.ByTag
[tagName
] = { };
2990 Object
.extend(Element
.Methods
.ByTag
[tagName
], methods
);
2993 function copy(methods
, destination
, onlyIfAbsent
) {
2994 onlyIfAbsent
= onlyIfAbsent
|| false;
2995 for (var property
in methods
) {
2996 var value
= methods
[property
];
2997 if (!Object
.isFunction(value
)) continue;
2998 if (!onlyIfAbsent
|| !(property
in destination
))
2999 destination
[property
] = value
.methodize();
3003 function findDOMClass(tagName
) {
3006 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3007 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3008 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3009 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3010 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3011 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3012 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3013 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3014 "FrameSet", "IFRAME": "IFrame"
3016 if (trans
[tagName
]) klass
= 'HTML' + trans
[tagName
] + 'Element';
3017 if (window
[klass
]) return window
[klass
];
3018 klass
= 'HTML' + tagName
+ 'Element';
3019 if (window
[klass
]) return window
[klass
];
3020 klass
= 'HTML' + tagName
.capitalize() + 'Element';
3021 if (window
[klass
]) return window
[klass
];
3023 var element
= document
.createElement(tagName
);
3024 var proto
= element
['__proto__'] || element
.constructor.prototype;
3029 var elementPrototype
= window
.HTMLElement
? HTMLElement
.prototype :
3032 if (F
.ElementExtensions
) {
3033 copy(Element
.Methods
, elementPrototype
);
3034 copy(Element
.Methods
.Simulated
, elementPrototype
, true);
3037 if (F
.SpecificElementExtensions
) {
3038 for (var tag
in Element
.Methods
.ByTag
) {
3039 var klass
= findDOMClass(tag
);
3040 if (Object
.isUndefined(klass
)) continue;
3041 copy(T
[tag
], klass
.prototype);
3045 Object
.extend(Element
, Element
.Methods
);
3046 delete Element
.ByTag
;
3048 if (Element
.extend
.refresh
) Element
.extend
.refresh();
3049 Element
.cache
= { };
3053 document
.viewport
= {
3055 getDimensions: function() {
3056 return { width
: this.getWidth(), height
: this.getHeight() };
3059 getScrollOffsets: function() {
3060 return Element
._returnOffset(
3061 window
.pageXOffset
|| document
.documentElement
.scrollLeft
|| document
.body
.scrollLeft
,
3062 window
.pageYOffset
|| document
.documentElement
.scrollTop
|| document
.body
.scrollTop
);
3066 (function(viewport
) {
3067 var B
= Prototype
.Browser
, doc
= document
, element
, property
= {};
3069 function getRootElement() {
3070 if (B
.WebKit
&& !doc
.evaluate
)
3073 if (B
.Opera
&& window
.parseFloat(window
.opera
.version()) < 9.5)
3074 return document
.body
;
3076 return document
.documentElement
;
3079 function define(D
) {
3080 if (!element
) element
= getRootElement();
3082 property
[D
] = 'client' + D
;
3084 viewport
['get' + D
] = function() { return element
[property
[D
]] };
3085 return viewport
['get' + D
]();
3088 viewport
.getWidth
= define
.curry('Width');
3090 viewport
.getHeight
= define
.curry('Height');
3091 })(document
.viewport
);
3098 Element
.addMethods({
3099 getStorage: function(element
) {
3100 if (!(element
= $(element
))) return;
3103 if (element
=== window
) {
3106 if (typeof element
._prototypeUID
=== "undefined")
3107 element
._prototypeUID
= [Element
.Storage
.UID
++];
3108 uid
= element
._prototypeUID
[0];
3111 if (!Element
.Storage
[uid
])
3112 Element
.Storage
[uid
] = $H();
3114 return Element
.Storage
[uid
];
3117 store: function(element
, key
, value
) {
3118 if (!(element
= $(element
))) return;
3120 if (arguments
.length
=== 2) {
3121 Element
.getStorage(element
).update(key
);
3123 Element
.getStorage(element
).set(key
, value
);
3129 retrieve: function(element
, key
, defaultValue
) {
3130 if (!(element
= $(element
))) return;
3131 var hash
= Element
.getStorage(element
), value
= hash
.get(key
);
3133 if (Object
.isUndefined(value
)) {
3134 hash
.set(key
, defaultValue
);
3135 value
= defaultValue
;
3141 clone: function(element
, deep
) {
3142 if (!(element
= $(element
))) return;
3143 var clone
= element
.cloneNode(deep
);
3144 clone
._prototypeUID
= void 0;
3146 var descendants
= Element
.select(clone
, '*'),
3147 i
= descendants
.length
;
3149 descendants
[i
]._prototypeUID
= void 0;
3152 return Element
.extend(clone
);
3155 /* Portions of the Selector class are derived from Jack Slocum's DomQuery,
3156 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
3157 * license. Please see http://www.yui-ext.com/ for more information. */
3159 var Selector
= Class
.create({
3160 initialize: function(expression
) {
3161 this.expression
= expression
.strip();
3163 if (this.shouldUseSelectorsAPI()) {
3164 this.mode
= 'selectorsAPI';
3165 } else if (this.shouldUseXPath()) {
3166 this.mode
= 'xpath';
3167 this.compileXPathMatcher();
3169 this.mode
= "normal";
3170 this.compileMatcher();
3175 shouldUseXPath
: (function() {
3177 var IS_DESCENDANT_SELECTOR_BUGGY
= (function(){
3178 var isBuggy
= false;
3179 if (document
.evaluate
&& window
.XPathResult
) {
3180 var el
= document
.createElement('div');
3181 el
.innerHTML
= '<ul><li></li></ul><div><ul><li></li></ul></div>';
3183 var xpath
= ".//*[local-name()='ul' or local-name()='UL']" +
3184 "//*[local-name()='li' or local-name()='LI']";
3186 var result
= document
.evaluate(xpath
, el
, null,
3187 XPathResult
.ORDERED_NODE_SNAPSHOT_TYPE
, null);
3189 isBuggy
= (result
.snapshotLength
!== 2);
3196 if (!Prototype
.BrowserFeatures
.XPath
) return false;
3198 var e
= this.expression
;
3200 if (Prototype
.Browser
.WebKit
&&
3201 (e
.include("-of-type") || e
.include(":empty")))
3204 if ((/(\[[\w-]*?:|:checked)/).test(e
))
3207 if (IS_DESCENDANT_SELECTOR_BUGGY
) return false;
3214 shouldUseSelectorsAPI: function() {
3215 if (!Prototype
.BrowserFeatures
.SelectorsAPI
) return false;
3217 if (Selector
.CASE_INSENSITIVE_CLASS_NAMES
) return false;
3219 if (!Selector
._div
) Selector
._div
= new Element('div');
3222 Selector
._div
.querySelector(this.expression
);
3230 compileMatcher: function() {
3231 var e
= this.expression
, ps
= Selector
.patterns
, h
= Selector
.handlers
,
3232 c
= Selector
.criteria
, le
, p
, m
, len
= ps
.length
, name
;
3234 if (Selector
._cache
[e
]) {
3235 this.matcher
= Selector
._cache
[e
];
3239 this.matcher
= ["this.matcher = function(root) {",
3240 "var r = root, h = Selector.handlers, c = false, n;"];
3242 while (e
&& le
!= e
&& (/\S/).test(e
)) {
3244 for (var i
= 0; i
<len
; i
++) {
3247 if (m
= e
.match(p
)) {
3248 this.matcher
.push(Object
.isFunction(c
[name
]) ? c
[name
](m
) :
3249 new Template(c
[name
]).evaluate(m
));
3250 e
= e
.replace(m
[0], '');
3256 this.matcher
.push("return h.unique(n);\n}");
3257 eval(this.matcher
.join('\n'));
3258 Selector
._cache
[this.expression
] = this.matcher
;
3261 compileXPathMatcher: function() {
3262 var e
= this.expression
, ps
= Selector
.patterns
,
3263 x
= Selector
.xpath
, le
, m
, len
= ps
.length
, name
;
3265 if (Selector
._cache
[e
]) {
3266 this.xpath
= Selector
._cache
[e
]; return;
3269 this.matcher
= ['.//*'];
3270 while (e
&& le
!= e
&& (/\S/).test(e
)) {
3272 for (var i
= 0; i
<len
; i
++) {
3274 if (m
= e
.match(ps
[i
].re
)) {
3275 this.matcher
.push(Object
.isFunction(x
[name
]) ? x
[name
](m
) :
3276 new Template(x
[name
]).evaluate(m
));
3277 e
= e
.replace(m
[0], '');
3283 this.xpath
= this.matcher
.join('');
3284 Selector
._cache
[this.expression
] = this.xpath
;
3287 findElements: function(root
) {
3288 root
= root
|| document
;
3289 var e
= this.expression
, results
;
3291 switch (this.mode
) {
3292 case 'selectorsAPI':
3293 if (root
!== document
) {
3294 var oldId
= root
.id
, id
= $(root
).identify();
3295 id
= id
.replace(/([\.:])/g, "\\$1");
3296 e
= "#" + id
+ " " + e
;
3299 results
= $A(root
.querySelectorAll(e
)).map(Element
.extend
);
3304 return document
._getElementsByXPath(this.xpath
, root
);
3306 return this.matcher(root
);
3310 match: function(element
) {
3313 var e
= this.expression
, ps
= Selector
.patterns
, as
= Selector
.assertions
;
3314 var le
, p
, m
, len
= ps
.length
, name
;
3316 while (e
&& le
!== e
&& (/\S/).test(e
)) {
3318 for (var i
= 0; i
<len
; i
++) {
3321 if (m
= e
.match(p
)) {
3323 this.tokens
.push([name
, Object
.clone(m
)]);
3324 e
= e
.replace(m
[0], '');
3326 return this.findElements(document
).include(element
);
3332 var match
= true, name
, matches
;
3333 for (var i
= 0, token
; token
= this.tokens
[i
]; i
++) {
3334 name
= token
[0], matches
= token
[1];
3335 if (!Selector
.assertions
[name
](element
, matches
)) {
3336 match
= false; break;
3343 toString: function() {
3344 return this.expression
;
3347 inspect: function() {
3348 return "#<Selector:" + this.expression
.inspect() + ">";
3352 if (Prototype
.BrowserFeatures
.SelectorsAPI
&&
3353 document
.compatMode
=== 'BackCompat') {
3354 Selector
.CASE_INSENSITIVE_CLASS_NAMES
= (function(){
3355 var div
= document
.createElement('div'),
3356 span
= document
.createElement('span');
3358 div
.id
= "prototype_test_id";
3359 span
.className
= 'Test';
3360 div
.appendChild(span
);
3361 var isIgnored
= (div
.querySelector('#prototype_test_id .test') !== null);
3367 Object
.extend(Selector
, {
3373 adjacent
: "/following-sibling::*[1]",
3374 laterSibling
: '/following-sibling::*',
3375 tagName: function(m
) {
3376 if (m
[1] == '*') return '';
3377 return "[local-name()='" + m
[1].toLowerCase() +
3378 "' or local-name()='" + m
[1].toUpperCase() + "']";
3380 className
: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
3382 attrPresence: function(m
) {
3383 m
[1] = m
[1].toLowerCase();
3384 return new Template("[@#{1}]").evaluate(m
);
3387 m
[1] = m
[1].toLowerCase();
3388 m
[3] = m
[5] || m
[6];
3389 return new Template(Selector
.xpath
.operators
[m
[2]]).evaluate(m
);
3391 pseudo: function(m
) {
3392 var h
= Selector
.xpath
.pseudos
[m
[1]];
3394 if (Object
.isFunction(h
)) return h(m
);
3395 return new Template(Selector
.xpath
.pseudos
[m
[1]]).evaluate(m
);
3398 '=': "[@#{1}='#{3}']",
3399 '!=': "[@#{1}!='#{3}']",
3400 '^=': "[starts-with(@#{1}, '#{3}')]",
3401 '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
3402 '*=': "[contains(@#{1}, '#{3}')]",
3403 '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
3404 '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
3407 'first-child': '[not(preceding-sibling::*)]',
3408 'last-child': '[not(following-sibling::*)]',
3409 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
3410 'empty': "[count(*) = 0 and (count(text()) = 0)]",
3411 'checked': "[@checked]",
3412 'disabled': "[(@disabled) and (@type!='hidden')]",
3413 'enabled': "[not(@disabled) and (@type!='hidden')]",
3414 'not': function(m
) {
3415 var e
= m
[6], p
= Selector
.patterns
,
3416 x
= Selector
.xpath
, le
, v
, len
= p
.length
, name
;
3419 while (e
&& le
!= e
&& (/\S/).test(e
)) {
3421 for (var i
= 0; i
<len
; i
++) {
3423 if (m
= e
.match(p
[i
].re
)) {
3424 v
= Object
.isFunction(x
[name
]) ? x
[name
](m
) : new Template(x
[name
]).evaluate(m
);
3425 exclusion
.push("(" + v
.substring(1, v
.length
- 1) + ")");
3426 e
= e
.replace(m
[0], '');
3431 return "[not(" + exclusion
.join(" and ") + ")]";
3433 'nth-child': function(m
) {
3434 return Selector
.xpath
.pseudos
.nth("(count(./preceding-sibling::*) + 1) ", m
);
3436 'nth-last-child': function(m
) {
3437 return Selector
.xpath
.pseudos
.nth("(count(./following-sibling::*) + 1) ", m
);
3439 'nth-of-type': function(m
) {
3440 return Selector
.xpath
.pseudos
.nth("position() ", m
);
3442 'nth-last-of-type': function(m
) {
3443 return Selector
.xpath
.pseudos
.nth("(last() + 1 - position()) ", m
);
3445 'first-of-type': function(m
) {
3446 m
[6] = "1"; return Selector
.xpath
.pseudos
['nth-of-type'](m
);
3448 'last-of-type': function(m
) {
3449 m
[6] = "1"; return Selector
.xpath
.pseudos
['nth-last-of-type'](m
);
3451 'only-of-type': function(m
) {
3452 var p
= Selector
.xpath
.pseudos
; return p
['first-of-type'](m
) + p
['last-of-type'](m
);
3454 nth: function(fragment
, m
) {
3455 var mm
, formula
= m
[6], predicate
;
3456 if (formula
== 'even') formula
= '2n+0';
3457 if (formula
== 'odd') formula
= '2n+1';
3458 if (mm
= formula
.match(/^(\d+)$/)) // digit only
3459 return '[' + fragment
+ "= " + mm
[1] + ']';
3460 if (mm
= formula
.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3461 if (mm
[1] == "-") mm
[1] = -1;
3462 var a
= mm
[1] ? Number(mm
[1]) : 1;
3463 var b
= mm
[2] ? Number(mm
[2]) : 0;
3464 predicate
= "[((#{fragment} - #{b}) mod #{a} = 0) and " +
3465 "((#{fragment} - #{b}) div #{a} >= 0)]";
3466 return new Template(predicate
).evaluate({
3467 fragment
: fragment
, a
: a
, b
: b
});
3474 tagName
: 'n = h.tagName(n, r, "#{1}", c); c = false;',
3475 className
: 'n = h.className(n, r, "#{1}", c); c = false;',
3476 id
: 'n = h.id(n, r, "#{1}", c); c = false;',
3477 attrPresence
: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
3479 m
[3] = (m
[5] || m
[6]);
3480 return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m
);
3482 pseudo: function(m
) {
3483 if (m
[6]) m
[6] = m
[6].replace(/"/g, '\\"');
3484 return new Template('n
= h
.pseudo(n
, "#{1}", "#{6}", r
, c
); c
= false;').evaluate(m);
3486 descendant: 'c
= "descendant";',
3487 child: 'c
= "child";',
3488 adjacent: 'c
= "adjacent";',
3489 laterSibling: 'c
= "laterSibling";'
3493 { name: 'laterSibling
', re: /^\s*~\s*/ },
3494 { name: 'child
', re: /^\s*>\s*/ },
3495 { name: 'adjacent
', re: /^\s*\+\s*/ },
3496 { name: 'descendant
', re: /^\s/ },
3498 { name: 'tagName
', re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
3499 { name: 'id
', re: /^#([\w\-\*]+)(\b|$)/ },
3500 { name: 'className
', re: /^\.([\w\-\*]+)(\b|$)/ },
3501 { name: 'pseudo
', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
3502 { name: 'attrPresence
', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
3503 { name: 'attr
', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
3507 tagName: function(element
, matches
) {
3508 return matches
[1].toUpperCase() == element
.tagName
.toUpperCase();
3511 className: function(element
, matches
) {
3512 return Element
.hasClassName(element
, matches
[1]);
3515 id: function(element
, matches
) {
3516 return element
.id
=== matches
[1];
3519 attrPresence: function(element
, matches
) {
3520 return Element
.hasAttribute(element
, matches
[1]);
3523 attr: function(element
, matches
) {
3524 var nodeValue
= Element
.readAttribute(element
, matches
[1]);
3525 return nodeValue
&& Selector
.operators
[matches
[2]](nodeValue
, matches
[5] || matches
[6]);
3530 concat: function(a
, b
) {
3531 for (var i
= 0, node
; node
= b
[i
]; i
++)
3536 mark: function(nodes
) {
3537 var _true
= Prototype
.emptyFunction
;
3538 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3539 node
._countedByPrototype
= _true
;
3543 unmark
: (function(){
3545 var PROPERTIES_ATTRIBUTES_MAP
= (function(){
3546 var el
= document
.createElement('div'),
3548 propName
= '_countedByPrototype',
3550 el
[propName
] = value
;
3551 isBuggy
= (el
.getAttribute(propName
) === value
);
3556 return PROPERTIES_ATTRIBUTES_MAP
?
3558 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3559 node
.removeAttribute('_countedByPrototype');
3563 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3564 node
._countedByPrototype
= void 0;
3569 index: function(parentNode
, reverse
, ofType
) {
3570 parentNode
._countedByPrototype
= Prototype
.emptyFunction
;
3572 for (var nodes
= parentNode
.childNodes
, i
= nodes
.length
- 1, j
= 1; i
>= 0; i
--) {
3573 var node
= nodes
[i
];
3574 if (node
.nodeType
== 1 && (!ofType
|| node
._countedByPrototype
)) node
.nodeIndex
= j
++;
3577 for (var i
= 0, j
= 1, nodes
= parentNode
.childNodes
; node
= nodes
[i
]; i
++)
3578 if (node
.nodeType
== 1 && (!ofType
|| node
._countedByPrototype
)) node
.nodeIndex
= j
++;
3582 unique: function(nodes
) {
3583 if (nodes
.length
== 0) return nodes
;
3584 var results
= [], n
;
3585 for (var i
= 0, l
= nodes
.length
; i
< l
; i
++)
3586 if (typeof (n
= nodes
[i
])._countedByPrototype
== 'undefined') {
3587 n
._countedByPrototype
= Prototype
.emptyFunction
;
3588 results
.push(Element
.extend(n
));
3590 return Selector
.handlers
.unmark(results
);
3593 descendant: function(nodes
) {
3594 var h
= Selector
.handlers
;
3595 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3596 h
.concat(results
, node
.getElementsByTagName('*'));
3600 child: function(nodes
) {
3601 var h
= Selector
.handlers
;
3602 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3603 for (var j
= 0, child
; child
= node
.childNodes
[j
]; j
++)
3604 if (child
.nodeType
== 1 && child
.tagName
!= '!') results
.push(child
);
3609 adjacent: function(nodes
) {
3610 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3611 var next
= this.nextElementSibling(node
);
3612 if (next
) results
.push(next
);
3617 laterSibling: function(nodes
) {
3618 var h
= Selector
.handlers
;
3619 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3620 h
.concat(results
, Element
.nextSiblings(node
));
3624 nextElementSibling: function(node
) {
3625 while (node
= node
.nextSibling
)
3626 if (node
.nodeType
== 1) return node
;
3630 previousElementSibling: function(node
) {
3631 while (node
= node
.previousSibling
)
3632 if (node
.nodeType
== 1) return node
;
3636 tagName: function(nodes
, root
, tagName
, combinator
) {
3637 var uTagName
= tagName
.toUpperCase();
3638 var results
= [], h
= Selector
.handlers
;
3641 if (combinator
== "descendant") {
3642 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3643 h
.concat(results
, node
.getElementsByTagName(tagName
));
3645 } else nodes
= this[combinator
](nodes
);
3646 if (tagName
== "*") return nodes
;
3648 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3649 if (node
.tagName
.toUpperCase() === uTagName
) results
.push(node
);
3651 } else return root
.getElementsByTagName(tagName
);
3654 id: function(nodes
, root
, id
, combinator
) {
3655 var targetNode
= $(id
), h
= Selector
.handlers
;
3657 if (root
== document
) {
3658 if (!targetNode
) return [];
3659 if (!nodes
) return [targetNode
];
3661 if (!root
.sourceIndex
|| root
.sourceIndex
< 1) {
3662 var nodes
= root
.getElementsByTagName('*');
3663 for (var j
= 0, node
; node
= nodes
[j
]; j
++) {
3664 if (node
.id
=== id
) return [node
];
3671 if (combinator
== 'child') {
3672 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3673 if (targetNode
.parentNode
== node
) return [targetNode
];
3674 } else if (combinator
== 'descendant') {
3675 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3676 if (Element
.descendantOf(targetNode
, node
)) return [targetNode
];
3677 } else if (combinator
== 'adjacent') {
3678 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3679 if (Selector
.handlers
.previousElementSibling(targetNode
) == node
)
3680 return [targetNode
];
3681 } else nodes
= h
[combinator
](nodes
);
3683 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3684 if (node
== targetNode
) return [targetNode
];
3687 return (targetNode
&& Element
.descendantOf(targetNode
, root
)) ? [targetNode
] : [];
3690 className: function(nodes
, root
, className
, combinator
) {
3691 if (nodes
&& combinator
) nodes
= this[combinator
](nodes
);
3692 return Selector
.handlers
.byClassName(nodes
, root
, className
);
3695 byClassName: function(nodes
, root
, className
) {
3696 if (!nodes
) nodes
= Selector
.handlers
.descendant([root
]);
3697 var needle
= ' ' + className
+ ' ';
3698 for (var i
= 0, results
= [], node
, nodeClassName
; node
= nodes
[i
]; i
++) {
3699 nodeClassName
= node
.className
;
3700 if (nodeClassName
.length
== 0) continue;
3701 if (nodeClassName
== className
|| (' ' + nodeClassName
+ ' ').include(needle
))
3707 attrPresence: function(nodes
, root
, attr
, combinator
) {
3708 if (!nodes
) nodes
= root
.getElementsByTagName("*");
3709 if (nodes
&& combinator
) nodes
= this[combinator
](nodes
);
3711 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3712 if (Element
.hasAttribute(node
, attr
)) results
.push(node
);
3716 attr: function(nodes
, root
, attr
, value
, operator
, combinator
) {
3717 if (!nodes
) nodes
= root
.getElementsByTagName("*");
3718 if (nodes
&& combinator
) nodes
= this[combinator
](nodes
);
3719 var handler
= Selector
.operators
[operator
], results
= [];
3720 for (var i
= 0, node
; node
= nodes
[i
]; i
++) {
3721 var nodeValue
= Element
.readAttribute(node
, attr
);
3722 if (nodeValue
=== null) continue;
3723 if (handler(nodeValue
, value
)) results
.push(node
);
3728 pseudo: function(nodes
, name
, value
, root
, combinator
) {
3729 if (nodes
&& combinator
) nodes
= this[combinator
](nodes
);
3730 if (!nodes
) nodes
= root
.getElementsByTagName("*");
3731 return Selector
.pseudos
[name
](nodes
, value
, root
);
3736 'first-child': function(nodes
, value
, root
) {
3737 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3738 if (Selector
.handlers
.previousElementSibling(node
)) continue;
3743 'last-child': function(nodes
, value
, root
) {
3744 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3745 if (Selector
.handlers
.nextElementSibling(node
)) continue;
3750 'only-child': function(nodes
, value
, root
) {
3751 var h
= Selector
.handlers
;
3752 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3753 if (!h
.previousElementSibling(node
) && !h
.nextElementSibling(node
))
3757 'nth-child': function(nodes
, formula
, root
) {
3758 return Selector
.pseudos
.nth(nodes
, formula
, root
);
3760 'nth-last-child': function(nodes
, formula
, root
) {
3761 return Selector
.pseudos
.nth(nodes
, formula
, root
, true);
3763 'nth-of-type': function(nodes
, formula
, root
) {
3764 return Selector
.pseudos
.nth(nodes
, formula
, root
, false, true);
3766 'nth-last-of-type': function(nodes
, formula
, root
) {
3767 return Selector
.pseudos
.nth(nodes
, formula
, root
, true, true);
3769 'first-of-type': function(nodes
, formula
, root
) {
3770 return Selector
.pseudos
.nth(nodes
, "1", root
, false, true);
3772 'last-of-type': function(nodes
, formula
, root
) {
3773 return Selector
.pseudos
.nth(nodes
, "1", root
, true, true);
3775 'only-of-type': function(nodes
, formula
, root
) {
3776 var p
= Selector
.pseudos
;
3777 return p
['last-of-type'](p
['first-of-type'](nodes
, formula
, root
), formula
, root
);
3780 getIndices: function(a
, b
, total
) {
3781 if (a
== 0) return b
> 0 ? [b
] : [];
3782 return $R(1, total
).inject([], function(memo
, i
) {
3783 if (0 == (i
- b
) % a
&& (i
- b
) / a
>= 0) memo
.push(i
);
3788 nth: function(nodes
, formula
, root
, reverse
, ofType
) {
3789 if (nodes
.length
== 0) return [];
3790 if (formula
== 'even') formula
= '2n+0';
3791 if (formula
== 'odd') formula
= '2n+1';
3792 var h
= Selector
.handlers
, results
= [], indexed
= [], m
;
3794 for (var i
= 0, node
; node
= nodes
[i
]; i
++) {
3795 if (!node
.parentNode
._countedByPrototype
) {
3796 h
.index(node
.parentNode
, reverse
, ofType
);
3797 indexed
.push(node
.parentNode
);
3800 if (formula
.match(/^\d+$/)) { // just a number
3801 formula
= Number(formula
);
3802 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3803 if (node
.nodeIndex
== formula
) results
.push(node
);
3804 } else if (m
= formula
.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3805 if (m
[1] == "-") m
[1] = -1;
3806 var a
= m
[1] ? Number(m
[1]) : 1;
3807 var b
= m
[2] ? Number(m
[2]) : 0;
3808 var indices
= Selector
.pseudos
.getIndices(a
, b
, nodes
.length
);
3809 for (var i
= 0, node
, l
= indices
.length
; node
= nodes
[i
]; i
++) {
3810 for (var j
= 0; j
< l
; j
++)
3811 if (node
.nodeIndex
== indices
[j
]) results
.push(node
);
3819 'empty': function(nodes
, value
, root
) {
3820 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3821 if (node
.tagName
== '!' || node
.firstChild
) continue;
3827 'not': function(nodes
, selector
, root
) {
3828 var h
= Selector
.handlers
, selectorType
, m
;
3829 var exclusions
= new Selector(selector
).findElements(root
);
3831 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3832 if (!node
._countedByPrototype
) results
.push(node
);
3833 h
.unmark(exclusions
);
3837 'enabled': function(nodes
, value
, root
) {
3838 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3839 if (!node
.disabled
&& (!node
.type
|| node
.type
!== 'hidden'))
3844 'disabled': function(nodes
, value
, root
) {
3845 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3846 if (node
.disabled
) results
.push(node
);
3850 'checked': function(nodes
, value
, root
) {
3851 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3852 if (node
.checked
) results
.push(node
);
3858 '=': function(nv
, v
) { return nv
== v
; },
3859 '!=': function(nv
, v
) { return nv
!= v
; },
3860 '^=': function(nv
, v
) { return nv
== v
|| nv
&& nv
.startsWith(v
); },
3861 '$=': function(nv
, v
) { return nv
== v
|| nv
&& nv
.endsWith(v
); },
3862 '*=': function(nv
, v
) { return nv
== v
|| nv
&& nv
.include(v
); },
3863 '~=': function(nv
, v
) { return (' ' + nv
+ ' ').include(' ' + v
+ ' '); },
3864 '|=': function(nv
, v
) { return ('-' + (nv
|| "").toUpperCase() +
3865 '-').include('-' + (v
|| "").toUpperCase() + '-'); }
3868 split: function(expression
) {
3869 var expressions
= [];
3870 expression
.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m
) {
3871 expressions
.push(m
[1].strip());
3876 matchElements: function(elements
, expression
) {
3877 var matches
= $$(expression
), h
= Selector
.handlers
;
3879 for (var i
= 0, results
= [], element
; element
= elements
[i
]; i
++)
3880 if (element
._countedByPrototype
) results
.push(element
);
3885 findElement: function(elements
, expression
, index
) {
3886 if (Object
.isNumber(expression
)) {
3887 index
= expression
; expression
= false;
3889 return Selector
.matchElements(elements
, expression
|| '*')[index
|| 0];
3892 findChildElements: function(element
, expressions
) {
3893 expressions
= Selector
.split(expressions
.join(','));
3894 var results
= [], h
= Selector
.handlers
;
3895 for (var i
= 0, l
= expressions
.length
, selector
; i
< l
; i
++) {
3896 selector
= new Selector(expressions
[i
].strip());
3897 h
.concat(results
, selector
.findElements(element
));
3899 return (l
> 1) ? h
.unique(results
) : results
;
3903 if (Prototype
.Browser
.IE
) {
3904 Object
.extend(Selector
.handlers
, {
3905 concat: function(a
, b
) {
3906 for (var i
= 0, node
; node
= b
[i
]; i
++)
3907 if (node
.tagName
!== "!") a
.push(node
);
3914 return Selector
.findChildElements(document
, $A(arguments
));
3918 reset: function(form
) {
3924 serializeElements: function(elements
, options
) {
3925 if (typeof options
!= 'object') options
= { hash
: !!options
};
3926 else if (Object
.isUndefined(options
.hash
)) options
.hash
= true;
3927 var key
, value
, submitted
= false, submit
= options
.submit
;
3929 var data
= elements
.inject({ }, function(result
, element
) {
3930 if (!element
.disabled
&& element
.name
) {
3931 key
= element
.name
; value
= $(element
).getValue();
3932 if (value
!= null && element
.type
!= 'file' && (element
.type
!= 'submit' || (!submitted
&&
3933 submit
!== false && (!submit
|| key
== submit
) && (submitted
= true)))) {
3934 if (key
in result
) {
3935 if (!Object
.isArray(result
[key
])) result
[key
] = [result
[key
]];
3936 result
[key
].push(value
);
3938 else result
[key
] = value
;
3944 return options
.hash
? data
: Object
.toQueryString(data
);
3949 serialize: function(form
, options
) {
3950 return Form
.serializeElements(Form
.getElements(form
), options
);
3953 getElements: function(form
) {
3954 var elements
= $(form
).getElementsByTagName('*'),
3957 serializers
= Form
.Element
.Serializers
;
3958 for (var i
= 0; element
= elements
[i
]; i
++) {
3961 return arr
.inject([], function(elements
, child
) {
3962 if (serializers
[child
.tagName
.toLowerCase()])
3963 elements
.push(Element
.extend(child
));
3968 getInputs: function(form
, typeName
, name
) {
3970 var inputs
= form
.getElementsByTagName('input');
3972 if (!typeName
&& !name
) return $A(inputs
).map(Element
.extend
);
3974 for (var i
= 0, matchingInputs
= [], length
= inputs
.length
; i
< length
; i
++) {
3975 var input
= inputs
[i
];
3976 if ((typeName
&& input
.type
!= typeName
) || (name
&& input
.name
!= name
))
3978 matchingInputs
.push(Element
.extend(input
));
3981 return matchingInputs
;
3984 disable: function(form
) {
3986 Form
.getElements(form
).invoke('disable');
3990 enable: function(form
) {
3992 Form
.getElements(form
).invoke('enable');
3996 findFirstElement: function(form
) {
3997 var elements
= $(form
).getElements().findAll(function(element
) {
3998 return 'hidden' != element
.type
&& !element
.disabled
;
4000 var firstByIndex
= elements
.findAll(function(element
) {
4001 return element
.hasAttribute('tabIndex') && element
.tabIndex
>= 0;
4002 }).sortBy(function(element
) { return element
.tabIndex
}).first();
4004 return firstByIndex
? firstByIndex
: elements
.find(function(element
) {
4005 return /^(?:input|select|textarea)$/i.test(element
.tagName
);
4009 focusFirstElement: function(form
) {
4011 form
.findFirstElement().activate();
4015 request: function(form
, options
) {
4016 form
= $(form
), options
= Object
.clone(options
|| { });
4018 var params
= options
.parameters
, action
= form
.readAttribute('action') || '';
4019 if (action
.blank()) action
= window
.location
.href
;
4020 options
.parameters
= form
.serialize(true);
4023 if (Object
.isString(params
)) params
= params
.toQueryParams();
4024 Object
.extend(options
.parameters
, params
);
4027 if (form
.hasAttribute('method') && !options
.method
)
4028 options
.method
= form
.method
;
4030 return new Ajax
.Request(action
, options
);
4034 /*--------------------------------------------------------------------------*/
4038 focus: function(element
) {
4043 select: function(element
) {
4044 $(element
).select();
4049 Form
.Element
.Methods
= {
4051 serialize: function(element
) {
4052 element
= $(element
);
4053 if (!element
.disabled
&& element
.name
) {
4054 var value
= element
.getValue();
4055 if (value
!= undefined) {
4057 pair
[element
.name
] = value
;
4058 return Object
.toQueryString(pair
);
4064 getValue: function(element
) {
4065 element
= $(element
);
4066 var method
= element
.tagName
.toLowerCase();
4067 return Form
.Element
.Serializers
[method
](element
);
4070 setValue: function(element
, value
) {
4071 element
= $(element
);
4072 var method
= element
.tagName
.toLowerCase();
4073 Form
.Element
.Serializers
[method
](element
, value
);
4077 clear: function(element
) {
4078 $(element
).value
= '';
4082 present: function(element
) {
4083 return $(element
).value
!= '';
4086 activate: function(element
) {
4087 element
= $(element
);
4090 if (element
.select
&& (element
.tagName
.toLowerCase() != 'input' ||
4091 !(/^(?:button|reset|submit)$/i.test(element
.type
))))
4097 disable: function(element
) {
4098 element
= $(element
);
4099 element
.disabled
= true;
4103 enable: function(element
) {
4104 element
= $(element
);
4105 element
.disabled
= false;
4110 /*--------------------------------------------------------------------------*/
4112 var Field
= Form
.Element
;
4114 var $F
= Form
.Element
.Methods
.getValue
;
4116 /*--------------------------------------------------------------------------*/
4118 Form
.Element
.Serializers
= {
4119 input: function(element
, value
) {
4120 switch (element
.type
.toLowerCase()) {
4123 return Form
.Element
.Serializers
.inputSelector(element
, value
);
4125 return Form
.Element
.Serializers
.textarea(element
, value
);
4129 inputSelector: function(element
, value
) {
4130 if (Object
.isUndefined(value
)) return element
.checked
? element
.value
: null;
4131 else element
.checked
= !!value
;
4134 textarea: function(element
, value
) {
4135 if (Object
.isUndefined(value
)) return element
.value
;
4136 else element
.value
= value
;
4139 select: function(element
, value
) {
4140 if (Object
.isUndefined(value
))
4141 return this[element
.type
== 'select-one' ?
4142 'selectOne' : 'selectMany'](element
);
4144 var opt
, currentValue
, single
= !Object
.isArray(value
);
4145 for (var i
= 0, length
= element
.length
; i
< length
; i
++) {
4146 opt
= element
.options
[i
];
4147 currentValue
= this.optionValue(opt
);
4149 if (currentValue
== value
) {
4150 opt
.selected
= true;
4154 else opt
.selected
= value
.include(currentValue
);
4159 selectOne: function(element
) {
4160 var index
= element
.selectedIndex
;
4161 return index
>= 0 ? this.optionValue(element
.options
[index
]) : null;
4164 selectMany: function(element
) {
4165 var values
, length
= element
.length
;
4166 if (!length
) return null;
4168 for (var i
= 0, values
= []; i
< length
; i
++) {
4169 var opt
= element
.options
[i
];
4170 if (opt
.selected
) values
.push(this.optionValue(opt
));
4175 optionValue: function(opt
) {
4176 return Element
.extend(opt
).hasAttribute('value') ? opt
.value
: opt
.text
;
4180 /*--------------------------------------------------------------------------*/
4183 Abstract
.TimedObserver
= Class
.create(PeriodicalExecuter
, {
4184 initialize: function($super, element
, frequency
, callback
) {
4185 $super(callback
, frequency
);
4186 this.element
= $(element
);
4187 this.lastValue
= this.getValue();
4190 execute: function() {
4191 var value
= this.getValue();
4192 if (Object
.isString(this.lastValue
) && Object
.isString(value
) ?
4193 this.lastValue
!= value
: String(this.lastValue
) != String(value
)) {
4194 this.callback(this.element
, value
);
4195 this.lastValue
= value
;
4200 Form
.Element
.Observer
= Class
.create(Abstract
.TimedObserver
, {
4201 getValue: function() {
4202 return Form
.Element
.getValue(this.element
);
4206 Form
.Observer
= Class
.create(Abstract
.TimedObserver
, {
4207 getValue: function() {
4208 return Form
.serialize(this.element
);
4212 /*--------------------------------------------------------------------------*/
4214 Abstract
.EventObserver
= Class
.create({
4215 initialize: function(element
, callback
) {
4216 this.element
= $(element
);
4217 this.callback
= callback
;
4219 this.lastValue
= this.getValue();
4220 if (this.element
.tagName
.toLowerCase() == 'form')
4221 this.registerFormCallbacks();
4223 this.registerCallback(this.element
);
4226 onElementEvent: function() {
4227 var value
= this.getValue();
4228 if (this.lastValue
!= value
) {
4229 this.callback(this.element
, value
);
4230 this.lastValue
= value
;
4234 registerFormCallbacks: function() {
4235 Form
.getElements(this.element
).each(this.registerCallback
, this);
4238 registerCallback: function(element
) {
4240 switch (element
.type
.toLowerCase()) {
4243 Event
.observe(element
, 'click', this.onElementEvent
.bind(this));
4246 Event
.observe(element
, 'change', this.onElementEvent
.bind(this));
4253 Form
.Element
.EventObserver
= Class
.create(Abstract
.EventObserver
, {
4254 getValue: function() {
4255 return Form
.Element
.getValue(this.element
);
4259 Form
.EventObserver
= Class
.create(Abstract
.EventObserver
, {
4260 getValue: function() {
4261 return Form
.serialize(this.element
);
4285 var docEl
= document
.documentElement
;
4286 var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED
= 'onmouseenter' in docEl
4287 && 'onmouseleave' in docEl
;
4290 if (Prototype
.Browser
.IE
) {
4291 var buttonMap
= { 0: 1, 1: 4, 2: 2 };
4292 _isButton = function(event
, code
) {
4293 return event
.button
=== buttonMap
[code
];
4295 } else if (Prototype
.Browser
.WebKit
) {
4296 _isButton = function(event
, code
) {
4298 case 0: return event
.which
== 1 && !event
.metaKey
;
4299 case 1: return event
.which
== 1 && event
.metaKey
;
4300 default: return false;
4304 _isButton = function(event
, code
) {
4305 return event
.which
? (event
.which
=== code
+ 1) : (event
.button
=== code
);
4309 function isLeftClick(event
) { return _isButton(event
, 0) }
4311 function isMiddleClick(event
) { return _isButton(event
, 1) }
4313 function isRightClick(event
) { return _isButton(event
, 2) }
4315 function element(event
) {
4316 event
= Event
.extend(event
);
4318 var node
= event
.target
, type
= event
.type
,
4319 currentTarget
= event
.currentTarget
;
4321 if (currentTarget
&& currentTarget
.tagName
) {
4322 if (type
=== 'load' || type
=== 'error' ||
4323 (type
=== 'click' && currentTarget
.tagName
.toLowerCase() === 'input'
4324 && currentTarget
.type
=== 'radio'))
4325 node
= currentTarget
;
4328 if (node
.nodeType
== Node
.TEXT_NODE
)
4329 node
= node
.parentNode
;
4331 return Element
.extend(node
);
4334 function findElement(event
, expression
) {
4335 var element
= Event
.element(event
);
4336 if (!expression
) return element
;
4337 var elements
= [element
].concat(element
.ancestors());
4338 return Selector
.findElement(elements
, expression
, 0);
4341 function pointer(event
) {
4342 return { x
: pointerX(event
), y
: pointerY(event
) };
4345 function pointerX(event
) {
4346 var docElement
= document
.documentElement
,
4347 body
= document
.body
|| { scrollLeft
: 0 };
4349 return event
.pageX
|| (event
.clientX
+
4350 (docElement
.scrollLeft
|| body
.scrollLeft
) -
4351 (docElement
.clientLeft
|| 0));
4354 function pointerY(event
) {
4355 var docElement
= document
.documentElement
,
4356 body
= document
.body
|| { scrollTop
: 0 };
4358 return event
.pageY
|| (event
.clientY
+
4359 (docElement
.scrollTop
|| body
.scrollTop
) -
4360 (docElement
.clientTop
|| 0));
4364 function stop(event
) {
4365 Event
.extend(event
);
4366 event
.preventDefault();
4367 event
.stopPropagation();
4369 event
.stopped
= true;
4373 isLeftClick
: isLeftClick
,
4374 isMiddleClick
: isMiddleClick
,
4375 isRightClick
: isRightClick
,
4378 findElement
: findElement
,
4388 var methods
= Object
.keys(Event
.Methods
).inject({ }, function(m
, name
) {
4389 m
[name
] = Event
.Methods
[name
].methodize();
4393 if (Prototype
.Browser
.IE
) {
4394 function _relatedTarget(event
) {
4396 switch (event
.type
) {
4397 case 'mouseover': element
= event
.fromElement
; break;
4398 case 'mouseout': element
= event
.toElement
; break;
4399 default: return null;
4401 return Element
.extend(element
);
4404 Object
.extend(methods
, {
4405 stopPropagation: function() { this.cancelBubble
= true },
4406 preventDefault: function() { this.returnValue
= false },
4407 inspect: function() { return '[object Event]' }
4410 Event
.extend = function(event
, element
) {
4411 if (!event
) return false;
4412 if (event
._extendedByPrototype
) return event
;
4414 event
._extendedByPrototype
= Prototype
.emptyFunction
;
4415 var pointer
= Event
.pointer(event
);
4417 Object
.extend(event
, {
4418 target
: event
.srcElement
|| element
,
4419 relatedTarget
: _relatedTarget(event
),
4424 return Object
.extend(event
, methods
);
4427 Event
.prototype = window
.Event
.prototype || document
.createEvent('HTMLEvents').__proto__
;
4428 Object
.extend(Event
.prototype, methods
);
4429 Event
.extend
= Prototype
.K
;
4432 function _createResponder(element
, eventName
, handler
) {
4433 var registry
= Element
.retrieve(element
, 'prototype_event_registry');
4435 if (Object
.isUndefined(registry
)) {
4436 CACHE
.push(element
);
4437 registry
= Element
.retrieve(element
, 'prototype_event_registry', $H());
4440 var respondersForEvent
= registry
.get(eventName
);
4441 if (Object
.isUndefined(respondersForEvent
)) {
4442 respondersForEvent
= [];
4443 registry
.set(eventName
, respondersForEvent
);
4446 if (respondersForEvent
.pluck('handler').include(handler
)) return false;
4449 if (eventName
.include(":")) {
4450 responder = function(event
) {
4451 if (Object
.isUndefined(event
.eventName
))
4454 if (event
.eventName
!== eventName
)
4457 Event
.extend(event
, element
);
4458 handler
.call(element
, event
);
4461 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED
&&
4462 (eventName
=== "mouseenter" || eventName
=== "mouseleave")) {
4463 if (eventName
=== "mouseenter" || eventName
=== "mouseleave") {
4464 responder = function(event
) {
4465 Event
.extend(event
, element
);
4467 var parent
= event
.relatedTarget
;
4468 while (parent
&& parent
!== element
) {
4469 try { parent
= parent
.parentNode
; }
4470 catch(e
) { parent
= element
; }
4473 if (parent
=== element
) return;
4475 handler
.call(element
, event
);
4479 responder = function(event
) {
4480 Event
.extend(event
, element
);
4481 handler
.call(element
, event
);
4486 responder
.handler
= handler
;
4487 respondersForEvent
.push(responder
);
4491 function _destroyCache() {
4492 for (var i
= 0, length
= CACHE
.length
; i
< length
; i
++) {
4493 Event
.stopObserving(CACHE
[i
]);
4500 if (Prototype
.Browser
.IE
)
4501 window
.attachEvent('onunload', _destroyCache
);
4503 if (Prototype
.Browser
.WebKit
)
4504 window
.addEventListener('unload', Prototype
.emptyFunction
, false);
4507 var _getDOMEventName
= Prototype
.K
;
4509 if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED
) {
4510 _getDOMEventName = function(eventName
) {
4511 var translations
= { mouseenter
: "mouseover", mouseleave
: "mouseout" };
4512 return eventName
in translations
? translations
[eventName
] : eventName
;
4516 function observe(element
, eventName
, handler
) {
4517 element
= $(element
);
4519 var responder
= _createResponder(element
, eventName
, handler
);
4521 if (!responder
) return element
;
4523 if (eventName
.include(':')) {
4524 if (element
.addEventListener
)
4525 element
.addEventListener("dataavailable", responder
, false);
4527 element
.attachEvent("ondataavailable", responder
);
4528 element
.attachEvent("onfilterchange", responder
);
4531 var actualEventName
= _getDOMEventName(eventName
);
4533 if (element
.addEventListener
)
4534 element
.addEventListener(actualEventName
, responder
, false);
4536 element
.attachEvent("on" + actualEventName
, responder
);
4542 function stopObserving(element
, eventName
, handler
) {
4543 element
= $(element
);
4545 var registry
= Element
.retrieve(element
, 'prototype_event_registry');
4547 if (Object
.isUndefined(registry
)) return element
;
4549 if (eventName
&& !handler
) {
4550 var responders
= registry
.get(eventName
);
4552 if (Object
.isUndefined(responders
)) return element
;
4554 responders
.each( function(r
) {
4555 Element
.stopObserving(element
, eventName
, r
.handler
);
4558 } else if (!eventName
) {
4559 registry
.each( function(pair
) {
4560 var eventName
= pair
.key
, responders
= pair
.value
;
4562 responders
.each( function(r
) {
4563 Element
.stopObserving(element
, eventName
, r
.handler
);
4569 var responders
= registry
.get(eventName
);
4571 if (!responders
) return;
4573 var responder
= responders
.find( function(r
) { return r
.handler
=== handler
; });
4574 if (!responder
) return element
;
4576 var actualEventName
= _getDOMEventName(eventName
);
4578 if (eventName
.include(':')) {
4579 if (element
.removeEventListener
)
4580 element
.removeEventListener("dataavailable", responder
, false);
4582 element
.detachEvent("ondataavailable", responder
);
4583 element
.detachEvent("onfilterchange", responder
);
4586 if (element
.removeEventListener
)
4587 element
.removeEventListener(actualEventName
, responder
, false);
4589 element
.detachEvent('on' + actualEventName
, responder
);
4592 registry
.set(eventName
, responders
.without(responder
));
4597 function fire(element
, eventName
, memo
, bubble
) {
4598 element
= $(element
);
4600 if (Object
.isUndefined(bubble
))
4603 if (element
== document
&& document
.createEvent
&& !element
.dispatchEvent
)
4604 element
= document
.documentElement
;
4607 if (document
.createEvent
) {
4608 event
= document
.createEvent('HTMLEvents');
4609 event
.initEvent('dataavailable', true, true);
4611 event
= document
.createEventObject();
4612 event
.eventType
= bubble
? 'ondataavailable' : 'onfilterchange';
4615 event
.eventName
= eventName
;
4616 event
.memo
= memo
|| { };
4618 if (document
.createEvent
)
4619 element
.dispatchEvent(event
);
4621 element
.fireEvent(event
.eventType
, event
);
4623 return Event
.extend(event
);
4627 Object
.extend(Event
, Event
.Methods
);
4629 Object
.extend(Event
, {
4632 stopObserving
: stopObserving
4635 Element
.addMethods({
4640 stopObserving
: stopObserving
4643 Object
.extend(document
, {
4644 fire
: fire
.methodize(),
4646 observe
: observe
.methodize(),
4648 stopObserving
: stopObserving
.methodize(),
4653 if (window
.Event
) Object
.extend(window
.Event
, Event
);
4654 else window
.Event
= Event
;
4658 /* Support for the DOMContentLoaded event is based on work by Dan Webb,
4659 Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
4663 function fireContentLoadedEvent() {
4664 if (document
.loaded
) return;
4665 if (timer
) window
.clearTimeout(timer
);
4666 document
.loaded
= true;
4667 document
.fire('dom:loaded');
4670 function checkReadyState() {
4671 if (document
.readyState
=== 'complete') {
4672 document
.stopObserving('readystatechange', checkReadyState
);
4673 fireContentLoadedEvent();
4677 function pollDoScroll() {
4678 try { document
.documentElement
.doScroll('left'); }
4680 timer
= pollDoScroll
.defer();
4683 fireContentLoadedEvent();
4686 if (document
.addEventListener
) {
4687 document
.addEventListener('DOMContentLoaded', fireContentLoadedEvent
, false);
4689 document
.observe('readystatechange', checkReadyState
);
4691 timer
= pollDoScroll
.defer();
4694 Event
.observe(window
, 'load', fireContentLoadedEvent
);
4697 Element
.addMethods();
4699 /*------------------------------- DEPRECATED -------------------------------*/
4701 Hash
.toQueryString
= Object
.toQueryString
;
4703 var Toggle
= { display
: Element
.toggle
};
4705 Element
.Methods
.childOf
= Element
.Methods
.descendantOf
;
4708 Before: function(element
, content
) {
4709 return Element
.insert(element
, {before
:content
});
4712 Top: function(element
, content
) {
4713 return Element
.insert(element
, {top
:content
});
4716 Bottom: function(element
, content
) {
4717 return Element
.insert(element
, {bottom
:content
});
4720 After: function(element
, content
) {
4721 return Element
.insert(element
, {after
:content
});
4725 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4728 includeScrollOffsets
: false,
4730 prepare: function() {
4731 this.deltaX
= window
.pageXOffset
4732 || document
.documentElement
.scrollLeft
4733 || document
.body
.scrollLeft
4735 this.deltaY
= window
.pageYOffset
4736 || document
.documentElement
.scrollTop
4737 || document
.body
.scrollTop
4741 within: function(element
, x
, y
) {
4742 if (this.includeScrollOffsets
)
4743 return this.withinIncludingScrolloffsets(element
, x
, y
);
4746 this.offset
= Element
.cumulativeOffset(element
);
4748 return (y
>= this.offset
[1] &&
4749 y
< this.offset
[1] + element
.offsetHeight
&&
4750 x
>= this.offset
[0] &&
4751 x
< this.offset
[0] + element
.offsetWidth
);
4754 withinIncludingScrolloffsets: function(element
, x
, y
) {
4755 var offsetcache
= Element
.cumulativeScrollOffset(element
);
4757 this.xcomp
= x
+ offsetcache
[0] - this.deltaX
;
4758 this.ycomp
= y
+ offsetcache
[1] - this.deltaY
;
4759 this.offset
= Element
.cumulativeOffset(element
);
4761 return (this.ycomp
>= this.offset
[1] &&
4762 this.ycomp
< this.offset
[1] + element
.offsetHeight
&&
4763 this.xcomp
>= this.offset
[0] &&
4764 this.xcomp
< this.offset
[0] + element
.offsetWidth
);
4767 overlap: function(mode
, element
) {
4768 if (!mode
) return 0;
4769 if (mode
== 'vertical')
4770 return ((this.offset
[1] + element
.offsetHeight
) - this.ycomp
) /
4771 element
.offsetHeight
;
4772 if (mode
== 'horizontal')
4773 return ((this.offset
[0] + element
.offsetWidth
) - this.xcomp
) /
4774 element
.offsetWidth
;
4778 cumulativeOffset
: Element
.Methods
.cumulativeOffset
,
4780 positionedOffset
: Element
.Methods
.positionedOffset
,
4782 absolutize: function(element
) {
4784 return Element
.absolutize(element
);
4787 relativize: function(element
) {
4789 return Element
.relativize(element
);
4792 realOffset
: Element
.Methods
.cumulativeScrollOffset
,
4794 offsetParent
: Element
.Methods
.getOffsetParent
,
4796 page
: Element
.Methods
.viewportOffset
,
4798 clone: function(source
, target
, options
) {
4799 options
= options
|| { };
4800 return Element
.clonePosition(target
, source
, options
);
4804 /*--------------------------------------------------------------------------*/
4806 if (!document
.getElementsByClassName
) document
.getElementsByClassName = function(instanceMethods
){
4807 function iter(name
) {
4808 return name
.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name
+ " ')]";
4811 instanceMethods
.getElementsByClassName
= Prototype
.BrowserFeatures
.XPath
?
4812 function(element
, className
) {
4813 className
= className
.toString().strip();
4814 var cond
= /\s/.test(className
) ? $w(className
).map(iter
).join('') : iter(className
);
4815 return cond
? document
._getElementsByXPath('.//*' + cond
, element
) : [];
4816 } : function(element
, className
) {
4817 className
= className
.toString().strip();
4818 var elements
= [], classNames
= (/\s/.test(className
) ? $w(className
) : null);
4819 if (!classNames
&& !className
) return elements
;
4821 var nodes
= $(element
).getElementsByTagName('*');
4822 className
= ' ' + className
+ ' ';
4824 for (var i
= 0, child
, cn
; child
= nodes
[i
]; i
++) {
4825 if (child
.className
&& (cn
= ' ' + child
.className
+ ' ') && (cn
.include(className
) ||
4826 (classNames
&& classNames
.all(function(name
) {
4827 return !name
.toString().blank() && cn
.include(' ' + name
+ ' ');
4829 elements
.push(Element
.extend(child
));
4834 return function(className
, parentElement
) {
4835 return $(parentElement
|| document
.body
).getElementsByClassName(className
);
4839 /*--------------------------------------------------------------------------*/
4841 Element
.ClassNames
= Class
.create();
4842 Element
.ClassNames
.prototype = {
4843 initialize: function(element
) {
4844 this.element
= $(element
);
4847 _each: function(iterator
) {
4848 this.element
.className
.split(/\s+/).select(function(name
) {
4849 return name
.length
> 0;
4853 set: function(className
) {
4854 this.element
.className
= className
;
4857 add: function(classNameToAdd
) {
4858 if (this.include(classNameToAdd
)) return;
4859 this.set($A(this).concat(classNameToAdd
).join(' '));
4862 remove: function(classNameToRemove
) {
4863 if (!this.include(classNameToRemove
)) return;
4864 this.set($A(this).without(classNameToRemove
).join(' '));
4867 toString: function() {
4868 return $A(this).join(' ');
4872 Object
.extend(Element
.ClassNames
.prototype, Enumerable
);
4874 /*--------------------------------------------------------------------------*/