added define-attributes macro
[clinton/lisp-on-lines.git] / wwwroot / prototype-1.3.1.js
1 /* Prototype JavaScript framework, version 1.3.1
2 * (c) 2005 Sam Stephenson <sam@conio.net>
3 *
4 * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
5 * against the source tree, available from the Prototype darcs repository.
6 *
7 * Prototype is freely distributable under the terms of an MIT-style license.
8 *
9 * For details, see the Prototype web site: http://prototype.conio.net/
10 *
11 /*--------------------------------------------------------------------------*/
12
13 var Prototype = {
14 Version: '1.3.1',
15 emptyFunction: function() {}
16 }
17
18 var Class = {
19 create: function() {
20 return function() {
21 this.initialize.apply(this, arguments);
22 }
23 }
24 }
25
26 var Abstract = new Object();
27
28 Object.extend = function(destination, source) {
29 for (property in source) {
30 destination[property] = source[property];
31 }
32 return destination;
33 }
34
35 Object.prototype.extend = function(object) {
36 return Object.extend.apply(this, [this, object]);
37 }
38
39 Function.prototype.bind = function(object) {
40 var __method = this;
41 return function() {
42 __method.apply(object, arguments);
43 }
44 }
45
46 Function.prototype.bindAsEventListener = function(object) {
47 var __method = this;
48 return function(event) {
49 __method.call(object, event || window.event);
50 }
51 }
52
53 Number.prototype.toColorPart = function() {
54 var digits = this.toString(16);
55 if (this < 16) return '0' + digits;
56 return digits;
57 }
58
59 var Try = {
60 these: function() {
61 var returnValue;
62
63 for (var i = 0; i < arguments.length; i++) {
64 var lambda = arguments[i];
65 try {
66 returnValue = lambda();
67 break;
68 } catch (e) {}
69 }
70
71 return returnValue;
72 }
73 }
74
75 /*--------------------------------------------------------------------------*/
76
77 var PeriodicalExecuter = Class.create();
78 PeriodicalExecuter.prototype = {
79 initialize: function(callback, frequency) {
80 this.callback = callback;
81 this.frequency = frequency;
82 this.currentlyExecuting = false;
83
84 this.registerCallback();
85 },
86
87 registerCallback: function() {
88 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
89 },
90
91 onTimerEvent: function() {
92 if (!this.currentlyExecuting) {
93 try {
94 this.currentlyExecuting = true;
95 this.callback();
96 } finally {
97 this.currentlyExecuting = false;
98 }
99 }
100 }
101 }
102
103 /*--------------------------------------------------------------------------*/
104
105 function $() {
106 var elements = new Array();
107
108 for (var i = 0; i < arguments.length; i++) {
109 var element = arguments[i];
110 if (typeof element == 'string')
111 element = document.getElementById(element);
112
113 if (arguments.length == 1)
114 return element;
115
116 elements.push(element);
117 }
118
119 return elements;
120 }
121
122 if (!Array.prototype.push) {
123 Array.prototype.push = function() {
124 var startLength = this.length;
125 for (var i = 0; i < arguments.length; i++)
126 this[startLength + i] = arguments[i];
127 return this.length;
128 }
129 }
130
131 if (!Function.prototype.apply) {
132 // Based on code from http://www.youngpup.net/
133 Function.prototype.apply = function(object, parameters) {
134 var parameterStrings = new Array();
135 if (!object) object = window;
136 if (!parameters) parameters = new Array();
137
138 for (var i = 0; i < parameters.length; i++)
139 parameterStrings[i] = 'parameters[' + i + ']';
140
141 object.__apply__ = this;
142 var result = eval('object.__apply__(' +
143 parameterStrings.join(', ') + ')');
144 object.__apply__ = null;
145
146 return result;
147 }
148 }
149
150 String.prototype.extend({
151 stripTags: function() {
152 return this.replace(/<\/?[^>]+>/gi, '');
153 },
154
155 escapeHTML: function() {
156 var div = document.createElement('div');
157 var text = document.createTextNode(this);
158 div.appendChild(text);
159 return div.innerHTML;
160 },
161
162 unescapeHTML: function() {
163 var div = document.createElement('div');
164 div.innerHTML = this.stripTags();
165 return div.childNodes[0].nodeValue;
166 }
167 });
168
169 var Ajax = {
170 getTransport: function() {
171 return Try.these(
172 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
173 function() {return new ActiveXObject('Microsoft.XMLHTTP')},
174 function() {return new XMLHttpRequest()}
175 ) || false;
176 }
177 }
178
179 Ajax.Base = function() {};
180 Ajax.Base.prototype = {
181 setOptions: function(options) {
182 this.options = {
183 method: 'post',
184 asynchronous: true,
185 parameters: ''
186 }.extend(options || {});
187 },
188
189 responseIsSuccess: function() {
190 return this.transport.status == undefined
191 || this.transport.status == 0
192 || (this.transport.status >= 200 && this.transport.status < 300);
193 },
194
195 responseIsFailure: function() {
196 return !this.responseIsSuccess();
197 }
198 }
199
200 Ajax.Request = Class.create();
201 Ajax.Request.Events =
202 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
203
204 Ajax.Request.prototype = (new Ajax.Base()).extend({
205 initialize: function(url, options) {
206 this.transport = Ajax.getTransport();
207 this.setOptions(options);
208 this.request(url);
209 },
210
211 request: function(url) {
212 var parameters = this.options.parameters || '';
213 if (parameters.length > 0) parameters += '&_=';
214
215 try {
216 if (this.options.method == 'get')
217 url += '?' + parameters;
218
219 this.transport.open(this.options.method, url,
220 this.options.asynchronous);
221
222 if (this.options.asynchronous) {
223 this.transport.onreadystatechange = this.onStateChange.bind(this);
224 setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
225 }
226
227 this.setRequestHeaders();
228
229 var body = this.options.postBody ? this.options.postBody : parameters;
230 this.transport.send(this.options.method == 'post' ? body : null);
231
232 } catch (e) {
233 }
234 },
235
236 setRequestHeaders: function() {
237 var requestHeaders =
238 ['X-Requested-With', 'XMLHttpRequest',
239 'X-Prototype-Version', Prototype.Version];
240
241 if (this.options.method == 'post') {
242 requestHeaders.push('Content-type',
243 'application/x-www-form-urlencoded');
244
245 /* Force "Connection: close" for Mozilla browsers to work around
246 * a bug where XMLHttpReqeuest sends an incorrect Content-length
247 * header. See Mozilla Bugzilla #246651.
248 */
249 if (this.transport.overrideMimeType)
250 requestHeaders.push('Connection', 'close');
251 }
252
253 if (this.options.requestHeaders)
254 requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
255
256 for (var i = 0; i < requestHeaders.length; i += 2)
257 this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
258 },
259
260 onStateChange: function() {
261 var readyState = this.transport.readyState;
262 if (readyState != 1)
263 this.respondToReadyState(this.transport.readyState);
264 },
265
266 respondToReadyState: function(readyState) {
267 var event = Ajax.Request.Events[readyState];
268
269 if (event == 'Complete')
270 (this.options['on' + this.transport.status]
271 || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
272 || Prototype.emptyFunction)(this.transport);
273
274 (this.options['on' + event] || Prototype.emptyFunction)(this.transport);
275
276 /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
277 if (event == 'Complete')
278 this.transport.onreadystatechange = Prototype.emptyFunction;
279 }
280 });
281
282 Ajax.Updater = Class.create();
283 Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
284
285 Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
286 initialize: function(container, url, options) {
287 this.containers = {
288 success: container.success ? $(container.success) : $(container),
289 failure: container.failure ? $(container.failure) :
290 (container.success ? null : $(container))
291 }
292
293 this.transport = Ajax.getTransport();
294 this.setOptions(options);
295
296 var onComplete = this.options.onComplete || Prototype.emptyFunction;
297 this.options.onComplete = (function() {
298 this.updateContent();
299 onComplete(this.transport);
300 }).bind(this);
301
302 this.request(url);
303 },
304
305 updateContent: function() {
306 var receiver = this.responseIsSuccess() ?
307 this.containers.success : this.containers.failure;
308
309 var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
310 var response = this.transport.responseText.replace(match, '');
311 var scripts = this.transport.responseText.match(match);
312
313 if (receiver) {
314 if (this.options.insertion) {
315 new this.options.insertion(receiver, response);
316 } else {
317 receiver.innerHTML = response;
318 }
319 }
320
321 if (this.responseIsSuccess()) {
322 if (this.onComplete)
323 setTimeout((function() {this.onComplete(
324 this.transport)}).bind(this), 10);
325 }
326
327 if (this.options.evalScripts && scripts) {
328 match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
329 setTimeout((function() {
330 for (var i = 0; i < scripts.length; i++)
331 eval(scripts[i].match(match)[1]);
332 }).bind(this), 10);
333 }
334 }
335 });
336
337 Ajax.PeriodicalUpdater = Class.create();
338 Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
339 initialize: function(container, url, options) {
340 this.setOptions(options);
341 this.onComplete = this.options.onComplete;
342
343 this.frequency = (this.options.frequency || 2);
344 this.decay = 1;
345
346 this.updater = {};
347 this.container = container;
348 this.url = url;
349
350 this.start();
351 },
352
353 start: function() {
354 this.options.onComplete = this.updateComplete.bind(this);
355 this.onTimerEvent();
356 },
357
358 stop: function() {
359 this.updater.onComplete = undefined;
360 clearTimeout(this.timer);
361 (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
362 },
363
364 updateComplete: function(request) {
365 if (this.options.decay) {
366 this.decay = (request.responseText == this.lastText ?
367 this.decay * this.options.decay : 1);
368
369 this.lastText = request.responseText;
370 }
371 this.timer = setTimeout(this.onTimerEvent.bind(this),
372 this.decay * this.frequency * 1000);
373 },
374
375 onTimerEvent: function() {
376 this.updater = new Ajax.Updater(this.container, this.url, this.options);
377 }
378 });
379
380 document.getElementsByClassName = function(className) {
381 var children = document.getElementsByTagName('*') || document.all;
382 var elements = new Array();
383
384 for (var i = 0; i < children.length; i++) {
385 var child = children[i];
386 var classNames = child.className.split(' ');
387 for (var j = 0; j < classNames.length; j++) {
388 if (classNames[j] == className) {
389 elements.push(child);
390 break;
391 }
392 }
393 }
394
395 return elements;
396 }
397
398 /*--------------------------------------------------------------------------*/
399
400 if (!window.Element) {
401 var Element = new Object();
402 }
403
404 Object.extend(Element, {
405 toggle: function() {
406 for (var i = 0; i < arguments.length; i++) {
407 var element = $(arguments[i]);
408 element.style.display =
409 (element.style.display == 'none' ? '' : 'none');
410 }
411 },
412
413 hide: function() {
414 for (var i = 0; i < arguments.length; i++) {
415 var element = $(arguments[i]);
416 element.style.display = 'none';
417 }
418 },
419
420 show: function() {
421 for (var i = 0; i < arguments.length; i++) {
422 var element = $(arguments[i]);
423 element.style.display = '';
424 }
425 },
426
427 remove: function(element) {
428 element = $(element);
429 element.parentNode.removeChild(element);
430 },
431
432 getHeight: function(element) {
433 element = $(element);
434 return element.offsetHeight;
435 },
436
437 hasClassName: function(element, className) {
438 element = $(element);
439 if (!element)
440 return;
441 var a = element.className.split(' ');
442 for (var i = 0; i < a.length; i++) {
443 if (a[i] == className)
444 return true;
445 }
446 return false;
447 },
448
449 addClassName: function(element, className) {
450 element = $(element);
451 Element.removeClassName(element, className);
452 element.className += ' ' + className;
453 },
454
455 removeClassName: function(element, className) {
456 element = $(element);
457 if (!element)
458 return;
459 var newClassName = '';
460 var a = element.className.split(' ');
461 for (var i = 0; i < a.length; i++) {
462 if (a[i] != className) {
463 if (i > 0)
464 newClassName += ' ';
465 newClassName += a[i];
466 }
467 }
468 element.className = newClassName;
469 },
470
471 // removes whitespace-only text node children
472 cleanWhitespace: function(element) {
473 var element = $(element);
474 for (var i = 0; i < element.childNodes.length; i++) {
475 var node = element.childNodes[i];
476 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
477 Element.remove(node);
478 }
479 }
480 });
481
482 var Toggle = new Object();
483 Toggle.display = Element.toggle;
484
485 /*--------------------------------------------------------------------------*/
486
487 Abstract.Insertion = function(adjacency) {
488 this.adjacency = adjacency;
489 }
490
491 Abstract.Insertion.prototype = {
492 initialize: function(element, content) {
493 this.element = $(element);
494 this.content = content;
495
496 if (this.adjacency && this.element.insertAdjacentHTML) {
497 this.element.insertAdjacentHTML(this.adjacency, this.content);
498 } else {
499 this.range = this.element.ownerDocument.createRange();
500 if (this.initializeRange) this.initializeRange();
501 this.fragment = this.range.createContextualFragment(this.content);
502 this.insertContent();
503 }
504 }
505 }
506
507 var Insertion = new Object();
508
509 Insertion.Before = Class.create();
510 Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
511 initializeRange: function() {
512 this.range.setStartBefore(this.element);
513 },
514
515 insertContent: function() {
516 this.element.parentNode.insertBefore(this.fragment, this.element);
517 }
518 });
519
520 Insertion.Top = Class.create();
521 Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
522 initializeRange: function() {
523 this.range.selectNodeContents(this.element);
524 this.range.collapse(true);
525 },
526
527 insertContent: function() {
528 this.element.insertBefore(this.fragment, this.element.firstChild);
529 }
530 });
531
532 Insertion.Bottom = Class.create();
533 Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
534 initializeRange: function() {
535 this.range.selectNodeContents(this.element);
536 this.range.collapse(this.element);
537 },
538
539 insertContent: function() {
540 this.element.appendChild(this.fragment);
541 }
542 });
543
544 Insertion.After = Class.create();
545 Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
546 initializeRange: function() {
547 this.range.setStartAfter(this.element);
548 },
549
550 insertContent: function() {
551 this.element.parentNode.insertBefore(this.fragment,
552 this.element.nextSibling);
553 }
554 });
555
556 var Field = {
557 clear: function() {
558 for (var i = 0; i < arguments.length; i++)
559 $(arguments[i]).value = '';
560 },
561
562 focus: function(element) {
563 $(element).focus();
564 },
565
566 present: function() {
567 for (var i = 0; i < arguments.length; i++)
568 if ($(arguments[i]).value == '') return false;
569 return true;
570 },
571
572 select: function(element) {
573 $(element).select();
574 },
575
576 activate: function(element) {
577 $(element).focus();
578 $(element).select();
579 }
580 }
581
582 /*--------------------------------------------------------------------------*/
583
584 var Form = {
585 serialize: function(form) {
586 var elements = Form.getElements($(form));
587 var queryComponents = new Array();
588
589 for (var i = 0; i < elements.length; i++) {
590 var queryComponent = Form.Element.serialize(elements[i]);
591 if (queryComponent)
592 queryComponents.push(queryComponent);
593 }
594
595 return queryComponents.join('&');
596 },
597
598 getElements: function(form) {
599 var form = $(form);
600 var elements = new Array();
601
602 for (tagName in Form.Element.Serializers) {
603 var tagElements = form.getElementsByTagName(tagName);
604 for (var j = 0; j < tagElements.length; j++)
605 elements.push(tagElements[j]);
606 }
607 return elements;
608 },
609
610 getInputs: function(form, typeName, name) {
611 var form = $(form);
612 var inputs = form.getElementsByTagName('input');
613
614 if (!typeName && !name)
615 return inputs;
616
617 var matchingInputs = new Array();
618 for (var i = 0; i < inputs.length; i++) {
619 var input = inputs[i];
620 if ((typeName && input.type != typeName) ||
621 (name && input.name != name))
622 continue;
623 matchingInputs.push(input);
624 }
625
626 return matchingInputs;
627 },
628
629 disable: function(form) {
630 var elements = Form.getElements(form);
631 for (var i = 0; i < elements.length; i++) {
632 var element = elements[i];
633 element.blur();
634 element.disabled = 'true';
635 }
636 },
637
638 enable: function(form) {
639 var elements = Form.getElements(form);
640 for (var i = 0; i < elements.length; i++) {
641 var element = elements[i];
642 element.disabled = '';
643 }
644 },
645
646 focusFirstElement: function(form) {
647 var form = $(form);
648 var elements = Form.getElements(form);
649 for (var i = 0; i < elements.length; i++) {
650 var element = elements[i];
651 if (element.type != 'hidden' && !element.disabled) {
652 Field.activate(element);
653 break;
654 }
655 }
656 },
657
658 reset: function(form) {
659 $(form).reset();
660 }
661 }
662
663 Form.Element = {
664 serialize: function(element) {
665 var element = $(element);
666 var method = element.tagName.toLowerCase();
667 var parameter = Form.Element.Serializers[method](element);
668
669 if (parameter)
670 return encodeURIComponent(parameter[0]) + '=' +
671 encodeURIComponent(parameter[1]);
672 },
673
674 getValue: function(element) {
675 var element = $(element);
676 var method = element.tagName.toLowerCase();
677 var parameter = Form.Element.Serializers[method](element);
678
679 if (parameter)
680 return parameter[1];
681 }
682 }
683
684 Form.Element.Serializers = {
685 input: function(element) {
686 switch (element.type.toLowerCase()) {
687 case 'submit':
688 case 'hidden':
689 case 'password':
690 case 'text':
691 return Form.Element.Serializers.textarea(element);
692 case 'checkbox':
693 case 'radio':
694 return Form.Element.Serializers.inputSelector(element);
695 }
696 return false;
697 },
698
699 inputSelector: function(element) {
700 if (element.checked)
701 return [element.name, element.value];
702 },
703
704 textarea: function(element) {
705 return [element.name, element.value];
706 },
707
708 select: function(element) {
709 var value = '';
710 if (element.type == 'select-one') {
711 var index = element.selectedIndex;
712 if (index >= 0)
713 value = element.options[index].value || element.options[index].text;
714 } else {
715 value = new Array();
716 for (var i = 0; i < element.length; i++) {
717 var opt = element.options[i];
718 if (opt.selected)
719 value.push(opt.value || opt.text);
720 }
721 }
722 return [element.name, value];
723 }
724 }
725
726 /*--------------------------------------------------------------------------*/
727
728 var $F = Form.Element.getValue;
729
730 /*--------------------------------------------------------------------------*/
731
732 Abstract.TimedObserver = function() {}
733 Abstract.TimedObserver.prototype = {
734 initialize: function(element, frequency, callback) {
735 this.frequency = frequency;
736 this.element = $(element);
737 this.callback = callback;
738
739 this.lastValue = this.getValue();
740 this.registerCallback();
741 },
742
743 registerCallback: function() {
744 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
745 },
746
747 onTimerEvent: function() {
748 var value = this.getValue();
749 if (this.lastValue != value) {
750 this.callback(this.element, value);
751 this.lastValue = value;
752 }
753 }
754 }
755
756 Form.Element.Observer = Class.create();
757 Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
758 getValue: function() {
759 return Form.Element.getValue(this.element);
760 }
761 });
762
763 Form.Observer = Class.create();
764 Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
765 getValue: function() {
766 return Form.serialize(this.element);
767 }
768 });
769
770 /*--------------------------------------------------------------------------*/
771
772 Abstract.EventObserver = function() {}
773 Abstract.EventObserver.prototype = {
774 initialize: function(element, callback) {
775 this.element = $(element);
776 this.callback = callback;
777
778 this.lastValue = this.getValue();
779 if (this.element.tagName.toLowerCase() == 'form')
780 this.registerFormCallbacks();
781 else
782 this.registerCallback(this.element);
783 },
784
785 onElementEvent: function() {
786 var value = this.getValue();
787 if (this.lastValue != value) {
788 this.callback(this.element, value);
789 this.lastValue = value;
790 }
791 },
792
793 registerFormCallbacks: function() {
794 var elements = Form.getElements(this.element);
795 for (var i = 0; i < elements.length; i++)
796 this.registerCallback(elements[i]);
797 },
798
799 registerCallback: function(element) {
800 if (element.type) {
801 switch (element.type.toLowerCase()) {
802 case 'checkbox':
803 case 'radio':
804 element.target = this;
805 element.prev_onclick = element.onclick || Prototype.emptyFunction;
806 element.onclick = function() {
807 this.prev_onclick();
808 this.target.onElementEvent();
809 }
810 break;
811 case 'password':
812 case 'text':
813 case 'textarea':
814 case 'select-one':
815 case 'select-multiple':
816 element.target = this;
817 element.prev_onchange = element.onchange || Prototype.emptyFunction;
818 element.onchange = function() {
819 this.prev_onchange();
820 this.target.onElementEvent();
821 }
822 break;
823 }
824 }
825 }
826 }
827
828 Form.Element.EventObserver = Class.create();
829 Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({
830 getValue: function() {
831 return Form.Element.getValue(this.element);
832 }
833 });
834
835 Form.EventObserver = Class.create();
836 Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({
837 getValue: function() {
838 return Form.serialize(this.element);
839 }
840 });
841
842
843 if (!window.Event) {
844 var Event = new Object();
845 }
846
847 Object.extend(Event, {
848 KEY_BACKSPACE: 8,
849 KEY_TAB: 9,
850 KEY_RETURN: 13,
851 KEY_ESC: 27,
852 KEY_LEFT: 37,
853 KEY_UP: 38,
854 KEY_RIGHT: 39,
855 KEY_DOWN: 40,
856 KEY_DELETE: 46,
857
858 element: function(event) {
859 return event.target || event.srcElement;
860 },
861
862 isLeftClick: function(event) {
863 return (((event.which) && (event.which == 1)) ||
864 ((event.button) && (event.button == 1)));
865 },
866
867 pointerX: function(event) {
868 return event.pageX || (event.clientX +
869 (document.documentElement.scrollLeft || document.body.scrollLeft));
870 },
871
872 pointerY: function(event) {
873 return event.pageY || (event.clientY +
874 (document.documentElement.scrollTop || document.body.scrollTop));
875 },
876
877 stop: function(event) {
878 if (event.preventDefault) {
879 event.preventDefault();
880 event.stopPropagation();
881 } else {
882 event.returnValue = false;
883 }
884 },
885
886 // find the first node with the given tagName, starting from the
887 // node the event was triggered on; traverses the DOM upwards
888 findElement: function(event, tagName) {
889 var element = Event.element(event);
890 while (element.parentNode && (!element.tagName ||
891 (element.tagName.toUpperCase() != tagName.toUpperCase())))
892 element = element.parentNode;
893 return element;
894 },
895
896 observers: false,
897
898 _observeAndCache: function(element, name, observer, useCapture) {
899 if (!this.observers) this.observers = [];
900 if (element.addEventListener) {
901 this.observers.push([element, name, observer, useCapture]);
902 element.addEventListener(name, observer, useCapture);
903 } else if (element.attachEvent) {
904 this.observers.push([element, name, observer, useCapture]);
905 element.attachEvent('on' + name, observer);
906 }
907 },
908
909 unloadCache: function() {
910 if (!Event.observers) return;
911 for (var i = 0; i < Event.observers.length; i++) {
912 Event.stopObserving.apply(this, Event.observers[i]);
913 Event.observers[i][0] = null;
914 }
915 Event.observers = false;
916 },
917
918 observe: function(element, name, observer, useCapture) {
919 var element = $(element);
920 useCapture = useCapture || false;
921
922 if (name == 'keypress' &&
923 ((navigator.appVersion.indexOf('AppleWebKit') > 0)
924 || element.attachEvent))
925 name = 'keydown';
926
927 this._observeAndCache(element, name, observer, useCapture);
928 },
929
930 stopObserving: function(element, name, observer, useCapture) {
931 var element = $(element);
932 useCapture = useCapture || false;
933
934 if (name == 'keypress' &&
935 ((navigator.appVersion.indexOf('AppleWebKit') > 0)
936 || element.detachEvent))
937 name = 'keydown';
938
939 if (element.removeEventListener) {
940 element.removeEventListener(name, observer, useCapture);
941 } else if (element.detachEvent) {
942 element.detachEvent('on' + name, observer);
943 }
944 }
945 });
946
947 /* prevent memory leaks in IE */
948 Event.observe(window, 'unload', Event.unloadCache, false);
949
950 var Position = {
951
952 // set to true if needed, warning: firefox performance problems
953 // NOT neeeded for page scrolling, only if draggable contained in
954 // scrollable elements
955 includeScrollOffsets: false,
956
957 // must be called before calling withinIncludingScrolloffset, every time the
958 // page is scrolled
959 prepare: function() {
960 this.deltaX = window.pageXOffset
961 || document.documentElement.scrollLeft
962 || document.body.scrollLeft
963 || 0;
964 this.deltaY = window.pageYOffset
965 || document.documentElement.scrollTop
966 || document.body.scrollTop
967 || 0;
968 },
969
970 realOffset: function(element) {
971 var valueT = 0, valueL = 0;
972 do {
973 valueT += element.scrollTop || 0;
974 valueL += element.scrollLeft || 0;
975 element = element.parentNode;
976 } while (element);
977 return [valueL, valueT];
978 },
979
980 cumulativeOffset: function(element) {
981 var valueT = 0, valueL = 0;
982 do {
983 valueT += element.offsetTop || 0;
984 valueL += element.offsetLeft || 0;
985 element = element.offsetParent;
986 } while (element);
987 return [valueL, valueT];
988 },
989
990 // caches x/y coordinate pair to use with overlap
991 within: function(element, x, y) {
992 if (this.includeScrollOffsets)
993 return this.withinIncludingScrolloffsets(element, x, y);
994 this.xcomp = x;
995 this.ycomp = y;
996 this.offset = this.cumulativeOffset(element);
997
998 return (y >= this.offset[1] &&
999 y < this.offset[1] + element.offsetHeight &&
1000 x >= this.offset[0] &&
1001 x < this.offset[0] + element.offsetWidth);
1002 },
1003
1004 withinIncludingScrolloffsets: function(element, x, y) {
1005 var offsetcache = this.realOffset(element);
1006
1007 this.xcomp = x + offsetcache[0] - this.deltaX;
1008 this.ycomp = y + offsetcache[1] - this.deltaY;
1009 this.offset = this.cumulativeOffset(element);
1010
1011 return (this.ycomp >= this.offset[1] &&
1012 this.ycomp < this.offset[1] + element.offsetHeight &&
1013 this.xcomp >= this.offset[0] &&
1014 this.xcomp < this.offset[0] + element.offsetWidth);
1015 },
1016
1017 // within must be called directly before
1018 overlap: function(mode, element) {
1019 if (!mode) return 0;
1020 if (mode == 'vertical')
1021 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1022 element.offsetHeight;
1023 if (mode == 'horizontal')
1024 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1025 element.offsetWidth;
1026 },
1027
1028 clone: function(source, target) {
1029 source = $(source);
1030 target = $(target);
1031 target.style.position = 'absolute';
1032 var offsets = this.cumulativeOffset(source);
1033 target.style.top = offsets[1] + 'px';
1034 target.style.left = offsets[0] + 'px';
1035 target.style.width = source.offsetWidth + 'px';
1036 target.style.height = source.offsetHeight + 'px';
1037 }
1038 }