1 /* Prototype JavaScript framework, version 1.4.0
2 * (c) 2005 Sam Stephenson <sam@conio.net>
4 * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
5 * against the source tree, available from the Prototype darcs repository.
7 * Prototype is freely distributable under the terms of an MIT-style license.
9 * For details, see the Prototype web site: http://prototype.conio.net/
11 /*--------------------------------------------------------------------------*/
15 ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
17 emptyFunction: function() {},
18 K: function(x
) {return x
}
24 this.initialize
.apply(this, arguments
);
29 var Abstract
= new Object();
31 Object
.extend = function(destination
, source
) {
32 for (property
in source
) {
33 destination
[property
] = source
[property
];
38 Object
.inspect = function(object
) {
40 if (object
== undefined) return 'undefined';
41 if (object
== null) return 'null';
42 return object
.inspect
? object
.inspect() : object
.toString();
44 if (e
instanceof RangeError
) return '...';
49 Function
.prototype.bind = function() {
50 var __method
= this, args
= $A(arguments
), object
= args
.shift();
52 return __method
.apply(object
, args
.concat($A(arguments
)));
56 Function
.prototype.bindAsEventListener = function(object
) {
58 return function(event
) {
59 return __method
.call(object
, event
|| window
.event
);
63 Object
.extend(Number
.prototype, {
64 toColorPart: function() {
65 var digits
= this.toString(16);
66 if (this < 16) return '0' + digits
;
74 times: function(iterator
) {
75 $R(0, this, true).each(iterator
);
84 for (var i
= 0; i
< arguments
.length
; i
++) {
85 var lambda
= arguments
[i
];
87 returnValue
= lambda();
96 /*--------------------------------------------------------------------------*/
98 var PeriodicalExecuter
= Class
.create();
99 PeriodicalExecuter
.prototype = {
100 initialize: function(callback
, frequency
) {
101 this.callback
= callback
;
102 this.frequency
= frequency
;
103 this.currentlyExecuting
= false;
105 this.registerCallback();
108 registerCallback: function() {
109 setInterval(this.onTimerEvent
.bind(this), this.frequency
* 1000);
112 onTimerEvent: function() {
113 if (!this.currentlyExecuting
) {
115 this.currentlyExecuting
= true;
118 this.currentlyExecuting
= false;
124 /*--------------------------------------------------------------------------*/
127 var elements
= new Array();
129 for (var i
= 0; i
< arguments
.length
; i
++) {
130 var element
= arguments
[i
];
131 if (typeof element
== 'string')
132 element
= document
.getElementById(element
);
134 if (arguments
.length
== 1)
137 elements
.push(element
);
142 Object
.extend(String
.prototype, {
143 stripTags: function() {
144 return this.replace(/<\/?[^>]+>/gi, '');
147 stripScripts: function() {
148 return this.replace(new RegExp(Prototype
.ScriptFragment
, 'img'), '');
151 extractScripts: function() {
152 var matchAll
= new RegExp(Prototype
.ScriptFragment
, 'img');
153 var matchOne
= new RegExp(Prototype
.ScriptFragment
, 'im');
154 return (this.match(matchAll
) || []).map(function(scriptTag
) {
155 return (scriptTag
.match(matchOne
) || ['', ''])[1];
159 evalScripts: function() {
160 return this.extractScripts().map(eval
);
163 escapeHTML: function() {
164 var div
= document
.createElement('div');
165 var text
= document
.createTextNode(this);
166 div
.appendChild(text
);
167 return div
.innerHTML
;
170 unescapeHTML: function() {
171 var div
= document
.createElement('div');
172 div
.innerHTML
= this.stripTags();
173 return div
.childNodes
[0] ? div
.childNodes
[0].nodeValue : '';
176 toQueryParams: function() {
177 var pairs
= this.match(/^\??(.*)$/)[1].split('&');
178 return pairs
.inject({}, function(params
, pairString
) {
179 var pair
= pairString
.split('=');
180 params
[pair
[0]] = pair
[1];
185 toArray: function() {
186 return this.split('');
189 camelize: function() {
190 var oStringList
= this.split('-');
191 if (oStringList
.length
== 1) return oStringList
[0];
193 var camelizedString
= this.indexOf('-') == 0
194 ? oStringList
[0].charAt(0).toUpperCase() + oStringList
[0].substring(1)
197 for (var i
= 1, len
= oStringList
.length
; i
< len
; i
++) {
198 var s
= oStringList
[i
];
199 camelizedString
+= s
.charAt(0).toUpperCase() + s
.substring(1);
202 return camelizedString
;
205 inspect: function() {
206 return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
210 String
.prototype.parseQuery
= String
.prototype.toQueryParams
;
212 var $break = new Object();
213 var $continue = new Object();
216 each: function(iterator
) {
219 this._each(function(value
) {
221 iterator(value
, index
++);
223 if (e
!= $continue) throw e
;
227 if (e
!= $break) throw e
;
231 all: function(iterator
) {
233 this.each(function(value
, index
) {
234 result
= result
&& !!(iterator
|| Prototype
.K
)(value
, index
);
235 if (!result
) throw $break;
240 any: function(iterator
) {
242 this.each(function(value
, index
) {
243 if (result
= !!(iterator
|| Prototype
.K
)(value
, index
))
249 collect: function(iterator
) {
251 this.each(function(value
, index
) {
252 results
.push(iterator(value
, index
));
257 detect: function (iterator
) {
259 this.each(function(value
, index
) {
260 if (iterator(value
, index
)) {
268 findAll: function(iterator
) {
270 this.each(function(value
, index
) {
271 if (iterator(value
, index
))
277 grep: function(pattern
, iterator
) {
279 this.each(function(value
, index
) {
280 var stringValue
= value
.toString();
281 if (stringValue
.match(pattern
))
282 results
.push((iterator
|| Prototype
.K
)(value
, index
));
287 include: function(object
) {
289 this.each(function(value
) {
290 if (value
== object
) {
298 inject: function(memo
, iterator
) {
299 this.each(function(value
, index
) {
300 memo
= iterator(memo
, value
, index
);
305 invoke: function(method
) {
306 var args
= $A(arguments
).slice(1);
307 return this.collect(function(value
) {
308 return value
[method
].apply(value
, args
);
312 max: function(iterator
) {
314 this.each(function(value
, index
) {
315 value
= (iterator
|| Prototype
.K
)(value
, index
);
316 if (value
>= (result
|| value
))
322 min: function(iterator
) {
324 this.each(function(value
, index
) {
325 value
= (iterator
|| Prototype
.K
)(value
, index
);
326 if (value
<= (result
|| value
))
332 partition: function(iterator
) {
333 var trues
= [], falses
= [];
334 this.each(function(value
, index
) {
335 ((iterator
|| Prototype
.K
)(value
, index
) ?
336 trues : falses
).push(value
);
338 return [trues
, falses
];
341 pluck: function(property
) {
343 this.each(function(value
, index
) {
344 results
.push(value
[property
]);
349 reject: function(iterator
) {
351 this.each(function(value
, index
) {
352 if (!iterator(value
, index
))
358 sortBy: function(iterator
) {
359 return this.collect(function(value
, index
) {
360 return {value: value
, criteria: iterator(value
, index
)};
361 }).sort(function(left
, right
) {
362 var a
= left
.criteria
, b
= right
.criteria
;
363 return a
< b
? -1 : a
> b
? 1 : 0;
367 toArray: function() {
368 return this.collect(Prototype
.K
);
372 var iterator
= Prototype
.K
, args
= $A(arguments
);
373 if (typeof args
.last() == 'function')
374 iterator
= args
.pop();
376 var collections
= [this].concat(args
).map($A
);
377 return this.map(function(value
, index
) {
378 iterator(value
= collections
.pluck(index
));
383 inspect: function() {
384 return '#<Enumerable:' + this.toArray().inspect() + '>';
388 Object
.extend(Enumerable
, {
389 map: Enumerable
.collect
,
390 find: Enumerable
.detect
,
391 select: Enumerable
.findAll
,
392 member: Enumerable
.include
,
393 entries: Enumerable
.toArray
395 var $A
= Array
.from = function(iterable
) {
396 if (!iterable
) return [];
397 if (iterable
.toArray
) {
398 return iterable
.toArray();
401 for (var i
= 0; i
< iterable
.length
; i
++)
402 results
.push(iterable
[i
]);
407 Object
.extend(Array
.prototype, Enumerable
);
409 Array
.prototype._reverse
= Array
.prototype.reverse
;
411 Object
.extend(Array
.prototype, {
412 _each: function(iterator
) {
413 for (var i
= 0; i
< this.length
; i
++)
427 return this[this.length
- 1];
430 compact: function() {
431 return this.select(function(value
) {
432 return value
!= undefined || value
!= null;
436 flatten: function() {
437 return this.inject([], function(array
, value
) {
438 return array
.concat(value
.constructor == Array
?
439 value
.flatten() : [value
]);
443 without: function() {
444 var values
= $A(arguments
);
445 return this.select(function(value
) {
446 return !values
.include(value
);
450 indexOf: function(object
) {
451 for (var i
= 0; i
< this.length
; i
++)
452 if (this[i
] == object
) return i
;
456 reverse: function(inline
) {
457 return (inline
!== false ? this : this.toArray())._reverse();
461 var result
= this[0];
462 for (var i
= 0; i
< this.length
- 1; i
++)
463 this[i
] = this[i
+ 1];
468 inspect: function() {
469 return '[' + this.map(Object
.inspect
).join(', ') + ']';
473 _each: function(iterator
) {
475 var value
= this[key
];
476 if (typeof value
== 'function') continue;
478 var pair
= [key
, value
];
486 return this.pluck('key');
490 return this.pluck('value');
493 merge: function(hash
) {
494 return $H(hash
).inject($H(this), function(mergedHash
, pair
) {
495 mergedHash
[pair
.key
] = pair
.value
;
500 toQueryString: function() {
501 return this.map(function(pair
) {
502 return pair
.map(encodeURIComponent
).join('=');
506 inspect: function() {
507 return '#<Hash:{' + this.map(function(pair
) {
508 return pair
.map(Object
.inspect
).join(': ');
509 }).join(', ') + '}>';
513 function $H(object
) {
514 var hash
= Object
.extend({}, object
|| {});
515 Object
.extend(hash
, Enumerable
);
516 Object
.extend(hash
, Hash
);
519 ObjectRange
= Class
.create();
520 Object
.extend(ObjectRange
.prototype, Enumerable
);
521 Object
.extend(ObjectRange
.prototype, {
522 initialize: function(start
, end
, exclusive
) {
525 this.exclusive
= exclusive
;
528 _each: function(iterator
) {
529 var value
= this.start
;
532 value
= value
.succ();
533 } while (this.include(value
));
536 include: function(value
) {
537 if (value
< this.start
)
540 return value
< this.end
;
541 return value
<= this.end
;
545 var $R = function(start
, end
, exclusive
) {
546 return new ObjectRange(start
, end
, exclusive
);
550 getTransport: function() {
552 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
553 function() {return new ActiveXObject('Microsoft.XMLHTTP')},
554 function() {return new XMLHttpRequest()}
558 activeRequestCount: 0
564 _each: function(iterator
) {
565 this.responders
._each(iterator
);
568 register: function(responderToAdd
) {
569 if (!this.include(responderToAdd
))
570 this.responders
.push(responderToAdd
);
573 unregister: function(responderToRemove
) {
574 this.responders
= this.responders
.without(responderToRemove
);
577 dispatch: function(callback
, request
, transport
, json
) {
578 this.each(function(responder
) {
579 if (responder
[callback
] && typeof responder
[callback
] == 'function') {
581 responder
[callback
].apply(responder
, [request
, transport
, json
]);
588 Object
.extend(Ajax
.Responders
, Enumerable
);
590 Ajax
.Responders
.register({
591 onCreate: function() {
592 Ajax
.activeRequestCount
++;
595 onComplete: function() {
596 Ajax
.activeRequestCount
--;
600 Ajax
.Base = function() {};
601 Ajax
.Base
.prototype = {
602 setOptions: function(options
) {
608 Object
.extend(this.options
, options
|| {});
611 responseIsSuccess: function() {
612 return this.transport
.status
== undefined
613 || this.transport
.status
== 0
614 || (this.transport
.status
>= 200 && this.transport
.status
< 300);
617 responseIsFailure: function() {
618 return !this.responseIsSuccess();
622 Ajax
.Request
= Class
.create();
623 Ajax
.Request
.Events
=
624 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
626 Ajax
.Request
.prototype = Object
.extend(new Ajax
.Base(), {
627 initialize: function(url
, options
) {
628 this.transport
= Ajax
.getTransport();
629 this.setOptions(options
);
633 request: function(url
) {
634 var parameters
= this.options
.parameters
|| '';
635 if (parameters
.length
> 0) parameters
+= '&_=';
639 if (this.options
.method
== 'get' && parameters
.length
> 0)
640 this.url
+= (this.url
.match(/\?/) ? '&' : '?') + parameters
;
642 Ajax
.Responders
.dispatch('onCreate', this, this.transport
);
644 this.transport
.open(this.options
.method
, this.url
,
645 this.options
.asynchronous
);
647 if (this.options
.asynchronous
) {
648 this.transport
.onreadystatechange
= this.onStateChange
.bind(this);
649 setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
652 this.setRequestHeaders();
654 var body
= this.options
.postBody
? this.options
.postBody : parameters
;
655 this.transport
.send(this.options
.method
== 'post' ? body : null);
658 this.dispatchException(e
);
662 setRequestHeaders: function() {
664 ['X-Requested-With', 'XMLHttpRequest',
665 'X-Prototype-Version', Prototype
.Version
];
667 if (this.options
.method
== 'post') {
668 requestHeaders
.push('Content-type',
669 'application/x-www-form-urlencoded');
671 /* Force "Connection: close" for Mozilla browsers to work around
672 * a bug where XMLHttpReqeuest sends an incorrect Content-length
673 * header. See Mozilla Bugzilla #246651.
675 if (this.transport
.overrideMimeType
)
676 requestHeaders
.push('Connection', 'close');
679 if (this.options
.requestHeaders
)
680 requestHeaders
.push
.apply(requestHeaders
, this.options
.requestHeaders
);
682 for (var i
= 0; i
< requestHeaders
.length
; i
+= 2)
683 this.transport
.setRequestHeader(requestHeaders
[i
], requestHeaders
[i
+1]);
686 onStateChange: function() {
687 var readyState
= this.transport
.readyState
;
689 this.respondToReadyState(this.transport
.readyState
);
692 header: function(name
) {
694 return this.transport
.getResponseHeader(name
);
698 evalJSON: function() {
700 return eval(this.header('X-JSON'));
704 evalResponse: function() {
706 return eval(this.transport
.responseText
);
708 this.dispatchException(e
);
712 respondToReadyState: function(readyState
) {
713 var event
= Ajax
.Request
.Events
[readyState
];
714 var transport
= this.transport
, json
= this.evalJSON();
716 if (event
== 'Complete') {
718 (this.options
['on' + this.transport
.status
]
719 || this.options
['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
720 || Prototype
.emptyFunction
)(transport
, json
);
722 this.dispatchException(e
);
725 if ((this.header('Content-type') || '').match(/^text\/javascript/i))
730 (this.options
['on' + event
] || Prototype
.emptyFunction
)(transport
, json
);
731 Ajax
.Responders
.dispatch('on' + event
, this, transport
, json
);
733 this.dispatchException(e
);
736 /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
737 if (event
== 'Complete')
738 this.transport
.onreadystatechange
= Prototype
.emptyFunction
;
741 dispatchException: function(exception
) {
742 (this.options
.onException
|| Prototype
.emptyFunction
)(this, exception
);
743 Ajax
.Responders
.dispatch('onException', this, exception
);
747 Ajax
.Updater
= Class
.create();
749 Object
.extend(Object
.extend(Ajax
.Updater
.prototype, Ajax
.Request
.prototype), {
750 initialize: function(container
, url
, options
) {
752 success: container
.success
? $(container
.success
) : $(container
),
753 failure: container
.failure
? $(container
.failure
) :
754 (container
.success
? null : $(container
))
757 this.transport
= Ajax
.getTransport();
758 this.setOptions(options
);
760 var onComplete
= this.options
.onComplete
|| Prototype
.emptyFunction
;
761 this.options
.onComplete
= (function(transport
, object
) {
762 this.updateContent();
763 onComplete(transport
, object
);
769 updateContent: function() {
770 var receiver
= this.responseIsSuccess() ?
771 this.containers
.success : this.containers
.failure
;
772 var response
= this.transport
.responseText
;
774 if (!this.options
.evalScripts
)
775 response
= response
.stripScripts();
778 if (this.options
.insertion
) {
779 new this.options
.insertion(receiver
, response
);
781 Element
.update(receiver
, response
);
785 if (this.responseIsSuccess()) {
787 setTimeout(this.onComplete
.bind(this), 10);
792 Ajax
.PeriodicalUpdater
= Class
.create();
793 Ajax
.PeriodicalUpdater
.prototype = Object
.extend(new Ajax
.Base(), {
794 initialize: function(container
, url
, options
) {
795 this.setOptions(options
);
796 this.onComplete
= this.options
.onComplete
;
798 this.frequency
= (this.options
.frequency
|| 2);
799 this.decay
= (this.options
.decay
|| 1);
802 this.container
= container
;
809 this.options
.onComplete
= this.updateComplete
.bind(this);
814 this.updater
.onComplete
= undefined;
815 clearTimeout(this.timer
);
816 (this.onComplete
|| Prototype
.emptyFunction
).apply(this, arguments
);
819 updateComplete: function(request
) {
820 if (this.options
.decay
) {
821 this.decay
= (request
.responseText
== this.lastText
?
822 this.decay
* this.options
.decay : 1);
824 this.lastText
= request
.responseText
;
826 this.timer
= setTimeout(this.onTimerEvent
.bind(this),
827 this.decay
* this.frequency
* 1000);
830 onTimerEvent: function() {
831 this.updater
= new Ajax
.Updater(this.container
, this.url
, this.options
);
834 document
.getElementsByClassName = function(className
, parentElement
) {
835 var children
= ($(parentElement
) || document
.body
).getElementsByTagName('*');
836 return $A(children
).inject([], function(elements
, child
) {
837 if (child
.className
.match(new RegExp("(^|\\s)" + className
+ "(\\s|$)")))
838 elements
.push(child
);
843 /*--------------------------------------------------------------------------*/
845 if (!window
.Element
) {
846 var Element
= new Object();
849 Object
.extend(Element
, {
850 visible: function(element
) {
851 return $(element
).style
.display
!= 'none';
855 for (var i
= 0; i
< arguments
.length
; i
++) {
856 var element
= $(arguments
[i
]);
857 Element
[Element
.visible(element
) ? 'hide' : 'show'](element
);
862 for (var i
= 0; i
< arguments
.length
; i
++) {
863 var element
= $(arguments
[i
]);
864 element
.style
.display
= 'none';
869 for (var i
= 0; i
< arguments
.length
; i
++) {
870 var element
= $(arguments
[i
]);
871 element
.style
.display
= '';
875 remove: function(element
) {
876 element
= $(element
);
877 element
.parentNode
.removeChild(element
);
880 update: function(element
, html
) {
881 $(element
).innerHTML
= html
.stripScripts();
882 setTimeout(function() {html
.evalScripts()}, 10);
885 getHeight: function(element
) {
886 element
= $(element
);
887 return element
.offsetHeight
;
890 classNames: function(element
) {
891 return new Element
.ClassNames(element
);
894 hasClassName: function(element
, className
) {
895 if (!(element
= $(element
))) return;
896 return Element
.classNames(element
).include(className
);
899 addClassName: function(element
, className
) {
900 if (!(element
= $(element
))) return;
901 return Element
.classNames(element
).add(className
);
904 removeClassName: function(element
, className
) {
905 if (!(element
= $(element
))) return;
906 return Element
.classNames(element
).remove(className
);
909 // removes whitespace-only text node children
910 cleanWhitespace: function(element
) {
911 element
= $(element
);
912 for (var i
= 0; i
< element
.childNodes
.length
; i
++) {
913 var node
= element
.childNodes
[i
];
914 if (node
.nodeType
== 3 && !/\S/.test(node
.nodeValue
))
915 Element
.remove(node
);
919 empty: function(element
) {
920 return $(element
).innerHTML
.match(/^\s*$/);
923 scrollTo: function(element
) {
924 element
= $(element
);
925 var x
= element
.x
? element
.x : element
.offsetLeft
,
926 y
= element
.y
? element
.y : element
.offsetTop
;
927 window
.scrollTo(x
, y
);
930 getStyle: function(element
, style
) {
931 element
= $(element
);
932 var value
= element
.style
[style
.camelize()];
934 if (document
.defaultView
&& document
.defaultView
.getComputedStyle
) {
935 var css
= document
.defaultView
.getComputedStyle(element
, null);
936 value
= css
? css
.getPropertyValue(style
) : null;
937 } else if (element
.currentStyle
) {
938 value
= element
.currentStyle
[style
.camelize()];
942 if (window
.opera
&& ['left', 'top', 'right', 'bottom'].include(style
))
943 if (Element
.getStyle(element
, 'position') == 'static') value
= 'auto';
945 return value
== 'auto' ? null : value
;
948 setStyle: function(element
, style
) {
949 element
= $(element
);
951 element
.style
[name
.camelize()] = style
[name
];
954 getDimensions: function(element
) {
955 element
= $(element
);
956 if (Element
.getStyle(element
, 'display') != 'none')
957 return {width: element
.offsetWidth
, height: element
.offsetHeight
};
959 // All *Width and *Height properties give 0 on elements with display none,
960 // so enable the element temporarily
961 var els
= element
.style
;
962 var originalVisibility
= els
.visibility
;
963 var originalPosition
= els
.position
;
964 els
.visibility
= 'hidden';
965 els
.position
= 'absolute';
967 var originalWidth
= element
.clientWidth
;
968 var originalHeight
= element
.clientHeight
;
969 els
.display
= 'none';
970 els
.position
= originalPosition
;
971 els
.visibility
= originalVisibility
;
972 return {width: originalWidth
, height: originalHeight
};
975 makePositioned: function(element
) {
976 element
= $(element
);
977 var pos
= Element
.getStyle(element
, 'position');
978 if (pos
== 'static' || !pos
) {
979 element
._madePositioned
= true;
980 element
.style
.position
= 'relative';
981 // Opera returns the offset relative to the positioning context, when an
982 // element is position relative but top and left have not been defined
984 element
.style
.top
= 0;
985 element
.style
.left
= 0;
990 undoPositioned: function(element
) {
991 element
= $(element
);
992 if (element
._madePositioned
) {
993 element
._madePositioned
= undefined;
994 element
.style
.position
=
997 element
.style
.bottom
=
998 element
.style
.right
= '';
1002 makeClipping: function(element
) {
1003 element
= $(element
);
1004 if (element
._overflow
) return;
1005 element
._overflow
= element
.style
.overflow
;
1006 if ((Element
.getStyle(element
, 'overflow') || 'visible') != 'hidden')
1007 element
.style
.overflow
= 'hidden';
1010 undoClipping: function(element
) {
1011 element
= $(element
);
1012 if (element
._overflow
) return;
1013 element
.style
.overflow
= element
._overflow
;
1014 element
._overflow
= undefined;
1018 var Toggle
= new Object();
1019 Toggle
.display
= Element
.toggle
;
1021 /*--------------------------------------------------------------------------*/
1023 Abstract
.Insertion = function(adjacency
) {
1024 this.adjacency
= adjacency
;
1027 Abstract
.Insertion
.prototype = {
1028 initialize: function(element
, content
) {
1029 this.element
= $(element
);
1030 this.content
= content
.stripScripts();
1032 if (this.adjacency
&& this.element
.insertAdjacentHTML
) {
1034 this.element
.insertAdjacentHTML(this.adjacency
, this.content
);
1036 if (this.element
.tagName
.toLowerCase() == 'tbody') {
1037 this.insertContent(this.contentFromAnonymousTable());
1043 this.range
= this.element
.ownerDocument
.createRange();
1044 if (this.initializeRange
) this.initializeRange();
1045 this.insertContent([this.range
.createContextualFragment(this.content
)]);
1048 setTimeout(function() {content
.evalScripts()}, 10);
1051 contentFromAnonymousTable: function() {
1052 var div
= document
.createElement('div');
1053 div
.innerHTML
= '<table><tbody>' + this.content
+ '</tbody></table>';
1054 return $A(div
.childNodes
[0].childNodes
[0].childNodes
);
1058 var Insertion
= new Object();
1060 Insertion
.Before
= Class
.create();
1061 Insertion
.Before
.prototype = Object
.extend(new Abstract
.Insertion('beforeBegin'), {
1062 initializeRange: function() {
1063 this.range
.setStartBefore(this.element
);
1066 insertContent: function(fragments
) {
1067 fragments
.each((function(fragment
) {
1068 this.element
.parentNode
.insertBefore(fragment
, this.element
);
1073 Insertion
.Top
= Class
.create();
1074 Insertion
.Top
.prototype = Object
.extend(new Abstract
.Insertion('afterBegin'), {
1075 initializeRange: function() {
1076 this.range
.selectNodeContents(this.element
);
1077 this.range
.collapse(true);
1080 insertContent: function(fragments
) {
1081 fragments
.reverse(false).each((function(fragment
) {
1082 this.element
.insertBefore(fragment
, this.element
.firstChild
);
1087 Insertion
.Bottom
= Class
.create();
1088 Insertion
.Bottom
.prototype = Object
.extend(new Abstract
.Insertion('beforeEnd'), {
1089 initializeRange: function() {
1090 this.range
.selectNodeContents(this.element
);
1091 this.range
.collapse(this.element
);
1094 insertContent: function(fragments
) {
1095 fragments
.each((function(fragment
) {
1096 this.element
.appendChild(fragment
);
1101 Insertion
.After
= Class
.create();
1102 Insertion
.After
.prototype = Object
.extend(new Abstract
.Insertion('afterEnd'), {
1103 initializeRange: function() {
1104 this.range
.setStartAfter(this.element
);
1107 insertContent: function(fragments
) {
1108 fragments
.each((function(fragment
) {
1109 this.element
.parentNode
.insertBefore(fragment
,
1110 this.element
.nextSibling
);
1115 /*--------------------------------------------------------------------------*/
1117 Element
.ClassNames
= Class
.create();
1118 Element
.ClassNames
.prototype = {
1119 initialize: function(element
) {
1120 this.element
= $(element
);
1123 _each: function(iterator
) {
1124 this.element
.className
.split(/\s+/).select(function(name
) {
1125 return name
.length
> 0;
1129 set: function(className
) {
1130 this.element
.className
= className
;
1133 add: function(classNameToAdd
) {
1134 if (this.include(classNameToAdd
)) return;
1135 this.set(this.toArray().concat(classNameToAdd
).join(' '));
1138 remove: function(classNameToRemove
) {
1139 if (!this.include(classNameToRemove
)) return;
1140 this.set(this.select(function(className
) {
1141 return className
!= classNameToRemove
;
1145 toString: function() {
1146 return this.toArray().join(' ');
1150 Object
.extend(Element
.ClassNames
.prototype, Enumerable
);
1153 for (var i
= 0; i
< arguments
.length
; i
++)
1154 $(arguments
[i
]).value
= '';
1157 focus: function(element
) {
1161 present: function() {
1162 for (var i
= 0; i
< arguments
.length
; i
++)
1163 if ($(arguments
[i
]).value
== '') return false;
1167 select: function(element
) {
1168 $(element
).select();
1171 activate: function(element
) {
1172 element
= $(element
);
1179 /*--------------------------------------------------------------------------*/
1182 serialize: function(form
) {
1183 var elements
= Form
.getElements($(form
));
1184 var queryComponents
= new Array();
1186 for (var i
= 0; i
< elements
.length
; i
++) {
1187 var queryComponent
= Form
.Element
.serialize(elements
[i
]);
1189 queryComponents
.push(queryComponent
);
1192 return queryComponents
.join('&');
1195 getElements: function(form
) {
1197 var elements
= new Array();
1199 for (tagName
in Form
.Element
.Serializers
) {
1200 var tagElements
= form
.getElementsByTagName(tagName
);
1201 for (var j
= 0; j
< tagElements
.length
; j
++)
1202 elements
.push(tagElements
[j
]);
1207 getInputs: function(form
, typeName
, name
) {
1209 var inputs
= form
.getElementsByTagName('input');
1211 if (!typeName
&& !name
)
1214 var matchingInputs
= new Array();
1215 for (var i
= 0; i
< inputs
.length
; i
++) {
1216 var input
= inputs
[i
];
1217 if ((typeName
&& input
.type
!= typeName
) ||
1218 (name
&& input
.name
!= name
))
1220 matchingInputs
.push(input
);
1223 return matchingInputs
;
1226 disable: function(form
) {
1227 var elements
= Form
.getElements(form
);
1228 for (var i
= 0; i
< elements
.length
; i
++) {
1229 var element
= elements
[i
];
1231 element
.disabled
= 'true';
1235 enable: function(form
) {
1236 var elements
= Form
.getElements(form
);
1237 for (var i
= 0; i
< elements
.length
; i
++) {
1238 var element
= elements
[i
];
1239 element
.disabled
= '';
1243 findFirstElement: function(form
) {
1244 return Form
.getElements(form
).find(function(element
) {
1245 return element
.type
!= 'hidden' && !element
.disabled
&&
1246 ['input', 'select', 'textarea'].include(element
.tagName
.toLowerCase());
1250 focusFirstElement: function(form
) {
1251 Field
.activate(Form
.findFirstElement(form
));
1254 reset: function(form
) {
1260 serialize: function(element
) {
1261 element
= $(element
);
1262 var method
= element
.tagName
.toLowerCase();
1263 var parameter
= Form
.Element
.Serializers
[method
](element
);
1266 var key
= encodeURIComponent(parameter
[0]);
1267 if (key
.length
== 0) return;
1269 if (parameter
[1].constructor != Array
)
1270 parameter
[1] = [parameter
[1]];
1272 return parameter
[1].map(function(value
) {
1273 return key
+ '=' + encodeURIComponent(value
);
1278 getValue: function(element
) {
1279 element
= $(element
);
1280 var method
= element
.tagName
.toLowerCase();
1281 var parameter
= Form
.Element
.Serializers
[method
](element
);
1284 return parameter
[1];
1288 Form
.Element
.Serializers
= {
1289 input: function(element
) {
1290 switch (element
.type
.toLowerCase()) {
1295 return Form
.Element
.Serializers
.textarea(element
);
1298 return Form
.Element
.Serializers
.inputSelector(element
);
1303 inputSelector: function(element
) {
1304 if (element
.checked
)
1305 return [element
.name
, element
.value
];
1308 textarea: function(element
) {
1309 return [element
.name
, element
.value
];
1312 select: function(element
) {
1313 return Form
.Element
.Serializers
[element
.type
== 'select-one' ?
1314 'selectOne' : 'selectMany'](element
);
1317 selectOne: function(element
) {
1318 var value
= '', opt
, index
= element
.selectedIndex
;
1320 opt
= element
.options
[index
];
1322 if (!value
&& !('value' in opt
))
1325 return [element
.name
, value
];
1328 selectMany: function(element
) {
1329 var value
= new Array();
1330 for (var i
= 0; i
< element
.length
; i
++) {
1331 var opt
= element
.options
[i
];
1333 var optValue
= opt
.value
;
1334 if (!optValue
&& !('value' in opt
))
1335 optValue
= opt
.text
;
1336 value
.push(optValue
);
1339 return [element
.name
, value
];
1343 /*--------------------------------------------------------------------------*/
1345 var $F
= Form
.Element
.getValue
;
1347 /*--------------------------------------------------------------------------*/
1349 Abstract
.TimedObserver = function() {}
1350 Abstract
.TimedObserver
.prototype = {
1351 initialize: function(element
, frequency
, callback
) {
1352 this.frequency
= frequency
;
1353 this.element
= $(element
);
1354 this.callback
= callback
;
1356 this.lastValue
= this.getValue();
1357 this.registerCallback();
1360 registerCallback: function() {
1361 setInterval(this.onTimerEvent
.bind(this), this.frequency
* 1000);
1364 onTimerEvent: function() {
1365 var value
= this.getValue();
1366 if (this.lastValue
!= value
) {
1367 this.callback(this.element
, value
);
1368 this.lastValue
= value
;
1373 Form
.Element
.Observer
= Class
.create();
1374 Form
.Element
.Observer
.prototype = Object
.extend(new Abstract
.TimedObserver(), {
1375 getValue: function() {
1376 return Form
.Element
.getValue(this.element
);
1380 Form
.Observer
= Class
.create();
1381 Form
.Observer
.prototype = Object
.extend(new Abstract
.TimedObserver(), {
1382 getValue: function() {
1383 return Form
.serialize(this.element
);
1387 /*--------------------------------------------------------------------------*/
1389 Abstract
.EventObserver = function() {}
1390 Abstract
.EventObserver
.prototype = {
1391 initialize: function(element
, callback
) {
1392 this.element
= $(element
);
1393 this.callback
= callback
;
1395 this.lastValue
= this.getValue();
1396 if (this.element
.tagName
.toLowerCase() == 'form')
1397 this.registerFormCallbacks();
1399 this.registerCallback(this.element
);
1402 onElementEvent: function() {
1403 var value
= this.getValue();
1404 if (this.lastValue
!= value
) {
1405 this.callback(this.element
, value
);
1406 this.lastValue
= value
;
1410 registerFormCallbacks: function() {
1411 var elements
= Form
.getElements(this.element
);
1412 for (var i
= 0; i
< elements
.length
; i
++)
1413 this.registerCallback(elements
[i
]);
1416 registerCallback: function(element
) {
1418 switch (element
.type
.toLowerCase()) {
1421 Event
.observe(element
, 'click', this.onElementEvent
.bind(this));
1427 case 'select-multiple':
1428 Event
.observe(element
, 'change', this.onElementEvent
.bind(this));
1435 Form
.Element
.EventObserver
= Class
.create();
1436 Form
.Element
.EventObserver
.prototype = Object
.extend(new Abstract
.EventObserver(), {
1437 getValue: function() {
1438 return Form
.Element
.getValue(this.element
);
1442 Form
.EventObserver
= Class
.create();
1443 Form
.EventObserver
.prototype = Object
.extend(new Abstract
.EventObserver(), {
1444 getValue: function() {
1445 return Form
.serialize(this.element
);
1448 if (!window
.Event
) {
1449 var Event
= new Object();
1452 Object
.extend(Event
, {
1463 element: function(event
) {
1464 return event
.target
|| event
.srcElement
;
1467 isLeftClick: function(event
) {
1468 return (((event
.which
) && (event
.which
== 1)) ||
1469 ((event
.button
) && (event
.button
== 1)));
1472 pointerX: function(event
) {
1473 return event
.pageX
|| (event
.clientX
+
1474 (document
.documentElement
.scrollLeft
|| document
.body
.scrollLeft
));
1477 pointerY: function(event
) {
1478 return event
.pageY
|| (event
.clientY
+
1479 (document
.documentElement
.scrollTop
|| document
.body
.scrollTop
));
1482 stop: function(event
) {
1483 if (event
.preventDefault
) {
1484 event
.preventDefault();
1485 event
.stopPropagation();
1487 event
.returnValue
= false;
1488 event
.cancelBubble
= true;
1492 // find the first node with the given tagName, starting from the
1493 // node the event was triggered on; traverses the DOM upwards
1494 findElement: function(event
, tagName
) {
1495 var element
= Event
.element(event
);
1496 while (element
.parentNode
&& (!element
.tagName
||
1497 (element
.tagName
.toUpperCase() != tagName
.toUpperCase())))
1498 element
= element
.parentNode
;
1504 _observeAndCache: function(element
, name
, observer
, useCapture
) {
1505 if (!this.observers
) this.observers
= [];
1506 if (element
.addEventListener
) {
1507 this.observers
.push([element
, name
, observer
, useCapture
]);
1508 element
.addEventListener(name
, observer
, useCapture
);
1509 } else if (element
.attachEvent
) {
1510 this.observers
.push([element
, name
, observer
, useCapture
]);
1511 element
.attachEvent('on' + name
, observer
);
1515 unloadCache: function() {
1516 if (!Event
.observers
) return;
1517 for (var i
= 0; i
< Event
.observers
.length
; i
++) {
1518 Event
.stopObserving
.apply(this, Event
.observers
[i
]);
1519 Event
.observers
[i
][0] = null;
1521 Event
.observers
= false;
1524 observe: function(element
, name
, observer
, useCapture
) {
1525 var element
= $(element
);
1526 useCapture
= useCapture
|| false;
1528 if (name
== 'keypress' &&
1529 (navigator
.appVersion
.match(/Konqueror|Safari|KHTML/)
1530 || element
.attachEvent
))
1533 this._observeAndCache(element
, name
, observer
, useCapture
);
1536 stopObserving: function(element
, name
, observer
, useCapture
) {
1537 var element
= $(element
);
1538 useCapture
= useCapture
|| false;
1540 if (name
== 'keypress' &&
1541 (navigator
.appVersion
.match(/Konqueror|Safari|KHTML/)
1542 || element
.detachEvent
))
1545 if (element
.removeEventListener
) {
1546 element
.removeEventListener(name
, observer
, useCapture
);
1547 } else if (element
.detachEvent
) {
1548 element
.detachEvent('on' + name
, observer
);
1553 /* prevent memory leaks in IE */
1554 Event
.observe(window
, 'unload', Event
.unloadCache
, false);
1556 // set to true if needed, warning: firefox performance problems
1557 // NOT neeeded for page scrolling, only if draggable contained in
1558 // scrollable elements
1559 includeScrollOffsets: false,
1561 // must be called before calling withinIncludingScrolloffset, every time the
1563 prepare: function() {
1564 this.deltaX
= window
.pageXOffset
1565 || document
.documentElement
.scrollLeft
1566 || document
.body
.scrollLeft
1568 this.deltaY
= window
.pageYOffset
1569 || document
.documentElement
.scrollTop
1570 || document
.body
.scrollTop
1574 realOffset: function(element
) {
1575 var valueT
= 0, valueL
= 0;
1577 valueT
+= element
.scrollTop
|| 0;
1578 valueL
+= element
.scrollLeft
|| 0;
1579 element
= element
.parentNode
;
1581 return [valueL
, valueT
];
1584 cumulativeOffset: function(element
) {
1585 var valueT
= 0, valueL
= 0;
1587 valueT
+= element
.offsetTop
|| 0;
1588 valueL
+= element
.offsetLeft
|| 0;
1589 element
= element
.offsetParent
;
1591 return [valueL
, valueT
];
1594 positionedOffset: function(element
) {
1595 var valueT
= 0, valueL
= 0;
1597 valueT
+= element
.offsetTop
|| 0;
1598 valueL
+= element
.offsetLeft
|| 0;
1599 element
= element
.offsetParent
;
1601 p
= Element
.getStyle(element
, 'position');
1602 if (p
== 'relative' || p
== 'absolute') break;
1605 return [valueL
, valueT
];
1608 offsetParent: function(element
) {
1609 if (element
.offsetParent
) return element
.offsetParent
;
1610 if (element
== document
.body
) return element
;
1612 while ((element
= element
.parentNode
) && element
!= document
.body
)
1613 if (Element
.getStyle(element
, 'position') != 'static')
1616 return document
.body
;
1619 // caches x/y coordinate pair to use with overlap
1620 within: function(element
, x
, y
) {
1621 if (this.includeScrollOffsets
)
1622 return this.withinIncludingScrolloffsets(element
, x
, y
);
1625 this.offset
= this.cumulativeOffset(element
);
1627 return (y
>= this.offset
[1] &&
1628 y
< this.offset
[1] + element
.offsetHeight
&&
1629 x
>= this.offset
[0] &&
1630 x
< this.offset
[0] + element
.offsetWidth
);
1633 withinIncludingScrolloffsets: function(element
, x
, y
) {
1634 var offsetcache
= this.realOffset(element
);
1636 this.xcomp
= x
+ offsetcache
[0] - this.deltaX
;
1637 this.ycomp
= y
+ offsetcache
[1] - this.deltaY
;
1638 this.offset
= this.cumulativeOffset(element
);
1640 return (this.ycomp
>= this.offset
[1] &&
1641 this.ycomp
< this.offset
[1] + element
.offsetHeight
&&
1642 this.xcomp
>= this.offset
[0] &&
1643 this.xcomp
< this.offset
[0] + element
.offsetWidth
);
1646 // within must be called directly before
1647 overlap: function(mode
, element
) {
1648 if (!mode
) return 0;
1649 if (mode
== 'vertical')
1650 return ((this.offset
[1] + element
.offsetHeight
) - this.ycomp
) /
1651 element
.offsetHeight
;
1652 if (mode
== 'horizontal')
1653 return ((this.offset
[0] + element
.offsetWidth
) - this.xcomp
) /
1654 element
.offsetWidth
;
1657 clone: function(source
, target
) {
1660 target
.style
.position
= 'absolute';
1661 var offsets
= this.cumulativeOffset(source
);
1662 target
.style
.top
= offsets
[1] + 'px';
1663 target
.style
.left
= offsets
[0] + 'px';
1664 target
.style
.width
= source
.offsetWidth
+ 'px';
1665 target
.style
.height
= source
.offsetHeight
+ 'px';
1668 page: function(forElement
) {
1669 var valueT
= 0, valueL
= 0;
1671 var element
= forElement
;
1673 valueT
+= element
.offsetTop
|| 0;
1674 valueL
+= element
.offsetLeft
|| 0;
1677 if (element
.offsetParent
==document
.body
)
1678 if (Element
.getStyle(element
,'position')=='absolute') break;
1680 } while (element
= element
.offsetParent
);
1682 element
= forElement
;
1684 valueT
-= element
.scrollTop
|| 0;
1685 valueL
-= element
.scrollLeft
|| 0;
1686 } while (element
= element
.parentNode
);
1688 return [valueL
, valueT
];
1691 clone: function(source
, target
) {
1692 var options
= Object
.extend({
1699 }, arguments
[2] || {})
1701 // find page position of source
1703 var p
= Position
.page(source
);
1705 // find coordinate system to use
1709 // delta [0,0] will do fine with position: fixed elements,
1710 // position:absolute needs offsetParent deltas
1711 if (Element
.getStyle(target
,'position') == 'absolute') {
1712 parent
= Position
.offsetParent(target
);
1713 delta
= Position
.page(parent
);
1716 // correct by body offsets (fixes Safari)
1717 if (parent
== document
.body
) {
1718 delta
[0] -= document
.body
.offsetLeft
;
1719 delta
[1] -= document
.body
.offsetTop
;
1723 if(options
.setLeft
) target
.style
.left
= (p
[0] - delta
[0] + options
.offsetLeft
) + 'px';
1724 if(options
.setTop
) target
.style
.top
= (p
[1] - delta
[1] + options
.offsetTop
) + 'px';
1725 if(options
.setWidth
) target
.style
.width
= source
.offsetWidth
+ 'px';
1726 if(options
.setHeight
) target
.style
.height
= source
.offsetHeight
+ 'px';
1729 absolutize: function(element
) {
1730 element
= $(element
);
1731 if (element
.style
.position
== 'absolute') return;
1734 var offsets
= Position
.positionedOffset(element
);
1735 var top
= offsets
[1];
1736 var left
= offsets
[0];
1737 var width
= element
.clientWidth
;
1738 var height
= element
.clientHeight
;
1740 element
._originalLeft
= left
- parseFloat(element
.style
.left
|| 0);
1741 element
._originalTop
= top
- parseFloat(element
.style
.top
|| 0);
1742 element
._originalWidth
= element
.style
.width
;
1743 element
._originalHeight
= element
.style
.height
;
1745 element
.style
.position
= 'absolute';
1746 element
.style
.top
= top
+ 'px';;
1747 element
.style
.left
= left
+ 'px';;
1748 element
.style
.width
= width
+ 'px';;
1749 element
.style
.height
= height
+ 'px';;
1752 relativize: function(element
) {
1753 element
= $(element
);
1754 if (element
.style
.position
== 'relative') return;
1757 element
.style
.position
= 'relative';
1758 var top
= parseFloat(element
.style
.top
|| 0) - (element
._originalTop
|| 0);
1759 var left
= parseFloat(element
.style
.left
|| 0) - (element
._originalLeft
|| 0);
1761 element
.style
.top
= top
+ 'px';
1762 element
.style
.left
= left
+ 'px';
1763 element
.style
.height
= element
._originalHeight
;
1764 element
.style
.width
= element
._originalWidth
;
1768 // Safari returns margins on body which is incorrect if the child is absolutely
1769 // positioned. For performance reasons, redefine Position.cumulativeOffset for
1770 // KHTML/WebKit only.
1771 if (/Konqueror|Safari|KHTML/.test(navigator
.userAgent
)) {
1772 Position
.cumulativeOffset = function(element
) {
1773 var valueT
= 0, valueL
= 0;
1775 valueT
+= element
.offsetTop
|| 0;
1776 valueL
+= element
.offsetLeft
|| 0;
1777 if (element
.offsetParent
== document
.body
)
1778 if (Element
.getStyle(element
, 'position') == 'absolute') break;
1780 element
= element
.offsetParent
;
1783 return [valueL
, valueT
];