3 ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
4 emptyFunction: function() {},
5 K: function(x) {return x}
10 this.initialize.apply(this, arguments);
14 var Abstract = new Object();
15 Object.extend = function(destination, source) {
16 for (property in source) {
17 destination[property] = source[property];
21 Object.inspect = function(object) {
23 if (object == undefined) return 'undefined';
24 if (object == null) return 'null';
25 return object.inspect ? object.inspect() : object.toString();
27 if (e instanceof RangeError) return '...';
31 Function.prototype.bind = function() {
32 var __method = this, args = $A(arguments), object = args.shift();
34 return __method.apply(object, args.concat($A(arguments)));
37 Function.prototype.bindAsEventListener = function(object) {
39 return function(event) {
40 return __method.call(object, event || window.event);
43 Object.extend(Number.prototype, {
44 toColorPart: function() {
45 var digits = this.toString(16);
46 if (this < 16) return '0' + digits;
52 times: function(iterator) {
53 $R(0, this, true).each(iterator);
60 for (var i = 0; i < arguments.length; i++) {
61 var lambda = arguments[i];
63 returnValue = lambda();
70 var PeriodicalExecuter = Class.create();
71 PeriodicalExecuter.prototype = {
72 initialize: function(callback, frequency) {
73 this.callback = callback;
74 this.frequency = frequency;
75 this.currentlyExecuting = false;
76 this.registerCallback();
78 registerCallback: function() {
79 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
81 onTimerEvent: function() {
82 if (!this.currentlyExecuting) {
84 this.currentlyExecuting = true;
87 this.currentlyExecuting = false;
93 var elements = new Array();
94 for (var i = 0; i < arguments.length; i++) {
95 var element = arguments[i];
96 if (typeof element == 'string')
97 element = document.getElementById(element);
98 if (arguments.length == 1)
100 elements.push(element);
104 Object.extend(String.prototype, {
105 stripTags: function() {
106 return this.replace(/<\/?[^>]+>/gi, '');
108 stripScripts: function() {
109 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
111 extractScripts: function() {
112 var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
113 var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
114 return (this.match(matchAll) || []).map(function(scriptTag) {
115 return (scriptTag.match(matchOne) || ['', ''])[1];
118 evalScripts: function() {
119 return this.extractScripts().map(eval);
121 escapeHTML: function() {
122 var div = document.createElement('div');
123 var text = document.createTextNode(this);
124 div.appendChild(text);
125 return div.innerHTML;
127 unescapeHTML: function() {
128 var div = document.createElement('div');
129 div.innerHTML = this.stripTags();
130 return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
132 toQueryParams: function() {
133 var pairs = this.match(/^\??(.*)$/)[1].split('&');
134 return pairs.inject({}, function(params, pairString) {
135 var pair = pairString.split('=');
136 params[pair[0]] = pair[1];
140 toArray: function() {
141 return this.split('');
143 camelize: function() {
144 var oStringList = this.split('-');
145 if (oStringList.length == 1) return oStringList[0];
146 var camelizedString = this.indexOf('-') == 0
147 ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
149 for (var i = 1, len = oStringList.length; i < len; i++) {
150 var s = oStringList[i];
151 camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
153 return camelizedString;
155 inspect: function() {
156 return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
159 String.prototype.parseQuery = String.prototype.toQueryParams;
160 var $break = new Object();
161 var $continue = new Object();
163 each: function(iterator) {
166 this._each(function(value) {
168 iterator(value, index++);
170 if (e != $continue) throw e;
174 if (e != $break) throw e;
177 all: function(iterator) {
179 this.each(function(value, index) {
180 result = result && !!(iterator || Prototype.K)(value, index);
181 if (!result) throw $break;
185 any: function(iterator) {
187 this.each(function(value, index) {
188 if (result = !!(iterator || Prototype.K)(value, index))
193 collect: function(iterator) {
195 this.each(function(value, index) {
196 results.push(iterator(value, index));
200 detect: function (iterator) {
202 this.each(function(value, index) {
203 if (iterator(value, index)) {
210 findAll: function(iterator) {
212 this.each(function(value, index) {
213 if (iterator(value, index))
218 grep: function(pattern, iterator) {
220 this.each(function(value, index) {
221 var stringValue = value.toString();
222 if (stringValue.match(pattern))
223 results.push((iterator || Prototype.K)(value, index));
227 include: function(object) {
229 this.each(function(value) {
230 if (value == object) {
237 inject: function(memo, iterator) {
238 this.each(function(value, index) {
239 memo = iterator(memo, value, index);
243 invoke: function(method) {
244 var args = $A(arguments).slice(1);
245 return this.collect(function(value) {
246 return value[method].apply(value, args);
249 max: function(iterator) {
251 this.each(function(value, index) {
252 value = (iterator || Prototype.K)(value, index);
253 if (value >= (result || value))
258 min: function(iterator) {
260 this.each(function(value, index) {
261 value = (iterator || Prototype.K)(value, index);
262 if (value <= (result || value))
267 partition: function(iterator) {
268 var trues = [], falses = [];
269 this.each(function(value, index) {
270 ((iterator || Prototype.K)(value, index) ?
271 trues : falses).push(value);
273 return [trues, falses];
275 pluck: function(property) {
277 this.each(function(value, index) {
278 results.push(value[property]);
282 reject: function(iterator) {
284 this.each(function(value, index) {
285 if (!iterator(value, index))
290 sortBy: function(iterator) {
291 return this.collect(function(value, index) {
292 return {value: value, criteria: iterator(value, index)};
293 }).sort(function(left, right) {
294 var a = left.criteria, b = right.criteria;
295 return a < b ? -1 : a > b ? 1 : 0;
298 toArray: function() {
299 return this.collect(Prototype.K);
302 var iterator = Prototype.K, args = $A(arguments);
303 if (typeof args.last() == 'function')
304 iterator = args.pop();
305 var collections = [this].concat(args).map($A);
306 return this.map(function(value, index) {
307 iterator(value = collections.pluck(index));
311 inspect: function() {
312 return '#<Enumerable:' + this.toArray().inspect() + '>';
315 Object.extend(Enumerable, {
316 map: Enumerable.collect,
317 find: Enumerable.detect,
318 select: Enumerable.findAll,
319 member: Enumerable.include,
320 entries: Enumerable.toArray
322 var $A = Array.from = function(iterable) {
323 if (!iterable) return [];
324 if (iterable.toArray) {
325 return iterable.toArray();
328 for (var i = 0; i < iterable.length; i++)
329 results.push(iterable[i]);
333 Object.extend(Array.prototype, Enumerable);
334 Array.prototype._reverse = Array.prototype.reverse;
335 Object.extend(Array.prototype, {
336 _each: function(iterator) {
337 for (var i = 0; i < this.length; i++)
348 return this[this.length - 1];
350 compact: function() {
351 return this.select(function(value) {
352 return value != undefined || value != null;
355 flatten: function() {
356 return this.inject([], function(array, value) {
357 return array.concat(value.constructor == Array ?
358 value.flatten() : [value]);
361 without: function() {
362 var values = $A(arguments);
363 return this.select(function(value) {
364 return !values.include(value);
367 indexOf: function(object) {
368 for (var i = 0; i < this.length; i++)
369 if (this[i] == object) return i;
372 reverse: function(inline) {
373 return (inline !== false ? this : this.toArray())._reverse();
376 var result = this[0];
377 for (var i = 0; i < this.length - 1; i++)
378 this[i] = this[i + 1];
382 inspect: function() {
383 return '[' + this.map(Object.inspect).join(', ') + ']';
387 _each: function(iterator) {
389 var value = this[key];
390 if (typeof value == 'function') continue;
391 var pair = [key, value];
398 return this.pluck('key');
401 return this.pluck('value');
403 merge: function(hash) {
404 return $H(hash).inject($H(this), function(mergedHash, pair) {
405 mergedHash[pair.key] = pair.value;
409 toQueryString: function() {
410 return this.map(function(pair) {
411 return pair.map(encodeURIComponent).join('=');
414 inspect: function() {
415 return '#<Hash:{' + this.map(function(pair) {
416 return pair.map(Object.inspect).join(': ');
417 }).join(', ') + '}>';
420 function $H(object) {
421 var hash = Object.extend({}, object || {});
422 Object.extend(hash, Enumerable);
423 Object.extend(hash, Hash);
426 ObjectRange = Class.create();
427 Object.extend(ObjectRange.prototype, Enumerable);
428 Object.extend(ObjectRange.prototype, {
429 initialize: function(start, end, exclusive) {
432 this.exclusive = exclusive;
434 _each: function(iterator) {
435 var value = this.start;
438 value = value.succ();
439 } while (this.include(value));
441 include: function(value) {
442 if (value < this.start)
445 return value < this.end;
446 return value <= this.end;
449 var $R = function(start, end, exclusive) {
450 return new ObjectRange(start, end, exclusive);
453 getTransport: function() {
455 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
456 function() {return new ActiveXObject('Microsoft.XMLHTTP')},
457 function() {return new XMLHttpRequest()}
460 activeRequestCount: 0
464 _each: function(iterator) {
465 this.responders._each(iterator);
467 register: function(responderToAdd) {
468 if (!this.include(responderToAdd))
469 this.responders.push(responderToAdd);
471 unregister: function(responderToRemove) {
472 this.responders = this.responders.without(responderToRemove);
474 dispatch: function(callback, request, transport, json) {
475 this.each(function(responder) {
476 if (responder[callback] && typeof responder[callback] == 'function') {
478 responder[callback].apply(responder, [request, transport, json]);
484 Object.extend(Ajax.Responders, Enumerable);
485 Ajax.Responders.register({
486 onCreate: function() {
487 Ajax.activeRequestCount++;
489 onComplete: function() {
490 Ajax.activeRequestCount--;
493 Ajax.Base = function() {};
494 Ajax.Base.prototype = {
495 setOptions: function(options) {
501 Object.extend(this.options, options || {});
503 responseIsSuccess: function() {
504 return this.transport.status == undefined
505 || this.transport.status == 0
506 || (this.transport.status >= 200 && this.transport.status < 300);
508 responseIsFailure: function() {
509 return !this.responseIsSuccess();
512 Ajax.Request = Class.create();
513 Ajax.Request.Events =
514 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
515 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
516 initialize: function(url, options) {
517 this.transport = Ajax.getTransport();
518 this.setOptions(options);
521 request: function(url) {
522 var parameters = this.options.parameters || '';
523 if (parameters.length > 0) parameters += '&_=';
526 if (this.options.method == 'get' && parameters.length > 0)
527 this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
528 Ajax.Responders.dispatch('onCreate', this, this.transport);
529 this.transport.open(this.options.method, this.url,
530 this.options.asynchronous);
531 if (this.options.asynchronous) {
532 this.transport.onreadystatechange = this.onStateChange.bind(this);
533 setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
535 this.setRequestHeaders();
536 var body = this.options.postBody ? this.options.postBody : parameters;
537 this.transport.send(this.options.method == 'post' ? body : null);
539 this.dispatchException(e);
542 setRequestHeaders: function() {
544 ['X-Requested-With', 'XMLHttpRequest',
545 'X-Prototype-Version', Prototype.Version];
546 if (this.options.method == 'post') {
547 requestHeaders.push('Content-type',
548 'application/x-www-form-urlencoded');
549 if (this.transport.overrideMimeType)
550 requestHeaders.push('Connection', 'close');
552 if (this.options.requestHeaders)
553 requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
554 for (var i = 0; i < requestHeaders.length; i += 2)
555 this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
557 onStateChange: function() {
558 var readyState = this.transport.readyState;
560 this.respondToReadyState(this.transport.readyState);
562 header: function(name) {
564 return this.transport.getResponseHeader(name);
567 evalJSON: function() {
569 return eval(this.header('X-JSON'));
572 evalResponse: function() {
574 return eval(this.transport.responseText);
576 this.dispatchException(e);
579 respondToReadyState: function(readyState) {
580 var event = Ajax.Request.Events[readyState];
581 var transport = this.transport, json = this.evalJSON();
582 if (event == 'Complete') {
584 (this.options['on' + this.transport.status]
585 || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
586 || Prototype.emptyFunction)(transport, json);
588 this.dispatchException(e);
590 if ((this.header('Content-type') || '').match(/^text\/javascript/i))
594 (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
595 Ajax.Responders.dispatch('on' + event, this, transport, json);
597 this.dispatchException(e);
599 if (event == 'Complete')
600 this.transport.onreadystatechange = Prototype.emptyFunction;
602 dispatchException: function(exception) {
603 (this.options.onException || Prototype.emptyFunction)(this, exception);
604 Ajax.Responders.dispatch('onException', this, exception);
607 Ajax.Updater = Class.create();
608 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
609 initialize: function(container, url, options) {
611 success: container.success ? $(container.success) : $(container),
612 failure: container.failure ? $(container.failure) :
613 (container.success ? null : $(container))
615 this.transport = Ajax.getTransport();
616 this.setOptions(options);
617 var onComplete = this.options.onComplete || Prototype.emptyFunction;
618 this.options.onComplete = (function(transport, object) {
619 this.updateContent();
620 onComplete(transport, object);
624 updateContent: function() {
625 var receiver = this.responseIsSuccess() ?
626 this.containers.success : this.containers.failure;
627 var response = this.transport.responseText;
628 if (!this.options.evalScripts)
629 response = response.stripScripts();
631 if (this.options.insertion) {
632 new this.options.insertion(receiver, response);
634 Element.update(receiver, response);
637 if (this.responseIsSuccess()) {
639 setTimeout(this.onComplete.bind(this), 10);
643 Ajax.PeriodicalUpdater = Class.create();
644 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
645 initialize: function(container, url, options) {
646 this.setOptions(options);
647 this.onComplete = this.options.onComplete;
648 this.frequency = (this.options.frequency || 2);
649 this.decay = (this.options.decay || 1);
651 this.container = container;
656 this.options.onComplete = this.updateComplete.bind(this);
660 this.updater.onComplete = undefined;
661 clearTimeout(this.timer);
662 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
664 updateComplete: function(request) {
665 if (this.options.decay) {
666 this.decay = (request.responseText == this.lastText ?
667 this.decay * this.options.decay : 1);
668 this.lastText = request.responseText;
670 this.timer = setTimeout(this.onTimerEvent.bind(this),
671 this.decay * this.frequency * 1000);
673 onTimerEvent: function() {
674 this.updater = new Ajax.Updater(this.container, this.url, this.options);
677 document.getElementsByClassName = function(className, parentElement) {
678 var children = ($(parentElement) || document.body).getElementsByTagName('*');
679 return $A(children).inject([], function(elements, child) {
680 if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
681 elements.push(child);
685 if (!window.Element) {
686 var Element = new Object();
688 Object.extend(Element, {
689 visible: function(element) {
690 return $(element).style.display != 'none';
693 for (var i = 0; i < arguments.length; i++) {
694 var element = $(arguments[i]);
695 Element[Element.visible(element) ? 'hide' : 'show'](element);
699 for (var i = 0; i < arguments.length; i++) {
700 var element = $(arguments[i]);
701 element.style.display = 'none';
705 for (var i = 0; i < arguments.length; i++) {
706 var element = $(arguments[i]);
707 element.style.display = '';
710 remove: function(element) {
711 element = $(element);
712 element.parentNode.removeChild(element);
714 update: function(element, html) {
715 $(element).innerHTML = html.stripScripts();
716 setTimeout(function() {html.evalScripts()}, 10);
718 getHeight: function(element) {
719 element = $(element);
720 return element.offsetHeight;
722 classNames: function(element) {
723 return new Element.ClassNames(element);
725 hasClassName: function(element, className) {
726 if (!(element = $(element))) return;
727 return Element.classNames(element).include(className);
729 addClassName: function(element, className) {
730 if (!(element = $(element))) return;
731 return Element.classNames(element).add(className);
733 removeClassName: function(element, className) {
734 if (!(element = $(element))) return;
735 return Element.classNames(element).remove(className);
737 cleanWhitespace: function(element) {
738 element = $(element);
739 for (var i = 0; i < element.childNodes.length; i++) {
740 var node = element.childNodes[i];
741 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
742 Element.remove(node);
745 empty: function(element) {
746 return $(element).innerHTML.match(/^\s*$/);
748 scrollTo: function(element) {
749 element = $(element);
750 var x = element.x ? element.x : element.offsetLeft,
751 y = element.y ? element.y : element.offsetTop;
752 window.scrollTo(x, y);
754 getStyle: function(element, style) {
755 element = $(element);
756 var value = element.style[style.camelize()];
758 if (document.defaultView && document.defaultView.getComputedStyle) {
759 var css = document.defaultView.getComputedStyle(element, null);
760 value = css ? css.getPropertyValue(style) : null;
761 } else if (element.currentStyle) {
762 value = element.currentStyle[style.camelize()];
765 if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
766 if (Element.getStyle(element, 'position') == 'static') value = 'auto';
767 return value == 'auto' ? null : value;
769 setStyle: function(element, style) {
770 element = $(element);
772 element.style[name.camelize()] = style[name];
774 getDimensions: function(element) {
775 element = $(element);
776 if (Element.getStyle(element, 'display') != 'none')
777 return {width: element.offsetWidth, height: element.offsetHeight};
778 var els = element.style;
779 var originalVisibility = els.visibility;
780 var originalPosition = els.position;
781 els.visibility = 'hidden';
782 els.position = 'absolute';
784 var originalWidth = element.clientWidth;
785 var originalHeight = element.clientHeight;
786 els.display = 'none';
787 els.position = originalPosition;
788 els.visibility = originalVisibility;
789 return {width: originalWidth, height: originalHeight};
791 makePositioned: function(element) {
792 element = $(element);
793 var pos = Element.getStyle(element, 'position');
794 if (pos == 'static' || !pos) {
795 element._madePositioned = true;
796 element.style.position = 'relative';
798 element.style.top = 0;
799 element.style.left = 0;
803 undoPositioned: function(element) {
804 element = $(element);
805 if (element._madePositioned) {
806 element._madePositioned = undefined;
807 element.style.position =
810 element.style.bottom =
811 element.style.right = '';
814 makeClipping: function(element) {
815 element = $(element);
816 if (element._overflow) return;
817 element._overflow = element.style.overflow;
818 if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
819 element.style.overflow = 'hidden';
821 undoClipping: function(element) {
822 element = $(element);
823 if (element._overflow) return;
824 element.style.overflow = element._overflow;
825 element._overflow = undefined;
828 var Toggle = new Object();
829 Toggle.display = Element.toggle;
830 Abstract.Insertion = function(adjacency) {
831 this.adjacency = adjacency;
833 Abstract.Insertion.prototype = {
834 initialize: function(element, content) {
835 this.element = $(element);
836 this.content = content.stripScripts();
837 if (this.adjacency && this.element.insertAdjacentHTML) {
839 this.element.insertAdjacentHTML(this.adjacency, this.content);
841 if (this.element.tagName.toLowerCase() == 'tbody') {
842 this.insertContent(this.contentFromAnonymousTable());
848 this.range = this.element.ownerDocument.createRange();
849 if (this.initializeRange) this.initializeRange();
850 this.insertContent([this.range.createContextualFragment(this.content)]);
852 setTimeout(function() {content.evalScripts()}, 10);
854 contentFromAnonymousTable: function() {
855 var div = document.createElement('div');
856 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
857 return $A(div.childNodes[0].childNodes[0].childNodes);
860 var Insertion = new Object();
861 Insertion.Before = Class.create();
862 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
863 initializeRange: function() {
864 this.range.setStartBefore(this.element);
866 insertContent: function(fragments) {
867 fragments.each((function(fragment) {
868 this.element.parentNode.insertBefore(fragment, this.element);
872 Insertion.Top = Class.create();
873 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
874 initializeRange: function() {
875 this.range.selectNodeContents(this.element);
876 this.range.collapse(true);
878 insertContent: function(fragments) {
879 fragments.reverse(false).each((function(fragment) {
880 this.element.insertBefore(fragment, this.element.firstChild);
884 Insertion.Bottom = Class.create();
885 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
886 initializeRange: function() {
887 this.range.selectNodeContents(this.element);
888 this.range.collapse(this.element);
890 insertContent: function(fragments) {
891 fragments.each((function(fragment) {
892 this.element.appendChild(fragment);
896 Insertion.After = Class.create();
897 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
898 initializeRange: function() {
899 this.range.setStartAfter(this.element);
901 insertContent: function(fragments) {
902 fragments.each((function(fragment) {
903 this.element.parentNode.insertBefore(fragment,
904 this.element.nextSibling);
908 Element.ClassNames = Class.create();
909 Element.ClassNames.prototype = {
910 initialize: function(element) {
911 this.element = $(element);
913 _each: function(iterator) {
914 this.element.className.split(/\s+/).select(function(name) {
915 return name.length > 0;
918 set: function(className) {
919 this.element.className = className;
921 add: function(classNameToAdd) {
922 if (this.include(classNameToAdd)) return;
923 this.set(this.toArray().concat(classNameToAdd).join(' '));
925 remove: function(classNameToRemove) {
926 if (!this.include(classNameToRemove)) return;
927 this.set(this.select(function(className) {
928 return className != classNameToRemove;
931 toString: function() {
932 return this.toArray().join(' ');
935 Object.extend(Element.ClassNames.prototype, Enumerable);
938 for (var i = 0; i < arguments.length; i++)
939 $(arguments[i]).value = '';
941 focus: function(element) {
944 present: function() {
945 for (var i = 0; i < arguments.length; i++)
946 if ($(arguments[i]).value == '') return false;
949 select: function(element) {
952 activate: function(element) {
953 element = $(element);
960 serialize: function(form) {
961 var elements = Form.getElements($(form));
962 var queryComponents = new Array();
963 for (var i = 0; i < elements.length; i++) {
964 var queryComponent = Form.Element.serialize(elements[i]);
966 queryComponents.push(queryComponent);
968 return queryComponents.join('&');
970 getElements: function(form) {
972 var elements = new Array();
973 for (tagName in Form.Element.Serializers) {
974 var tagElements = form.getElementsByTagName(tagName);
975 for (var j = 0; j < tagElements.length; j++)
976 elements.push(tagElements[j]);
980 getInputs: function(form, typeName, name) {
982 var inputs = form.getElementsByTagName('input');
983 if (!typeName && !name)
985 var matchingInputs = new Array();
986 for (var i = 0; i < inputs.length; i++) {
987 var input = inputs[i];
988 if ((typeName && input.type != typeName) ||
989 (name && input.name != name))
991 matchingInputs.push(input);
993 return matchingInputs;
995 disable: function(form) {
996 var elements = Form.getElements(form);
997 for (var i = 0; i < elements.length; i++) {
998 var element = elements[i];
1000 element.disabled = 'true';
1003 enable: function(form) {
1004 var elements = Form.getElements(form);
1005 for (var i = 0; i < elements.length; i++) {
1006 var element = elements[i];
1007 element.disabled = '';
1010 findFirstElement: function(form) {
1011 return Form.getElements(form).find(function(element) {
1012 return element.type != 'hidden' && !element.disabled &&
1013 ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1016 focusFirstElement: function(form) {
1017 Field.activate(Form.findFirstElement(form));
1019 reset: function(form) {
1024 serialize: function(element) {
1025 element = $(element);
1026 var method = element.tagName.toLowerCase();
1027 var parameter = Form.Element.Serializers[method](element);
1029 var key = encodeURIComponent(parameter[0]);
1030 if (key.length == 0) return;
1031 if (parameter[1].constructor != Array)
1032 parameter[1] = [parameter[1]];
1033 return parameter[1].map(function(value) {
1034 return key + '=' + encodeURIComponent(value);
1038 getValue: function(element) {
1039 element = $(element);
1040 var method = element.tagName.toLowerCase();
1041 var parameter = Form.Element.Serializers[method](element);
1043 return parameter[1];
1046 Form.Element.Serializers = {
1047 input: function(element) {
1048 switch (element.type.toLowerCase()) {
1053 return Form.Element.Serializers.textarea(element);
1056 return Form.Element.Serializers.inputSelector(element);
1060 inputSelector: function(element) {
1061 if (element.checked)
1062 return [element.name, element.value];
1064 textarea: function(element) {
1065 return [element.name, element.value];
1067 select: function(element) {
1068 return Form.Element.Serializers[element.type == 'select-one' ?
1069 'selectOne' : 'selectMany'](element);
1071 selectOne: function(element) {
1072 var value = '', opt, index = element.selectedIndex;
1074 opt = element.options[index];
1076 if (!value && !('value' in opt))
1079 return [element.name, value];
1081 selectMany: function(element) {
1082 var value = new Array();
1083 for (var i = 0; i < element.length; i++) {
1084 var opt = element.options[i];
1086 var optValue = opt.value;
1087 if (!optValue && !('value' in opt))
1088 optValue = opt.text;
1089 value.push(optValue);
1092 return [element.name, value];
1095 var $F = Form.Element.getValue;
1096 Abstract.TimedObserver = function() {}
1097 Abstract.TimedObserver.prototype = {
1098 initialize: function(element, frequency, callback) {
1099 this.frequency = frequency;
1100 this.element = $(element);
1101 this.callback = callback;
1102 this.lastValue = this.getValue();
1103 this.registerCallback();
1105 registerCallback: function() {
1106 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
1108 onTimerEvent: function() {
1109 var value = this.getValue();
1110 if (this.lastValue != value) {
1111 this.callback(this.element, value);
1112 this.lastValue = value;
1116 Form.Element.Observer = Class.create();
1117 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1118 getValue: function() {
1119 return Form.Element.getValue(this.element);
1122 Form.Observer = Class.create();
1123 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1124 getValue: function() {
1125 return Form.serialize(this.element);
1128 Abstract.EventObserver = function() {}
1129 Abstract.EventObserver.prototype = {
1130 initialize: function(element, callback) {
1131 this.element = $(element);
1132 this.callback = callback;
1133 this.lastValue = this.getValue();
1134 if (this.element.tagName.toLowerCase() == 'form')
1135 this.registerFormCallbacks();
1137 this.registerCallback(this.element);
1139 onElementEvent: function() {
1140 var value = this.getValue();
1141 if (this.lastValue != value) {
1142 this.callback(this.element, value);
1143 this.lastValue = value;
1146 registerFormCallbacks: function() {
1147 var elements = Form.getElements(this.element);
1148 for (var i = 0; i < elements.length; i++)
1149 this.registerCallback(elements[i]);
1151 registerCallback: function(element) {
1153 switch (element.type.toLowerCase()) {
1156 Event.observe(element, 'click', this.onElementEvent.bind(this));
1162 case 'select-multiple':
1163 Event.observe(element, 'change', this.onElementEvent.bind(this));
1169 Form.Element.EventObserver = Class.create();
1170 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1171 getValue: function() {
1172 return Form.Element.getValue(this.element);
1175 Form.EventObserver = Class.create();
1176 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1177 getValue: function() {
1178 return Form.serialize(this.element);
1181 if (!window.Event) {
1182 var Event = new Object();
1184 Object.extend(Event, {
1194 element: function(event) {
1195 return event.target || event.srcElement;
1197 isLeftClick: function(event) {
1198 return (((event.which) && (event.which == 1)) ||
1199 ((event.button) && (event.button == 1)));
1201 pointerX: function(event) {
1202 return event.pageX || (event.clientX +
1203 (document.documentElement.scrollLeft || document.body.scrollLeft));
1205 pointerY: function(event) {
1206 return event.pageY || (event.clientY +
1207 (document.documentElement.scrollTop || document.body.scrollTop));
1209 stop: function(event) {
1210 if (event.preventDefault) {
1211 event.preventDefault();
1212 event.stopPropagation();
1214 event.returnValue = false;
1215 event.cancelBubble = true;
1218 findElement: function(event, tagName) {
1219 var element = Event.element(event);
1220 while (element.parentNode && (!element.tagName ||
1221 (element.tagName.toUpperCase() != tagName.toUpperCase())))
1222 element = element.parentNode;
1226 _observeAndCache: function(element, name, observer, useCapture) {
1227 if (!this.observers) this.observers = [];
1228 if (element.addEventListener) {
1229 this.observers.push([element, name, observer, useCapture]);
1230 element.addEventListener(name, observer, useCapture);
1231 } else if (element.attachEvent) {
1232 this.observers.push([element, name, observer, useCapture]);
1233 element.attachEvent('on' + name, observer);
1236 unloadCache: function() {
1237 if (!Event.observers) return;
1238 for (var i = 0; i < Event.observers.length; i++) {
1239 Event.stopObserving.apply(this, Event.observers[i]);
1240 Event.observers[i][0] = null;
1242 Event.observers = false;
1244 observe: function(element, name, observer, useCapture) {
1245 var element = $(element);
1246 useCapture = useCapture || false;
1247 if (name == 'keypress' &&
1248 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1249 || element.attachEvent))
1251 this._observeAndCache(element, name, observer, useCapture);
1253 stopObserving: function(element, name, observer, useCapture) {
1254 var element = $(element);
1255 useCapture = useCapture || false;
1256 if (name == 'keypress' &&
1257 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1258 || element.detachEvent))
1260 if (element.removeEventListener) {
1261 element.removeEventListener(name, observer, useCapture);
1262 } else if (element.detachEvent) {
1263 element.detachEvent('on' + name, observer);
1267 Event.observe(window, 'unload', Event.unloadCache, false);
1269 includeScrollOffsets: false,
1270 prepare: function() {
1271 this.deltaX = window.pageXOffset
1272 || document.documentElement.scrollLeft
1273 || document.body.scrollLeft
1275 this.deltaY = window.pageYOffset
1276 || document.documentElement.scrollTop
1277 || document.body.scrollTop
1280 realOffset: function(element) {
1281 var valueT = 0, valueL = 0;
1283 valueT += element.scrollTop || 0;
1284 valueL += element.scrollLeft || 0;
1285 element = element.parentNode;
1287 return [valueL, valueT];
1289 cumulativeOffset: function(element) {
1290 var valueT = 0, valueL = 0;
1292 valueT += element.offsetTop || 0;
1293 valueL += element.offsetLeft || 0;
1294 element = element.offsetParent;
1296 return [valueL, valueT];
1298 positionedOffset: function(element) {
1299 var valueT = 0, valueL = 0;
1301 valueT += element.offsetTop || 0;
1302 valueL += element.offsetLeft || 0;
1303 element = element.offsetParent;
1305 p = Element.getStyle(element, 'position');
1306 if (p == 'relative' || p == 'absolute') break;
1309 return [valueL, valueT];
1311 offsetParent: function(element) {
1312 if (element.offsetParent) return element.offsetParent;
1313 if (element == document.body) return element;
1314 while ((element = element.parentNode) && element != document.body)
1315 if (Element.getStyle(element, 'position') != 'static')
1317 return document.body;
1319 within: function(element, x, y) {
1320 if (this.includeScrollOffsets)
1321 return this.withinIncludingScrolloffsets(element, x, y);
1324 this.offset = this.cumulativeOffset(element);
1325 return (y >= this.offset[1] &&
1326 y < this.offset[1] + element.offsetHeight &&
1327 x >= this.offset[0] &&
1328 x < this.offset[0] + element.offsetWidth);
1330 withinIncludingScrolloffsets: function(element, x, y) {
1331 var offsetcache = this.realOffset(element);
1332 this.xcomp = x + offsetcache[0] - this.deltaX;
1333 this.ycomp = y + offsetcache[1] - this.deltaY;
1334 this.offset = this.cumulativeOffset(element);
1335 return (this.ycomp >= this.offset[1] &&
1336 this.ycomp < this.offset[1] + element.offsetHeight &&
1337 this.xcomp >= this.offset[0] &&
1338 this.xcomp < this.offset[0] + element.offsetWidth);
1340 overlap: function(mode, element) {
1341 if (!mode) return 0;
1342 if (mode == 'vertical')
1343 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1344 element.offsetHeight;
1345 if (mode == 'horizontal')
1346 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1347 element.offsetWidth;
1349 clone: function(source, target) {
1352 target.style.position = 'absolute';
1353 var offsets = this.cumulativeOffset(source);
1354 target.style.top = offsets[1] + 'px';
1355 target.style.left = offsets[0] + 'px';
1356 target.style.width = source.offsetWidth + 'px';
1357 target.style.height = source.offsetHeight + 'px';
1359 page: function(forElement) {
1360 var valueT = 0, valueL = 0;
1361 var element = forElement;
1363 valueT += element.offsetTop || 0;
1364 valueL += element.offsetLeft || 0;
1365 if (element.offsetParent==document.body)
1366 if (Element.getStyle(element,'position')=='absolute') break;
1367 } while (element = element.offsetParent);
1368 element = forElement;
1370 valueT -= element.scrollTop || 0;
1371 valueL -= element.scrollLeft || 0;
1372 } while (element = element.parentNode);
1373 return [valueL, valueT];
1375 clone: function(source, target) {
1376 var options = Object.extend({
1383 }, arguments[2] || {})
1385 var p = Position.page(source);
1389 if (Element.getStyle(target,'position') == 'absolute') {
1390 parent = Position.offsetParent(target);
1391 delta = Position.page(parent);
1393 if (parent == document.body) {
1394 delta[0] -= document.body.offsetLeft;
1395 delta[1] -= document.body.offsetTop;
1397 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
1398 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
1399 if(options.setWidth) target.style.width = source.offsetWidth + 'px';
1400 if(options.setHeight) target.style.height = source.offsetHeight + 'px';
1402 absolutize: function(element) {
1403 element = $(element);
1404 if (element.style.position == 'absolute') return;
1406 var offsets = Position.positionedOffset(element);
1407 var top = offsets[1];
1408 var left = offsets[0];
1409 var width = element.clientWidth;
1410 var height = element.clientHeight;
1411 element._originalLeft = left - parseFloat(element.style.left || 0);
1412 element._originalTop = top - parseFloat(element.style.top || 0);
1413 element._originalWidth = element.style.width;
1414 element._originalHeight = element.style.height;
1415 element.style.position = 'absolute';
1416 element.style.top = top + 'px';;
1417 element.style.left = left + 'px';;
1418 element.style.width = width + 'px';;
1419 element.style.height = height + 'px';;
1421 relativize: function(element) {
1422 element = $(element);
1423 if (element.style.position == 'relative') return;
1425 element.style.position = 'relative';
1426 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
1427 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
1428 element.style.top = top + 'px';
1429 element.style.left = left + 'px';
1430 element.style.height = element._originalHeight;
1431 element.style.width = element._originalWidth;
1434 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
1435 Position.cumulativeOffset = function(element) {
1436 var valueT = 0, valueL = 0;
1438 valueT += element.offsetTop || 0;
1439 valueL += element.offsetLeft || 0;
1440 if (element.offsetParent == document.body)
1441 if (Element.getStyle(element, 'position') == 'absolute') break;
1442 element = element.offsetParent;
1444 return [valueL, valueT];