source: trunk/monitor/htdocs/prototype.js @ 1417

Last change on this file since 1417 was 1417, checked in by Malte Marquarding, 16 years ago

added the javascript ajax library

File size: 27.1 KB
Line 
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
13var Prototype = {
14  Version: '1.3.1',
15  emptyFunction: function() {}
16}
17
18var Class = {
19  create: function() {
20    return function() {
21      this.initialize.apply(this, arguments);
22    }
23  }
24}
25
26var Abstract = new Object();
27
28Object.extend = function(destination, source) {
29  for (property in source) {
30    destination[property] = source[property];
31  }
32  return destination;
33}
34
35Object.prototype.extend = function(object) {
36  return Object.extend.apply(this, [this, object]);
37}
38
39Function.prototype.bind = function(object) {
40  var __method = this;
41  return function() {
42    __method.apply(object, arguments);
43  }
44}
45
46Function.prototype.bindAsEventListener = function(object) {
47  var __method = this;
48  return function(event) {
49    __method.call(object, event || window.event);
50  }
51}
52
53Number.prototype.toColorPart = function() {
54  var digits = this.toString(16);
55  if (this < 16) return '0' + digits;
56  return digits;
57}
58
59var 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
77var PeriodicalExecuter = Class.create();
78PeriodicalExecuter.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
105function $() {
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
122if (!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
131if (!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
150String.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
169var 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
179Ajax.Base = function() {};
180Ajax.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
200Ajax.Request = Class.create();
201Ajax.Request.Events =
202  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
203
204Ajax.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
282Ajax.Updater = Class.create();
283Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
284
285Ajax.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
337Ajax.PeriodicalUpdater = Class.create();
338Ajax.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
380document.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
400if (!window.Element) {
401  var Element = new Object();
402}
403
404Object.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
482var Toggle = new Object();
483Toggle.display = Element.toggle;
484
485/*--------------------------------------------------------------------------*/
486
487Abstract.Insertion = function(adjacency) {
488  this.adjacency = adjacency;
489}
490
491Abstract.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
507var Insertion = new Object();
508
509Insertion.Before = Class.create();
510Insertion.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
520Insertion.Top = Class.create();
521Insertion.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
532Insertion.Bottom = Class.create();
533Insertion.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
544Insertion.After = Class.create();
545Insertion.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
556var 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
584var 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
663Form.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
684Form.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
728var $F = Form.Element.getValue;
729
730/*--------------------------------------------------------------------------*/
731
732Abstract.TimedObserver = function() {}
733Abstract.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
756Form.Element.Observer = Class.create();
757Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
758  getValue: function() {
759    return Form.Element.getValue(this.element);
760  }
761});
762
763Form.Observer = Class.create();
764Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
765  getValue: function() {
766    return Form.serialize(this.element);
767  }
768});
769
770/*--------------------------------------------------------------------------*/
771
772Abstract.EventObserver = function() {}
773Abstract.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
828Form.Element.EventObserver = Class.create();
829Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({
830  getValue: function() {
831    return Form.Element.getValue(this.element);
832  }
833});
834
835Form.EventObserver = Class.create();
836Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({
837  getValue: function() {
838    return Form.serialize(this.element);
839  }
840});
841
842
843if (!window.Event) {
844  var Event = new Object();
845}
846
847Object.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 */
948Event.observe(window, 'unload', Event.unloadCache, false);
949
950var 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}
Note: See TracBrowser for help on using the repository browser.