1 /* Prototype JavaScript framework, version 1.4.0_rc2
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 /*--------------------------------------------------------------------------*/
16 emptyFunction: function() {},
17 K: function(x) {return x}
23 this.initialize.apply(this, arguments);
28 var Abstract = new Object();
30 Object.extend = function(destination, source) {
31 for (property in source) {
32 destination[property] = source[property];
37 Object.inspect = function(object) {
39 if (object == undefined) return 'undefined';
40 if (object == null) return 'null';
41 return object.inspect ? object.inspect() : object.toString();
43 if (e instanceof RangeError) return '...';
48 Function.prototype.bind = function(object) {
51 return __method.apply(object, arguments);
55 Function.prototype.bindAsEventListener = function(object) {
57 return function(event) {
58 return __method.call(object, event || window.event);
62 Object.extend(Number.prototype, {
63 toColorPart: function() {
64 var digits = this.toString(16);
65 if (this < 16) return '0' + digits;
73 times: function(iterator) {
74 $R(0, this, true).each(iterator);
83 for (var i = 0; i < arguments.length; i++) {
84 var lambda = arguments[i];
86 returnValue = lambda();
95 /*--------------------------------------------------------------------------*/
97 var PeriodicalExecuter = Class.create();
98 PeriodicalExecuter.prototype = {
99 initialize: function(callback, frequency) {
100 this.callback = callback;
101 this.frequency = frequency;
102 this.currentlyExecuting = false;
104 this.registerCallback();
107 registerCallback: function() {
108 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
111 onTimerEvent: function() {
112 if (!this.currentlyExecuting) {
114 this.currentlyExecuting = true;
117 this.currentlyExecuting = false;
123 /*--------------------------------------------------------------------------*/
126 var elements = new Array();
128 for (var i = 0; i < arguments.length; i++) {
129 var element = arguments[i];
130 if (typeof element == 'string')
131 element = document.getElementById(element);
133 if (arguments.length == 1)
136 elements.push(element);
141 Object.extend(String.prototype, {
142 stripTags: function() {
143 return this.replace(/<\/?[^>]+>/gi, '');
146 escapeHTML: function() {
147 var div = document.createElement('div');
148 var text = document.createTextNode(this);
149 div.appendChild(text);
150 return div.innerHTML;
153 unescapeHTML: function() {
154 var div = document.createElement('div');
155 div.innerHTML = this.stripTags();
156 return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
159 toQueryParams: function() {
160 var pairs = this.match(/^\??(.*)$/)[1].split('&');
161 return pairs.inject({}, function(params, pairString) {
162 var pair = pairString.split('=');
163 params[pair[0]] = pair[1];
168 toArray: function() {
169 return this.split('');
172 camelize: function() {
173 var oStringList = this.split('-');
174 if (oStringList.length == 1) return oStringList[0];
176 var camelizedString = this.indexOf('-') == 0
177 ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
180 for (var i = 1, len = oStringList.length; i < len; i++) {
181 var s = oStringList[i];
182 camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
185 return camelizedString;
188 inspect: function() {
189 return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
193 String.prototype.parseQuery = String.prototype.toQueryParams;
195 var $break = new Object();
196 var $continue = new Object();
199 each: function(iterator) {
202 this._each(function(value) {
204 iterator(value, index++);
206 if (e != $continue) throw e;
210 if (e != $break) throw e;
214 all: function(iterator) {
216 this.each(function(value, index) {
217 if (!(result &= (iterator || Prototype.K)(value, index)))
223 any: function(iterator) {
225 this.each(function(value, index) {
226 if (result &= (iterator || Prototype.K)(value, index))
232 collect: function(iterator) {
234 this.each(function(value, index) {
235 results.push(iterator(value, index));
240 detect: function (iterator) {
242 this.each(function(value, index) {
243 if (iterator(value, index)) {
251 findAll: function(iterator) {
253 this.each(function(value, index) {
254 if (iterator(value, index))
260 grep: function(pattern, iterator) {
262 this.each(function(value, index) {
263 var stringValue = value.toString();
264 if (stringValue.match(pattern))
265 results.push((iterator || Prototype.K)(value, index));
270 include: function(object) {
272 this.each(function(value) {
273 if (value == object) {
281 inject: function(memo, iterator) {
282 this.each(function(value, index) {
283 memo = iterator(memo, value, index);
288 invoke: function(method) {
289 var args = $A(arguments).slice(1);
290 return this.collect(function(value) {
291 return value[method].apply(value, args);
295 max: function(iterator) {
297 this.each(function(value, index) {
298 value = (iterator || Prototype.K)(value, index);
299 if (value >= (result || value))
305 min: function(iterator) {
307 this.each(function(value, index) {
308 value = (iterator || Prototype.K)(value, index);
309 if (value <= (result || value))
315 partition: function(iterator) {
316 var trues = [], falses = [];
317 this.each(function(value, index) {
318 ((iterator || Prototype.K)(value, index) ?
319 trues : falses).push(value);
321 return [trues, falses];
324 pluck: function(property) {
326 this.each(function(value, index) {
327 results.push(value[property]);
332 reject: function(iterator) {
334 this.each(function(value, index) {
335 if (!iterator(value, index))
341 sortBy: function(iterator) {
342 return this.collect(function(value, index) {
343 return {value: value, criteria: iterator(value, index)};
344 }).sort(function(left, right) {
345 var a = left.criteria, b = right.criteria;
346 return a < b ? -1 : a > b ? 1 : 0;
350 toArray: function() {
351 return this.collect(Prototype.K);
355 var iterator = Prototype.K, args = $A(arguments);
356 if (typeof args.last() == 'function')
357 iterator = args.pop();
359 var collections = [this].concat(args).map($A);
360 return this.map(function(value, index) {
361 iterator(value = collections.pluck(index));
366 inspect: function() {
367 return '#<Enumerable:' + this.toArray().inspect() + '>';
371 Object.extend(Enumerable, {
372 map: Enumerable.collect,
373 find: Enumerable.detect,
374 select: Enumerable.findAll,
375 member: Enumerable.include,
376 entries: Enumerable.toArray
378 var $A = Array.from = function(iterable) {
379 if (iterable.toArray) {
380 return iterable.toArray();
383 for (var i = 0; i < iterable.length; i++)
384 results.push(iterable[i]);
389 Object.extend(Array.prototype, Enumerable);
391 Object.extend(Array.prototype, {
392 _each: function(iterator) {
393 for (var i = 0; i < this.length; i++)
402 return this[this.length - 1];
405 compact: function() {
406 return this.select(function(value) {
407 return value != undefined || value != null;
411 flatten: function() {
412 return this.inject([], function(array, value) {
413 return array.concat(value.constructor == Array ?
414 value.flatten() : [value]);
418 without: function() {
419 var values = $A(arguments);
420 return this.select(function(value) {
421 return !values.include(value);
425 indexOf: function(object) {
426 for (var i = 0; i < this.length; i++)
427 if (this[i] == object) return i;
431 reverse: function() {
433 for (var i = this.length; i > 0; i--)
434 result.push(this[i-1]);
438 inspect: function() {
439 return '[' + this.map(Object.inspect).join(', ') + ']';
443 _each: function(iterator) {
445 var value = this[key];
446 if (typeof value == 'function') continue;
448 var pair = [key, value];
456 return this.pluck('key');
460 return this.pluck('value');
463 merge: function(hash) {
464 return $H(hash).inject($H(this), function(mergedHash, pair) {
465 mergedHash[pair.key] = pair.value;
470 toQueryString: function() {
471 return this.map(function(pair) {
472 return pair.map(encodeURIComponent).join('=');
476 inspect: function() {
477 return '#<Hash:{' + this.map(function(pair) {
478 return pair.map(Object.inspect).join(': ');
479 }).join(', ') + '}>';
483 function $H(object) {
484 var hash = Object.extend({}, object || {});
485 Object.extend(hash, Enumerable);
486 Object.extend(hash, Hash);
489 var Range = Class.create();
490 Object.extend(Range.prototype, Enumerable);
491 Object.extend(Range.prototype, {
492 initialize: function(start, end, exclusive) {
495 this.exclusive = exclusive;
498 _each: function(iterator) {
499 var value = this.start;
502 value = value.succ();
503 } while (this.include(value));
506 include: function(value) {
507 if (value < this.start)
510 return value < this.end;
511 return value <= this.end;
515 var $R = function(start, end, exclusive) {
516 return new Range(start, end, exclusive);
520 getTransport: function() {
522 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
523 function() {return new ActiveXObject('Microsoft.XMLHTTP')},
524 function() {return new XMLHttpRequest()}
528 activeRequestCount: 0
534 _each: function(iterator) {
535 this.responders._each(iterator);
538 register: function(responderToAdd) {
539 if (!this.include(responderToAdd))
540 this.responders.push(responderToAdd);
543 unregister: function(responderToRemove) {
544 this.responders = this.responders.without(responderToRemove);
547 dispatch: function(callback, request, transport, json) {
548 this.each(function(responder) {
549 if (responder[callback] && typeof responder[callback] == 'function') {
551 responder[callback].apply(responder, [request, transport, json]);
559 Object.extend(Ajax.Responders, Enumerable);
561 Ajax.Responders.register({
562 onCreate: function() {
563 Ajax.activeRequestCount++;
566 onComplete: function() {
567 Ajax.activeRequestCount--;
571 Ajax.Base = function() {};
572 Ajax.Base.prototype = {
573 setOptions: function(options) {
579 Object.extend(this.options, options || {});
582 responseIsSuccess: function() {
583 return this.transport.status == undefined
584 || this.transport.status == 0
585 || (this.transport.status >= 200 && this.transport.status < 300);
588 responseIsFailure: function() {
589 return !this.responseIsSuccess();
593 Ajax.Request = Class.create();
594 Ajax.Request.Events =
595 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
597 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
598 initialize: function(url, options) {
599 this.transport = Ajax.getTransport();
600 this.setOptions(options);
604 request: function(url) {
605 var parameters = this.options.parameters || '';
606 if (parameters.length > 0) parameters += '&_=';
610 if (this.options.method == 'get' && parameters.length > 0)
611 this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
613 Ajax.Responders.dispatch('onCreate', this, this.transport);
615 this.transport.open(this.options.method, this.url,
616 this.options.asynchronous);
618 if (this.options.asynchronous) {
619 this.transport.onreadystatechange = this.onStateChange.bind(this);
620 setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
623 this.setRequestHeaders();
625 var body = this.options.postBody ? this.options.postBody : parameters;
626 this.transport.send(this.options.method == 'post' ? body : null);
629 (this.options.onException || Prototype.emptyFunction)(this, e);
630 Ajax.Responders.dispatch('onException', this, e);
634 setRequestHeaders: function() {
636 ['X-Requested-With', 'XMLHttpRequest',
637 'X-Prototype-Version', Prototype.Version];
639 if (this.options.method == 'post') {
640 requestHeaders.push('Content-type',
641 'application/x-www-form-urlencoded');
643 /* Force "Connection: close" for Mozilla browsers to work around
644 * a bug where XMLHttpReqeuest sends an incorrect Content-length
645 * header. See Mozilla Bugzilla #246651.
647 if (this.transport.overrideMimeType)
648 requestHeaders.push('Connection', 'close');
651 if (this.options.requestHeaders)
652 requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
654 for (var i = 0; i < requestHeaders.length; i += 2)
655 this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
658 onStateChange: function() {
659 var readyState = this.transport.readyState;
661 this.respondToReadyState(this.transport.readyState);
664 evalJSON: function() {
666 var json = this.transport.getResponseHeader('X-JSON'), object;
673 respondToReadyState: function(readyState) {
674 var event = Ajax.Request.Events[readyState];
675 var transport = this.transport, json = this.evalJSON();
677 if (event == 'Complete')
678 (this.options['on' + this.transport.status]
679 || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
680 || Prototype.emptyFunction)(transport, json);
682 (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
683 Ajax.Responders.dispatch('on' + event, this, transport, json);
685 /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
686 if (event == 'Complete')
687 this.transport.onreadystatechange = Prototype.emptyFunction;
691 Ajax.Updater = Class.create();
692 Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
694 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
695 initialize: function(container, url, options) {
697 success: container.success ? $(container.success) : $(container),
698 failure: container.failure ? $(container.failure) :
699 (container.success ? null : $(container))
702 this.transport = Ajax.getTransport();
703 this.setOptions(options);
705 var onComplete = this.options.onComplete || Prototype.emptyFunction;
706 this.options.onComplete = (function(transport, object) {
707 this.updateContent();
708 onComplete(transport, object);
714 updateContent: function() {
715 var receiver = this.responseIsSuccess() ?
716 this.containers.success : this.containers.failure;
718 var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
719 var response = this.transport.responseText.replace(match, '');
720 var scripts = this.transport.responseText.match(match);
723 if (this.options.insertion) {
724 new this.options.insertion(receiver, response);
726 receiver.innerHTML = response;
730 if (this.responseIsSuccess()) {
732 setTimeout(this.onComplete.bind(this), 10);
735 if (this.options.evalScripts && scripts) {
736 match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
737 setTimeout((function() {
738 for (var i = 0; i < scripts.length; i++)
739 eval(scripts[i].match(match)[1]);
745 Ajax.PeriodicalUpdater = Class.create();
746 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
747 initialize: function(container, url, options) {
748 this.setOptions(options);
749 this.onComplete = this.options.onComplete;
751 this.frequency = (this.options.frequency || 2);
752 this.decay = (this.options.decay || 1);
755 this.container = container;
762 this.options.onComplete = this.updateComplete.bind(this);
767 this.updater.onComplete = undefined;
768 clearTimeout(this.timer);
769 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
772 updateComplete: function(request) {
773 if (this.options.decay) {
774 this.decay = (request.responseText == this.lastText ?
775 this.decay * this.options.decay : 1);
777 this.lastText = request.responseText;
779 this.timer = setTimeout(this.onTimerEvent.bind(this),
780 this.decay * this.frequency * 1000);
783 onTimerEvent: function() {
784 this.updater = new Ajax.Updater(this.container, this.url, this.options);
787 document.getElementsByClassName = function(className, parentElement) {
788 var children = ($(parentElement) || document.body).getElementsByTagName('*');
789 return $A(children).inject([], function(elements, child) {
790 if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
791 elements.push(child);
796 /*--------------------------------------------------------------------------*/
798 if (!window.Element) {
799 var Element = new Object();
802 Object.extend(Element, {
803 visible: function(element) {
804 return $(element).style.display != 'none';
808 for (var i = 0; i < arguments.length; i++) {
809 var element = $(arguments[i]);
810 Element[Element.visible(element) ? 'hide' : 'show'](element);
815 for (var i = 0; i < arguments.length; i++) {
816 var element = $(arguments[i]);
817 element.style.display = 'none';
822 for (var i = 0; i < arguments.length; i++) {
823 var element = $(arguments[i]);
824 element.style.display = '';
828 remove: function(element) {
829 element = $(element);
830 element.parentNode.removeChild(element);
833 getHeight: function(element) {
834 element = $(element);
835 return element.offsetHeight;
838 classNames: function(element) {
839 return new Element.ClassNames(element);
842 hasClassName: function(element, className) {
843 if (!(element = $(element))) return;
844 return Element.classNames(element).include(className);
847 addClassName: function(element, className) {
848 if (!(element = $(element))) return;
849 return Element.classNames(element).add(className);
852 removeClassName: function(element, className) {
853 if (!(element = $(element))) return;
854 return Element.classNames(element).remove(className);
857 // removes whitespace-only text node children
858 cleanWhitespace: function(element) {
859 element = $(element);
860 for (var i = 0; i < element.childNodes.length; i++) {
861 var node = element.childNodes[i];
862 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
863 Element.remove(node);
867 empty: function(element) {
868 return $(element).innerHTML.match(/^\s*$/);
871 scrollTo: function(element) {
872 element = $(element);
873 var x = element.x ? element.x : element.offsetLeft,
874 y = element.y ? element.y : element.offsetTop;
875 window.scrollTo(x, y);
878 getStyle: function(element, style) {
879 element = $(element);
880 var value = element.style[style.camelize()];
882 if (document.defaultView && document.defaultView.getComputedStyle) {
883 var css = document.defaultView.getComputedStyle(element, null);
884 value = css ? css.getPropertyValue(style) : null;
885 } else if (element.currentStyle) {
886 value = element.currentStyle[style.camelize()];
890 if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
891 if (Element.getStyle(element, 'position') == 'static') value = 'auto';
893 return value == 'auto' ? null : value;
896 getDimensions: function(element) {
897 element = $(element);
898 if (Element.getStyle(element, 'display') != 'none')
899 return {width: element.offsetWidth, height: element.offsetHeight};
901 // All *Width and *Height properties give 0 on elements with display none,
902 // so enable the element temporarily
903 var els = element.style;
904 var originalVisibility = els.visibility;
905 var originalPosition = els.position;
906 els.visibility = 'hidden';
907 els.position = 'absolute';
909 var originalWidth = element.clientWidth;
910 var originalHeight = element.clientHeight;
911 els.display = 'none';
912 els.position = originalPosition;
913 els.visibility = originalVisibility;
914 return {width: originalWidth, height: originalHeight};
917 makePositioned: function(element) {
918 element = $(element);
919 var pos = Element.getStyle(element, 'position');
920 if (pos == 'static' || !pos) {
921 element._madePositioned = true;
922 element.style.position = 'relative';
923 // Opera returns the offset relative to the positioning context, when an
924 // element is position relative but top and left have not been defined
926 element.style.top = 0;
927 element.style.left = 0;
932 undoPositioned: function(element) {
933 element = $(element);
934 if (element._madePositioned) {
935 element._madePositioned = undefined;
936 element.style.position =
939 element.style.bottom =
940 element.style.right = '';
944 makeClipping: function(element) {
945 element = $(element);
946 if (element._overflow) return;
947 element._overflow = element.style.overflow;
948 if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
949 element.style.overflow = 'hidden';
952 undoClipping: function(element) {
953 element = $(element);
954 if (element._overflow) return;
955 element.style.overflow = element._overflow;
956 element._overflow = undefined;
960 var Toggle = new Object();
961 Toggle.display = Element.toggle;
963 /*--------------------------------------------------------------------------*/
965 Abstract.Insertion = function(adjacency) {
966 this.adjacency = adjacency;
969 Abstract.Insertion.prototype = {
970 initialize: function(element, content) {
971 this.element = $(element);
972 this.content = content;
974 if (this.adjacency && this.element.insertAdjacentHTML) {
976 this.element.insertAdjacentHTML(this.adjacency, this.content);
978 if (this.element.tagName.toLowerCase() == 'tbody') {
979 this.insertContent(this.contentFromAnonymousTable());
985 this.range = this.element.ownerDocument.createRange();
986 if (this.initializeRange) this.initializeRange();
987 this.insertContent([this.range.createContextualFragment(this.content)]);
991 contentFromAnonymousTable: function() {
992 var div = document.createElement('div');
993 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
994 return $A(div.childNodes[0].childNodes[0].childNodes);
998 var Insertion = new Object();
1000 Insertion.Before = Class.create();
1001 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1002 initializeRange: function() {
1003 this.range.setStartBefore(this.element);
1006 insertContent: function(fragments) {
1007 fragments.each((function(fragment) {
1008 this.element.parentNode.insertBefore(fragment, this.element);
1013 Insertion.Top = Class.create();
1014 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1015 initializeRange: function() {
1016 this.range.selectNodeContents(this.element);
1017 this.range.collapse(true);
1020 insertContent: function(fragments) {
1021 fragments.reverse().each((function(fragment) {
1022 this.element.insertBefore(fragment, this.element.firstChild);
1027 Insertion.Bottom = Class.create();
1028 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1029 initializeRange: function() {
1030 this.range.selectNodeContents(this.element);
1031 this.range.collapse(this.element);
1034 insertContent: function(fragments) {
1035 fragments.each((function(fragment) {
1036 this.element.appendChild(fragment);
1041 Insertion.After = Class.create();
1042 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1043 initializeRange: function() {
1044 this.range.setStartAfter(this.element);
1047 insertContent: function(fragments) {
1048 fragments.each((function(fragment) {
1049 this.element.parentNode.insertBefore(fragment,
1050 this.element.nextSibling);
1055 /*--------------------------------------------------------------------------*/
1057 Element.ClassNames = Class.create();
1058 Element.ClassNames.prototype = {
1059 initialize: function(element) {
1060 this.element = $(element);
1063 _each: function(iterator) {
1064 this.element.className.split(/\s+/).select(function(name) {
1065 return name.length > 0;
1069 set: function(className) {
1070 this.element.className = className;
1073 add: function(classNameToAdd) {
1074 if (this.include(classNameToAdd)) return;
1075 this.set(this.toArray().concat(classNameToAdd).join(' '));
1078 remove: function(classNameToRemove) {
1079 if (!this.include(classNameToRemove)) return;
1080 this.set(this.select(function(className) {
1081 return className != classNameToRemove;
1085 toString: function() {
1086 return this.toArray().join(' ');
1090 Object.extend(Element.ClassNames.prototype, Enumerable);
1093 for (var i = 0; i < arguments.length; i++)
1094 $(arguments[i]).value = '';
1097 focus: function(element) {
1101 present: function() {
1102 for (var i = 0; i < arguments.length; i++)
1103 if ($(arguments[i]).value == '') return false;
1107 select: function(element) {
1108 $(element).select();
1111 activate: function(element) {
1113 $(element).select();
1117 /*--------------------------------------------------------------------------*/
1120 serialize: function(form) {
1121 var elements = Form.getElements($(form));
1122 var queryComponents = new Array();
1124 for (var i = 0; i < elements.length; i++) {
1125 var queryComponent = Form.Element.serialize(elements[i]);
1127 queryComponents.push(queryComponent);
1130 return queryComponents.join('&');
1133 getElements: function(form) {
1135 var elements = new Array();
1137 for (tagName in Form.Element.Serializers) {
1138 var tagElements = form.getElementsByTagName(tagName);
1139 for (var j = 0; j < tagElements.length; j++)
1140 elements.push(tagElements[j]);
1145 getInputs: function(form, typeName, name) {
1147 var inputs = form.getElementsByTagName('input');
1149 if (!typeName && !name)
1152 var matchingInputs = new Array();
1153 for (var i = 0; i < inputs.length; i++) {
1154 var input = inputs[i];
1155 if ((typeName && input.type != typeName) ||
1156 (name && input.name != name))
1158 matchingInputs.push(input);
1161 return matchingInputs;
1164 disable: function(form) {
1165 var elements = Form.getElements(form);
1166 for (var i = 0; i < elements.length; i++) {
1167 var element = elements[i];
1169 element.disabled = 'true';
1173 enable: function(form) {
1174 var elements = Form.getElements(form);
1175 for (var i = 0; i < elements.length; i++) {
1176 var element = elements[i];
1177 element.disabled = '';
1181 focusFirstElement: function(form) {
1183 var elements = Form.getElements(form);
1184 for (var i = 0; i < elements.length; i++) {
1185 var element = elements[i];
1186 if (element.type != 'hidden' && !element.disabled) {
1187 Field.activate(element);
1193 reset: function(form) {
1199 serialize: function(element) {
1200 element = $(element);
1201 var method = element.tagName.toLowerCase();
1202 var parameter = Form.Element.Serializers[method](element);
1205 return encodeURIComponent(parameter[0]) + '=' +
1206 encodeURIComponent(parameter[1]);
1209 getValue: function(element) {
1210 element = $(element);
1211 var method = element.tagName.toLowerCase();
1212 var parameter = Form.Element.Serializers[method](element);
1215 return parameter[1];
1219 Form.Element.Serializers = {
1220 input: function(element) {
1221 switch (element.type.toLowerCase()) {
1226 return Form.Element.Serializers.textarea(element);
1229 return Form.Element.Serializers.inputSelector(element);
1234 inputSelector: function(element) {
1235 if (element.checked)
1236 return [element.name, element.value];
1239 textarea: function(element) {
1240 return [element.name, element.value];
1243 select: function(element) {
1244 return Form.Element.Serializers[element.type == 'select-one' ?
1245 'selectOne' : 'selectMany'](element);
1248 selectOne: function(element) {
1249 var value = '', opt, index = element.selectedIndex;
1251 opt = element.options[index];
1253 if (!value && !('value' in opt))
1256 return [element.name, value];
1259 selectMany: function(element) {
1260 var value = new Array();
1261 for (var i = 0; i < element.length; i++) {
1262 var opt = element.options[i];
1264 var optValue = opt.value;
1265 if (!optValue && !('value' in opt))
1266 optValue = opt.text;
1267 value.push(optValue);
1270 return [element.name, value];
1274 /*--------------------------------------------------------------------------*/
1276 var $F = Form.Element.getValue;
1278 /*--------------------------------------------------------------------------*/
1280 Abstract.TimedObserver = function() {}
1281 Abstract.TimedObserver.prototype = {
1282 initialize: function(element, frequency, callback) {
1283 this.frequency = frequency;
1284 this.element = $(element);
1285 this.callback = callback;
1287 this.lastValue = this.getValue();
1288 this.registerCallback();
1291 registerCallback: function() {
1292 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
1295 onTimerEvent: function() {
1296 var value = this.getValue();
1297 if (this.lastValue != value) {
1298 this.callback(this.element, value);
1299 this.lastValue = value;
1304 Form.Element.Observer = Class.create();
1305 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1306 getValue: function() {
1307 return Form.Element.getValue(this.element);
1311 Form.Observer = Class.create();
1312 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1313 getValue: function() {
1314 return Form.serialize(this.element);
1318 /*--------------------------------------------------------------------------*/
1320 Abstract.EventObserver = function() {}
1321 Abstract.EventObserver.prototype = {
1322 initialize: function(element, callback) {
1323 this.element = $(element);
1324 this.callback = callback;
1326 this.lastValue = this.getValue();
1327 if (this.element.tagName.toLowerCase() == 'form')
1328 this.registerFormCallbacks();
1330 this.registerCallback(this.element);
1333 onElementEvent: function() {
1334 var value = this.getValue();
1335 if (this.lastValue != value) {
1336 this.callback(this.element, value);
1337 this.lastValue = value;
1341 registerFormCallbacks: function() {
1342 var elements = Form.getElements(this.element);
1343 for (var i = 0; i < elements.length; i++)
1344 this.registerCallback(elements[i]);
1347 registerCallback: function(element) {
1349 switch (element.type.toLowerCase()) {
1352 element.target = this;
1353 element.prev_onclick = element.onclick || Prototype.emptyFunction;
1354 element.onclick = function() {
1355 this.prev_onclick();
1356 this.target.onElementEvent();
1363 case 'select-multiple':
1364 element.target = this;
1365 element.prev_onchange = element.onchange || Prototype.emptyFunction;
1366 element.onchange = function() {
1367 this.prev_onchange();
1368 this.target.onElementEvent();
1376 Form.Element.EventObserver = Class.create();
1377 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1378 getValue: function() {
1379 return Form.Element.getValue(this.element);
1383 Form.EventObserver = Class.create();
1384 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1385 getValue: function() {
1386 return Form.serialize(this.element);
1389 if (!window.Event) {
1390 var Event = new Object();
1393 Object.extend(Event, {
1404 element: function(event) {
1405 return event.target || event.srcElement;
1408 isLeftClick: function(event) {
1409 return (((event.which) && (event.which == 1)) ||
1410 ((event.button) && (event.button == 1)));
1413 pointerX: function(event) {
1414 return event.pageX || (event.clientX +
1415 (document.documentElement.scrollLeft || document.body.scrollLeft));
1418 pointerY: function(event) {
1419 return event.pageY || (event.clientY +
1420 (document.documentElement.scrollTop || document.body.scrollTop));
1423 stop: function(event) {
1424 if (event.preventDefault) {
1425 event.preventDefault();
1426 event.stopPropagation();
1428 event.returnValue = false;
1429 event.cancelBubble = true;
1433 // find the first node with the given tagName, starting from the
1434 // node the event was triggered on; traverses the DOM upwards
1435 findElement: function(event, tagName) {
1436 var element = Event.element(event);
1437 while (element.parentNode && (!element.tagName ||
1438 (element.tagName.toUpperCase() != tagName.toUpperCase())))
1439 element = element.parentNode;
1445 _observeAndCache: function(element, name, observer, useCapture) {
1446 if (!this.observers) this.observers = [];
1447 if (element.addEventListener) {
1448 this.observers.push([element, name, observer, useCapture]);
1449 element.addEventListener(name, observer, useCapture);
1450 } else if (element.attachEvent) {
1451 this.observers.push([element, name, observer, useCapture]);
1452 element.attachEvent('on' + name, observer);
1456 unloadCache: function() {
1457 if (!Event.observers) return;
1458 for (var i = 0; i < Event.observers.length; i++) {
1459 Event.stopObserving.apply(this, Event.observers[i]);
1460 Event.observers[i][0] = null;
1462 Event.observers = false;
1465 observe: function(element, name, observer, useCapture) {
1466 var element = $(element);
1467 useCapture = useCapture || false;
1469 if (name == 'keypress' &&
1470 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1471 || element.attachEvent))
1474 this._observeAndCache(element, name, observer, useCapture);
1477 stopObserving: function(element, name, observer, useCapture) {
1478 var element = $(element);
1479 useCapture = useCapture || false;
1481 if (name == 'keypress' &&
1482 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1483 || element.detachEvent))
1486 if (element.removeEventListener) {
1487 element.removeEventListener(name, observer, useCapture);
1488 } else if (element.detachEvent) {
1489 element.detachEvent('on' + name, observer);
1494 /* prevent memory leaks in IE */
1495 Event.observe(window, 'unload', Event.unloadCache, false);
1497 // set to true if needed, warning: firefox performance problems
1498 // NOT neeeded for page scrolling, only if draggable contained in
1499 // scrollable elements
1500 includeScrollOffsets: false,
1502 // must be called before calling withinIncludingScrolloffset, every time the
1504 prepare: function() {
1505 this.deltaX = window.pageXOffset
1506 || document.documentElement.scrollLeft
1507 || document.body.scrollLeft
1509 this.deltaY = window.pageYOffset
1510 || document.documentElement.scrollTop
1511 || document.body.scrollTop
1515 realOffset: function(element) {
1516 var valueT = 0, valueL = 0;
1518 valueT += element.scrollTop || 0;
1519 valueL += element.scrollLeft || 0;
1520 element = element.parentNode;
1522 return [valueL, valueT];
1525 cumulativeOffset: function(element) {
1526 var valueT = 0, valueL = 0;
1528 valueT += element.offsetTop || 0;
1529 valueL += element.offsetLeft || 0;
1530 element = element.offsetParent;
1532 return [valueL, valueT];
1535 positionedOffset: function(element) {
1536 var valueT = 0, valueL = 0;
1538 valueT += element.offsetTop || 0;
1539 valueL += element.offsetLeft || 0;
1540 element = element.offsetParent;
1542 p = Element.getStyle(element, 'position');
1543 if (p == 'relative' || p == 'absolute') break;
1546 return [valueL, valueT];
1549 offsetParent: function(element) {
1550 if (element.offsetParent) return element.offsetParent;
1551 if (element == document.body) return element;
1553 while ((element = element.parentNode) && element != document.body)
1554 if (Element.getStyle(element, 'position') != 'static')
1557 return document.body;
1560 // caches x/y coordinate pair to use with overlap
1561 within: function(element, x, y) {
1562 if (this.includeScrollOffsets)
1563 return this.withinIncludingScrolloffsets(element, x, y);
1566 this.offset = this.cumulativeOffset(element);
1568 return (y >= this.offset[1] &&
1569 y < this.offset[1] + element.offsetHeight &&
1570 x >= this.offset[0] &&
1571 x < this.offset[0] + element.offsetWidth);
1574 withinIncludingScrolloffsets: function(element, x, y) {
1575 var offsetcache = this.realOffset(element);
1577 this.xcomp = x + offsetcache[0] - this.deltaX;
1578 this.ycomp = y + offsetcache[1] - this.deltaY;
1579 this.offset = this.cumulativeOffset(element);
1581 return (this.ycomp >= this.offset[1] &&
1582 this.ycomp < this.offset[1] + element.offsetHeight &&
1583 this.xcomp >= this.offset[0] &&
1584 this.xcomp < this.offset[0] + element.offsetWidth);
1587 // within must be called directly before
1588 overlap: function(mode, element) {
1589 if (!mode) return 0;
1590 if (mode == 'vertical')
1591 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1592 element.offsetHeight;
1593 if (mode == 'horizontal')
1594 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1595 element.offsetWidth;
1598 clone: function(source, target) {
1601 target.style.position = 'absolute';
1602 var offsets = this.cumulativeOffset(source);
1603 target.style.top = offsets[1] + 'px';
1604 target.style.left = offsets[0] + 'px';
1605 target.style.width = source.offsetWidth + 'px';
1606 target.style.height = source.offsetHeight + 'px';
1609 page: function(forElement) {
1610 var valueT = 0, valueL = 0;
1612 var element = forElement;
1614 valueT += element.offsetTop || 0;
1615 valueL += element.offsetLeft || 0;
1618 if (element.offsetParent==document.body)
1619 if (Element.getStyle(element,'position')=='absolute') break;
1621 } while (element = element.offsetParent);
1623 element = forElement;
1625 valueT -= element.scrollTop || 0;
1626 valueL -= element.scrollLeft || 0;
1627 } while (element = element.parentNode);
1629 return [valueL, valueT];
1632 clone: function(source, target) {
1633 var options = Object.extend({
1640 }, arguments[2] || {})
1642 // find page position of source
1644 var p = Position.page(source);
1646 // find coordinate system to use
1650 // delta [0,0] will do fine with position: fixed elements,
1651 // position:absolute needs offsetParent deltas
1652 if (Element.getStyle(target,'position') == 'absolute') {
1653 parent = Position.offsetParent(target);
1654 delta = Position.page(parent);
1657 // correct by body offsets (fixes Safari)
1658 if (parent == document.body) {
1659 delta[0] -= document.body.offsetLeft;
1660 delta[1] -= document.body.offsetTop;
1664 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
1665 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
1666 if(options.setWidth) target.style.width = source.offsetWidth + 'px';
1667 if(options.setHeight) target.style.height = source.offsetHeight + 'px';
1670 absolutize: function(element) {
1671 element = $(element);
1672 if (element.style.position == 'absolute') return;
1675 var offsets = Position.positionedOffset(element);
1676 var top = offsets[1];
1677 var left = offsets[0];
1678 var width = element.clientWidth;
1679 var height = element.clientHeight;
1681 element._originalLeft = left - parseFloat(element.style.left || 0);
1682 element._originalTop = top - parseFloat(element.style.top || 0);
1683 element._originalWidth = element.style.width;
1684 element._originalHeight = element.style.height;
1686 element.style.position = 'absolute';
1687 element.style.top = top + 'px';;
1688 element.style.left = left + 'px';;
1689 element.style.width = width + 'px';;
1690 element.style.height = height + 'px';;
1693 relativize: function(element) {
1694 element = $(element);
1695 if (element.style.position == 'relative') return;
1698 element.style.position = 'relative';
1699 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
1700 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
1702 element.style.top = top + 'px';
1703 element.style.left = left + 'px';
1704 element.style.height = element._originalHeight;
1705 element.style.width = element._originalWidth;
1709 // Safari returns margins on body which is incorrect if the child is absolutely
1710 // positioned. For performance reasons, redefine Position.cumulativeOffset for
1711 // KHTML/WebKit only.
1712 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
1713 Position.cumulativeOffset = function(element) {
1714 var valueT = 0, valueL = 0;
1716 valueT += element.offsetTop || 0;
1717 valueL += element.offsetLeft || 0;
1718 if (element.offsetParent == document.body)
1719 if (Element.getStyle(element, 'position') == 'absolute') break;
1721 element = element.offsetParent;
1724 return [valueL, valueT];