source: products/qPloneTabs/trunk/skins/qPloneTabs/javascripts/prototype.js @ 1

Last change on this file since 1 was 1, checked in by myroslav, 19 years ago

Building directory structure

File size: 45.8 KB
Line 
1/*  Prototype JavaScript framework, version 1.4.0_rc3
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.4.0_rc3',
15  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
16
17  emptyFunction: function() {},
18  K: function(x) {return x}
19}
20
21var Class = {
22  create: function() {
23    return function() {
24      this.initialize.apply(this, arguments);
25    }
26  }
27}
28
29var Abstract = new Object();
30
31Object.extend = function(destination, source) {
32  for (property in source) {
33    destination[property] = source[property];
34  }
35  return destination;
36}
37
38Object.inspect = function(object) {
39  try {
40    if (object == undefined) return 'undefined';
41    if (object == null) return 'null';
42    return object.inspect ? object.inspect() : object.toString();
43  } catch (e) {
44    if (e instanceof RangeError) return '...';
45    throw e;
46  }
47}
48
49Function.prototype.bind = function(object) {
50  var __method = this;
51  return function() {
52    return __method.apply(object, arguments);
53  }
54}
55
56Function.prototype.bindAsEventListener = function(object) {
57  var __method = this;
58  return function(event) {
59    return __method.call(object, event || window.event);
60  }
61}
62
63Object.extend(Number.prototype, {
64  toColorPart: function() {
65    var digits = this.toString(16);
66    if (this < 16) return '0' + digits;
67    return digits;
68  },
69
70  succ: function() {
71    return this + 1;
72  },
73
74  times: function(iterator) {
75    $R(0, this, true).each(iterator);
76    return this;
77  }
78});
79
80var Try = {
81  these: function() {
82    var returnValue;
83
84    for (var i = 0; i < arguments.length; i++) {
85      var lambda = arguments[i];
86      try {
87        returnValue = lambda();
88        break;
89      } catch (e) {}
90    }
91
92    return returnValue;
93  }
94}
95
96/*--------------------------------------------------------------------------*/
97
98var PeriodicalExecuter = Class.create();
99PeriodicalExecuter.prototype = {
100  initialize: function(callback, frequency) {
101    this.callback = callback;
102    this.frequency = frequency;
103    this.currentlyExecuting = false;
104
105    this.registerCallback();
106  },
107
108  registerCallback: function() {
109    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
110  },
111
112  onTimerEvent: function() {
113    if (!this.currentlyExecuting) {
114      try {
115        this.currentlyExecuting = true;
116        this.callback();
117      } finally {
118        this.currentlyExecuting = false;
119      }
120    }
121  }
122}
123
124/*--------------------------------------------------------------------------*/
125
126function $() {
127  var elements = new Array();
128
129  for (var i = 0; i < arguments.length; i++) {
130    var element = arguments[i];
131    if (typeof element == 'string')
132      element = document.getElementById(element);
133
134    if (arguments.length == 1)
135      return element;
136
137    elements.push(element);
138  }
139
140  return elements;
141}
142Object.extend(String.prototype, {
143  stripTags: function() {
144    return this.replace(/<\/?[^>]+>/gi, '');
145  },
146
147  stripScripts: function() {
148    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
149  },
150
151  extractScripts: function() {
152    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
153    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
154    return (this.match(matchAll) || []).map(function(scriptTag) {
155      return (scriptTag.match(matchOne) || ['', ''])[1];
156    });
157  },
158
159  evalScripts: function() {
160    return this.extractScripts().map(eval);
161  },
162
163  escapeHTML: function() {
164    var div = document.createElement('div');
165    var text = document.createTextNode(this);
166    div.appendChild(text);
167    return div.innerHTML;
168  },
169
170  unescapeHTML: function() {
171    var div = document.createElement('div');
172    div.innerHTML = this.stripTags();
173    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
174  },
175
176  toQueryParams: function() {
177    var pairs = this.match(/^\??(.*)$/)[1].split('&');
178    return pairs.inject({}, function(params, pairString) {
179      var pair = pairString.split('=');
180      params[pair[0]] = pair[1];
181      return params;
182    });
183  },
184
185  toArray: function() {
186    return this.split('');
187  },
188
189  camelize: function() {
190    var oStringList = this.split('-');
191    if (oStringList.length == 1) return oStringList[0];
192
193    var camelizedString = this.indexOf('-') == 0
194      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
195      : oStringList[0];
196
197    for (var i = 1, len = oStringList.length; i < len; i++) {
198      var s = oStringList[i];
199      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
200    }
201
202    return camelizedString;
203  },
204
205  inspect: function() {
206    return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
207  }
208});
209
210String.prototype.parseQuery = String.prototype.toQueryParams;
211
212var $break    = new Object();
213var $continue = new Object();
214
215var Enumerable = {
216  each: function(iterator) {
217    var index = 0;
218    try {
219      this._each(function(value) {
220        try {
221          iterator(value, index++);
222        } catch (e) {
223          if (e != $continue) throw e;
224        }
225      });
226    } catch (e) {
227      if (e != $break) throw e;
228    }
229  },
230
231  all: function(iterator) {
232    var result = true;
233    this.each(function(value, index) {
234      result = result && !!(iterator || Prototype.K)(value, index);
235      if (!result) throw $break;
236    });
237    return result;
238  },
239
240  any: function(iterator) {
241    var result = true;
242    this.each(function(value, index) {
243      if (result = !!(iterator || Prototype.K)(value, index))
244        throw $break;
245    });
246    return result;
247  },
248
249  collect: function(iterator) {
250    var results = [];
251    this.each(function(value, index) {
252      results.push(iterator(value, index));
253    });
254    return results;
255  },
256
257  detect: function (iterator) {
258    var result;
259    this.each(function(value, index) {
260      if (iterator(value, index)) {
261        result = value;
262        throw $break;
263      }
264    });
265    return result;
266  },
267
268  findAll: function(iterator) {
269    var results = [];
270    this.each(function(value, index) {
271      if (iterator(value, index))
272        results.push(value);
273    });
274    return results;
275  },
276
277  grep: function(pattern, iterator) {
278    var results = [];
279    this.each(function(value, index) {
280      var stringValue = value.toString();
281      if (stringValue.match(pattern))
282        results.push((iterator || Prototype.K)(value, index));
283    })
284    return results;
285  },
286
287  include: function(object) {
288    var found = false;
289    this.each(function(value) {
290      if (value == object) {
291        found = true;
292        throw $break;
293      }
294    });
295    return found;
296  },
297
298  inject: function(memo, iterator) {
299    this.each(function(value, index) {
300      memo = iterator(memo, value, index);
301    });
302    return memo;
303  },
304
305  invoke: function(method) {
306    var args = $A(arguments).slice(1);
307    return this.collect(function(value) {
308      return value[method].apply(value, args);
309    });
310  },
311
312  max: function(iterator) {
313    var result;
314    this.each(function(value, index) {
315      value = (iterator || Prototype.K)(value, index);
316      if (value >= (result || value))
317        result = value;
318    });
319    return result;
320  },
321
322  min: function(iterator) {
323    var result;
324    this.each(function(value, index) {
325      value = (iterator || Prototype.K)(value, index);
326      if (value <= (result || value))
327        result = value;
328    });
329    return result;
330  },
331
332  partition: function(iterator) {
333    var trues = [], falses = [];
334    this.each(function(value, index) {
335      ((iterator || Prototype.K)(value, index) ?
336        trues : falses).push(value);
337    });
338    return [trues, falses];
339  },
340
341  pluck: function(property) {
342    var results = [];
343    this.each(function(value, index) {
344      results.push(value[property]);
345    });
346    return results;
347  },
348
349  reject: function(iterator) {
350    var results = [];
351    this.each(function(value, index) {
352      if (!iterator(value, index))
353        results.push(value);
354    });
355    return results;
356  },
357
358  sortBy: function(iterator) {
359    return this.collect(function(value, index) {
360      return {value: value, criteria: iterator(value, index)};
361    }).sort(function(left, right) {
362      var a = left.criteria, b = right.criteria;
363      return a < b ? -1 : a > b ? 1 : 0;
364    }).pluck('value');
365  },
366
367  toArray: function() {
368    return this.collect(Prototype.K);
369  },
370
371  zip: function() {
372    var iterator = Prototype.K, args = $A(arguments);
373    if (typeof args.last() == 'function')
374      iterator = args.pop();
375
376    var collections = [this].concat(args).map($A);
377    return this.map(function(value, index) {
378      iterator(value = collections.pluck(index));
379      return value;
380    });
381  },
382
383  inspect: function() {
384    return '#<Enumerable:' + this.toArray().inspect() + '>';
385  }
386}
387
388Object.extend(Enumerable, {
389  map:     Enumerable.collect,
390  find:    Enumerable.detect,
391  select:  Enumerable.findAll,
392  member:  Enumerable.include,
393  entries: Enumerable.toArray
394});
395var $A = Array.from = function(iterable) {
396  if (iterable.toArray) {
397    return iterable.toArray();
398  } else {
399    var results = [];
400    for (var i = 0; i < iterable.length; i++)
401      results.push(iterable[i]);
402    return results;
403  }
404}
405
406Object.extend(Array.prototype, Enumerable);
407
408Object.extend(Array.prototype, {
409  _each: function(iterator) {
410    for (var i = 0; i < this.length; i++)
411      iterator(this[i]);
412  },
413
414  first: function() {
415    return this[0];
416  },
417
418  last: function() {
419    return this[this.length - 1];
420  },
421
422  compact: function() {
423    return this.select(function(value) {
424      return value != undefined || value != null;
425    });
426  },
427
428  flatten: function() {
429    return this.inject([], function(array, value) {
430      return array.concat(value.constructor == Array ?
431        value.flatten() : [value]);
432    });
433  },
434
435  without: function() {
436    var values = $A(arguments);
437    return this.select(function(value) {
438      return !values.include(value);
439    });
440  },
441
442  indexOf: function(object) {
443    for (var i = 0; i < this.length; i++)
444      if (this[i] == object) return i;
445    return -1;
446  },
447
448  reverse: function() {
449    var result = [];
450    for (var i = this.length; i > 0; i--)
451      result.push(this[i-1]);
452    return result;
453  },
454
455  inspect: function() {
456    return '[' + this.map(Object.inspect).join(', ') + ']';
457  }
458});
459var Hash = {
460  _each: function(iterator) {
461    for (key in this) {
462      var value = this[key];
463      if (typeof value == 'function') continue;
464
465      var pair = [key, value];
466      pair.key = key;
467      pair.value = value;
468      iterator(pair);
469    }
470  },
471
472  keys: function() {
473    return this.pluck('key');
474  },
475
476  values: function() {
477    return this.pluck('value');
478  },
479
480  merge: function(hash) {
481    return $H(hash).inject($H(this), function(mergedHash, pair) {
482      mergedHash[pair.key] = pair.value;
483      return mergedHash;
484    });
485  },
486
487  toQueryString: function() {
488    return this.map(function(pair) {
489      return pair.map(encodeURIComponent).join('=');
490    }).join('&');
491  },
492
493  inspect: function() {
494    return '#<Hash:{' + this.map(function(pair) {
495      return pair.map(Object.inspect).join(': ');
496    }).join(', ') + '}>';
497  }
498}
499
500function $H(object) {
501  var hash = Object.extend({}, object || {});
502  Object.extend(hash, Enumerable);
503  Object.extend(hash, Hash);
504  return hash;
505}
506ObjectRange = Class.create();
507Object.extend(ObjectRange.prototype, Enumerable);
508Object.extend(ObjectRange.prototype, {
509  initialize: function(start, end, exclusive) {
510    this.start = start;
511    this.end = end;
512    this.exclusive = exclusive;
513  },
514
515  _each: function(iterator) {
516    var value = this.start;
517    do {
518      iterator(value);
519      value = value.succ();
520    } while (this.include(value));
521  },
522
523  include: function(value) {
524    if (value < this.start)
525      return false;
526    if (this.exclusive)
527      return value < this.end;
528    return value <= this.end;
529  }
530});
531
532var $R = function(start, end, exclusive) {
533  return new ObjectRange(start, end, exclusive);
534}
535
536var Ajax = {
537  getTransport: function() {
538    return Try.these(
539      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
540      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
541      function() {return new XMLHttpRequest()}
542    ) || false;
543  },
544
545  activeRequestCount: 0
546}
547
548Ajax.Responders = {
549  responders: [],
550
551  _each: function(iterator) {
552    this.responders._each(iterator);
553  },
554
555  register: function(responderToAdd) {
556    if (!this.include(responderToAdd))
557      this.responders.push(responderToAdd);
558  },
559
560  unregister: function(responderToRemove) {
561    this.responders = this.responders.without(responderToRemove);
562  },
563
564  dispatch: function(callback, request, transport, json) {
565    this.each(function(responder) {
566      if (responder[callback] && typeof responder[callback] == 'function') {
567        try {
568          responder[callback].apply(responder, [request, transport, json]);
569        } catch (e) {}
570      }
571    });
572  }
573};
574
575Object.extend(Ajax.Responders, Enumerable);
576
577Ajax.Responders.register({
578  onCreate: function() {
579    Ajax.activeRequestCount++;
580  },
581
582  onComplete: function() {
583    Ajax.activeRequestCount--;
584  }
585});
586
587Ajax.Base = function() {};
588Ajax.Base.prototype = {
589  setOptions: function(options) {
590    this.options = {
591      method:       'post',
592      asynchronous: true,
593      parameters:   ''
594    }
595    Object.extend(this.options, options || {});
596  },
597
598  responseIsSuccess: function() {
599    return this.transport.status == undefined
600        || this.transport.status == 0
601        || (this.transport.status >= 200 && this.transport.status < 300);
602  },
603
604  responseIsFailure: function() {
605    return !this.responseIsSuccess();
606  }
607}
608
609Ajax.Request = Class.create();
610Ajax.Request.Events =
611  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
612
613Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
614  initialize: function(url, options) {
615    this.transport = Ajax.getTransport();
616    this.setOptions(options);
617    this.request(url);
618  },
619
620  request: function(url) {
621    var parameters = this.options.parameters || '';
622    if (parameters.length > 0) parameters += '&_=';
623
624    try {
625      this.url = url;
626      if (this.options.method == 'get' && parameters.length > 0)
627        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
628
629      Ajax.Responders.dispatch('onCreate', this, this.transport);
630
631      this.transport.open(this.options.method, this.url,
632        this.options.asynchronous);
633
634      if (this.options.asynchronous) {
635        this.transport.onreadystatechange = this.onStateChange.bind(this);
636        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
637      }
638
639      this.setRequestHeaders();
640
641      var body = this.options.postBody ? this.options.postBody : parameters;
642      this.transport.send(this.options.method == 'post' ? body : null);
643
644    } catch (e) {
645      this.dispatchException(e);
646    }
647  },
648
649  setRequestHeaders: function() {
650    var requestHeaders =
651      ['X-Requested-With', 'XMLHttpRequest',
652       'X-Prototype-Version', Prototype.Version];
653
654    if (this.options.method == 'post') {
655      requestHeaders.push('Content-type',
656        'application/x-www-form-urlencoded');
657
658      /* Force "Connection: close" for Mozilla browsers to work around
659       * a bug where XMLHttpReqeuest sends an incorrect Content-length
660       * header. See Mozilla Bugzilla #246651.
661       */
662      if (this.transport.overrideMimeType)
663        requestHeaders.push('Connection', 'close');
664    }
665
666    if (this.options.requestHeaders)
667      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
668
669    for (var i = 0; i < requestHeaders.length; i += 2)
670      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
671  },
672
673  onStateChange: function() {
674    var readyState = this.transport.readyState;
675    if (readyState != 1)
676      this.respondToReadyState(this.transport.readyState);
677  },
678
679  header: function(name) {
680    try {
681      return this.transport.getResponseHeader(name);
682    } catch (e) {}
683  },
684
685  evalJSON: function() {
686    try {
687      return eval(this.header('X-JSON'));
688    } catch (e) {}
689  },
690
691  evalResponse: function() {
692    try {
693      return eval(this.transport.responseText);
694    } catch (e) {
695      this.dispatchException(e);
696    }
697  },
698
699  respondToReadyState: function(readyState) {
700    var event = Ajax.Request.Events[readyState];
701    var transport = this.transport, json = this.evalJSON();
702
703    if (event == 'Complete') {
704      try {
705        (this.options['on' + this.transport.status]
706         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
707         || Prototype.emptyFunction)(transport, json);
708      } catch (e) {
709        this.dispatchException(e);
710      }
711
712      if (this.header('Content-type') == 'text/javascript')
713        this.evalResponse();
714    }
715
716    try {
717      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
718      Ajax.Responders.dispatch('on' + event, this, transport, json);
719    } catch (e) {
720      this.dispatchException(e);
721    }
722
723    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
724    if (event == 'Complete')
725      this.transport.onreadystatechange = Prototype.emptyFunction;
726  },
727
728  dispatchException: function(exception) {
729    (this.options.onException || Prototype.emptyFunction)(this, exception);
730    Ajax.Responders.dispatch('onException', this, exception);
731  }
732});
733
734Ajax.Updater = Class.create();
735
736Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
737  initialize: function(container, url, options) {
738    this.containers = {
739      success: container.success ? $(container.success) : $(container),
740      failure: container.failure ? $(container.failure) :
741        (container.success ? null : $(container))
742    }
743
744    this.transport = Ajax.getTransport();
745    this.setOptions(options);
746
747    var onComplete = this.options.onComplete || Prototype.emptyFunction;
748    this.options.onComplete = (function(transport, object) {
749      this.updateContent();
750      onComplete(transport, object);
751    }).bind(this);
752
753    this.request(url);
754  },
755
756  updateContent: function() {
757    var receiver = this.responseIsSuccess() ?
758      this.containers.success : this.containers.failure;
759    var response = this.transport.responseText;
760
761    if (!this.options.evalScripts)
762      response = response.stripScripts();
763
764    if (receiver) {
765      if (this.options.insertion) {
766        new this.options.insertion(receiver, response);
767      } else {
768        Element.update(receiver, response);
769      }
770    }
771
772    if (this.responseIsSuccess()) {
773      if (this.onComplete)
774        setTimeout(this.onComplete.bind(this), 10);
775    }
776  }
777});
778
779Ajax.PeriodicalUpdater = Class.create();
780Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
781  initialize: function(container, url, options) {
782    this.setOptions(options);
783    this.onComplete = this.options.onComplete;
784
785    this.frequency = (this.options.frequency || 2);
786    this.decay = (this.options.decay || 1);
787
788    this.updater = {};
789    this.container = container;
790    this.url = url;
791
792    this.start();
793  },
794
795  start: function() {
796    this.options.onComplete = this.updateComplete.bind(this);
797    this.onTimerEvent();
798  },
799
800  stop: function() {
801    this.updater.onComplete = undefined;
802    clearTimeout(this.timer);
803    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
804  },
805
806  updateComplete: function(request) {
807    if (this.options.decay) {
808      this.decay = (request.responseText == this.lastText ?
809        this.decay * this.options.decay : 1);
810
811      this.lastText = request.responseText;
812    }
813    this.timer = setTimeout(this.onTimerEvent.bind(this),
814      this.decay * this.frequency * 1000);
815  },
816
817  onTimerEvent: function() {
818    this.updater = new Ajax.Updater(this.container, this.url, this.options);
819  }
820});
821document.getElementsByClassName = function(className, parentElement) {
822  var children = ($(parentElement) || document.body).getElementsByTagName('*');
823  return $A(children).inject([], function(elements, child) {
824    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
825      elements.push(child);
826    return elements;
827  });
828}
829
830/*--------------------------------------------------------------------------*/
831
832if (!window.Element) {
833  var Element = new Object();
834}
835
836Object.extend(Element, {
837  visible: function(element) {
838    return $(element).style.display != 'none';
839  },
840
841  toggle: function() {
842    for (var i = 0; i < arguments.length; i++) {
843      var element = $(arguments[i]);
844      Element[Element.visible(element) ? 'hide' : 'show'](element);
845    }
846  },
847
848  hide: function() {
849    for (var i = 0; i < arguments.length; i++) {
850      var element = $(arguments[i]);
851      element.style.display = 'none';
852    }
853  },
854
855  show: function() {
856    for (var i = 0; i < arguments.length; i++) {
857      var element = $(arguments[i]);
858      element.style.display = '';
859    }
860  },
861
862  remove: function(element) {
863    element = $(element);
864    element.parentNode.removeChild(element);
865  },
866
867  update: function(element, html) {
868    $(element).innerHTML = html.stripScripts();
869    setTimeout(function() {html.evalScripts()}, 10);
870  },
871
872  getHeight: function(element) {
873    element = $(element);
874    return element.offsetHeight;
875  },
876
877  classNames: function(element) {
878    return new Element.ClassNames(element);
879  },
880
881  hasClassName: function(element, className) {
882    if (!(element = $(element))) return;
883    return Element.classNames(element).include(className);
884  },
885
886  addClassName: function(element, className) {
887    if (!(element = $(element))) return;
888    return Element.classNames(element).add(className);
889  },
890
891  removeClassName: function(element, className) {
892    if (!(element = $(element))) return;
893    return Element.classNames(element).remove(className);
894  },
895
896  // removes whitespace-only text node children
897  cleanWhitespace: function(element) {
898    element = $(element);
899    for (var i = 0; i < element.childNodes.length; i++) {
900      var node = element.childNodes[i];
901      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
902        Element.remove(node);
903    }
904  },
905
906  empty: function(element) {
907    return $(element).innerHTML.match(/^\s*$/);
908  },
909
910  scrollTo: function(element) {
911    element = $(element);
912    var x = element.x ? element.x : element.offsetLeft,
913        y = element.y ? element.y : element.offsetTop;
914    window.scrollTo(x, y);
915  },
916
917  getStyle: function(element, style) {
918    element = $(element);
919    var value = element.style[style.camelize()];
920    if (!value) {
921      if (document.defaultView && document.defaultView.getComputedStyle) {
922        var css = document.defaultView.getComputedStyle(element, null);
923        value = css ? css.getPropertyValue(style) : null;
924      } else if (element.currentStyle) {
925        value = element.currentStyle[style.camelize()];
926      }
927    }
928
929    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
930      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
931
932    return value == 'auto' ? null : value;
933  },
934
935  getDimensions: function(element) {
936    element = $(element);
937    if (Element.getStyle(element, 'display') != 'none')
938      return {width: element.offsetWidth, height: element.offsetHeight};
939
940    // All *Width and *Height properties give 0 on elements with display none,
941    // so enable the element temporarily
942    var els = element.style;
943    var originalVisibility = els.visibility;
944    var originalPosition = els.position;
945    els.visibility = 'hidden';
946    els.position = 'absolute';
947    els.display = '';
948    var originalWidth = element.clientWidth;
949    var originalHeight = element.clientHeight;
950    els.display = 'none';
951    els.position = originalPosition;
952    els.visibility = originalVisibility;
953    return {width: originalWidth, height: originalHeight};
954  },
955
956  makePositioned: function(element) {
957    element = $(element);
958    var pos = Element.getStyle(element, 'position');
959    if (pos == 'static' || !pos) {
960      element._madePositioned = true;
961      element.style.position = 'relative';
962      // Opera returns the offset relative to the positioning context, when an
963      // element is position relative but top and left have not been defined
964      if (window.opera) {
965        element.style.top = 0;
966        element.style.left = 0;
967      }
968    }
969  },
970
971  undoPositioned: function(element) {
972    element = $(element);
973    if (element._madePositioned) {
974      element._madePositioned = undefined;
975      element.style.position =
976        element.style.top =
977        element.style.left =
978        element.style.bottom =
979        element.style.right = '';
980    }
981  },
982
983  makeClipping: function(element) {
984    element = $(element);
985    if (element._overflow) return;
986    element._overflow = element.style.overflow;
987    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
988      element.style.overflow = 'hidden';
989  },
990
991  undoClipping: function(element) {
992    element = $(element);
993    if (element._overflow) return;
994    element.style.overflow = element._overflow;
995    element._overflow = undefined;
996  }
997});
998
999var Toggle = new Object();
1000Toggle.display = Element.toggle;
1001
1002/*--------------------------------------------------------------------------*/
1003
1004Abstract.Insertion = function(adjacency) {
1005  this.adjacency = adjacency;
1006}
1007
1008Abstract.Insertion.prototype = {
1009  initialize: function(element, content) {
1010    this.element = $(element);
1011    this.content = content.stripScripts();
1012
1013    if (this.adjacency && this.element.insertAdjacentHTML) {
1014      try {
1015        this.element.insertAdjacentHTML(this.adjacency, this.content);
1016      } catch (e) {
1017        if (this.element.tagName.toLowerCase() == 'tbody') {
1018          this.insertContent(this.contentFromAnonymousTable());
1019        } else {
1020          throw e;
1021        }
1022      }
1023    } else {
1024      this.range = this.element.ownerDocument.createRange();
1025      if (this.initializeRange) this.initializeRange();
1026      this.insertContent([this.range.createContextualFragment(this.content)]);
1027    }
1028
1029    setTimeout(function() {content.evalScripts()}, 10);
1030  },
1031
1032  contentFromAnonymousTable: function() {
1033    var div = document.createElement('div');
1034    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1035    return $A(div.childNodes[0].childNodes[0].childNodes);
1036  }
1037}
1038
1039var Insertion = new Object();
1040
1041Insertion.Before = Class.create();
1042Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1043  initializeRange: function() {
1044    this.range.setStartBefore(this.element);
1045  },
1046
1047  insertContent: function(fragments) {
1048    fragments.each((function(fragment) {
1049      this.element.parentNode.insertBefore(fragment, this.element);
1050    }).bind(this));
1051  }
1052});
1053
1054Insertion.Top = Class.create();
1055Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1056  initializeRange: function() {
1057    this.range.selectNodeContents(this.element);
1058    this.range.collapse(true);
1059  },
1060
1061  insertContent: function(fragments) {
1062    fragments.reverse().each((function(fragment) {
1063      this.element.insertBefore(fragment, this.element.firstChild);
1064    }).bind(this));
1065  }
1066});
1067
1068Insertion.Bottom = Class.create();
1069Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1070  initializeRange: function() {
1071    this.range.selectNodeContents(this.element);
1072    this.range.collapse(this.element);
1073  },
1074
1075  insertContent: function(fragments) {
1076    fragments.each((function(fragment) {
1077      this.element.appendChild(fragment);
1078    }).bind(this));
1079  }
1080});
1081
1082Insertion.After = Class.create();
1083Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1084  initializeRange: function() {
1085    this.range.setStartAfter(this.element);
1086  },
1087
1088  insertContent: function(fragments) {
1089    fragments.each((function(fragment) {
1090      this.element.parentNode.insertBefore(fragment,
1091        this.element.nextSibling);
1092    }).bind(this));
1093  }
1094});
1095
1096/*--------------------------------------------------------------------------*/
1097
1098Element.ClassNames = Class.create();
1099Element.ClassNames.prototype = {
1100  initialize: function(element) {
1101    this.element = $(element);
1102  },
1103
1104  _each: function(iterator) {
1105    this.element.className.split(/\s+/).select(function(name) {
1106      return name.length > 0;
1107    })._each(iterator);
1108  },
1109
1110  set: function(className) {
1111    this.element.className = className;
1112  },
1113
1114  add: function(classNameToAdd) {
1115    if (this.include(classNameToAdd)) return;
1116    this.set(this.toArray().concat(classNameToAdd).join(' '));
1117  },
1118
1119  remove: function(classNameToRemove) {
1120    if (!this.include(classNameToRemove)) return;
1121    this.set(this.select(function(className) {
1122      return className != classNameToRemove;
1123    }).join(' '));
1124  },
1125
1126  toString: function() {
1127    return this.toArray().join(' ');
1128  }
1129}
1130
1131Object.extend(Element.ClassNames.prototype, Enumerable);
1132var Field = {
1133  clear: function() {
1134    for (var i = 0; i < arguments.length; i++)
1135      $(arguments[i]).value = '';
1136  },
1137
1138  focus: function(element) {
1139    $(element).focus();
1140  },
1141
1142  present: function() {
1143    for (var i = 0; i < arguments.length; i++)
1144      if ($(arguments[i]).value == '') return false;
1145    return true;
1146  },
1147
1148  select: function(element) {
1149    $(element).select();
1150  },
1151
1152  activate: function(element) {
1153    element = $(element);
1154    element.focus();
1155    if (element.select)
1156      element.select();
1157  }
1158}
1159
1160/*--------------------------------------------------------------------------*/
1161
1162var Form = {
1163  serialize: function(form) {
1164    var elements = Form.getElements($(form));
1165    var queryComponents = new Array();
1166
1167    for (var i = 0; i < elements.length; i++) {
1168      var queryComponent = Form.Element.serialize(elements[i]);
1169      if (queryComponent)
1170        queryComponents.push(queryComponent);
1171    }
1172
1173    return queryComponents.join('&');
1174  },
1175
1176  getElements: function(form) {
1177    form = $(form);
1178    var elements = new Array();
1179
1180    for (tagName in Form.Element.Serializers) {
1181      var tagElements = form.getElementsByTagName(tagName);
1182      for (var j = 0; j < tagElements.length; j++)
1183        elements.push(tagElements[j]);
1184    }
1185    return elements;
1186  },
1187
1188  getInputs: function(form, typeName, name) {
1189    form = $(form);
1190    var inputs = form.getElementsByTagName('input');
1191
1192    if (!typeName && !name)
1193      return inputs;
1194
1195    var matchingInputs = new Array();
1196    for (var i = 0; i < inputs.length; i++) {
1197      var input = inputs[i];
1198      if ((typeName && input.type != typeName) ||
1199          (name && input.name != name))
1200        continue;
1201      matchingInputs.push(input);
1202    }
1203
1204    return matchingInputs;
1205  },
1206
1207  disable: function(form) {
1208    var elements = Form.getElements(form);
1209    for (var i = 0; i < elements.length; i++) {
1210      var element = elements[i];
1211      element.blur();
1212      element.disabled = 'true';
1213    }
1214  },
1215
1216  enable: function(form) {
1217    var elements = Form.getElements(form);
1218    for (var i = 0; i < elements.length; i++) {
1219      var element = elements[i];
1220      element.disabled = '';
1221    }
1222  },
1223
1224  findFirstElement: function(form) {
1225    return Form.getElements(form).find(function(element) {
1226      return element.type != 'hidden' && !element.disabled &&
1227        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1228    });
1229  },
1230
1231  focusFirstElement: function(form) {
1232    Field.activate(Form.findFirstElement(form));
1233  },
1234
1235  reset: function(form) {
1236    $(form).reset();
1237  }
1238}
1239
1240Form.Element = {
1241  serialize: function(element) {
1242    element = $(element);
1243    var method = element.tagName.toLowerCase();
1244    var parameter = Form.Element.Serializers[method](element);
1245
1246    if (parameter)
1247      return encodeURIComponent(parameter[0]) + '=' +
1248        encodeURIComponent(parameter[1]);
1249  },
1250
1251  getValue: function(element) {
1252    element = $(element);
1253    var method = element.tagName.toLowerCase();
1254    var parameter = Form.Element.Serializers[method](element);
1255
1256    if (parameter)
1257      return parameter[1];
1258  }
1259}
1260
1261Form.Element.Serializers = {
1262  input: function(element) {
1263    switch (element.type.toLowerCase()) {
1264      case 'submit':
1265      case 'hidden':
1266      case 'password':
1267      case 'text':
1268        return Form.Element.Serializers.textarea(element);
1269      case 'checkbox':
1270      case 'radio':
1271        return Form.Element.Serializers.inputSelector(element);
1272    }
1273    return false;
1274  },
1275
1276  inputSelector: function(element) {
1277    if (element.checked)
1278      return [element.name, element.value];
1279  },
1280
1281  textarea: function(element) {
1282    return [element.name, element.value];
1283  },
1284
1285  select: function(element) {
1286    return Form.Element.Serializers[element.type == 'select-one' ?
1287      'selectOne' : 'selectMany'](element);
1288  },
1289
1290  selectOne: function(element) {
1291    var value = '', opt, index = element.selectedIndex;
1292    if (index >= 0) {
1293      opt = element.options[index];
1294      value = opt.value;
1295      if (!value && !('value' in opt))
1296        value = opt.text;
1297    }
1298    return [element.name, value];
1299  },
1300
1301  selectMany: function(element) {
1302    var value = new Array();
1303    for (var i = 0; i < element.length; i++) {
1304      var opt = element.options[i];
1305      if (opt.selected) {
1306        var optValue = opt.value;
1307        if (!optValue && !('value' in opt))
1308          optValue = opt.text;
1309        value.push(optValue);
1310      }
1311    }
1312    return [element.name, value];
1313  }
1314}
1315
1316/*--------------------------------------------------------------------------*/
1317
1318var $F = Form.Element.getValue;
1319
1320/*--------------------------------------------------------------------------*/
1321
1322Abstract.TimedObserver = function() {}
1323Abstract.TimedObserver.prototype = {
1324  initialize: function(element, frequency, callback) {
1325    this.frequency = frequency;
1326    this.element   = $(element);
1327    this.callback  = callback;
1328
1329    this.lastValue = this.getValue();
1330    this.registerCallback();
1331  },
1332
1333  registerCallback: function() {
1334    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
1335  },
1336
1337  onTimerEvent: function() {
1338    var value = this.getValue();
1339    if (this.lastValue != value) {
1340      this.callback(this.element, value);
1341      this.lastValue = value;
1342    }
1343  }
1344}
1345
1346Form.Element.Observer = Class.create();
1347Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1348  getValue: function() {
1349    return Form.Element.getValue(this.element);
1350  }
1351});
1352
1353Form.Observer = Class.create();
1354Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1355  getValue: function() {
1356    return Form.serialize(this.element);
1357  }
1358});
1359
1360/*--------------------------------------------------------------------------*/
1361
1362Abstract.EventObserver = function() {}
1363Abstract.EventObserver.prototype = {
1364  initialize: function(element, callback) {
1365    this.element  = $(element);
1366    this.callback = callback;
1367
1368    this.lastValue = this.getValue();
1369    if (this.element.tagName.toLowerCase() == 'form')
1370      this.registerFormCallbacks();
1371    else
1372      this.registerCallback(this.element);
1373  },
1374
1375  onElementEvent: function() {
1376    var value = this.getValue();
1377    if (this.lastValue != value) {
1378      this.callback(this.element, value);
1379      this.lastValue = value;
1380    }
1381  },
1382
1383  registerFormCallbacks: function() {
1384    var elements = Form.getElements(this.element);
1385    for (var i = 0; i < elements.length; i++)
1386      this.registerCallback(elements[i]);
1387  },
1388
1389  registerCallback: function(element) {
1390    if (element.type) {
1391      switch (element.type.toLowerCase()) {
1392        case 'checkbox':
1393        case 'radio':
1394          Event.observe(element, 'click', this.onElementEvent.bind(this));
1395          break;
1396        case 'password':
1397        case 'text':
1398        case 'textarea':
1399        case 'select-one':
1400        case 'select-multiple':
1401          Event.observe(element, 'change', this.onElementEvent.bind(this));
1402          break;
1403      }
1404    }
1405  }
1406}
1407
1408Form.Element.EventObserver = Class.create();
1409Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1410  getValue: function() {
1411    return Form.Element.getValue(this.element);
1412  }
1413});
1414
1415Form.EventObserver = Class.create();
1416Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1417  getValue: function() {
1418    return Form.serialize(this.element);
1419  }
1420});
1421if (!window.Event) {
1422  var Event = new Object();
1423}
1424
1425Object.extend(Event, {
1426  KEY_BACKSPACE: 8,
1427  KEY_TAB:       9,
1428  KEY_RETURN:   13,
1429  KEY_ESC:      27,
1430  KEY_LEFT:     37,
1431  KEY_UP:       38,
1432  KEY_RIGHT:    39,
1433  KEY_DOWN:     40,
1434  KEY_DELETE:   46,
1435
1436  element: function(event) {
1437    return event.target || event.srcElement;
1438  },
1439
1440  isLeftClick: function(event) {
1441    return (((event.which) && (event.which == 1)) ||
1442            ((event.button) && (event.button == 1)));
1443  },
1444
1445  pointerX: function(event) {
1446    return event.pageX || (event.clientX +
1447      (document.documentElement.scrollLeft || document.body.scrollLeft));
1448  },
1449
1450  pointerY: function(event) {
1451    return event.pageY || (event.clientY +
1452      (document.documentElement.scrollTop || document.body.scrollTop));
1453  },
1454
1455  stop: function(event) {
1456    if (event.preventDefault) {
1457      event.preventDefault();
1458      event.stopPropagation();
1459    } else {
1460      event.returnValue = false;
1461      event.cancelBubble = true;
1462    }
1463  },
1464
1465  // find the first node with the given tagName, starting from the
1466  // node the event was triggered on; traverses the DOM upwards
1467  findElement: function(event, tagName) {
1468    var element = Event.element(event);
1469    while (element.parentNode && (!element.tagName ||
1470        (element.tagName.toUpperCase() != tagName.toUpperCase())))
1471      element = element.parentNode;
1472    return element;
1473  },
1474
1475  observers: false,
1476
1477  _observeAndCache: function(element, name, observer, useCapture) {
1478    if (!this.observers) this.observers = [];
1479    if (element.addEventListener) {
1480      this.observers.push([element, name, observer, useCapture]);
1481      element.addEventListener(name, observer, useCapture);
1482    } else if (element.attachEvent) {
1483      this.observers.push([element, name, observer, useCapture]);
1484      element.attachEvent('on' + name, observer);
1485    }
1486  },
1487
1488  unloadCache: function() {
1489    if (!Event.observers) return;
1490    for (var i = 0; i < Event.observers.length; i++) {
1491      Event.stopObserving.apply(this, Event.observers[i]);
1492      Event.observers[i][0] = null;
1493    }
1494    Event.observers = false;
1495  },
1496
1497  observe: function(element, name, observer, useCapture) {
1498    var element = $(element);
1499    useCapture = useCapture || false;
1500
1501    if (name == 'keypress' &&
1502        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1503        || element.attachEvent))
1504      name = 'keydown';
1505
1506    this._observeAndCache(element, name, observer, useCapture);
1507  },
1508
1509  stopObserving: function(element, name, observer, useCapture) {
1510    var element = $(element);
1511    useCapture = useCapture || false;
1512
1513    if (name == 'keypress' &&
1514        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1515        || element.detachEvent))
1516      name = 'keydown';
1517
1518    if (element.removeEventListener) {
1519      element.removeEventListener(name, observer, useCapture);
1520    } else if (element.detachEvent) {
1521      element.detachEvent('on' + name, observer);
1522    }
1523  }
1524});
1525
1526/* prevent memory leaks in IE */
1527Event.observe(window, 'unload', Event.unloadCache, false);
1528var Position = {
1529  // set to true if needed, warning: firefox performance problems
1530  // NOT neeeded for page scrolling, only if draggable contained in
1531  // scrollable elements
1532  includeScrollOffsets: false,
1533
1534  // must be called before calling withinIncludingScrolloffset, every time the
1535  // page is scrolled
1536  prepare: function() {
1537    this.deltaX =  window.pageXOffset
1538                || document.documentElement.scrollLeft
1539                || document.body.scrollLeft
1540                || 0;
1541    this.deltaY =  window.pageYOffset
1542                || document.documentElement.scrollTop
1543                || document.body.scrollTop
1544                || 0;
1545  },
1546
1547  realOffset: function(element) {
1548    var valueT = 0, valueL = 0;
1549    do {
1550      valueT += element.scrollTop  || 0;
1551      valueL += element.scrollLeft || 0;
1552      element = element.parentNode;
1553    } while (element);
1554    return [valueL, valueT];
1555  },
1556
1557  cumulativeOffset: function(element) {
1558    var valueT = 0, valueL = 0;
1559    do {
1560      valueT += element.offsetTop  || 0;
1561      valueL += element.offsetLeft || 0;
1562      element = element.offsetParent;
1563    } while (element);
1564    return [valueL, valueT];
1565  },
1566
1567  positionedOffset: function(element) {
1568    var valueT = 0, valueL = 0;
1569    do {
1570      valueT += element.offsetTop  || 0;
1571      valueL += element.offsetLeft || 0;
1572      element = element.offsetParent;
1573      if (element) {
1574        p = Element.getStyle(element, 'position');
1575        if (p == 'relative' || p == 'absolute') break;
1576      }
1577    } while (element);
1578    return [valueL, valueT];
1579  },
1580
1581  offsetParent: function(element) {
1582    if (element.offsetParent) return element.offsetParent;
1583    if (element == document.body) return element;
1584
1585    while ((element = element.parentNode) && element != document.body)
1586      if (Element.getStyle(element, 'position') != 'static')
1587        return element;
1588
1589    return document.body;
1590  },
1591
1592  // caches x/y coordinate pair to use with overlap
1593  within: function(element, x, y) {
1594    if (this.includeScrollOffsets)
1595      return this.withinIncludingScrolloffsets(element, x, y);
1596    this.xcomp = x;
1597    this.ycomp = y;
1598    this.offset = this.cumulativeOffset(element);
1599
1600    return (y >= this.offset[1] &&
1601            y <  this.offset[1] + element.offsetHeight &&
1602            x >= this.offset[0] &&
1603            x <  this.offset[0] + element.offsetWidth);
1604  },
1605
1606  withinIncludingScrolloffsets: function(element, x, y) {
1607    var offsetcache = this.realOffset(element);
1608
1609    this.xcomp = x + offsetcache[0] - this.deltaX;
1610    this.ycomp = y + offsetcache[1] - this.deltaY;
1611    this.offset = this.cumulativeOffset(element);
1612
1613    return (this.ycomp >= this.offset[1] &&
1614            this.ycomp <  this.offset[1] + element.offsetHeight &&
1615            this.xcomp >= this.offset[0] &&
1616            this.xcomp <  this.offset[0] + element.offsetWidth);
1617  },
1618
1619  // within must be called directly before
1620  overlap: function(mode, element) {
1621    if (!mode) return 0;
1622    if (mode == 'vertical')
1623      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1624        element.offsetHeight;
1625    if (mode == 'horizontal')
1626      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1627        element.offsetWidth;
1628  },
1629
1630  clone: function(source, target) {
1631    source = $(source);
1632    target = $(target);
1633    target.style.position = 'absolute';
1634    var offsets = this.cumulativeOffset(source);
1635    target.style.top    = offsets[1] + 'px';
1636    target.style.left   = offsets[0] + 'px';
1637    target.style.width  = source.offsetWidth + 'px';
1638    target.style.height = source.offsetHeight + 'px';
1639  },
1640
1641  page: function(forElement) {
1642    var valueT = 0, valueL = 0;
1643
1644    var element = forElement;
1645    do {
1646      valueT += element.offsetTop  || 0;
1647      valueL += element.offsetLeft || 0;
1648
1649      // Safari fix
1650      if (element.offsetParent==document.body)
1651        if (Element.getStyle(element,'position')=='absolute') break;
1652
1653    } while (element = element.offsetParent);
1654
1655    element = forElement;
1656    do {
1657      valueT -= element.scrollTop  || 0;
1658      valueL -= element.scrollLeft || 0;
1659    } while (element = element.parentNode);
1660
1661    return [valueL, valueT];
1662  },
1663
1664  clone: function(source, target) {
1665    var options = Object.extend({
1666      setLeft:    true,
1667      setTop:     true,
1668      setWidth:   true,
1669      setHeight:  true,
1670      offsetTop:  0,
1671      offsetLeft: 0
1672    }, arguments[2] || {})
1673
1674    // find page position of source
1675    source = $(source);
1676    var p = Position.page(source);
1677
1678    // find coordinate system to use
1679    target = $(target);
1680    var delta = [0, 0];
1681    var parent = null;
1682    // delta [0,0] will do fine with position: fixed elements,
1683    // position:absolute needs offsetParent deltas
1684    if (Element.getStyle(target,'position') == 'absolute') {
1685      parent = Position.offsetParent(target);
1686      delta = Position.page(parent);
1687    }
1688
1689    // correct by body offsets (fixes Safari)
1690    if (parent == document.body) {
1691      delta[0] -= document.body.offsetLeft;
1692      delta[1] -= document.body.offsetTop;
1693    }
1694
1695    // set position
1696    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
1697    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
1698    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
1699    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
1700  },
1701
1702  absolutize: function(element) {
1703    element = $(element);
1704    if (element.style.position == 'absolute') return;
1705    Position.prepare();
1706
1707    var offsets = Position.positionedOffset(element);
1708    var top     = offsets[1];
1709    var left    = offsets[0];
1710    var width   = element.clientWidth;
1711    var height  = element.clientHeight;
1712
1713    element._originalLeft   = left - parseFloat(element.style.left  || 0);
1714    element._originalTop    = top  - parseFloat(element.style.top || 0);
1715    element._originalWidth  = element.style.width;
1716    element._originalHeight = element.style.height;
1717
1718    element.style.position = 'absolute';
1719    element.style.top    = top + 'px';;
1720    element.style.left   = left + 'px';;
1721    element.style.width  = width + 'px';;
1722    element.style.height = height + 'px';;
1723  },
1724
1725  relativize: function(element) {
1726    element = $(element);
1727    if (element.style.position == 'relative') return;
1728    Position.prepare();
1729
1730    element.style.position = 'relative';
1731    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
1732    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
1733
1734    element.style.top    = top + 'px';
1735    element.style.left   = left + 'px';
1736    element.style.height = element._originalHeight;
1737    element.style.width  = element._originalWidth;
1738  }
1739}
1740
1741// Safari returns margins on body which is incorrect if the child is absolutely
1742// positioned.  For performance reasons, redefine Position.cumulativeOffset for
1743// KHTML/WebKit only.
1744if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
1745  Position.cumulativeOffset = function(element) {
1746    var valueT = 0, valueL = 0;
1747    do {
1748      valueT += element.offsetTop  || 0;
1749      valueL += element.offsetLeft || 0;
1750      if (element.offsetParent == document.body)
1751        if (Element.getStyle(element, 'position') == 'absolute') break;
1752
1753      element = element.offsetParent;
1754    } while (element);
1755
1756    return [valueL, valueT];
1757  }
1758}
Note: See TracBrowser for help on using the repository browser.