1 /* Prototype JavaScript framework, version 1.6.0.1
2 * (c) 2005-2007 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 IE
: !!(window
.attachEvent
&& !window
.opera
),
14 Opera
: !!window
.opera
,
15 WebKit
: navigator
.userAgent
.indexOf('AppleWebKit/') > -1,
16 Gecko
: navigator
.userAgent
.indexOf('Gecko') > -1 && navigator
.userAgent
.indexOf('KHTML') == -1,
17 MobileSafari
: !!navigator
.userAgent
.match(/Apple.*Mobile.*Safari/)
21 XPath
: !!document
.evaluate
,
22 ElementExtensions
: !!window
.HTMLElement
,
23 SpecificElementExtensions
:
24 document
.createElement('div').__proto__
&&
25 document
.createElement('div').__proto__
!==
26 document
.createElement('form').__proto__
29 ScriptFragment
: '<script[^>]*>([\\S\\s]*?)<\/script>',
30 JSONFilter
: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
32 emptyFunction: function() { },
33 K: function(x
) { return x
}
36 if (Prototype
.Browser
.MobileSafari
)
37 Prototype
.BrowserFeatures
.SpecificElementExtensions
= false;
40 /* Based on Alex Arnell's inheritance implementation. */
43 var parent
= null, properties
= $A(arguments
);
44 if (Object
.isFunction(properties
[0]))
45 parent
= properties
.shift();
48 this.initialize
.apply(this, arguments
);
51 Object
.extend(klass
, Class
.Methods
);
52 klass
.superclass
= parent
;
53 klass
.subclasses
= [];
56 var subclass = function() { };
57 subclass
.prototype = parent
.prototype;
58 klass
.prototype = new subclass
;
59 parent
.subclasses
.push(klass
);
62 for (var i
= 0; i
< properties
.length
; i
++)
63 klass
.addMethods(properties
[i
]);
65 if (!klass
.prototype.initialize
)
66 klass
.prototype.initialize
= Prototype
.emptyFunction
;
68 klass
.prototype.constructor = klass
;
75 addMethods: function(source
) {
76 var ancestor
= this.superclass
&& this.superclass
.prototype;
77 var properties
= Object
.keys(source
);
79 if (!Object
.keys({ toString
: true }).length
)
80 properties
.push("toString", "valueOf");
82 for (var i
= 0, length
= properties
.length
; i
< length
; i
++) {
83 var property
= properties
[i
], value
= source
[property
];
84 if (ancestor
&& Object
.isFunction(value
) &&
85 value
.argumentNames().first() == "$super") {
86 var method
= value
, value
= Object
.extend((function(m
) {
87 return function() { return ancestor
[m
].apply(this, arguments
) };
88 })(property
).wrap(method
), {
89 valueOf: function() { return method
},
90 toString: function() { return method
.toString() }
93 this.prototype[property
] = value
;
102 Object
.extend = function(destination
, source
) {
103 for (var property
in source
)
104 destination
[property
] = source
[property
];
108 Object
.extend(Object
, {
109 inspect: function(object
) {
111 if (Object
.isUndefined(object
)) return 'undefined';
112 if (object
=== null) return 'null';
113 return object
.inspect
? object
.inspect() : object
.toString();
115 if (e
instanceof RangeError
) return '...';
120 toJSON: function(object
) {
121 var type
= typeof object
;
125 case 'unknown': return;
126 case 'boolean': return object
.toString();
129 if (object
=== null) return 'null';
130 if (object
.toJSON
) return object
.toJSON();
131 if (Object
.isElement(object
)) return;
134 for (var property
in object
) {
135 var value
= Object
.toJSON(object
[property
]);
136 if (!Object
.isUndefined(value
))
137 results
.push(property
.toJSON() + ': ' + value
);
140 return '{' + results
.join(', ') + '}';
143 toQueryString: function(object
) {
144 return $H(object
).toQueryString();
147 toHTML: function(object
) {
148 return object
&& object
.toHTML
? object
.toHTML() : String
.interpret(object
);
151 keys: function(object
) {
153 for (var property
in object
)
158 values: function(object
) {
160 for (var property
in object
)
161 values
.push(object
[property
]);
165 clone: function(object
) {
166 return Object
.extend({ }, object
);
169 isElement: function(object
) {
170 return object
&& object
.nodeType
== 1;
173 isArray: function(object
) {
174 return object
&& object
.constructor === Array
;
177 isHash: function(object
) {
178 return object
instanceof Hash
;
181 isFunction: function(object
) {
182 return typeof object
== "function";
185 isString: function(object
) {
186 return typeof object
== "string";
189 isNumber: function(object
) {
190 return typeof object
== "number";
193 isUndefined: function(object
) {
194 return typeof object
== "undefined";
198 Object
.extend(Function
.prototype, {
199 argumentNames: function() {
200 var names
= this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
201 return names
.length
== 1 && !names
[0] ? [] : names
;
205 if (arguments
.length
< 2 && Object
.isUndefined(arguments
[0])) return this;
206 var __method
= this, args
= $A(arguments
), object
= args
.shift();
208 return __method
.apply(object
, args
.concat($A(arguments
)));
212 bindAsEventListener: function() {
213 var __method
= this, args
= $A(arguments
), object
= args
.shift();
214 return function(event
) {
215 return __method
.apply(object
, [event
|| window
.event
].concat(args
));
220 if (!arguments
.length
) return this;
221 var __method
= this, args
= $A(arguments
);
223 return __method
.apply(this, args
.concat($A(arguments
)));
228 var __method
= this, args
= $A(arguments
), timeout
= args
.shift() * 1000;
229 return window
.setTimeout(function() {
230 return __method
.apply(__method
, args
);
234 wrap: function(wrapper
) {
237 return wrapper
.apply(this, [__method
.bind(this)].concat($A(arguments
)));
241 methodize: function() {
242 if (this._methodized
) return this._methodized
;
244 return this._methodized = function() {
245 return __method
.apply(null, [this].concat($A(arguments
)));
250 Function
.prototype.defer
= Function
.prototype.delay
.curry(0.01);
252 Date
.prototype.toJSON = function() {
253 return '"' + this.getUTCFullYear() + '-' +
254 (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
255 this.getUTCDate().toPaddedString(2) + 'T' +
256 this.getUTCHours().toPaddedString(2) + ':' +
257 this.getUTCMinutes().toPaddedString(2) + ':' +
258 this.getUTCSeconds().toPaddedString(2) + 'Z"';
265 for (var i
= 0, length
= arguments
.length
; i
< length
; i
++) {
266 var lambda
= arguments
[i
];
268 returnValue
= lambda();
277 RegExp
.prototype.match
= RegExp
.prototype.test
;
279 RegExp
.escape = function(str
) {
280 return String(str
).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
283 /*--------------------------------------------------------------------------*/
285 var PeriodicalExecuter
= Class
.create({
286 initialize: function(callback
, frequency
) {
287 this.callback
= callback
;
288 this.frequency
= frequency
;
289 this.currentlyExecuting
= false;
291 this.registerCallback();
294 registerCallback: function() {
295 this.timer
= setInterval(this.onTimerEvent
.bind(this), this.frequency
* 1000);
298 execute: function() {
303 if (!this.timer
) return;
304 clearInterval(this.timer
);
308 onTimerEvent: function() {
309 if (!this.currentlyExecuting
) {
311 this.currentlyExecuting
= true;
314 this.currentlyExecuting
= false;
319 Object
.extend(String
, {
320 interpret: function(value
) {
321 return value
== null ? '' : String(value
);
333 Object
.extend(String
.prototype, {
334 gsub: function(pattern
, replacement
) {
335 var result
= '', source
= this, match
;
336 replacement
= arguments
.callee
.prepareReplacement(replacement
);
338 while (source
.length
> 0) {
339 if (match
= source
.match(pattern
)) {
340 result
+= source
.slice(0, match
.index
);
341 result
+= String
.interpret(replacement(match
));
342 source
= source
.slice(match
.index
+ match
[0].length
);
344 result
+= source
, source
= '';
350 sub: function(pattern
, replacement
, count
) {
351 replacement
= this.gsub
.prepareReplacement(replacement
);
352 count
= Object
.isUndefined(count
) ? 1 : count
;
354 return this.gsub(pattern
, function(match
) {
355 if (--count
< 0) return match
[0];
356 return replacement(match
);
360 scan: function(pattern
, iterator
) {
361 this.gsub(pattern
, iterator
);
365 truncate: function(length
, truncation
) {
366 length
= length
|| 30;
367 truncation
= Object
.isUndefined(truncation
) ? '...' : truncation
;
368 return this.length
> length
?
369 this.slice(0, length
- truncation
.length
) + truncation
: String(this);
373 return this.replace(/^\s+/, '').replace(/\s+$/, '');
376 stripTags: function() {
377 return this.replace(/<\/?[^>]+>/gi, '');
380 stripScripts: function() {
381 return this.replace(new RegExp(Prototype
.ScriptFragment
, 'img'), '');
384 extractScripts: function() {
385 var matchAll
= new RegExp(Prototype
.ScriptFragment
, 'img');
386 var matchOne
= new RegExp(Prototype
.ScriptFragment
, 'im');
387 return (this.match(matchAll
) || []).map(function(scriptTag
) {
388 return (scriptTag
.match(matchOne
) || ['', ''])[1];
392 evalScripts: function() {
393 return this.extractScripts().map(function(script
) { return eval(script
) });
396 escapeHTML: function() {
397 var self
= arguments
.callee
;
398 self
.text
.data
= this;
399 return self
.div
.innerHTML
;
402 unescapeHTML: function() {
403 var div
= new Element('div');
404 div
.innerHTML
= this.stripTags();
405 return div
.childNodes
[0] ? (div
.childNodes
.length
> 1 ?
406 $A(div
.childNodes
).inject('', function(memo
, node
) { return memo
+node
.nodeValue
}) :
407 div
.childNodes
[0].nodeValue
) : '';
410 toQueryParams: function(separator
) {
411 var match
= this.strip().match(/([^?#]*)(#.*)?$/);
412 if (!match
) return { };
414 return match
[1].split(separator
|| '&').inject({ }, function(hash
, pair
) {
415 if ((pair
= pair
.split('='))[0]) {
416 var key
= decodeURIComponent(pair
.shift());
417 var value
= pair
.length
> 1 ? pair
.join('=') : pair
[0];
418 if (value
!= undefined) value
= decodeURIComponent(value
);
421 if (!Object
.isArray(hash
[key
])) hash
[key
] = [hash
[key
]];
422 hash
[key
].push(value
);
424 else hash
[key
] = value
;
430 toArray: function() {
431 return this.split('');
435 return this.slice(0, this.length
- 1) +
436 String
.fromCharCode(this.charCodeAt(this.length
- 1) + 1);
439 times: function(count
) {
440 return count
< 1 ? '' : new Array(count
+ 1).join(this);
443 camelize: function() {
444 var parts
= this.split('-'), len
= parts
.length
;
445 if (len
== 1) return parts
[0];
447 var camelized
= this.charAt(0) == '-'
448 ? parts
[0].charAt(0).toUpperCase() + parts
[0].substring(1)
451 for (var i
= 1; i
< len
; i
++)
452 camelized
+= parts
[i
].charAt(0).toUpperCase() + parts
[i
].substring(1);
457 capitalize: function() {
458 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
461 underscore: function() {
462 return this.gsub(/::/, '/').gsub(/([A
-Z
]+)([A
-Z
][a
-z
])/,'#{1}_#{2}').gsub(/([a
-z
\d
])([A
-Z
])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
465 dasherize: function() {
466 return this.gsub(/_
/,'-');
469 inspect: function(useDoubleQuotes
) {
470 var escapedString
= this.gsub(/[\x00-\x1f\\]/, function(match
) {
471 var character
= String
.specialChar
[match
[0]];
472 return character
? character
: '\\u00' + match
[0].charCodeAt().toPaddedString(2, 16);
474 if (useDoubleQuotes
) return '"' + escapedString
.replace(/"/g, '\\"') + '"';
475 return "'" + escapedString.replace(/'/g
, '\\\'') + "'";
479 return this.inspect(true);
482 unfilterJSON: function(filter
) {
483 return this.sub(filter
|| Prototype
.JSONFilter
, '#{1}');
488 if (str
.blank()) return false;
489 str
= this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
490 return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str
);
493 evalJSON: function(sanitize
) {
494 var json
= this.unfilterJSON();
496 if (!sanitize
|| json
.isJSON()) return eval('(' + json
+ ')');
498 throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
501 include: function(pattern
) {
502 return this.indexOf(pattern
) > -1;
505 startsWith: function(pattern
) {
506 return this.indexOf(pattern
) === 0;
509 endsWith: function(pattern
) {
510 var d
= this.length
- pattern
.length
;
511 return d
>= 0 && this.lastIndexOf(pattern
) === d
;
519 return /^\s*$/.test(this);
522 interpolate: function(object
, pattern
) {
523 return new Template(this, pattern
).evaluate(object
);
527 if (Prototype
.Browser
.WebKit
|| Prototype
.Browser
.IE
) Object
.extend(String
.prototype, {
528 escapeHTML: function() {
529 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g
,'>');
531 unescapeHTML: function() {
532 return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
536 String
.prototype.gsub
.prepareReplacement = function(replacement
) {
537 if (Object
.isFunction(replacement
)) return replacement
;
538 var template
= new Template(replacement
);
539 return function(match
) { return template
.evaluate(match
) };
542 String
.prototype.parseQuery
= String
.prototype.toQueryParams
;
544 Object
.extend(String
.prototype.escapeHTML
, {
545 div
: document
.createElement('div'),
546 text
: document
.createTextNode('')
549 with (String
.prototype.escapeHTML
) div
.appendChild(text
);
551 var Template
= Class
.create({
552 initialize: function(template
, pattern
) {
553 this.template
= template
.toString();
554 this.pattern
= pattern
|| Template
.Pattern
;
557 evaluate: function(object
) {
558 if (Object
.isFunction(object
.toTemplateReplacements
))
559 object
= object
.toTemplateReplacements();
561 return this.template
.gsub(this.pattern
, function(match
) {
562 if (object
== null) return '';
564 var before
= match
[1] || '';
565 if (before
== '\\') return match
[2];
567 var ctx
= object
, expr
= match
[3];
568 var pattern
= /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
569 match
= pattern
.exec(expr
);
570 if (match
== null) return before
;
572 while (match
!= null) {
573 var comp
= match
[1].startsWith('[') ? match
[2].gsub('\\\\]', ']') : match
[1];
575 if (null == ctx
|| '' == match
[3]) break;
576 expr
= expr
.substring('[' == match
[3] ? match
[1].length
: match
[0].length
);
577 match
= pattern
.exec(expr
);
580 return before
+ String
.interpret(ctx
);
584 Template
.Pattern
= /(^|.|\r|\n)(#\{(.*?)\})/;
589 each: function(iterator
, context
) {
591 iterator
= iterator
.bind(context
);
593 this._each(function(value
) {
594 iterator(value
, index
++);
597 if (e
!= $break) throw e
;
602 eachSlice: function(number
, iterator
, context
) {
603 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
604 var index
= -number
, slices
= [], array
= this.toArray();
605 while ((index
+= number
) < array
.length
)
606 slices
.push(array
.slice(index
, index
+number
));
607 return slices
.collect(iterator
, context
);
610 all: function(iterator
, context
) {
611 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
613 this.each(function(value
, index
) {
614 result
= result
&& !!iterator(value
, index
);
615 if (!result
) throw $break;
620 any: function(iterator
, context
) {
621 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
623 this.each(function(value
, index
) {
624 if (result
= !!iterator(value
, index
))
630 collect: function(iterator
, context
) {
631 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
633 this.each(function(value
, index
) {
634 results
.push(iterator(value
, index
));
639 detect: function(iterator
, context
) {
640 iterator
= iterator
.bind(context
);
642 this.each(function(value
, index
) {
643 if (iterator(value
, index
)) {
651 findAll: function(iterator
, context
) {
652 iterator
= iterator
.bind(context
);
654 this.each(function(value
, index
) {
655 if (iterator(value
, index
))
661 grep: function(filter
, iterator
, context
) {
662 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
665 if (Object
.isString(filter
))
666 filter
= new RegExp(filter
);
668 this.each(function(value
, index
) {
669 if (filter
.match(value
))
670 results
.push(iterator(value
, index
));
675 include: function(object
) {
676 if (Object
.isFunction(this.indexOf
))
677 if (this.indexOf(object
) != -1) return true;
680 this.each(function(value
) {
681 if (value
== object
) {
689 inGroupsOf: function(number
, fillWith
) {
690 fillWith
= Object
.isUndefined(fillWith
) ? null : fillWith
;
691 return this.eachSlice(number
, function(slice
) {
692 while(slice
.length
< number
) slice
.push(fillWith
);
697 inject: function(memo
, iterator
, context
) {
698 iterator
= iterator
.bind(context
);
699 this.each(function(value
, index
) {
700 memo
= iterator(memo
, value
, index
);
705 invoke: function(method
) {
706 var args
= $A(arguments
).slice(1);
707 return this.map(function(value
) {
708 return value
[method
].apply(value
, args
);
712 max: function(iterator
, context
) {
713 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
715 this.each(function(value
, index
) {
716 value
= iterator(value
, index
);
717 if (result
== null || value
>= result
)
723 min: function(iterator
, context
) {
724 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
726 this.each(function(value
, index
) {
727 value
= iterator(value
, index
);
728 if (result
== null || value
< result
)
734 partition: function(iterator
, context
) {
735 iterator
= iterator
? iterator
.bind(context
) : Prototype
.K
;
736 var trues
= [], falses
= [];
737 this.each(function(value
, index
) {
738 (iterator(value
, index
) ?
739 trues
: falses
).push(value
);
741 return [trues
, falses
];
744 pluck: function(property
) {
746 this.each(function(value
) {
747 results
.push(value
[property
]);
752 reject: function(iterator
, context
) {
753 iterator
= iterator
.bind(context
);
755 this.each(function(value
, index
) {
756 if (!iterator(value
, index
))
762 sortBy: function(iterator
, context
) {
763 iterator
= iterator
.bind(context
);
764 return this.map(function(value
, index
) {
765 return {value
: value
, criteria
: iterator(value
, index
)};
766 }).sort(function(left
, right
) {
767 var a
= left
.criteria
, b
= right
.criteria
;
768 return a
< b
? -1 : a
> b
? 1 : 0;
772 toArray: function() {
777 var iterator
= Prototype
.K
, args
= $A(arguments
);
778 if (Object
.isFunction(args
.last()))
779 iterator
= args
.pop();
781 var collections
= [this].concat(args
).map($A
);
782 return this.map(function(value
, index
) {
783 return iterator(collections
.pluck(index
));
788 return this.toArray().length
;
791 inspect: function() {
792 return '#<Enumerable:' + this.toArray().inspect() + '>';
796 Object
.extend(Enumerable
, {
797 map
: Enumerable
.collect
,
798 find
: Enumerable
.detect
,
799 select
: Enumerable
.findAll
,
800 filter
: Enumerable
.findAll
,
801 member
: Enumerable
.include
,
802 entries
: Enumerable
.toArray
,
803 every
: Enumerable
.all
,
806 function $A(iterable
) {
807 if (!iterable
) return [];
808 if (iterable
.toArray
) return iterable
.toArray();
809 var length
= iterable
.length
, results
= new Array(length
);
810 while (length
--) results
[length
] = iterable
[length
];
814 if (Prototype
.Browser
.WebKit
) {
815 function $A(iterable
) {
816 if (!iterable
) return [];
817 if (!(Object
.isFunction(iterable
) && iterable
== '[object NodeList]') &&
818 iterable
.toArray
) return iterable
.toArray();
819 var length
= iterable
.length
, results
= new Array(length
);
820 while (length
--) results
[length
] = iterable
[length
];
827 Object
.extend(Array
.prototype, Enumerable
);
829 if (!Array
.prototype._reverse
) Array
.prototype._reverse
= Array
.prototype.reverse
;
831 Object
.extend(Array
.prototype, {
832 _each: function(iterator
) {
833 for (var i
= 0, length
= this.length
; i
< length
; i
++)
847 return this[this.length
- 1];
850 compact: function() {
851 return this.select(function(value
) {
852 return value
!= null;
856 flatten: function() {
857 return this.inject([], function(array
, value
) {
858 return array
.concat(Object
.isArray(value
) ?
859 value
.flatten() : [value
]);
863 without: function() {
864 var values
= $A(arguments
);
865 return this.select(function(value
) {
866 return !values
.include(value
);
870 reverse: function(inline
) {
871 return (inline
!== false ? this : this.toArray())._reverse();
875 return this.length
> 1 ? this : this[0];
878 uniq: function(sorted
) {
879 return this.inject([], function(array
, value
, index
) {
880 if (0 == index
|| (sorted
? array
.last() != value
: !array
.include(value
)))
886 intersect: function(array
) {
887 return this.uniq().findAll(function(item
) {
888 return array
.detect(function(value
) { return item
=== value
});
893 return [].concat(this);
900 inspect: function() {
901 return '[' + this.map(Object
.inspect
).join(', ') + ']';
906 this.each(function(object
) {
907 var value
= Object
.toJSON(object
);
908 if (!Object
.isUndefined(value
)) results
.push(value
);
910 return '[' + results
.join(', ') + ']';
914 // use native browser JS 1.6 implementation if available
915 if (Object
.isFunction(Array
.prototype.forEach
))
916 Array
.prototype._each
= Array
.prototype.forEach
;
918 if (!Array
.prototype.indexOf
) Array
.prototype.indexOf = function(item
, i
) {
920 var length
= this.length
;
921 if (i
< 0) i
= length
+ i
;
922 for (; i
< length
; i
++)
923 if (this[i
] === item
) return i
;
927 if (!Array
.prototype.lastIndexOf
) Array
.prototype.lastIndexOf = function(item
, i
) {
928 i
= isNaN(i
) ? this.length
: (i
< 0 ? this.length
+ i
: i
) + 1;
929 var n
= this.slice(0, i
).reverse().indexOf(item
);
930 return (n
< 0) ? n
: i
- n
- 1;
933 Array
.prototype.toArray
= Array
.prototype.clone
;
935 function $w(string
) {
936 if (!Object
.isString(string
)) return [];
937 string
= string
.strip();
938 return string
? string
.split(/\s+/) : [];
941 if (Prototype
.Browser
.Opera
){
942 Array
.prototype.concat = function() {
944 for (var i
= 0, length
= this.length
; i
< length
; i
++) array
.push(this[i
]);
945 for (var i
= 0, length
= arguments
.length
; i
< length
; i
++) {
946 if (Object
.isArray(arguments
[i
])) {
947 for (var j
= 0, arrayLength
= arguments
[i
].length
; j
< arrayLength
; j
++)
948 array
.push(arguments
[i
][j
]);
950 array
.push(arguments
[i
]);
956 Object
.extend(Number
.prototype, {
957 toColorPart: function() {
958 return this.toPaddedString(2, 16);
965 times: function(iterator
) {
966 $R(0, this, true).each(iterator
);
970 toPaddedString: function(length
, radix
) {
971 var string
= this.toString(radix
|| 10);
972 return '0'.times(length
- string
.length
) + string
;
976 return isFinite(this) ? this.toString() : 'null';
980 $w('abs round ceil floor').each(function(method
){
981 Number
.prototype[method
] = Math
[method
].methodize();
983 function $H(object
) {
984 return new Hash(object
);
987 var Hash
= Class
.create(Enumerable
, (function() {
989 function toQueryPair(key
, value
) {
990 if (Object
.isUndefined(value
)) return key
;
991 return key
+ '=' + encodeURIComponent(String
.interpret(value
));
995 initialize: function(object
) {
996 this._object
= Object
.isHash(object
) ? object
.toObject() : Object
.clone(object
);
999 _each: function(iterator
) {
1000 for (var key
in this._object
) {
1001 var value
= this._object
[key
], pair
= [key
, value
];
1008 set: function(key
, value
) {
1009 return this._object
[key
] = value
;
1012 get: function(key
) {
1013 return this._object
[key
];
1016 unset: function(key
) {
1017 var value
= this._object
[key
];
1018 delete this._object
[key
];
1022 toObject: function() {
1023 return Object
.clone(this._object
);
1027 return this.pluck('key');
1030 values: function() {
1031 return this.pluck('value');
1034 index: function(value
) {
1035 var match
= this.detect(function(pair
) {
1036 return pair
.value
=== value
;
1038 return match
&& match
.key
;
1041 merge: function(object
) {
1042 return this.clone().update(object
);
1045 update: function(object
) {
1046 return new Hash(object
).inject(this, function(result
, pair
) {
1047 result
.set(pair
.key
, pair
.value
);
1052 toQueryString: function() {
1053 return this.map(function(pair
) {
1054 var key
= encodeURIComponent(pair
.key
), values
= pair
.value
;
1056 if (values
&& typeof values
== 'object') {
1057 if (Object
.isArray(values
))
1058 return values
.map(toQueryPair
.curry(key
)).join('&');
1060 return toQueryPair(key
, values
);
1064 inspect: function() {
1065 return '#<Hash:{' + this.map(function(pair
) {
1066 return pair
.map(Object
.inspect
).join(': ');
1067 }).join(', ') + '}>';
1070 toJSON: function() {
1071 return Object
.toJSON(this.toObject());
1075 return new Hash(this);
1080 Hash
.prototype.toTemplateReplacements
= Hash
.prototype.toObject
;
1082 var ObjectRange
= Class
.create(Enumerable
, {
1083 initialize: function(start
, end
, exclusive
) {
1086 this.exclusive
= exclusive
;
1089 _each: function(iterator
) {
1090 var value
= this.start
;
1091 while (this.include(value
)) {
1093 value
= value
.succ();
1097 include: function(value
) {
1098 if (value
< this.start
)
1101 return value
< this.end
;
1102 return value
<= this.end
;
1106 var $R = function(start
, end
, exclusive
) {
1107 return new ObjectRange(start
, end
, exclusive
);
1111 getTransport: function() {
1113 function() {return new XMLHttpRequest()},
1114 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1115 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1119 activeRequestCount
: 0
1125 _each: function(iterator
) {
1126 this.responders
._each(iterator
);
1129 register: function(responder
) {
1130 if (!this.include(responder
))
1131 this.responders
.push(responder
);
1134 unregister: function(responder
) {
1135 this.responders
= this.responders
.without(responder
);
1138 dispatch: function(callback
, request
, transport
, json
) {
1139 this.each(function(responder
) {
1140 if (Object
.isFunction(responder
[callback
])) {
1142 responder
[callback
].apply(responder
, [request
, transport
, json
]);
1149 Object
.extend(Ajax
.Responders
, Enumerable
);
1151 Ajax
.Responders
.register({
1152 onCreate: function() { Ajax
.activeRequestCount
++ },
1153 onComplete: function() { Ajax
.activeRequestCount
-- }
1156 Ajax
.Base
= Class
.create({
1157 initialize: function(options
) {
1161 contentType
: 'application/x-www-form-urlencoded',
1167 Object
.extend(this.options
, options
|| { });
1169 this.options
.method
= this.options
.method
.toLowerCase();
1171 if (Object
.isString(this.options
.parameters
))
1172 this.options
.parameters
= this.options
.parameters
.toQueryParams();
1173 else if (Object
.isHash(this.options
.parameters
))
1174 this.options
.parameters
= this.options
.parameters
.toObject();
1178 Ajax
.Request
= Class
.create(Ajax
.Base
, {
1181 initialize: function($super, url
, options
) {
1183 this.transport
= Ajax
.getTransport();
1187 request: function(url
) {
1189 this.method
= this.options
.method
;
1190 var params
= Object
.clone(this.options
.parameters
);
1192 if (!['get', 'post'].include(this.method
)) {
1193 // simulate other verbs over post
1194 params
['_method'] = this.method
;
1195 this.method
= 'post';
1198 this.parameters
= params
;
1200 if (params
= Object
.toQueryString(params
)) {
1201 // when GET, append parameters to URL
1202 if (this.method
== 'get')
1203 this.url
+= (this.url
.include('?') ? '&' : '?') + params
;
1204 else if (/Konqueror|Safari|KHTML/.test(navigator
.userAgent
))
1209 var response
= new Ajax
.Response(this);
1210 if (this.options
.onCreate
) this.options
.onCreate(response
);
1211 Ajax
.Responders
.dispatch('onCreate', this, response
);
1213 this.transport
.open(this.method
.toUpperCase(), this.url
,
1214 this.options
.asynchronous
);
1216 if (this.options
.asynchronous
) this.respondToReadyState
.bind(this).defer(1);
1218 this.transport
.onreadystatechange
= this.onStateChange
.bind(this);
1219 this.setRequestHeaders();
1221 this.body
= this.method
== 'post' ? (this.options
.postBody
|| params
) : null;
1222 this.transport
.send(this.body
);
1224 /* Force Firefox to handle ready state 4 for synchronous requests */
1225 if (!this.options
.asynchronous
&& this.transport
.overrideMimeType
)
1226 this.onStateChange();
1230 this.dispatchException(e
);
1234 onStateChange: function() {
1235 var readyState
= this.transport
.readyState
;
1236 if (readyState
> 1 && !((readyState
== 4) && this._complete
))
1237 this.respondToReadyState(this.transport
.readyState
);
1240 setRequestHeaders: function() {
1242 'X-Requested-With': 'XMLHttpRequest',
1243 'X-Prototype-Version': Prototype
.Version
,
1244 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1247 if (this.method
== 'post') {
1248 headers
['Content-type'] = this.options
.contentType
+
1249 (this.options
.encoding
? '; charset=' + this.options
.encoding
: '');
1251 /* Force "Connection: close" for older Mozilla browsers to work
1252 * around a bug where XMLHttpRequest sends an incorrect
1253 * Content-length header. See Mozilla Bugzilla #246651.
1255 if (this.transport
.overrideMimeType
&&
1256 (navigator
.userAgent
.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1257 headers
['Connection'] = 'close';
1260 // user-defined headers
1261 if (typeof this.options
.requestHeaders
== 'object') {
1262 var extras
= this.options
.requestHeaders
;
1264 if (Object
.isFunction(extras
.push
))
1265 for (var i
= 0, length
= extras
.length
; i
< length
; i
+= 2)
1266 headers
[extras
[i
]] = extras
[i
+1];
1268 $H(extras
).each(function(pair
) { headers
[pair
.key
] = pair
.value
});
1271 for (var name
in headers
)
1272 this.transport
.setRequestHeader(name
, headers
[name
]);
1275 success: function() {
1276 var status
= this.getStatus();
1277 return !status
|| (status
>= 200 && status
< 300);
1280 getStatus: function() {
1282 return this.transport
.status
|| 0;
1283 } catch (e
) { return 0 }
1286 respondToReadyState: function(readyState
) {
1287 var state
= Ajax
.Request
.Events
[readyState
], response
= new Ajax
.Response(this);
1289 if (state
== 'Complete') {
1291 this._complete
= true;
1292 (this.options
['on' + response
.status
]
1293 || this.options
['on' + (this.success() ? 'Success' : 'Failure')]
1294 || Prototype
.emptyFunction
)(response
, response
.headerJSON
);
1296 this.dispatchException(e
);
1299 var contentType
= response
.getHeader('Content-type');
1300 if (this.options
.evalJS
== 'force'
1301 || (this.options
.evalJS
&& contentType
1302 && contentType
.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1303 this.evalResponse();
1307 (this.options
['on' + state
] || Prototype
.emptyFunction
)(response
, response
.headerJSON
);
1308 Ajax
.Responders
.dispatch('on' + state
, this, response
, response
.headerJSON
);
1310 this.dispatchException(e
);
1313 if (state
== 'Complete') {
1314 // avoid memory leak in MSIE: clean up
1315 this.transport
.onreadystatechange
= Prototype
.emptyFunction
;
1319 getHeader: function(name
) {
1321 return this.transport
.getResponseHeader(name
);
1322 } catch (e
) { return null }
1325 evalResponse: function() {
1327 return eval((this.transport
.responseText
|| '').unfilterJSON());
1329 this.dispatchException(e
);
1333 dispatchException: function(exception
) {
1334 (this.options
.onException
|| Prototype
.emptyFunction
)(this, exception
);
1335 Ajax
.Responders
.dispatch('onException', this, exception
);
1339 Ajax
.Request
.Events
=
1340 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1342 Ajax
.Response
= Class
.create({
1343 initialize: function(request
){
1344 this.request
= request
;
1345 var transport
= this.transport
= request
.transport
,
1346 readyState
= this.readyState
= transport
.readyState
;
1348 if((readyState
> 2 && !Prototype
.Browser
.IE
) || readyState
== 4) {
1349 this.status
= this.getStatus();
1350 this.statusText
= this.getStatusText();
1351 this.responseText
= String
.interpret(transport
.responseText
);
1352 this.headerJSON
= this._getHeaderJSON();
1355 if(readyState
== 4) {
1356 var xml
= transport
.responseXML
;
1357 this.responseXML
= Object
.isUndefined(xml
) ? null : xml
;
1358 this.responseJSON
= this._getResponseJSON();
1365 getStatus
: Ajax
.Request
.prototype.getStatus
,
1367 getStatusText: function() {
1369 return this.transport
.statusText
|| '';
1370 } catch (e
) { return '' }
1373 getHeader
: Ajax
.Request
.prototype.getHeader
,
1375 getAllHeaders: function() {
1377 return this.getAllResponseHeaders();
1378 } catch (e
) { return null }
1381 getResponseHeader: function(name
) {
1382 return this.transport
.getResponseHeader(name
);
1385 getAllResponseHeaders: function() {
1386 return this.transport
.getAllResponseHeaders();
1389 _getHeaderJSON: function() {
1390 var json
= this.getHeader('X-JSON');
1391 if (!json
) return null;
1392 json
= decodeURIComponent(escape(json
));
1394 return json
.evalJSON(this.request
.options
.sanitizeJSON
);
1396 this.request
.dispatchException(e
);
1400 _getResponseJSON: function() {
1401 var options
= this.request
.options
;
1402 if (!options
.evalJSON
|| (options
.evalJSON
!= 'force' &&
1403 !(this.getHeader('Content-type') || '').include('application/json')) ||
1404 this.responseText
.blank())
1407 return this.responseText
.evalJSON(options
.sanitizeJSON
);
1409 this.request
.dispatchException(e
);
1414 Ajax
.Updater
= Class
.create(Ajax
.Request
, {
1415 initialize: function($super, container
, url
, options
) {
1417 success
: (container
.success
|| container
),
1418 failure
: (container
.failure
|| (container
.success
? null : container
))
1421 options
= Object
.clone(options
);
1422 var onComplete
= options
.onComplete
;
1423 options
.onComplete
= (function(response
, json
) {
1424 this.updateContent(response
.responseText
);
1425 if (Object
.isFunction(onComplete
)) onComplete(response
, json
);
1428 $super(url
, options
);
1431 updateContent: function(responseText
) {
1432 var receiver
= this.container
[this.success() ? 'success' : 'failure'],
1433 options
= this.options
;
1435 if (!options
.evalScripts
) responseText
= responseText
.stripScripts();
1437 if (receiver
= $(receiver
)) {
1438 if (options
.insertion
) {
1439 if (Object
.isString(options
.insertion
)) {
1440 var insertion
= { }; insertion
[options
.insertion
] = responseText
;
1441 receiver
.insert(insertion
);
1443 else options
.insertion(receiver
, responseText
);
1445 else receiver
.update(responseText
);
1450 Ajax
.PeriodicalUpdater
= Class
.create(Ajax
.Base
, {
1451 initialize: function($super, container
, url
, options
) {
1453 this.onComplete
= this.options
.onComplete
;
1455 this.frequency
= (this.options
.frequency
|| 2);
1456 this.decay
= (this.options
.decay
|| 1);
1459 this.container
= container
;
1466 this.options
.onComplete
= this.updateComplete
.bind(this);
1467 this.onTimerEvent();
1471 this.updater
.options
.onComplete
= undefined;
1472 clearTimeout(this.timer
);
1473 (this.onComplete
|| Prototype
.emptyFunction
).apply(this, arguments
);
1476 updateComplete: function(response
) {
1477 if (this.options
.decay
) {
1478 this.decay
= (response
.responseText
== this.lastText
?
1479 this.decay
* this.options
.decay
: 1);
1481 this.lastText
= response
.responseText
;
1483 this.timer
= this.onTimerEvent
.bind(this).delay(this.decay
* this.frequency
);
1486 onTimerEvent: function() {
1487 this.updater
= new Ajax
.Updater(this.container
, this.url
, this.options
);
1490 function $(element
) {
1491 if (arguments
.length
> 1) {
1492 for (var i
= 0, elements
= [], length
= arguments
.length
; i
< length
; i
++)
1493 elements
.push($(arguments
[i
]));
1496 if (Object
.isString(element
))
1497 element
= document
.getElementById(element
);
1498 return Element
.extend(element
);
1501 if (Prototype
.BrowserFeatures
.XPath
) {
1502 document
._getElementsByXPath = function(expression
, parentElement
) {
1504 var query
= document
.evaluate(expression
, $(parentElement
) || document
,
1505 null, XPathResult
.ORDERED_NODE_SNAPSHOT_TYPE
, null);
1506 for (var i
= 0, length
= query
.snapshotLength
; i
< length
; i
++)
1507 results
.push(Element
.extend(query
.snapshotItem(i
)));
1512 /*--------------------------------------------------------------------------*/
1514 if (!window
.Node
) var Node
= { };
1516 if (!Node
.ELEMENT_NODE
) {
1517 // DOM level 2 ECMAScript Language Binding
1518 Object
.extend(Node
, {
1522 CDATA_SECTION_NODE
: 4,
1523 ENTITY_REFERENCE_NODE
: 5,
1525 PROCESSING_INSTRUCTION_NODE
: 7,
1528 DOCUMENT_TYPE_NODE
: 10,
1529 DOCUMENT_FRAGMENT_NODE
: 11,
1535 var element
= this.Element
;
1536 this.Element = function(tagName
, attributes
) {
1537 attributes
= attributes
|| { };
1538 tagName
= tagName
.toLowerCase();
1539 var cache
= Element
.cache
;
1540 if (Prototype
.Browser
.IE
&& attributes
.name
) {
1541 tagName
= '<' + tagName
+ ' name="' + attributes
.name
+ '">';
1542 delete attributes
.name
;
1543 return Element
.writeAttribute(document
.createElement(tagName
), attributes
);
1545 if (!cache
[tagName
]) cache
[tagName
] = Element
.extend(document
.createElement(tagName
));
1546 return Element
.writeAttribute(cache
[tagName
].cloneNode(false), attributes
);
1548 Object
.extend(this.Element
, element
|| { });
1551 Element
.cache
= { };
1554 visible: function(element
) {
1555 return $(element
).style
.display
!= 'none';
1558 toggle: function(element
) {
1559 element
= $(element
);
1560 Element
[Element
.visible(element
) ? 'hide' : 'show'](element
);
1564 hide: function(element
) {
1565 $(element
).style
.display
= 'none';
1569 show: function(element
) {
1570 $(element
).style
.display
= '';
1574 remove: function(element
) {
1575 element
= $(element
);
1576 element
.parentNode
.removeChild(element
);
1580 update: function(element
, content
) {
1581 element
= $(element
);
1582 if (content
&& content
.toElement
) content
= content
.toElement();
1583 if (Object
.isElement(content
)) return element
.update().insert(content
);
1584 content
= Object
.toHTML(content
);
1585 element
.innerHTML
= content
.stripScripts();
1586 content
.evalScripts
.bind(content
).defer();
1590 replace: function(element
, content
) {
1591 element
= $(element
);
1592 if (content
&& content
.toElement
) content
= content
.toElement();
1593 else if (!Object
.isElement(content
)) {
1594 content
= Object
.toHTML(content
);
1595 var range
= element
.ownerDocument
.createRange();
1596 range
.selectNode(element
);
1597 content
.evalScripts
.bind(content
).defer();
1598 content
= range
.createContextualFragment(content
.stripScripts());
1600 element
.parentNode
.replaceChild(content
, element
);
1604 insert: function(element
, insertions
) {
1605 element
= $(element
);
1607 if (Object
.isString(insertions
) || Object
.isNumber(insertions
) ||
1608 Object
.isElement(insertions
) || (insertions
&& (insertions
.toElement
|| insertions
.toHTML
)))
1609 insertions
= {bottom
:insertions
};
1611 var content
, t
, range
;
1613 for (position
in insertions
) {
1614 content
= insertions
[position
];
1615 position
= position
.toLowerCase();
1616 t
= Element
._insertionTranslations
[position
];
1618 if (content
&& content
.toElement
) content
= content
.toElement();
1619 if (Object
.isElement(content
)) {
1620 t
.insert(element
, content
);
1624 content
= Object
.toHTML(content
);
1626 range
= element
.ownerDocument
.createRange();
1627 t
.initializeRange(element
, range
);
1628 t
.insert(element
, range
.createContextualFragment(content
.stripScripts()));
1630 content
.evalScripts
.bind(content
).defer();
1636 wrap: function(element
, wrapper
, attributes
) {
1637 element
= $(element
);
1638 if (Object
.isElement(wrapper
))
1639 $(wrapper
).writeAttribute(attributes
|| { });
1640 else if (Object
.isString(wrapper
)) wrapper
= new Element(wrapper
, attributes
);
1641 else wrapper
= new Element('div', wrapper
);
1642 if (element
.parentNode
)
1643 element
.parentNode
.replaceChild(wrapper
, element
);
1644 wrapper
.appendChild(element
);
1648 inspect: function(element
) {
1649 element
= $(element
);
1650 var result
= '<' + element
.tagName
.toLowerCase();
1651 $H({'id': 'id', 'className': 'class'}).each(function(pair
) {
1652 var property
= pair
.first(), attribute
= pair
.last();
1653 var value
= (element
[property
] || '').toString();
1654 if (value
) result
+= ' ' + attribute
+ '=' + value
.inspect(true);
1656 return result
+ '>';
1659 recursivelyCollect: function(element
, property
) {
1660 element
= $(element
);
1662 while (element
= element
[property
])
1663 if (element
.nodeType
== 1)
1664 elements
.push(Element
.extend(element
));
1668 ancestors: function(element
) {
1669 return $(element
).recursivelyCollect('parentNode');
1672 descendants: function(element
) {
1673 return $(element
).getElementsBySelector("*");
1676 firstDescendant: function(element
) {
1677 element
= $(element
).firstChild
;
1678 while (element
&& element
.nodeType
!= 1) element
= element
.nextSibling
;
1682 immediateDescendants: function(element
) {
1683 if (!(element
= $(element
).firstChild
)) return [];
1684 while (element
&& element
.nodeType
!= 1) element
= element
.nextSibling
;
1685 if (element
) return [element
].concat($(element
).nextSiblings());
1689 previousSiblings: function(element
) {
1690 return $(element
).recursivelyCollect('previousSibling');
1693 nextSiblings: function(element
) {
1694 return $(element
).recursivelyCollect('nextSibling');
1697 siblings: function(element
) {
1698 element
= $(element
);
1699 return element
.previousSiblings().reverse().concat(element
.nextSiblings());
1702 match: function(element
, selector
) {
1703 if (Object
.isString(selector
))
1704 selector
= new Selector(selector
);
1705 return selector
.match($(element
));
1708 up: function(element
, expression
, index
) {
1709 element
= $(element
);
1710 if (arguments
.length
== 1) return $(element
.parentNode
);
1711 var ancestors
= element
.ancestors();
1712 return expression
? Selector
.findElement(ancestors
, expression
, index
) :
1713 ancestors
[index
|| 0];
1716 down: function(element
, expression
, index
) {
1717 element
= $(element
);
1718 if (arguments
.length
== 1) return element
.firstDescendant();
1719 var descendants
= element
.descendants();
1720 return expression
? Selector
.findElement(descendants
, expression
, index
) :
1721 descendants
[index
|| 0];
1724 previous: function(element
, expression
, index
) {
1725 element
= $(element
);
1726 if (arguments
.length
== 1) return $(Selector
.handlers
.previousElementSibling(element
));
1727 var previousSiblings
= element
.previousSiblings();
1728 return expression
? Selector
.findElement(previousSiblings
, expression
, index
) :
1729 previousSiblings
[index
|| 0];
1732 next: function(element
, expression
, index
) {
1733 element
= $(element
);
1734 if (arguments
.length
== 1) return $(Selector
.handlers
.nextElementSibling(element
));
1735 var nextSiblings
= element
.nextSiblings();
1736 return expression
? Selector
.findElement(nextSiblings
, expression
, index
) :
1737 nextSiblings
[index
|| 0];
1740 select: function() {
1741 var args
= $A(arguments
), element
= $(args
.shift());
1742 return Selector
.findChildElements(element
, args
);
1745 adjacent: function() {
1746 var args
= $A(arguments
), element
= $(args
.shift());
1747 return Selector
.findChildElements(element
.parentNode
, args
).without(element
);
1750 identify: function(element
) {
1751 element
= $(element
);
1752 var id
= element
.readAttribute('id'), self
= arguments
.callee
;
1754 do { id
= 'anonymous_element_' + self
.counter
++ } while ($(id
));
1755 element
.writeAttribute('id', id
);
1759 readAttribute: function(element
, name
) {
1760 element
= $(element
);
1761 if (Prototype
.Browser
.IE
) {
1762 var t
= Element
._attributeTranslations
.read
;
1763 if (t
.values
[name
]) return t
.values
[name
](element
, name
);
1764 if (t
.names
[name
]) name
= t
.names
[name
];
1765 if (name
.include(':')) {
1766 return (!element
.attributes
|| !element
.attributes
[name
]) ? null :
1767 element
.attributes
[name
].value
;
1770 return element
.getAttribute(name
);
1773 writeAttribute: function(element
, name
, value
) {
1774 element
= $(element
);
1775 var attributes
= { }, t
= Element
._attributeTranslations
.write
;
1777 if (typeof name
== 'object') attributes
= name
;
1778 else attributes
[name
] = Object
.isUndefined(value
) ? true : value
;
1780 for (var attr
in attributes
) {
1781 name
= t
.names
[attr
] || attr
;
1782 value
= attributes
[attr
];
1783 if (t
.values
[attr
]) name
= t
.values
[attr
](element
, value
);
1784 if (value
=== false || value
=== null)
1785 element
.removeAttribute(name
);
1786 else if (value
=== true)
1787 element
.setAttribute(name
, name
);
1788 else element
.setAttribute(name
, value
);
1793 getHeight: function(element
) {
1794 return $(element
).getDimensions().height
;
1797 getWidth: function(element
) {
1798 return $(element
).getDimensions().width
;
1801 classNames: function(element
) {
1802 return new Element
.ClassNames(element
);
1805 hasClassName: function(element
, className
) {
1806 if (!(element
= $(element
))) return;
1807 var elementClassName
= element
.className
;
1808 return (elementClassName
.length
> 0 && (elementClassName
== className
||
1809 new RegExp("(^|\\s)" + className
+ "(\\s|$)").test(elementClassName
)));
1812 addClassName: function(element
, className
) {
1813 if (!(element
= $(element
))) return;
1814 if (!element
.hasClassName(className
))
1815 element
.className
+= (element
.className
? ' ' : '') + className
;
1819 removeClassName: function(element
, className
) {
1820 if (!(element
= $(element
))) return;
1821 element
.className
= element
.className
.replace(
1822 new RegExp("(^|\\s+)" + className
+ "(\\s+|$)"), ' ').strip();
1826 toggleClassName: function(element
, className
) {
1827 if (!(element
= $(element
))) return;
1828 return element
[element
.hasClassName(className
) ?
1829 'removeClassName' : 'addClassName'](className
);
1832 // removes whitespace-only text node children
1833 cleanWhitespace: function(element
) {
1834 element
= $(element
);
1835 var node
= element
.firstChild
;
1837 var nextNode
= node
.nextSibling
;
1838 if (node
.nodeType
== 3 && !/\S/.test(node
.nodeValue
))
1839 element
.removeChild(node
);
1845 empty: function(element
) {
1846 return $(element
).innerHTML
.blank();
1849 descendantOf: function(element
, ancestor
) {
1850 element
= $(element
), ancestor
= $(ancestor
);
1851 var originalAncestor
= ancestor
;
1853 if (element
.compareDocumentPosition
)
1854 return (element
.compareDocumentPosition(ancestor
) & 8) === 8;
1856 if (element
.sourceIndex
&& !Prototype
.Browser
.Opera
) {
1857 var e
= element
.sourceIndex
, a
= ancestor
.sourceIndex
,
1858 nextAncestor
= ancestor
.nextSibling
;
1859 if (!nextAncestor
) {
1860 do { ancestor
= ancestor
.parentNode
; }
1861 while (!(nextAncestor
= ancestor
.nextSibling
) && ancestor
.parentNode
);
1863 if (nextAncestor
) return (e
> a
&& e
< nextAncestor
.sourceIndex
);
1866 while (element
= element
.parentNode
)
1867 if (element
== originalAncestor
) return true;
1871 scrollTo: function(element
) {
1872 element
= $(element
);
1873 var pos
= element
.cumulativeOffset();
1874 window
.scrollTo(pos
[0], pos
[1]);
1878 getStyle: function(element
, style
) {
1879 element
= $(element
);
1880 style
= style
== 'float' ? 'cssFloat' : style
.camelize();
1881 var value
= element
.style
[style
];
1883 var css
= document
.defaultView
.getComputedStyle(element
, null);
1884 value
= css
? css
[style
] : null;
1886 if (style
== 'opacity') return value
? parseFloat(value
) : 1.0;
1887 return value
== 'auto' ? null : value
;
1890 getOpacity: function(element
) {
1891 return $(element
).getStyle('opacity');
1894 setStyle: function(element
, styles
) {
1895 element
= $(element
);
1896 var elementStyle
= element
.style
, match
;
1897 if (Object
.isString(styles
)) {
1898 element
.style
.cssText
+= ';' + styles
;
1899 return styles
.include('opacity') ?
1900 element
.setOpacity(styles
.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element
;
1902 for (var property
in styles
)
1903 if (property
== 'opacity') element
.setOpacity(styles
[property
]);
1905 elementStyle
[(property
== 'float' || property
== 'cssFloat') ?
1906 (Object
.isUndefined(elementStyle
.styleFloat
) ? 'cssFloat' : 'styleFloat') :
1907 property
] = styles
[property
];
1912 setOpacity: function(element
, value
) {
1913 element
= $(element
);
1914 element
.style
.opacity
= (value
== 1 || value
=== '') ? '' :
1915 (value
< 0.00001) ? 0 : value
;
1919 getDimensions: function(element
) {
1920 element
= $(element
);
1921 var display
= $(element
).getStyle('display');
1922 if (display
!= 'none' && display
!= null) // Safari bug
1923 return {width
: element
.offsetWidth
, height
: element
.offsetHeight
};
1925 // All *Width and *Height properties give 0 on elements with display none,
1926 // so enable the element temporarily
1927 var els
= element
.style
;
1928 var originalVisibility
= els
.visibility
;
1929 var originalPosition
= els
.position
;
1930 var originalDisplay
= els
.display
;
1931 els
.visibility
= 'hidden';
1932 els
.position
= 'absolute';
1933 els
.display
= 'block';
1934 var originalWidth
= element
.clientWidth
;
1935 var originalHeight
= element
.clientHeight
;
1936 els
.display
= originalDisplay
;
1937 els
.position
= originalPosition
;
1938 els
.visibility
= originalVisibility
;
1939 return {width
: originalWidth
, height
: originalHeight
};
1942 makePositioned: function(element
) {
1943 element
= $(element
);
1944 var pos
= Element
.getStyle(element
, 'position');
1945 if (pos
== 'static' || !pos
) {
1946 element
._madePositioned
= true;
1947 element
.style
.position
= 'relative';
1948 // Opera returns the offset relative to the positioning context, when an
1949 // element is position relative but top and left have not been defined
1951 element
.style
.top
= 0;
1952 element
.style
.left
= 0;
1958 undoPositioned: function(element
) {
1959 element
= $(element
);
1960 if (element
._madePositioned
) {
1961 element
._madePositioned
= undefined;
1962 element
.style
.position
=
1964 element
.style
.left
=
1965 element
.style
.bottom
=
1966 element
.style
.right
= '';
1971 makeClipping: function(element
) {
1972 element
= $(element
);
1973 if (element
._overflow
) return element
;
1974 element
._overflow
= Element
.getStyle(element
, 'overflow') || 'auto';
1975 if (element
._overflow
!== 'hidden')
1976 element
.style
.overflow
= 'hidden';
1980 undoClipping: function(element
) {
1981 element
= $(element
);
1982 if (!element
._overflow
) return element
;
1983 element
.style
.overflow
= element
._overflow
== 'auto' ? '' : element
._overflow
;
1984 element
._overflow
= null;
1988 cumulativeOffset: function(element
) {
1989 var valueT
= 0, valueL
= 0;
1991 valueT
+= element
.offsetTop
|| 0;
1992 valueL
+= element
.offsetLeft
|| 0;
1993 element
= element
.offsetParent
;
1995 return Element
._returnOffset(valueL
, valueT
);
1998 positionedOffset: function(element
) {
1999 var valueT
= 0, valueL
= 0;
2001 valueT
+= element
.offsetTop
|| 0;
2002 valueL
+= element
.offsetLeft
|| 0;
2003 element
= element
.offsetParent
;
2005 if (element
.tagName
== 'BODY') break;
2006 var p
= Element
.getStyle(element
, 'position');
2007 if (p
== 'relative' || p
== 'absolute') break;
2010 return Element
._returnOffset(valueL
, valueT
);
2013 absolutize: function(element
) {
2014 element
= $(element
);
2015 if (element
.getStyle('position') == 'absolute') return;
2016 // Position.prepare(); // To be done manually by Scripty when it needs it.
2018 var offsets
= element
.positionedOffset();
2019 var top
= offsets
[1];
2020 var left
= offsets
[0];
2021 var width
= element
.clientWidth
;
2022 var height
= element
.clientHeight
;
2024 element
._originalLeft
= left
- parseFloat(element
.style
.left
|| 0);
2025 element
._originalTop
= top
- parseFloat(element
.style
.top
|| 0);
2026 element
._originalWidth
= element
.style
.width
;
2027 element
._originalHeight
= element
.style
.height
;
2029 element
.style
.position
= 'absolute';
2030 element
.style
.top
= top
+ 'px';
2031 element
.style
.left
= left
+ 'px';
2032 element
.style
.width
= width
+ 'px';
2033 element
.style
.height
= height
+ 'px';
2037 relativize: function(element
) {
2038 element
= $(element
);
2039 if (element
.getStyle('position') == 'relative') return;
2040 // Position.prepare(); // To be done manually by Scripty when it needs it.
2042 element
.style
.position
= 'relative';
2043 var top
= parseFloat(element
.style
.top
|| 0) - (element
._originalTop
|| 0);
2044 var left
= parseFloat(element
.style
.left
|| 0) - (element
._originalLeft
|| 0);
2046 element
.style
.top
= top
+ 'px';
2047 element
.style
.left
= left
+ 'px';
2048 element
.style
.height
= element
._originalHeight
;
2049 element
.style
.width
= element
._originalWidth
;
2053 cumulativeScrollOffset: function(element
) {
2054 var valueT
= 0, valueL
= 0;
2056 valueT
+= element
.scrollTop
|| 0;
2057 valueL
+= element
.scrollLeft
|| 0;
2058 element
= element
.parentNode
;
2060 return Element
._returnOffset(valueL
, valueT
);
2063 getOffsetParent: function(element
) {
2064 if (element
.offsetParent
) return $(element
.offsetParent
);
2065 if (element
== document
.body
) return $(element
);
2067 while ((element
= element
.parentNode
) && element
!= document
.body
)
2068 if (Element
.getStyle(element
, 'position') != 'static')
2071 return $(document
.body
);
2074 viewportOffset: function(forElement
) {
2075 var valueT
= 0, valueL
= 0;
2077 var element
= forElement
;
2079 valueT
+= element
.offsetTop
|| 0;
2080 valueL
+= element
.offsetLeft
|| 0;
2083 if (element
.offsetParent
== document
.body
&&
2084 Element
.getStyle(element
, 'position') == 'absolute') break;
2086 } while (element
= element
.offsetParent
);
2088 element
= forElement
;
2090 if (!Prototype
.Browser
.Opera
|| element
.tagName
== 'BODY') {
2091 valueT
-= element
.scrollTop
|| 0;
2092 valueL
-= element
.scrollLeft
|| 0;
2094 } while (element
= element
.parentNode
);
2096 return Element
._returnOffset(valueL
, valueT
);
2099 clonePosition: function(element
, source
) {
2100 var options
= Object
.extend({
2107 }, arguments
[2] || { });
2109 // find page position of source
2111 var p
= source
.viewportOffset();
2113 // find coordinate system to use
2114 element
= $(element
);
2117 // delta [0,0] will do fine with position: fixed elements,
2118 // position:absolute needs offsetParent deltas
2119 if (Element
.getStyle(element
, 'position') == 'absolute') {
2120 parent
= element
.getOffsetParent();
2121 delta
= parent
.viewportOffset();
2124 // correct by body offsets (fixes Safari)
2125 if (parent
== document
.body
) {
2126 delta
[0] -= document
.body
.offsetLeft
;
2127 delta
[1] -= document
.body
.offsetTop
;
2131 if (options
.setLeft
) element
.style
.left
= (p
[0] - delta
[0] + options
.offsetLeft
) + 'px';
2132 if (options
.setTop
) element
.style
.top
= (p
[1] - delta
[1] + options
.offsetTop
) + 'px';
2133 if (options
.setWidth
) element
.style
.width
= source
.offsetWidth
+ 'px';
2134 if (options
.setHeight
) element
.style
.height
= source
.offsetHeight
+ 'px';
2139 Element
.Methods
.identify
.counter
= 1;
2141 Object
.extend(Element
.Methods
, {
2142 getElementsBySelector
: Element
.Methods
.select
,
2143 childElements
: Element
.Methods
.immediateDescendants
2146 Element
._attributeTranslations
= {
2157 if (!document
.createRange
|| Prototype
.Browser
.Opera
) {
2158 Element
.Methods
.insert = function(element
, insertions
) {
2159 element
= $(element
);
2161 if (Object
.isString(insertions
) || Object
.isNumber(insertions
) ||
2162 Object
.isElement(insertions
) || (insertions
&& (insertions
.toElement
|| insertions
.toHTML
)))
2163 insertions
= { bottom
: insertions
};
2165 var t
= Element
._insertionTranslations
, content
, position
, pos
, tagName
;
2167 for (position
in insertions
) {
2168 content
= insertions
[position
];
2169 position
= position
.toLowerCase();
2172 if (content
&& content
.toElement
) content
= content
.toElement();
2173 if (Object
.isElement(content
)) {
2174 pos
.insert(element
, content
);
2178 content
= Object
.toHTML(content
);
2179 tagName
= ((position
== 'before' || position
== 'after')
2180 ? element
.parentNode
: element
).tagName
.toUpperCase();
2182 if (t
.tags
[tagName
]) {
2183 var fragments
= Element
._getContentFromAnonymousElement(tagName
, content
.stripScripts());
2184 if (position
== 'top' || position
== 'after') fragments
.reverse();
2185 fragments
.each(pos
.insert
.curry(element
));
2187 else element
.insertAdjacentHTML(pos
.adjacency
, content
.stripScripts());
2189 content
.evalScripts
.bind(content
).defer();
2196 if (Prototype
.Browser
.Opera
) {
2197 Element
.Methods
.getStyle
= Element
.Methods
.getStyle
.wrap(
2198 function(proceed
, element
, style
) {
2200 case 'left': case 'top': case 'right': case 'bottom':
2201 if (proceed(element
, 'position') === 'static') return null;
2202 case 'height': case 'width':
2203 // returns '0px' for hidden elements; we want it to return null
2204 if (!Element
.visible(element
)) return null;
2206 // returns the border-box dimensions rather than the content-box
2207 // dimensions, so we subtract padding and borders from the value
2208 var dim
= parseInt(proceed(element
, style
), 10);
2210 if (dim
!== element
['offset' + style
.capitalize()])
2214 if (style
=== 'height') {
2215 properties
= ['border-top-width', 'padding-top',
2216 'padding-bottom', 'border-bottom-width'];
2219 properties
= ['border-left-width', 'padding-left',
2220 'padding-right', 'border-right-width'];
2222 return properties
.inject(dim
, function(memo
, property
) {
2223 var val
= proceed(element
, property
);
2224 return val
=== null ? memo
: memo
- parseInt(val
, 10);
2226 default: return proceed(element
, style
);
2231 Element
.Methods
.readAttribute
= Element
.Methods
.readAttribute
.wrap(
2232 function(proceed
, element
, attribute
) {
2233 if (attribute
=== 'title') return element
.title
;
2234 return proceed(element
, attribute
);
2239 else if (Prototype
.Browser
.IE
) {
2240 $w('positionedOffset getOffsetParent viewportOffset').each(function(method
) {
2241 Element
.Methods
[method
] = Element
.Methods
[method
].wrap(
2242 function(proceed
, element
) {
2243 element
= $(element
);
2244 var position
= element
.getStyle('position');
2245 if (position
!= 'static') return proceed(element
);
2246 element
.setStyle({ position
: 'relative' });
2247 var value
= proceed(element
);
2248 element
.setStyle({ position
: position
});
2254 Element
.Methods
.getStyle = function(element
, style
) {
2255 element
= $(element
);
2256 style
= (style
== 'float' || style
== 'cssFloat') ? 'styleFloat' : style
.camelize();
2257 var value
= element
.style
[style
];
2258 if (!value
&& element
.currentStyle
) value
= element
.currentStyle
[style
];
2260 if (style
== 'opacity') {
2261 if (value
= (element
.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2262 if (value
[1]) return parseFloat(value
[1]) / 100;
2266 if (value
== 'auto') {
2267 if ((style
== 'width' || style
== 'height') && (element
.getStyle('display') != 'none'))
2268 return element
['offset' + style
.capitalize()] + 'px';
2274 Element
.Methods
.setOpacity = function(element
, value
) {
2275 function stripAlpha(filter
){
2276 return filter
.replace(/alpha\([^\)]*\)/gi,'');
2278 element
= $(element
);
2279 var currentStyle
= element
.currentStyle
;
2280 if ((currentStyle
&& !currentStyle
.hasLayout
) ||
2281 (!currentStyle
&& element
.style
.zoom
== 'normal'))
2282 element
.style
.zoom
= 1;
2284 var filter
= element
.getStyle('filter'), style
= element
.style
;
2285 if (value
== 1 || value
=== '') {
2286 (filter
= stripAlpha(filter
)) ?
2287 style
.filter
= filter
: style
.removeAttribute('filter');
2289 } else if (value
< 0.00001) value
= 0;
2290 style
.filter
= stripAlpha(filter
) +
2291 'alpha(opacity=' + (value
* 100) + ')';
2295 Element
._attributeTranslations
= {
2298 'class': 'className',
2302 _getAttr: function(element
, attribute
) {
2303 return element
.getAttribute(attribute
, 2);
2305 _getAttrNode: function(element
, attribute
) {
2306 var node
= element
.getAttributeNode(attribute
);
2307 return node
? node
.value
: "";
2309 _getEv: function(element
, attribute
) {
2310 attribute
= element
.getAttribute(attribute
);
2311 return attribute
? attribute
.toString().slice(23, -2) : null;
2313 _flag: function(element
, attribute
) {
2314 return $(element
).hasAttribute(attribute
) ? attribute
: null;
2316 style: function(element
) {
2317 return element
.style
.cssText
.toLowerCase();
2319 title: function(element
) {
2320 return element
.title
;
2326 Element
._attributeTranslations
.write
= {
2327 names
: Object
.clone(Element
._attributeTranslations
.read
.names
),
2329 checked: function(element
, value
) {
2330 element
.checked
= !!value
;
2333 style: function(element
, value
) {
2334 element
.style
.cssText
= value
? value
: '';
2339 Element
._attributeTranslations
.has
= {};
2341 $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2342 'encType maxLength readOnly longDesc').each(function(attr
) {
2343 Element
._attributeTranslations
.write
.names
[attr
.toLowerCase()] = attr
;
2344 Element
._attributeTranslations
.has
[attr
.toLowerCase()] = attr
;
2352 action
: v
._getAttrNode
,
2360 ondblclick
: v
._getEv
,
2361 onmousedown
: v
._getEv
,
2362 onmouseup
: v
._getEv
,
2363 onmouseover
: v
._getEv
,
2364 onmousemove
: v
._getEv
,
2365 onmouseout
: v
._getEv
,
2368 onkeypress
: v
._getEv
,
2369 onkeydown
: v
._getEv
,
2376 })(Element
._attributeTranslations
.read
.values
);
2379 else if (Prototype
.Browser
.Gecko
&& /rv:1\.8\.0/.test(navigator
.userAgent
)) {
2380 Element
.Methods
.setOpacity = function(element
, value
) {
2381 element
= $(element
);
2382 element
.style
.opacity
= (value
== 1) ? 0.999999 :
2383 (value
=== '') ? '' : (value
< 0.00001) ? 0 : value
;
2388 else if (Prototype
.Browser
.WebKit
) {
2389 Element
.Methods
.setOpacity = function(element
, value
) {
2390 element
= $(element
);
2391 element
.style
.opacity
= (value
== 1 || value
=== '') ? '' :
2392 (value
< 0.00001) ? 0 : value
;
2395 if(element
.tagName
== 'IMG' && element
.width
) {
2396 element
.width
++; element
.width
--;
2398 var n
= document
.createTextNode(' ');
2399 element
.appendChild(n
);
2400 element
.removeChild(n
);
2406 // Safari returns margins on body which is incorrect if the child is absolutely
2407 // positioned. For performance reasons, redefine Element#cumulativeOffset for
2408 // KHTML/WebKit only.
2409 Element
.Methods
.cumulativeOffset = function(element
) {
2410 var valueT
= 0, valueL
= 0;
2412 valueT
+= element
.offsetTop
|| 0;
2413 valueL
+= element
.offsetLeft
|| 0;
2414 if (element
.offsetParent
== document
.body
)
2415 if (Element
.getStyle(element
, 'position') == 'absolute') break;
2417 element
= element
.offsetParent
;
2420 return Element
._returnOffset(valueL
, valueT
);
2424 if (Prototype
.Browser
.IE
|| Prototype
.Browser
.Opera
) {
2425 // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
2426 Element
.Methods
.update = function(element
, content
) {
2427 element
= $(element
);
2429 if (content
&& content
.toElement
) content
= content
.toElement();
2430 if (Object
.isElement(content
)) return element
.update().insert(content
);
2432 content
= Object
.toHTML(content
);
2433 var tagName
= element
.tagName
.toUpperCase();
2435 if (tagName
in Element
._insertionTranslations
.tags
) {
2436 $A(element
.childNodes
).each(function(node
) { element
.removeChild(node
) });
2437 Element
._getContentFromAnonymousElement(tagName
, content
.stripScripts())
2438 .each(function(node
) { element
.appendChild(node
) });
2440 else element
.innerHTML
= content
.stripScripts();
2442 content
.evalScripts
.bind(content
).defer();
2447 if (document
.createElement('div').outerHTML
) {
2448 Element
.Methods
.replace = function(element
, content
) {
2449 element
= $(element
);
2451 if (content
&& content
.toElement
) content
= content
.toElement();
2452 if (Object
.isElement(content
)) {
2453 element
.parentNode
.replaceChild(content
, element
);
2457 content
= Object
.toHTML(content
);
2458 var parent
= element
.parentNode
, tagName
= parent
.tagName
.toUpperCase();
2460 if (Element
._insertionTranslations
.tags
[tagName
]) {
2461 var nextSibling
= element
.next();
2462 var fragments
= Element
._getContentFromAnonymousElement(tagName
, content
.stripScripts());
2463 parent
.removeChild(element
);
2465 fragments
.each(function(node
) { parent
.insertBefore(node
, nextSibling
) });
2467 fragments
.each(function(node
) { parent
.appendChild(node
) });
2469 else element
.outerHTML
= content
.stripScripts();
2471 content
.evalScripts
.bind(content
).defer();
2476 Element
._returnOffset = function(l
, t
) {
2477 var result
= [l
, t
];
2483 Element
._getContentFromAnonymousElement = function(tagName
, html
) {
2484 var div
= new Element('div'), t
= Element
._insertionTranslations
.tags
[tagName
];
2485 div
.innerHTML
= t
[0] + html
+ t
[1];
2486 t
[2].times(function() { div
= div
.firstChild
});
2487 return $A(div
.childNodes
);
2490 Element
._insertionTranslations
= {
2492 adjacency
: 'beforeBegin',
2493 insert: function(element
, node
) {
2494 element
.parentNode
.insertBefore(node
, element
);
2496 initializeRange: function(element
, range
) {
2497 range
.setStartBefore(element
);
2501 adjacency
: 'afterBegin',
2502 insert: function(element
, node
) {
2503 element
.insertBefore(node
, element
.firstChild
);
2505 initializeRange: function(element
, range
) {
2506 range
.selectNodeContents(element
);
2507 range
.collapse(true);
2511 adjacency
: 'beforeEnd',
2512 insert: function(element
, node
) {
2513 element
.appendChild(node
);
2517 adjacency
: 'afterEnd',
2518 insert: function(element
, node
) {
2519 element
.parentNode
.insertBefore(node
, element
.nextSibling
);
2521 initializeRange: function(element
, range
) {
2522 range
.setStartAfter(element
);
2526 TABLE
: ['<table>', '</table>', 1],
2527 TBODY
: ['<table><tbody>', '</tbody></table>', 2],
2528 TR
: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
2529 TD
: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2530 SELECT
: ['<select>', '</select>', 1]
2535 this.bottom
.initializeRange
= this.top
.initializeRange
;
2536 Object
.extend(this.tags
, {
2537 THEAD
: this.tags
.TBODY
,
2538 TFOOT
: this.tags
.TBODY
,
2541 }).call(Element
._insertionTranslations
);
2543 Element
.Methods
.Simulated
= {
2544 hasAttribute: function(element
, attribute
) {
2545 attribute
= Element
._attributeTranslations
.has
[attribute
] || attribute
;
2546 var node
= $(element
).getAttributeNode(attribute
);
2547 return node
&& node
.specified
;
2551 Element
.Methods
.ByTag
= { };
2553 Object
.extend(Element
, Element
.Methods
);
2555 if (!Prototype
.BrowserFeatures
.ElementExtensions
&&
2556 document
.createElement('div').__proto__
) {
2557 window
.HTMLElement
= { };
2558 window
.HTMLElement
.prototype = document
.createElement('div').__proto__
;
2559 Prototype
.BrowserFeatures
.ElementExtensions
= true;
2562 Element
.extend
= (function() {
2563 if (Prototype
.BrowserFeatures
.SpecificElementExtensions
)
2566 var Methods
= { }, ByTag
= Element
.Methods
.ByTag
;
2568 var extend
= Object
.extend(function(element
) {
2569 if (!element
|| element
._extendedByPrototype
||
2570 element
.nodeType
!= 1 || element
== window
) return element
;
2572 var methods
= Object
.clone(Methods
),
2573 tagName
= element
.tagName
, property
, value
;
2575 // extend methods for specific tags
2576 if (ByTag
[tagName
]) Object
.extend(methods
, ByTag
[tagName
]);
2578 for (property
in methods
) {
2579 value
= methods
[property
];
2580 if (Object
.isFunction(value
) && !(property
in element
))
2581 element
[property
] = value
.methodize();
2584 element
._extendedByPrototype
= Prototype
.emptyFunction
;
2588 refresh: function() {
2589 // extend methods for all tags (Safari doesn't need this)
2590 if (!Prototype
.BrowserFeatures
.ElementExtensions
) {
2591 Object
.extend(Methods
, Element
.Methods
);
2592 Object
.extend(Methods
, Element
.Methods
.Simulated
);
2601 Element
.hasAttribute = function(element
, attribute
) {
2602 if (element
.hasAttribute
) return element
.hasAttribute(attribute
);
2603 return Element
.Methods
.Simulated
.hasAttribute(element
, attribute
);
2606 Element
.addMethods = function(methods
) {
2607 var F
= Prototype
.BrowserFeatures
, T
= Element
.Methods
.ByTag
;
2610 Object
.extend(Form
, Form
.Methods
);
2611 Object
.extend(Form
.Element
, Form
.Element
.Methods
);
2612 Object
.extend(Element
.Methods
.ByTag
, {
2613 "FORM": Object
.clone(Form
.Methods
),
2614 "INPUT": Object
.clone(Form
.Element
.Methods
),
2615 "SELECT": Object
.clone(Form
.Element
.Methods
),
2616 "TEXTAREA": Object
.clone(Form
.Element
.Methods
)
2620 if (arguments
.length
== 2) {
2621 var tagName
= methods
;
2622 methods
= arguments
[1];
2625 if (!tagName
) Object
.extend(Element
.Methods
, methods
|| { });
2627 if (Object
.isArray(tagName
)) tagName
.each(extend
);
2628 else extend(tagName
);
2631 function extend(tagName
) {
2632 tagName
= tagName
.toUpperCase();
2633 if (!Element
.Methods
.ByTag
[tagName
])
2634 Element
.Methods
.ByTag
[tagName
] = { };
2635 Object
.extend(Element
.Methods
.ByTag
[tagName
], methods
);
2638 function copy(methods
, destination
, onlyIfAbsent
) {
2639 onlyIfAbsent
= onlyIfAbsent
|| false;
2640 for (var property
in methods
) {
2641 var value
= methods
[property
];
2642 if (!Object
.isFunction(value
)) continue;
2643 if (!onlyIfAbsent
|| !(property
in destination
))
2644 destination
[property
] = value
.methodize();
2648 function findDOMClass(tagName
) {
2651 "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
2652 "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
2653 "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
2654 "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
2655 "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
2656 "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
2657 "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
2658 "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
2659 "FrameSet", "IFRAME": "IFrame"
2661 if (trans
[tagName
]) klass
= 'HTML' + trans
[tagName
] + 'Element';
2662 if (window
[klass
]) return window
[klass
];
2663 klass
= 'HTML' + tagName
+ 'Element';
2664 if (window
[klass
]) return window
[klass
];
2665 klass
= 'HTML' + tagName
.capitalize() + 'Element';
2666 if (window
[klass
]) return window
[klass
];
2668 window
[klass
] = { };
2669 window
[klass
].prototype = document
.createElement(tagName
).__proto__
;
2670 return window
[klass
];
2673 if (F
.ElementExtensions
) {
2674 copy(Element
.Methods
, HTMLElement
.prototype);
2675 copy(Element
.Methods
.Simulated
, HTMLElement
.prototype, true);
2678 if (F
.SpecificElementExtensions
) {
2679 for (var tag
in Element
.Methods
.ByTag
) {
2680 var klass
= findDOMClass(tag
);
2681 if (Object
.isUndefined(klass
)) continue;
2682 copy(T
[tag
], klass
.prototype);
2686 Object
.extend(Element
, Element
.Methods
);
2687 delete Element
.ByTag
;
2689 if (Element
.extend
.refresh
) Element
.extend
.refresh();
2690 Element
.cache
= { };
2693 document
.viewport
= {
2694 getDimensions: function() {
2695 var dimensions
= { };
2696 var B
= Prototype
.Browser
;
2697 $w('width height').each(function(d
) {
2698 var D
= d
.capitalize();
2699 dimensions
[d
] = (B
.WebKit
&& !document
.evaluate
) ? self
['inner' + D
] :
2700 (B
.Opera
) ? document
.body
['client' + D
] : document
.documentElement
['client' + D
];
2705 getWidth: function() {
2706 return this.getDimensions().width
;
2709 getHeight: function() {
2710 return this.getDimensions().height
;
2713 getScrollOffsets: function() {
2714 return Element
._returnOffset(
2715 window
.pageXOffset
|| document
.documentElement
.scrollLeft
|| document
.body
.scrollLeft
,
2716 window
.pageYOffset
|| document
.documentElement
.scrollTop
|| document
.body
.scrollTop
);
2719 /* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2720 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2721 * license. Please see http://www.yui-ext.com/ for more information. */
2723 var Selector
= Class
.create({
2724 initialize: function(expression
) {
2725 this.expression
= expression
.strip();
2726 this.compileMatcher();
2729 shouldUseXPath: function() {
2730 if (!Prototype
.BrowserFeatures
.XPath
) return false;
2732 var e
= this.expression
;
2734 // Safari 3 chokes on :*-of-type and :empty
2735 if (Prototype
.Browser
.WebKit
&&
2736 (e
.include("-of-type") || e
.include(":empty")))
2739 // XPath can't do namespaced attributes, nor can it read
2740 // the "checked" property from DOM nodes
2741 if ((/(\[[\w-]*?:|:checked)/).test(this.expression
))
2747 compileMatcher: function() {
2748 if (this.shouldUseXPath())
2749 return this.compileXPathMatcher();
2751 var e
= this.expression
, ps
= Selector
.patterns
, h
= Selector
.handlers
,
2752 c
= Selector
.criteria
, le
, p
, m
;
2754 if (Selector
._cache
[e
]) {
2755 this.matcher
= Selector
._cache
[e
];
2759 this.matcher
= ["this.matcher = function(root) {",
2760 "var r = root, h = Selector.handlers, c = false, n;"];
2762 while (e
&& le
!= e
&& (/\S/).test(e
)) {
2766 if (m
= e
.match(p
)) {
2767 this.matcher
.push(Object
.isFunction(c
[i
]) ? c
[i
](m
) :
2768 new Template(c
[i
]).evaluate(m
));
2769 e
= e
.replace(m
[0], '');
2775 this.matcher
.push("return h.unique(n);\n}");
2776 eval(this.matcher
.join('\n'));
2777 Selector
._cache
[this.expression
] = this.matcher
;
2780 compileXPathMatcher: function() {
2781 var e
= this.expression
, ps
= Selector
.patterns
,
2782 x
= Selector
.xpath
, le
, m
;
2784 if (Selector
._cache
[e
]) {
2785 this.xpath
= Selector
._cache
[e
]; return;
2788 this.matcher
= ['.//*'];
2789 while (e
&& le
!= e
&& (/\S/).test(e
)) {
2792 if (m
= e
.match(ps
[i
])) {
2793 this.matcher
.push(Object
.isFunction(x
[i
]) ? x
[i
](m
) :
2794 new Template(x
[i
]).evaluate(m
));
2795 e
= e
.replace(m
[0], '');
2801 this.xpath
= this.matcher
.join('');
2802 Selector
._cache
[this.expression
] = this.xpath
;
2805 findElements: function(root
) {
2806 root
= root
|| document
;
2807 if (this.xpath
) return document
._getElementsByXPath(this.xpath
, root
);
2808 return this.matcher(root
);
2811 match: function(element
) {
2814 var e
= this.expression
, ps
= Selector
.patterns
, as
= Selector
.assertions
;
2817 while (e
&& le
!== e
&& (/\S/).test(e
)) {
2821 if (m
= e
.match(p
)) {
2822 // use the Selector.assertions methods unless the selector
2825 this.tokens
.push([i
, Object
.clone(m
)]);
2826 e
= e
.replace(m
[0], '');
2828 // reluctantly do a document-wide search
2829 // and look for a match in the array
2830 return this.findElements(document
).include(element
);
2836 var match
= true, name
, matches
;
2837 for (var i
= 0, token
; token
= this.tokens
[i
]; i
++) {
2838 name
= token
[0], matches
= token
[1];
2839 if (!Selector
.assertions
[name
](element
, matches
)) {
2840 match
= false; break;
2847 toString: function() {
2848 return this.expression
;
2851 inspect: function() {
2852 return "#<Selector:" + this.expression
.inspect() + ">";
2856 Object
.extend(Selector
, {
2862 adjacent
: "/following-sibling::*[1]",
2863 laterSibling
: '/following-sibling::*',
2864 tagName: function(m
) {
2865 if (m
[1] == '*') return '';
2866 return "[local-name()='" + m
[1].toLowerCase() +
2867 "' or local-name()='" + m
[1].toUpperCase() + "']";
2869 className
: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2871 attrPresence: function(m
) {
2872 m
[1] = m
[1].toLowerCase();
2873 return new Template("[@#{1}]").evaluate(m
);
2876 m
[1] = m
[1].toLowerCase();
2877 m
[3] = m
[5] || m
[6];
2878 return new Template(Selector
.xpath
.operators
[m
[2]]).evaluate(m
);
2880 pseudo: function(m
) {
2881 var h
= Selector
.xpath
.pseudos
[m
[1]];
2883 if (Object
.isFunction(h
)) return h(m
);
2884 return new Template(Selector
.xpath
.pseudos
[m
[1]]).evaluate(m
);
2887 '=': "[@#{1}='#{3}']",
2888 '!=': "[@#{1}!='#{3}']",
2889 '^=': "[starts-with(@#{1}, '#{3}')]",
2890 '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2891 '*=': "[contains(@#{1}, '#{3}')]",
2892 '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2893 '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2896 'first-child': '[not(preceding-sibling::*)]',
2897 'last-child': '[not(following-sibling::*)]',
2898 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
2899 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2900 'checked': "[@checked]",
2901 'disabled': "[@disabled]",
2902 'enabled': "[not(@disabled)]",
2903 'not': function(m
) {
2904 var e
= m
[6], p
= Selector
.patterns
,
2905 x
= Selector
.xpath
, le
, v
;
2908 while (e
&& le
!= e
&& (/\S/).test(e
)) {
2911 if (m
= e
.match(p
[i
])) {
2912 v
= Object
.isFunction(x
[i
]) ? x
[i
](m
) : new Template(x
[i
]).evaluate(m
);
2913 exclusion
.push("(" + v
.substring(1, v
.length
- 1) + ")");
2914 e
= e
.replace(m
[0], '');
2919 return "[not(" + exclusion
.join(" and ") + ")]";
2921 'nth-child': function(m
) {
2922 return Selector
.xpath
.pseudos
.nth("(count(./preceding-sibling::*) + 1) ", m
);
2924 'nth-last-child': function(m
) {
2925 return Selector
.xpath
.pseudos
.nth("(count(./following-sibling::*) + 1) ", m
);
2927 'nth-of-type': function(m
) {
2928 return Selector
.xpath
.pseudos
.nth("position() ", m
);
2930 'nth-last-of-type': function(m
) {
2931 return Selector
.xpath
.pseudos
.nth("(last() + 1 - position()) ", m
);
2933 'first-of-type': function(m
) {
2934 m
[6] = "1"; return Selector
.xpath
.pseudos
['nth-of-type'](m
);
2936 'last-of-type': function(m
) {
2937 m
[6] = "1"; return Selector
.xpath
.pseudos
['nth-last-of-type'](m
);
2939 'only-of-type': function(m
) {
2940 var p
= Selector
.xpath
.pseudos
; return p
['first-of-type'](m
) + p
['last-of-type'](m
);
2942 nth: function(fragment
, m
) {
2943 var mm
, formula
= m
[6], predicate
;
2944 if (formula
== 'even') formula
= '2n+0';
2945 if (formula
== 'odd') formula
= '2n+1';
2946 if (mm
= formula
.match(/^(\d+)$/)) // digit only
2947 return '[' + fragment
+ "= " + mm
[1] + ']';
2948 if (mm
= formula
.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2949 if (mm
[1] == "-") mm
[1] = -1;
2950 var a
= mm
[1] ? Number(mm
[1]) : 1;
2951 var b
= mm
[2] ? Number(mm
[2]) : 0;
2952 predicate
= "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2953 "((#{fragment} - #{b}) div #{a} >= 0)]";
2954 return new Template(predicate
).evaluate({
2955 fragment
: fragment
, a
: a
, b
: b
});
2962 tagName
: 'n = h.tagName(n, r, "#{1}", c); c = false;',
2963 className
: 'n = h.className(n, r, "#{1}", c); c = false;',
2964 id
: 'n = h.id(n, r, "#{1}", c); c = false;',
2965 attrPresence
: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2967 m
[3] = (m
[5] || m
[6]);
2968 return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m
);
2970 pseudo: function(m
) {
2971 if (m
[6]) m
[6] = m
[6].replace(/"/g, '\\"');
2972 return new Template('n
= h
.pseudo(n
, "#{1}", "#{6}", r
, c
); c
= false;').evaluate(m);
2974 descendant: 'c
= "descendant";',
2975 child: 'c
= "child";',
2976 adjacent: 'c
= "adjacent";',
2977 laterSibling: 'c
= "laterSibling";'
2981 // combinators must be listed first
2982 // (and descendant needs to be last combinator)
2983 laterSibling: /^\s*~\s*/,
2985 adjacent: /^\s*\+\s*/,
2989 tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
2990 id: /^#([\w\-\*]+)(\b|$)/,
2991 className: /^\.([\w\-\*]+)(\b|$)/,
2992 pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
2993 attrPresence: /^\[([\w]+)\]/,
2994 attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
2997 // for Selector.match and Element#match
2999 tagName: function(element
, matches
) {
3000 return matches
[1].toUpperCase() == element
.tagName
.toUpperCase();
3003 className: function(element
, matches
) {
3004 return Element
.hasClassName(element
, matches
[1]);
3007 id: function(element
, matches
) {
3008 return element
.id
=== matches
[1];
3011 attrPresence: function(element
, matches
) {
3012 return Element
.hasAttribute(element
, matches
[1]);
3015 attr: function(element
, matches
) {
3016 var nodeValue
= Element
.readAttribute(element
, matches
[1]);
3017 return Selector
.operators
[matches
[2]](nodeValue
, matches
[3]);
3022 // UTILITY FUNCTIONS
3023 // joins two collections
3024 concat: function(a
, b
) {
3025 for (var i
= 0, node
; node
= b
[i
]; i
++)
3030 // marks an array of nodes for counting
3031 mark: function(nodes
) {
3032 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3033 node
._counted
= true;
3037 unmark: function(nodes
) {
3038 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3039 node
._counted
= undefined;
3043 // mark each child node with its position (for nth calls)
3044 // "ofType" flag indicates whether we're indexing for nth-of-type
3045 // rather than nth-child
3046 index: function(parentNode
, reverse
, ofType
) {
3047 parentNode
._counted
= true;
3049 for (var nodes
= parentNode
.childNodes
, i
= nodes
.length
- 1, j
= 1; i
>= 0; i
--) {
3050 var node
= nodes
[i
];
3051 if (node
.nodeType
== 1 && (!ofType
|| node
._counted
)) node
.nodeIndex
= j
++;
3054 for (var i
= 0, j
= 1, nodes
= parentNode
.childNodes
; node
= nodes
[i
]; i
++)
3055 if (node
.nodeType
== 1 && (!ofType
|| node
._counted
)) node
.nodeIndex
= j
++;
3059 // filters out duplicates and extends all nodes
3060 unique: function(nodes
) {
3061 if (nodes
.length
== 0) return nodes
;
3062 var results
= [], n
;
3063 for (var i
= 0, l
= nodes
.length
; i
< l
; i
++)
3064 if (!(n
= nodes
[i
])._counted
) {
3066 results
.push(Element
.extend(n
));
3068 return Selector
.handlers
.unmark(results
);
3071 // COMBINATOR FUNCTIONS
3072 descendant: function(nodes
) {
3073 var h
= Selector
.handlers
;
3074 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3075 h
.concat(results
, node
.getElementsByTagName('*'));
3079 child: function(nodes
) {
3080 var h
= Selector
.handlers
;
3081 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3082 for (var j
= 0, child
; child
= node
.childNodes
[j
]; j
++)
3083 if (child
.nodeType
== 1 && child
.tagName
!= '!') results
.push(child
);
3088 adjacent: function(nodes
) {
3089 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3090 var next
= this.nextElementSibling(node
);
3091 if (next
) results
.push(next
);
3096 laterSibling: function(nodes
) {
3097 var h
= Selector
.handlers
;
3098 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3099 h
.concat(results
, Element
.nextSiblings(node
));
3103 nextElementSibling: function(node
) {
3104 while (node
= node
.nextSibling
)
3105 if (node
.nodeType
== 1) return node
;
3109 previousElementSibling: function(node
) {
3110 while (node
= node
.previousSibling
)
3111 if (node
.nodeType
== 1) return node
;
3116 tagName: function(nodes
, root
, tagName
, combinator
) {
3117 tagName
= tagName
.toUpperCase();
3118 var results
= [], h
= Selector
.handlers
;
3121 // fastlane for ordinary descendant combinators
3122 if (combinator
== "descendant") {
3123 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3124 h
.concat(results
, node
.getElementsByTagName(tagName
));
3126 } else nodes
= this[combinator
](nodes
);
3127 if (tagName
== "*") return nodes
;
3129 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3130 if (node
.tagName
.toUpperCase() == tagName
) results
.push(node
);
3132 } else return root
.getElementsByTagName(tagName
);
3135 id: function(nodes
, root
, id
, combinator
) {
3136 var targetNode
= $(id
), h
= Selector
.handlers
;
3137 if (!targetNode
) return [];
3138 if (!nodes
&& root
== document
) return [targetNode
];
3141 if (combinator
== 'child') {
3142 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3143 if (targetNode
.parentNode
== node
) return [targetNode
];
3144 } else if (combinator
== 'descendant') {
3145 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3146 if (Element
.descendantOf(targetNode
, node
)) return [targetNode
];
3147 } else if (combinator
== 'adjacent') {
3148 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3149 if (Selector
.handlers
.previousElementSibling(targetNode
) == node
)
3150 return [targetNode
];
3151 } else nodes
= h
[combinator
](nodes
);
3153 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3154 if (node
== targetNode
) return [targetNode
];
3157 return (targetNode
&& Element
.descendantOf(targetNode
, root
)) ? [targetNode
] : [];
3160 className: function(nodes
, root
, className
, combinator
) {
3161 if (nodes
&& combinator
) nodes
= this[combinator
](nodes
);
3162 return Selector
.handlers
.byClassName(nodes
, root
, className
);
3165 byClassName: function(nodes
, root
, className
) {
3166 if (!nodes
) nodes
= Selector
.handlers
.descendant([root
]);
3167 var needle
= ' ' + className
+ ' ';
3168 for (var i
= 0, results
= [], node
, nodeClassName
; node
= nodes
[i
]; i
++) {
3169 nodeClassName
= node
.className
;
3170 if (nodeClassName
.length
== 0) continue;
3171 if (nodeClassName
== className
|| (' ' + nodeClassName
+ ' ').include(needle
))
3177 attrPresence: function(nodes
, root
, attr
) {
3178 if (!nodes
) nodes
= root
.getElementsByTagName("*");
3180 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3181 if (Element
.hasAttribute(node
, attr
)) results
.push(node
);
3185 attr: function(nodes
, root
, attr
, value
, operator
) {
3186 if (!nodes
) nodes
= root
.getElementsByTagName("*");
3187 var handler
= Selector
.operators
[operator
], results
= [];
3188 for (var i
= 0, node
; node
= nodes
[i
]; i
++) {
3189 var nodeValue
= Element
.readAttribute(node
, attr
);
3190 if (nodeValue
=== null) continue;
3191 if (handler(nodeValue
, value
)) results
.push(node
);
3196 pseudo: function(nodes
, name
, value
, root
, combinator
) {
3197 if (nodes
&& combinator
) nodes
= this[combinator
](nodes
);
3198 if (!nodes
) nodes
= root
.getElementsByTagName("*");
3199 return Selector
.pseudos
[name
](nodes
, value
, root
);
3204 'first-child': function(nodes
, value
, root
) {
3205 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3206 if (Selector
.handlers
.previousElementSibling(node
)) continue;
3211 'last-child': function(nodes
, value
, root
) {
3212 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3213 if (Selector
.handlers
.nextElementSibling(node
)) continue;
3218 'only-child': function(nodes
, value
, root
) {
3219 var h
= Selector
.handlers
;
3220 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3221 if (!h
.previousElementSibling(node
) && !h
.nextElementSibling(node
))
3225 'nth-child': function(nodes
, formula
, root
) {
3226 return Selector
.pseudos
.nth(nodes
, formula
, root
);
3228 'nth-last-child': function(nodes
, formula
, root
) {
3229 return Selector
.pseudos
.nth(nodes
, formula
, root
, true);
3231 'nth-of-type': function(nodes
, formula
, root
) {
3232 return Selector
.pseudos
.nth(nodes
, formula
, root
, false, true);
3234 'nth-last-of-type': function(nodes
, formula
, root
) {
3235 return Selector
.pseudos
.nth(nodes
, formula
, root
, true, true);
3237 'first-of-type': function(nodes
, formula
, root
) {
3238 return Selector
.pseudos
.nth(nodes
, "1", root
, false, true);
3240 'last-of-type': function(nodes
, formula
, root
) {
3241 return Selector
.pseudos
.nth(nodes
, "1", root
, true, true);
3243 'only-of-type': function(nodes
, formula
, root
) {
3244 var p
= Selector
.pseudos
;
3245 return p
['last-of-type'](p
['first-of-type'](nodes
, formula
, root
), formula
, root
);
3248 // handles the an+b logic
3249 getIndices: function(a
, b
, total
) {
3250 if (a
== 0) return b
> 0 ? [b
] : [];
3251 return $R(1, total
).inject([], function(memo
, i
) {
3252 if (0 == (i
- b
) % a
&& (i
- b
) / a
>= 0) memo
.push(i
);
3257 // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
3258 nth: function(nodes
, formula
, root
, reverse
, ofType
) {
3259 if (nodes
.length
== 0) return [];
3260 if (formula
== 'even') formula
= '2n+0';
3261 if (formula
== 'odd') formula
= '2n+1';
3262 var h
= Selector
.handlers
, results
= [], indexed
= [], m
;
3264 for (var i
= 0, node
; node
= nodes
[i
]; i
++) {
3265 if (!node
.parentNode
._counted
) {
3266 h
.index(node
.parentNode
, reverse
, ofType
);
3267 indexed
.push(node
.parentNode
);
3270 if (formula
.match(/^\d+$/)) { // just a number
3271 formula
= Number(formula
);
3272 for (var i
= 0, node
; node
= nodes
[i
]; i
++)
3273 if (node
.nodeIndex
== formula
) results
.push(node
);
3274 } else if (m
= formula
.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3275 if (m
[1] == "-") m
[1] = -1;
3276 var a
= m
[1] ? Number(m
[1]) : 1;
3277 var b
= m
[2] ? Number(m
[2]) : 0;
3278 var indices
= Selector
.pseudos
.getIndices(a
, b
, nodes
.length
);
3279 for (var i
= 0, node
, l
= indices
.length
; node
= nodes
[i
]; i
++) {
3280 for (var j
= 0; j
< l
; j
++)
3281 if (node
.nodeIndex
== indices
[j
]) results
.push(node
);
3289 'empty': function(nodes
, value
, root
) {
3290 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++) {
3291 // IE treats comments as element nodes
3292 if (node
.tagName
== '!' || (node
.firstChild
&& !node
.innerHTML
.match(/^\s*$/))) continue;
3298 'not': function(nodes
, selector
, root
) {
3299 var h
= Selector
.handlers
, selectorType
, m
;
3300 var exclusions
= new Selector(selector
).findElements(root
);
3302 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3303 if (!node
._counted
) results
.push(node
);
3304 h
.unmark(exclusions
);
3308 'enabled': function(nodes
, value
, root
) {
3309 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3310 if (!node
.disabled
) results
.push(node
);
3314 'disabled': function(nodes
, value
, root
) {
3315 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3316 if (node
.disabled
) results
.push(node
);
3320 'checked': function(nodes
, value
, root
) {
3321 for (var i
= 0, results
= [], node
; node
= nodes
[i
]; i
++)
3322 if (node
.checked
) results
.push(node
);
3328 '=': function(nv
, v
) { return nv
== v
; },
3329 '!=': function(nv
, v
) { return nv
!= v
; },
3330 '^=': function(nv
, v
) { return nv
.startsWith(v
); },
3331 '$=': function(nv
, v
) { return nv
.endsWith(v
); },
3332 '*=': function(nv
, v
) { return nv
.include(v
); },
3333 '~=': function(nv
, v
) { return (' ' + nv
+ ' ').include(' ' + v
+ ' '); },
3334 '|=': function(nv
, v
) { return ('-' + nv
.toUpperCase() + '-').include('-' + v
.toUpperCase() + '-'); }
3337 matchElements: function(elements
, expression
) {
3338 var matches
= new Selector(expression
).findElements(), h
= Selector
.handlers
;
3340 for (var i
= 0, results
= [], element
; element
= elements
[i
]; i
++)
3341 if (element
._counted
) results
.push(element
);
3346 findElement: function(elements
, expression
, index
) {
3347 if (Object
.isNumber(expression
)) {
3348 index
= expression
; expression
= false;
3350 return Selector
.matchElements(elements
, expression
|| '*')[index
|| 0];
3353 findChildElements: function(element
, expressions
) {
3354 var exprs
= expressions
.join(',');
3356 exprs
.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m
) {
3357 expressions
.push(m
[1].strip());
3359 var results
= [], h
= Selector
.handlers
;
3360 for (var i
= 0, l
= expressions
.length
, selector
; i
< l
; i
++) {
3361 selector
= new Selector(expressions
[i
].strip());
3362 h
.concat(results
, selector
.findElements(element
));
3364 return (l
> 1) ? h
.unique(results
) : results
;
3368 if (Prototype
.Browser
.IE
) {
3369 // IE returns comment nodes on getElementsByTagName("*").
3371 Selector
.handlers
.concat = function(a
, b
) {
3372 for (var i
= 0, node
; node
= b
[i
]; i
++)
3373 if (node
.tagName
!== "!") a
.push(node
);
3379 return Selector
.findChildElements(document
, $A(arguments
));
3382 reset: function(form
) {
3387 serializeElements: function(elements
, options
) {
3388 if (typeof options
!= 'object') options
= { hash
: !!options
};
3389 else if (Object
.isUndefined(options
.hash
)) options
.hash
= true;
3390 var key
, value
, submitted
= false, submit
= options
.submit
;
3392 var data
= elements
.inject({ }, function(result
, element
) {
3393 if (!element
.disabled
&& element
.name
) {
3394 key
= element
.name
; value
= $(element
).getValue();
3395 if (value
!= null && (element
.type
!= 'submit' || (!submitted
&&
3396 submit
!== false && (!submit
|| key
== submit
) && (submitted
= true)))) {
3397 if (key
in result
) {
3398 // a key is already present; construct an array of values
3399 if (!Object
.isArray(result
[key
])) result
[key
] = [result
[key
]];
3400 result
[key
].push(value
);
3402 else result
[key
] = value
;
3408 return options
.hash
? data
: Object
.toQueryString(data
);
3413 serialize: function(form
, options
) {
3414 return Form
.serializeElements(Form
.getElements(form
), options
);
3417 getElements: function(form
) {
3418 return $A($(form
).getElementsByTagName('*')).inject([],
3419 function(elements
, child
) {
3420 if (Form
.Element
.Serializers
[child
.tagName
.toLowerCase()])
3421 elements
.push(Element
.extend(child
));
3427 getInputs: function(form
, typeName
, name
) {
3429 var inputs
= form
.getElementsByTagName('input');
3431 if (!typeName
&& !name
) return $A(inputs
).map(Element
.extend
);
3433 for (var i
= 0, matchingInputs
= [], length
= inputs
.length
; i
< length
; i
++) {
3434 var input
= inputs
[i
];
3435 if ((typeName
&& input
.type
!= typeName
) || (name
&& input
.name
!= name
))
3437 matchingInputs
.push(Element
.extend(input
));
3440 return matchingInputs
;
3443 disable: function(form
) {
3445 Form
.getElements(form
).invoke('disable');
3449 enable: function(form
) {
3451 Form
.getElements(form
).invoke('enable');
3455 findFirstElement: function(form
) {
3456 var elements
= $(form
).getElements().findAll(function(element
) {
3457 return 'hidden' != element
.type
&& !element
.disabled
;
3459 var firstByIndex
= elements
.findAll(function(element
) {
3460 return element
.hasAttribute('tabIndex') && element
.tabIndex
>= 0;
3461 }).sortBy(function(element
) { return element
.tabIndex
}).first();
3463 return firstByIndex
? firstByIndex
: elements
.find(function(element
) {
3464 return ['input', 'select', 'textarea'].include(element
.tagName
.toLowerCase());
3468 focusFirstElement: function(form
) {
3470 form
.findFirstElement().activate();
3474 request: function(form
, options
) {
3475 form
= $(form
), options
= Object
.clone(options
|| { });
3477 var params
= options
.parameters
, action
= form
.readAttribute('action') || '';
3478 if (action
.blank()) action
= window
.location
.href
;
3479 options
.parameters
= form
.serialize(true);
3482 if (Object
.isString(params
)) params
= params
.toQueryParams();
3483 Object
.extend(options
.parameters
, params
);
3486 if (form
.hasAttribute('method') && !options
.method
)
3487 options
.method
= form
.method
;
3489 return new Ajax
.Request(action
, options
);
3493 /*--------------------------------------------------------------------------*/
3496 focus: function(element
) {
3501 select: function(element
) {
3502 $(element
).select();
3507 Form
.Element
.Methods
= {
3508 serialize: function(element
) {
3509 element
= $(element
);
3510 if (!element
.disabled
&& element
.name
) {
3511 var value
= element
.getValue();
3512 if (value
!= undefined) {
3514 pair
[element
.name
] = value
;
3515 return Object
.toQueryString(pair
);
3521 getValue: function(element
) {
3522 element
= $(element
);
3523 var method
= element
.tagName
.toLowerCase();
3524 return Form
.Element
.Serializers
[method
](element
);
3527 setValue: function(element
, value
) {
3528 element
= $(element
);
3529 var method
= element
.tagName
.toLowerCase();
3530 Form
.Element
.Serializers
[method
](element
, value
);
3534 clear: function(element
) {
3535 $(element
).value
= '';
3539 present: function(element
) {
3540 return $(element
).value
!= '';
3543 activate: function(element
) {
3544 element
= $(element
);
3547 if (element
.select
&& (element
.tagName
.toLowerCase() != 'input' ||
3548 !['button', 'reset', 'submit'].include(element
.type
)))
3554 disable: function(element
) {
3555 element
= $(element
);
3557 element
.disabled
= true;
3561 enable: function(element
) {
3562 element
= $(element
);
3563 element
.disabled
= false;
3568 /*--------------------------------------------------------------------------*/
3570 var Field
= Form
.Element
;
3571 var $F
= Form
.Element
.Methods
.getValue
;
3573 /*--------------------------------------------------------------------------*/
3575 Form
.Element
.Serializers
= {
3576 input: function(element
, value
) {
3577 switch (element
.type
.toLowerCase()) {
3580 return Form
.Element
.Serializers
.inputSelector(element
, value
);
3582 return Form
.Element
.Serializers
.textarea(element
, value
);
3586 inputSelector: function(element
, value
) {
3587 if (Object
.isUndefined(value
)) return element
.checked
? element
.value
: null;
3588 else element
.checked
= !!value
;
3591 textarea: function(element
, value
) {
3592 if (Object
.isUndefined(value
)) return element
.value
;
3593 else element
.value
= value
;
3596 select: function(element
, index
) {
3597 if (Object
.isUndefined(index
))
3598 return this[element
.type
== 'select-one' ?
3599 'selectOne' : 'selectMany'](element
);
3601 var opt
, value
, single
= !Object
.isArray(index
);
3602 for (var i
= 0, length
= element
.length
; i
< length
; i
++) {
3603 opt
= element
.options
[i
];
3604 value
= this.optionValue(opt
);
3606 if (value
== index
) {
3607 opt
.selected
= true;
3611 else opt
.selected
= index
.include(value
);
3616 selectOne: function(element
) {
3617 var index
= element
.selectedIndex
;
3618 return index
>= 0 ? this.optionValue(element
.options
[index
]) : null;
3621 selectMany: function(element
) {
3622 var values
, length
= element
.length
;
3623 if (!length
) return null;
3625 for (var i
= 0, values
= []; i
< length
; i
++) {
3626 var opt
= element
.options
[i
];
3627 if (opt
.selected
) values
.push(this.optionValue(opt
));
3632 optionValue: function(opt
) {
3633 // extend element because hasAttribute may not be native
3634 return Element
.extend(opt
).hasAttribute('value') ? opt
.value
: opt
.text
;
3638 /*--------------------------------------------------------------------------*/
3640 Abstract
.TimedObserver
= Class
.create(PeriodicalExecuter
, {
3641 initialize: function($super, element
, frequency
, callback
) {
3642 $super(callback
, frequency
);
3643 this.element
= $(element
);
3644 this.lastValue
= this.getValue();
3647 execute: function() {
3648 var value
= this.getValue();
3649 if (Object
.isString(this.lastValue
) && Object
.isString(value
) ?
3650 this.lastValue
!= value
: String(this.lastValue
) != String(value
)) {
3651 this.callback(this.element
, value
);
3652 this.lastValue
= value
;
3657 Form
.Element
.Observer
= Class
.create(Abstract
.TimedObserver
, {
3658 getValue: function() {
3659 return Form
.Element
.getValue(this.element
);
3663 Form
.Observer
= Class
.create(Abstract
.TimedObserver
, {
3664 getValue: function() {
3665 return Form
.serialize(this.element
);
3669 /*--------------------------------------------------------------------------*/
3671 Abstract
.EventObserver
= Class
.create({
3672 initialize: function(element
, callback
) {
3673 this.element
= $(element
);
3674 this.callback
= callback
;
3676 this.lastValue
= this.getValue();
3677 if (this.element
.tagName
.toLowerCase() == 'form')
3678 this.registerFormCallbacks();
3680 this.registerCallback(this.element
);
3683 onElementEvent: function() {
3684 var value
= this.getValue();
3685 if (this.lastValue
!= value
) {
3686 this.callback(this.element
, value
);
3687 this.lastValue
= value
;
3691 registerFormCallbacks: function() {
3692 Form
.getElements(this.element
).each(this.registerCallback
, this);
3695 registerCallback: function(element
) {
3697 switch (element
.type
.toLowerCase()) {
3700 Event
.observe(element
, 'click', this.onElementEvent
.bind(this));
3703 Event
.observe(element
, 'change', this.onElementEvent
.bind(this));
3710 Form
.Element
.EventObserver
= Class
.create(Abstract
.EventObserver
, {
3711 getValue: function() {
3712 return Form
.Element
.getValue(this.element
);
3716 Form
.EventObserver
= Class
.create(Abstract
.EventObserver
, {
3717 getValue: function() {
3718 return Form
.serialize(this.element
);
3721 if (!window
.Event
) var Event
= { };
3723 Object
.extend(Event
, {
3741 relatedTarget: function(event
) {
3743 switch(event
.type
) {
3744 case 'mouseover': element
= event
.fromElement
; break;
3745 case 'mouseout': element
= event
.toElement
; break;
3746 default: return null;
3748 return Element
.extend(element
);
3752 Event
.Methods
= (function() {
3755 if (Prototype
.Browser
.IE
) {
3756 var buttonMap
= { 0: 1, 1: 4, 2: 2 };
3757 isButton = function(event
, code
) {
3758 return event
.button
== buttonMap
[code
];
3761 } else if (Prototype
.Browser
.WebKit
) {
3762 isButton = function(event
, code
) {
3764 case 0: return event
.which
== 1 && !event
.metaKey
;
3765 case 1: return event
.which
== 1 && event
.metaKey
;
3766 default: return false;
3771 isButton = function(event
, code
) {
3772 return event
.which
? (event
.which
=== code
+ 1) : (event
.button
=== code
);
3777 isLeftClick: function(event
) { return isButton(event
, 0) },
3778 isMiddleClick: function(event
) { return isButton(event
, 1) },
3779 isRightClick: function(event
) { return isButton(event
, 2) },
3781 element: function(event
) {
3782 var node
= Event
.extend(event
).target
;
3783 return Element
.extend(node
.nodeType
== Node
.TEXT_NODE
? node
.parentNode
: node
);
3786 findElement: function(event
, expression
) {
3787 var element
= Event
.element(event
);
3788 if (!expression
) return element
;
3789 var elements
= [element
].concat(element
.ancestors());
3790 return Selector
.findElement(elements
, expression
, 0);
3793 pointer: function(event
) {
3795 x
: event
.pageX
|| (event
.clientX
+
3796 (document
.documentElement
.scrollLeft
|| document
.body
.scrollLeft
)),
3797 y
: event
.pageY
|| (event
.clientY
+
3798 (document
.documentElement
.scrollTop
|| document
.body
.scrollTop
))
3802 pointerX: function(event
) { return Event
.pointer(event
).x
},
3803 pointerY: function(event
) { return Event
.pointer(event
).y
},
3805 stop: function(event
) {
3806 Event
.extend(event
);
3807 event
.preventDefault();
3808 event
.stopPropagation();
3809 event
.stopped
= true;
3814 Event
.extend
= (function() {
3815 var methods
= Object
.keys(Event
.Methods
).inject({ }, function(m
, name
) {
3816 m
[name
] = Event
.Methods
[name
].methodize();
3820 if (Prototype
.Browser
.IE
) {
3821 Object
.extend(methods
, {
3822 stopPropagation: function() { this.cancelBubble
= true },
3823 preventDefault: function() { this.returnValue
= false },
3824 inspect: function() { return "[object Event]" }
3827 return function(event
) {
3828 if (!event
) return false;
3829 if (event
._extendedByPrototype
) return event
;
3831 event
._extendedByPrototype
= Prototype
.emptyFunction
;
3832 var pointer
= Event
.pointer(event
);
3833 Object
.extend(event
, {
3834 target
: event
.srcElement
,
3835 relatedTarget
: Event
.relatedTarget(event
),
3839 return Object
.extend(event
, methods
);
3843 Event
.prototype = Event
.prototype || document
.createEvent("HTMLEvents").__proto__
;
3844 Object
.extend(Event
.prototype, methods
);
3849 Object
.extend(Event
, (function() {
3850 var cache
= Event
.cache
;
3852 function getEventID(element
) {
3853 if (element
._eventID
) return element
._eventID
;
3854 arguments
.callee
.id
= arguments
.callee
.id
|| 1;
3855 return element
._eventID
= ++arguments
.callee
.id
;
3858 function getDOMEventName(eventName
) {
3859 if (eventName
&& eventName
.include(':')) return "dataavailable";
3863 function getCacheForID(id
) {
3864 return cache
[id
] = cache
[id
] || { };
3867 function getWrappersForEventName(id
, eventName
) {
3868 var c
= getCacheForID(id
);
3869 return c
[eventName
] = c
[eventName
] || [];
3872 function createWrapper(element
, eventName
, handler
) {
3873 var id
= getEventID(element
);
3874 var c
= getWrappersForEventName(id
, eventName
);
3875 if (c
.pluck("handler").include(handler
)) return false;
3877 var wrapper = function(event
) {
3878 if (!Event
|| !Event
.extend
||
3879 (event
.eventName
&& event
.eventName
!= eventName
))
3882 Event
.extend(event
);
3883 handler
.call(element
, event
)
3886 wrapper
.handler
= handler
;
3891 function findWrapper(id
, eventName
, handler
) {
3892 var c
= getWrappersForEventName(id
, eventName
);
3893 return c
.find(function(wrapper
) { return wrapper
.handler
== handler
});
3896 function destroyWrapper(id
, eventName
, handler
) {
3897 var c
= getCacheForID(id
);
3898 if (!c
[eventName
]) return false;
3899 c
[eventName
] = c
[eventName
].without(findWrapper(id
, eventName
, handler
));
3902 function destroyCache() {
3903 for (var id
in cache
)
3904 for (var eventName
in cache
[id
])
3905 cache
[id
][eventName
] = null;
3908 if (window
.attachEvent
) {
3909 window
.attachEvent("onunload", destroyCache
);
3913 observe: function(element
, eventName
, handler
) {
3914 element
= $(element
);
3915 var name
= getDOMEventName(eventName
);
3917 var wrapper
= createWrapper(element
, eventName
, handler
);
3918 if (!wrapper
) return element
;
3920 if (element
.addEventListener
) {
3921 element
.addEventListener(name
, wrapper
, false);
3923 element
.attachEvent("on" + name
, wrapper
);
3929 stopObserving: function(element
, eventName
, handler
) {
3930 element
= $(element
);
3931 var id
= getEventID(element
), name
= getDOMEventName(eventName
);
3933 if (!handler
&& eventName
) {
3934 getWrappersForEventName(id
, eventName
).each(function(wrapper
) {
3935 element
.stopObserving(eventName
, wrapper
.handler
);
3939 } else if (!eventName
) {
3940 Object
.keys(getCacheForID(id
)).each(function(eventName
) {
3941 element
.stopObserving(eventName
);
3946 var wrapper
= findWrapper(id
, eventName
, handler
);
3947 if (!wrapper
) return element
;
3949 if (element
.removeEventListener
) {
3950 element
.removeEventListener(name
, wrapper
, false);
3952 element
.detachEvent("on" + name
, wrapper
);
3955 destroyWrapper(id
, eventName
, handler
);
3960 fire: function(element
, eventName
, memo
) {
3961 element
= $(element
);
3962 if (element
== document
&& document
.createEvent
&& !element
.dispatchEvent
)
3963 element
= document
.documentElement
;
3965 if (document
.createEvent
) {
3966 var event
= document
.createEvent("HTMLEvents");
3967 event
.initEvent("dataavailable", true, true);
3969 var event
= document
.createEventObject();
3970 event
.eventType
= "ondataavailable";
3973 event
.eventName
= eventName
;
3974 event
.memo
= memo
|| { };
3976 if (document
.createEvent
) {
3977 element
.dispatchEvent(event
);
3979 element
.fireEvent(event
.eventType
, event
);
3982 return Event
.extend(event
);
3987 Object
.extend(Event
, Event
.Methods
);
3989 Element
.addMethods({
3991 observe
: Event
.observe
,
3992 stopObserving
: Event
.stopObserving
3995 Object
.extend(document
, {
3996 fire
: Element
.Methods
.fire
.methodize(),
3997 observe
: Element
.Methods
.observe
.methodize(),
3998 stopObserving
: Element
.Methods
.stopObserving
.methodize()
4002 /* Support for the DOMContentLoaded event is based on work by Dan Webb,
4003 Matthias Miller, Dean Edwards and John Resig. */
4005 var timer
, fired
= false;
4007 function fireContentLoadedEvent() {
4009 if (timer
) window
.clearInterval(timer
);
4010 document
.fire("dom:loaded");
4014 if (document
.addEventListener
) {
4015 if (Prototype
.Browser
.WebKit
) {
4016 timer
= window
.setInterval(function() {
4017 if (/loaded|complete/.test(document
.readyState
))
4018 fireContentLoadedEvent();
4021 Event
.observe(window
, "load", fireContentLoadedEvent
);
4024 document
.addEventListener("DOMContentLoaded",
4025 fireContentLoadedEvent
, false);
4029 document
.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
4030 $("__onDOMContentLoaded").onreadystatechange = function() {
4031 if (this.readyState
== "complete") {
4032 this.onreadystatechange
= null;
4033 fireContentLoadedEvent();
4038 /*------------------------------- DEPRECATED -------------------------------*/
4040 Hash
.toQueryString
= Object
.toQueryString
;
4042 var Toggle
= { display
: Element
.toggle
};
4044 Element
.Methods
.childOf
= Element
.Methods
.descendantOf
;
4047 Before: function(element
, content
) {
4048 return Element
.insert(element
, {before
:content
});
4051 Top: function(element
, content
) {
4052 return Element
.insert(element
, {top
:content
});
4055 Bottom: function(element
, content
) {
4056 return Element
.insert(element
, {bottom
:content
});
4059 After: function(element
, content
) {
4060 return Element
.insert(element
, {after
:content
});
4064 var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4066 // This should be moved to script.aculo.us; notice the deprecated methods
4067 // further below, that map to the newer Element methods.
4069 // set to true if needed, warning: firefox performance problems
4070 // NOT neeeded for page scrolling, only if draggable contained in
4071 // scrollable elements
4072 includeScrollOffsets
: false,
4074 // must be called before calling withinIncludingScrolloffset, every time the
4076 prepare: function() {
4077 this.deltaX
= window
.pageXOffset
4078 || document
.documentElement
.scrollLeft
4079 || document
.body
.scrollLeft
4081 this.deltaY
= window
.pageYOffset
4082 || document
.documentElement
.scrollTop
4083 || document
.body
.scrollTop
4087 // caches x/y coordinate pair to use with overlap
4088 within: function(element
, x
, y
) {
4089 if (this.includeScrollOffsets
)
4090 return this.withinIncludingScrolloffsets(element
, x
, y
);
4093 this.offset
= Element
.cumulativeOffset(element
);
4095 return (y
>= this.offset
[1] &&
4096 y
< this.offset
[1] + element
.offsetHeight
&&
4097 x
>= this.offset
[0] &&
4098 x
< this.offset
[0] + element
.offsetWidth
);
4101 withinIncludingScrolloffsets: function(element
, x
, y
) {
4102 var offsetcache
= Element
.cumulativeScrollOffset(element
);
4104 this.xcomp
= x
+ offsetcache
[0] - this.deltaX
;
4105 this.ycomp
= y
+ offsetcache
[1] - this.deltaY
;
4106 this.offset
= Element
.cumulativeOffset(element
);
4108 return (this.ycomp
>= this.offset
[1] &&
4109 this.ycomp
< this.offset
[1] + element
.offsetHeight
&&
4110 this.xcomp
>= this.offset
[0] &&
4111 this.xcomp
< this.offset
[0] + element
.offsetWidth
);
4114 // within must be called directly before
4115 overlap: function(mode
, element
) {
4116 if (!mode
) return 0;
4117 if (mode
== 'vertical')
4118 return ((this.offset
[1] + element
.offsetHeight
) - this.ycomp
) /
4119 element
.offsetHeight
;
4120 if (mode
== 'horizontal')
4121 return ((this.offset
[0] + element
.offsetWidth
) - this.xcomp
) /
4122 element
.offsetWidth
;
4125 // Deprecation layer -- use newer Element methods now (1.5.2).
4127 cumulativeOffset
: Element
.Methods
.cumulativeOffset
,
4129 positionedOffset
: Element
.Methods
.positionedOffset
,
4131 absolutize: function(element
) {
4133 return Element
.absolutize(element
);
4136 relativize: function(element
) {
4138 return Element
.relativize(element
);
4141 realOffset
: Element
.Methods
.cumulativeScrollOffset
,
4143 offsetParent
: Element
.Methods
.getOffsetParent
,
4145 page
: Element
.Methods
.viewportOffset
,
4147 clone: function(source
, target
, options
) {
4148 options
= options
|| { };
4149 return Element
.clonePosition(target
, source
, options
);
4153 /*--------------------------------------------------------------------------*/
4155 if (!document
.getElementsByClassName
) document
.getElementsByClassName = function(instanceMethods
){
4156 function iter(name
) {
4157 return name
.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name
+ " ')]";
4160 instanceMethods
.getElementsByClassName
= Prototype
.BrowserFeatures
.XPath
?
4161 function(element
, className
) {
4162 className
= className
.toString().strip();
4163 var cond
= /\s/.test(className
) ? $w(className
).map(iter
).join('') : iter(className
);
4164 return cond
? document
._getElementsByXPath('.//*' + cond
, element
) : [];
4165 } : function(element
, className
) {
4166 className
= className
.toString().strip();
4167 var elements
= [], classNames
= (/\s/.test(className
) ? $w(className
) : null);
4168 if (!classNames
&& !className
) return elements
;
4170 var nodes
= $(element
).getElementsByTagName('*');
4171 className
= ' ' + className
+ ' ';
4173 for (var i
= 0, child
, cn
; child
= nodes
[i
]; i
++) {
4174 if (child
.className
&& (cn
= ' ' + child
.className
+ ' ') && (cn
.include(className
) ||
4175 (classNames
&& classNames
.all(function(name
) {
4176 return !name
.toString().blank() && cn
.include(' ' + name
+ ' ');
4178 elements
.push(Element
.extend(child
));
4183 return function(className
, parentElement
) {
4184 return $(parentElement
|| document
.body
).getElementsByClassName(className
);
4188 /*--------------------------------------------------------------------------*/
4190 Element
.ClassNames
= Class
.create();
4191 Element
.ClassNames
.prototype = {
4192 initialize: function(element
) {
4193 this.element
= $(element
);
4196 _each: function(iterator
) {
4197 this.element
.className
.split(/\s+/).select(function(name
) {
4198 return name
.length
> 0;
4202 set: function(className
) {
4203 this.element
.className
= className
;
4206 add: function(classNameToAdd
) {
4207 if (this.include(classNameToAdd
)) return;
4208 this.set($A(this).concat(classNameToAdd
).join(' '));
4211 remove: function(classNameToRemove
) {
4212 if (!this.include(classNameToRemove
)) return;
4213 this.set($A(this).without(classNameToRemove
).join(' '));
4216 toString: function() {
4217 return $A(this).join(' ');
4221 Object
.extend(Element
.ClassNames
.prototype, Enumerable
);
4223 /*--------------------------------------------------------------------------*/
4225 Element
.addMethods();