source: branches/1.2/workflow/js/jscode/prototype.js @ 1349

Revision 1349, 69.8 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

  • Property svn:executable set to *
Line 
1/*  Prototype JavaScript framework, version 1.5.0
2 *  (c) 2005-2007 Sam Stephenson
3 *
4 *  Prototype is freely distributable under the terms of an MIT-style license.
5 *  For details, see the Prototype web site: http://prototype.conio.net/
6 *
7/*--------------------------------------------------------------------------*/
8
9var Prototype = {
10  Version: '1.5.0',
11  BrowserFeatures: {
12    XPath: !!document.evaluate
13  },
14
15  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
16  emptyFunction: function() {},
17  K: function(x) { return x }
18};
19
20var Class = {
21  create: function() {
22    return function() {
23      this.initialize.apply(this, arguments);
24    }
25  }
26};
27
28var Abstract = new Object();
29
30Object.extend = function(destination, source) {
31  for (var property in source) {
32    destination[property] = source[property];
33  }
34  return destination;
35};
36
37Object.extend(Object, {
38  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
49  keys: function(object) {
50    var keys = [];
51    for (var property in object)
52      keys.push(property);
53    return keys;
54  },
55
56  values: function(object) {
57    var values = [];
58    for (var property in object)
59      values.push(object[property]);
60    return values;
61  },
62
63  clone: function(object) {
64    return Object.extend({}, object);
65  }
66});
67
68Function.prototype.bind = function() {
69  var __method = this, args = $A(arguments), object = args.shift();
70  return function() {
71    return __method.apply(object, args.concat($A(arguments)));
72  }
73};
74
75Function.prototype.bindAsEventListener = function(object) {
76  var __method = this, args = $A(arguments), object = args.shift();
77  return function(event) {
78    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
79  }
80};
81
82Object.extend(Number.prototype, {
83  toColorPart: function() {
84    var digits = this.toString(16);
85    if (this < 16) return '0' + digits;
86    return digits;
87  },
88
89  succ: function() {
90    return this + 1;
91  },
92
93  times: function(iterator) {
94    $R(0, this, true).each(iterator);
95    return this;
96  }
97});
98
99var Try = {
100  these: function() {
101    var returnValue;
102
103    for (var i = 0, length = arguments.length; i < length; i++) {
104      var lambda = arguments[i];
105      try {
106        returnValue = lambda();
107        break;
108      } catch (e) {}
109    }
110
111    return returnValue;
112  }
113};
114
115/*--------------------------------------------------------------------------*/
116
117var PeriodicalExecuter = Class.create();
118PeriodicalExecuter.prototype = {
119  initialize: function(callback, frequency) {
120    this.callback = callback;
121    this.frequency = frequency;
122    this.currentlyExecuting = false;
123
124    this.registerCallback();
125  },
126
127  registerCallback: function() {
128    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
129  },
130
131  stop: function() {
132    if (!this.timer) return;
133    clearInterval(this.timer);
134    this.timer = null;
135  },
136
137  onTimerEvent: function() {
138    if (!this.currentlyExecuting) {
139      try {
140        this.currentlyExecuting = true;
141        this.callback(this);
142      } finally {
143        this.currentlyExecuting = false;
144      }
145    }
146  }
147};
148String.interpret = function(value){
149  return value == null ? '' : String(value);
150};
151
152Object.extend(String.prototype, {
153  gsub: function(pattern, replacement) {
154    var result = '', source = this, match;
155    replacement = arguments.callee.prepareReplacement(replacement);
156
157    while (source.length > 0) {
158      if (match = source.match(pattern)) {
159        result += source.slice(0, match.index);
160        result += String.interpret(replacement(match));
161        source  = source.slice(match.index + match[0].length);
162      } else {
163        result += source, source = '';
164      }
165    }
166    return result;
167  },
168
169  sub: function(pattern, replacement, count) {
170    replacement = this.gsub.prepareReplacement(replacement);
171    count = count === undefined ? 1 : count;
172
173    return this.gsub(pattern, function(match) {
174      if (--count < 0) return match[0];
175      return replacement(match);
176    });
177  },
178
179  scan: function(pattern, iterator) {
180    this.gsub(pattern, iterator);
181    return this;
182  },
183
184  truncate: function(length, truncation) {
185    length = length || 30;
186    truncation = truncation === undefined ? '...' : truncation;
187    return this.length > length ?
188      this.slice(0, length - truncation.length) + truncation : this;
189  },
190
191  strip: function() {
192    return this.replace(/^\s+/, '').replace(/\s+$/, '');
193  },
194
195  stripTags: function() {
196    return this.replace(/<\/?[^>]+>/gi, '');
197  },
198
199  stripScripts: function() {
200    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
201  },
202
203  extractScripts: function() {
204    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
205    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
206    return (this.match(matchAll) || []).map(function(scriptTag) {
207      return (scriptTag.match(matchOne) || ['', ''])[1];
208    });
209  },
210
211  evalScripts: function() {
212    return this.extractScripts().map(function(script) { return eval(script) });
213  },
214
215  escapeHTML: function() {
216    var div = document.createElement('div');
217    var text = document.createTextNode(this);
218    div.appendChild(text);
219    return div.innerHTML;
220  },
221
222  unescapeHTML: function() {
223    var div = document.createElement('div');
224    div.innerHTML = this.stripTags();
225    return div.childNodes[0] ? (div.childNodes.length > 1 ?
226      $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
227      div.childNodes[0].nodeValue) : '';
228  },
229
230  toQueryParams: function(separator) {
231    var match = this.strip().match(/([^?#]*)(#.*)?$/);
232    if (!match) return {};
233
234    return match[1].split(separator || '&').inject({}, function(hash, pair) {
235      if ((pair = pair.split('='))[0]) {
236        var name = decodeURIComponent(pair[0]);
237        var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
238
239        if (hash[name] !== undefined) {
240          if (hash[name].constructor != Array)
241            hash[name] = [hash[name]];
242          if (value) hash[name].push(value);
243        }
244        else hash[name] = value;
245      }
246      return hash;
247    });
248  },
249
250  toArray: function() {
251    return this.split('');
252  },
253
254  succ: function() {
255    return this.slice(0, this.length - 1) +
256      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257  },
258
259  camelize: function() {
260    var parts = this.split('-'), len = parts.length;
261    if (len == 1) return parts[0];
262
263    var camelized = this.charAt(0) == '-'
264      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
265      : parts[0];
266
267    for (var i = 1; i < len; i++)
268      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
269
270    return camelized;
271  },
272
273  capitalize: function(){
274    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
275  },
276
277  underscore: function() {
278    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
279  },
280
281  dasherize: function() {
282    return this.gsub(/_/,'-');
283  },
284
285  inspect: function(useDoubleQuotes) {
286    var escapedString = this.replace(/\\/g, '\\\\');
287    if (useDoubleQuotes)
288      return '"' + escapedString.replace(/"/g, '\\"') + '"';
289    else
290      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
291  }
292});
293
294String.prototype.gsub.prepareReplacement = function(replacement) {
295  if (typeof replacement == 'function') return replacement;
296  var template = new Template(replacement);
297  return function(match) { return template.evaluate(match) };
298};
299
300String.prototype.parseQuery = String.prototype.toQueryParams;
301
302var Template = Class.create();
303Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
304Template.prototype = {
305  initialize: function(template, pattern) {
306    this.template = template.toString();
307    this.pattern  = pattern || Template.Pattern;
308  },
309
310  evaluate: function(object) {
311    return this.template.gsub(this.pattern, function(match) {
312      var before = match[1];
313      if (before == '\\') return match[2];
314      return before + String.interpret(object[match[3]]);
315    });
316  }
317};
318
319var $break    = new Object();
320var $continue = new Object();
321
322var Enumerable = {
323  each: function(iterator) {
324    var index = 0;
325    try {
326      this._each(function(value) {
327        try {
328          iterator(value, index++);
329        } catch (e) {
330          if (e != $continue) throw e;
331        }
332      });
333    } catch (e) {
334      if (e != $break) throw e;
335    }
336    return this;
337  },
338
339  eachSlice: function(number, iterator) {
340    var index = -number, slices = [], array = this.toArray();
341    while ((index += number) < array.length)
342      slices.push(array.slice(index, index+number));
343    return slices.map(iterator);
344  },
345
346  all: function(iterator) {
347    var result = true;
348    this.each(function(value, index) {
349      result = result && !!(iterator || Prototype.K)(value, index);
350      if (!result) throw $break;
351    });
352    return result;
353  },
354
355  any: function(iterator) {
356    var result = false;
357    this.each(function(value, index) {
358      if (result = !!(iterator || Prototype.K)(value, index))
359        throw $break;
360    });
361    return result;
362  },
363
364  collect: function(iterator) {
365    var results = [];
366    this.each(function(value, index) {
367      results.push((iterator || Prototype.K)(value, index));
368    });
369    return results;
370  },
371
372  detect: function(iterator) {
373    var result;
374    this.each(function(value, index) {
375      if (iterator(value, index)) {
376        result = value;
377        throw $break;
378      }
379    });
380    return result;
381  },
382
383  findAll: function(iterator) {
384    var results = [];
385    this.each(function(value, index) {
386      if (iterator(value, index))
387        results.push(value);
388    });
389    return results;
390  },
391
392  grep: function(pattern, iterator) {
393    var results = [];
394    this.each(function(value, index) {
395      var stringValue = value.toString();
396      if (stringValue.match(pattern))
397        results.push((iterator || Prototype.K)(value, index));
398    });
399    return results;
400  },
401
402  include: function(object) {
403    var found = false;
404    this.each(function(value) {
405      if (value == object) {
406        found = true;
407        throw $break;
408      }
409    });
410    return found;
411  },
412
413  inGroupsOf: function(number, fillWith) {
414    fillWith = fillWith === undefined ? null : fillWith;
415    return this.eachSlice(number, function(slice) {
416      while(slice.length < number) slice.push(fillWith);
417      return slice;
418    });
419  },
420
421  inject: function(memo, iterator) {
422    this.each(function(value, index) {
423      memo = iterator(memo, value, index);
424    });
425    return memo;
426  },
427
428  invoke: function(method) {
429    var args = $A(arguments).slice(1);
430    return this.map(function(value) {
431      return value[method].apply(value, args);
432    });
433  },
434
435  max: function(iterator) {
436    var result;
437    this.each(function(value, index) {
438      value = (iterator || Prototype.K)(value, index);
439      if (result == undefined || value >= result)
440        result = value;
441    });
442    return result;
443  },
444
445  min: function(iterator) {
446    var result;
447    this.each(function(value, index) {
448      value = (iterator || Prototype.K)(value, index);
449      if (result == undefined || value < result)
450        result = value;
451    });
452    return result;
453  },
454
455  partition: function(iterator) {
456    var trues = [], falses = [];
457    this.each(function(value, index) {
458      ((iterator || Prototype.K)(value, index) ?
459        trues : falses).push(value);
460    });
461    return [trues, falses];
462  },
463
464  pluck: function(property) {
465    var results = [];
466    this.each(function(value, index) {
467      results.push(value[property]);
468    });
469    return results;
470  },
471
472  reject: function(iterator) {
473    var results = [];
474    this.each(function(value, index) {
475      if (!iterator(value, index))
476        results.push(value);
477    });
478    return results;
479  },
480
481  sortBy: function(iterator) {
482    return this.map(function(value, index) {
483      return {value: value, criteria: iterator(value, index)};
484    }).sort(function(left, right) {
485      var a = left.criteria, b = right.criteria;
486      return a < b ? -1 : a > b ? 1 : 0;
487    }).pluck('value');
488  },
489
490  toArray: function() {
491    return this.map();
492  },
493
494  zip: function() {
495    var iterator = Prototype.K, args = $A(arguments);
496    if (typeof args.last() == 'function')
497      iterator = args.pop();
498
499    var collections = [this].concat(args).map($A);
500    return this.map(function(value, index) {
501      return iterator(collections.pluck(index));
502    });
503  },
504
505  size: function() {
506    return this.toArray().length;
507  },
508
509  inspect: function() {
510    return '#<Enumerable:' + this.toArray().inspect() + '>';
511  }
512};
513
514Object.extend(Enumerable, {
515  map:     Enumerable.collect,
516  find:    Enumerable.detect,
517  select:  Enumerable.findAll,
518  member:  Enumerable.include,
519  entries: Enumerable.toArray
520});
521var $A = Array.from = function(iterable) {
522  if (!iterable) return [];
523  if (iterable.toArray) {
524    return iterable.toArray();
525  } else {
526    var results = [];
527    for (var i = 0, length = iterable.length; i < length; i++)
528      results.push(iterable[i]);
529    return results;
530  }
531};
532
533Object.extend(Array.prototype, Enumerable);
534
535if (!Array.prototype._reverse)
536  Array.prototype._reverse = Array.prototype.reverse;
537
538Object.extend(Array.prototype, {
539  _each: function(iterator) {
540    for (var i = 0, length = this.length; i < length; i++)
541      iterator(this[i]);
542  },
543
544  clear: function() {
545    this.length = 0;
546    return this;
547  },
548
549  first: function() {
550    return this[0];
551  },
552
553  last: function() {
554    return this[this.length - 1];
555  },
556
557  compact: function() {
558    return this.select(function(value) {
559      return value != null;
560    });
561  },
562
563  flatten: function() {
564    return this.inject([], function(array, value) {
565      return array.concat(value && value.constructor == Array ?
566        value.flatten() : [value]);
567    });
568  },
569
570  without: function() {
571    var values = $A(arguments);
572    return this.select(function(value) {
573      return !values.include(value);
574    });
575  },
576
577  indexOf: function(object) {
578    for (var i = 0, length = this.length; i < length; i++)
579      if (this[i] == object) return i;
580    return -1;
581  },
582
583  reverse: function(inline) {
584    return (inline !== false ? this : this.toArray())._reverse();
585  },
586
587  reduce: function() {
588    return this.length > 1 ? this : this[0];
589  },
590
591  uniq: function() {
592    return this.inject([], function(array, value) {
593      return array.include(value) ? array : array.concat([value]);
594    });
595  },
596
597  clone: function() {
598    return [].concat(this);
599  },
600
601  size: function() {
602    return this.length;
603  },
604
605  inspect: function() {
606    return '[' + this.map(Object.inspect).join(', ') + ']';
607  }
608});
609
610Array.prototype.toArray = Array.prototype.clone;
611
612function $w(string){
613  string = string.strip();
614  return string ? string.split(/\s+/) : [];
615};
616
617if(window.opera){
618  Array.prototype.concat = function(){
619    var array = [];
620    for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
621    for(var i = 0, length = arguments.length; i < length; i++) {
622      if(arguments[i].constructor == Array) {
623        for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
624          array.push(arguments[i][j]);
625      } else {
626        array.push(arguments[i]);
627      }
628    }
629    return array;
630  }
631};
632var Hash = function(obj) {
633  Object.extend(this, obj || {});
634};
635
636Object.extend(Hash, {
637  toQueryString: function(obj) {
638    var parts = [];
639
640          this.prototype._each.call(obj, function(pair) {
641      if (!pair.key) return;
642
643      if (pair.value && pair.value.constructor == Array) {
644        var values = pair.value.compact();
645        if (values.length < 2) pair.value = values.reduce();
646        else {
647                key = encodeURIComponent(pair.key);
648          values.each(function(value) {
649            value = value != undefined ? encodeURIComponent(value) : '';
650            parts.push(key + '=' + encodeURIComponent(value));
651          });
652          return;
653        }
654      }
655      if (pair.value == undefined) pair[1] = '';
656      parts.push(pair.join('='));
657          });
658
659    return parts.join('&');
660  }
661});
662
663Object.extend(Hash.prototype, Enumerable);
664Object.extend(Hash.prototype, {
665  _each: function(iterator) {
666    for (var key in this) {
667      var value = this[key];
668      if (value && value == Hash.prototype[key]) continue;
669
670      var pair = [key, value];
671      pair.key = key;
672      pair.value = value;
673      iterator(pair);
674    }
675  },
676
677  keys: function() {
678    return this.pluck('key');
679  },
680
681  values: function() {
682    return this.pluck('value');
683  },
684
685  merge: function(hash) {
686    return $H(hash).inject(this, function(mergedHash, pair) {
687      mergedHash[pair.key] = pair.value;
688      return mergedHash;
689    });
690  },
691
692  remove: function() {
693    var result;
694    for(var i = 0, length = arguments.length; i < length; i++) {
695      var value = this[arguments[i]];
696      if (value !== undefined){
697        if (result === undefined) result = value;
698        else {
699          if (result.constructor != Array) result = [result];
700          result.push(value)
701        }
702      }
703      delete this[arguments[i]];
704    }
705    return result;
706  },
707
708  toQueryString: function() {
709    return Hash.toQueryString(this);
710  },
711
712  inspect: function() {
713    return '#<Hash:{' + this.map(function(pair) {
714      return pair.map(Object.inspect).join(': ');
715    }).join(', ') + '}>';
716  },
717
718  customInspect: function() {
719    return '{' + this.map(function(pair) {
720      return pair.map(Object.inspect).join(': ');
721    }).join(', ') + '}';
722  }
723});
724
725function $H(object) {
726  if (object && object.constructor == Hash) return object;
727  return new Hash(object);
728};
729ObjectRange = Class.create();
730Object.extend(ObjectRange.prototype, Enumerable);
731Object.extend(ObjectRange.prototype, {
732  initialize: function(start, end, exclusive) {
733    this.start = start;
734    this.end = end;
735    this.exclusive = exclusive;
736  },
737
738  _each: function(iterator) {
739    var value = this.start;
740    while (this.include(value)) {
741      iterator(value);
742      value = value.succ();
743    }
744  },
745
746  include: function(value) {
747    if (value < this.start)
748      return false;
749    if (this.exclusive)
750      return value < this.end;
751    return value <= this.end;
752  }
753});
754
755var $R = function(start, end, exclusive) {
756  return new ObjectRange(start, end, exclusive);
757};
758
759var Ajax = {
760  getTransport: function() {
761    return Try.these(
762      function() {return new XMLHttpRequest()},
763      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
764      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
765    ) || false;
766  },
767
768  activeRequestCount: 0
769};
770
771Ajax.Responders = {
772  responders: [],
773
774  _each: function(iterator) {
775    this.responders._each(iterator);
776  },
777
778  register: function(responder) {
779    if (!this.include(responder))
780      this.responders.push(responder);
781  },
782
783  unregister: function(responder) {
784    this.responders = this.responders.without(responder);
785  },
786
787  dispatch: function(callback, request, transport, json) {
788    this.each(function(responder) {
789      if (typeof responder[callback] == 'function') {
790        try {
791          responder[callback].apply(responder, [request, transport, json]);
792        } catch (e) {}
793      }
794    });
795  }
796};
797
798Object.extend(Ajax.Responders, Enumerable);
799
800Ajax.Responders.register({
801  onCreate: function() {
802    Ajax.activeRequestCount++;
803  },
804  onComplete: function() {
805    Ajax.activeRequestCount--;
806  }
807});
808
809Ajax.Base = function() {};
810Ajax.Base.prototype = {
811  setOptions: function(options) {
812    this.options = {
813      method:       'post',
814      asynchronous: true,
815      contentType:  'application/x-www-form-urlencoded',
816      encoding:     'UTF-8',
817      parameters:   ''
818    };
819    Object.extend(this.options, options || {});
820
821    this.options.method = this.options.method.toLowerCase();
822    if (typeof this.options.parameters == 'string')
823      this.options.parameters = this.options.parameters.toQueryParams();
824  }
825};
826
827Ajax.Request = Class.create();
828Ajax.Request.Events =
829  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
830
831Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
832  _complete: false,
833
834  initialize: function(url, options) {
835    this.transport = Ajax.getTransport();
836    this.setOptions(options);
837    this.request(url);
838  },
839
840  request: function(url) {
841    this.url = url;
842    this.method = this.options.method;
843    var params = this.options.parameters;
844
845    if (!['get', 'post'].include(this.method)) {
846      // simulate other verbs over post
847      params['_method'] = this.method;
848      this.method = 'post';
849    }
850
851    params = Hash.toQueryString(params);
852    if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_=';
853
854    // when GET, append parameters to URL
855    if (this.method == 'get' && params)
856      this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
857
858    try {
859      Ajax.Responders.dispatch('onCreate', this, this.transport);
860
861      this.transport.open(this.method.toUpperCase(), this.url,
862        this.options.asynchronous);
863
864      if (this.options.asynchronous)
865        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
866
867      this.transport.onreadystatechange = this.onStateChange.bind(this);
868      this.setRequestHeaders();
869
870      var body = this.method == 'post' ? (this.options.postBody || params) : null;
871
872      this.transport.send(body);
873
874      /* Force Firefox to handle ready state 4 for synchronous requests */
875      if (!this.options.asynchronous && this.transport.overrideMimeType)
876        this.onStateChange();
877
878    }
879    catch (e) {
880      this.dispatchException(e);
881    }
882  },
883
884  onStateChange: function() {
885    var readyState = this.transport.readyState;
886    if (readyState > 1 && !((readyState == 4) && this._complete))
887      this.respondToReadyState(this.transport.readyState);
888  },
889
890  setRequestHeaders: function() {
891    var headers = {
892      'X-Requested-With': 'XMLHttpRequest',
893      'X-Prototype-Version': Prototype.Version,
894      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
895    };
896
897    if (this.method == 'post') {
898      headers['Content-type'] = this.options.contentType +
899        (this.options.encoding ? '; charset=' + this.options.encoding : '');
900
901      /* Force "Connection: close" for older Mozilla browsers to work
902       * around a bug where XMLHttpRequest sends an incorrect
903       * Content-length header. See Mozilla Bugzilla #246651.
904       */
905      if (this.transport.overrideMimeType &&
906          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
907            headers['Connection'] = 'close';
908    }
909
910    // user-defined headers
911    if (typeof this.options.requestHeaders == 'object') {
912      var extras = this.options.requestHeaders;
913
914      if (typeof extras.push == 'function')
915        for (var i = 0, length = extras.length; i < length; i += 2)
916          headers[extras[i]] = extras[i+1];
917      else
918        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
919    }
920
921    for (var name in headers)
922      this.transport.setRequestHeader(name, headers[name]);
923  },
924
925  success: function() {
926    return !this.transport.status
927        || (this.transport.status >= 200 && this.transport.status < 300);
928  },
929
930  respondToReadyState: function(readyState) {
931    var state = Ajax.Request.Events[readyState];
932    var transport = this.transport, json = this.evalJSON();
933
934    if (state == 'Complete') {
935      try {
936        this._complete = true;
937        (this.options['on' + this.transport.status]
938         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
939         || Prototype.emptyFunction)(transport, json);
940      } catch (e) {
941        this.dispatchException(e);
942      }
943
944      if ((this.getHeader('Content-type') || 'text/javascript').strip().
945        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
946          this.evalResponse();
947    }
948
949    try {
950      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
951      Ajax.Responders.dispatch('on' + state, this, transport, json);
952    } catch (e) {
953      this.dispatchException(e);
954    }
955
956    if (state == 'Complete') {
957      // avoid memory leak in MSIE: clean up
958      this.transport.onreadystatechange = Prototype.emptyFunction;
959    }
960  },
961
962  getHeader: function(name) {
963    try {
964      return this.transport.getResponseHeader(name);
965    } catch (e) { return null }
966  },
967
968  evalJSON: function() {
969    try {
970      var json = this.getHeader('X-JSON');
971      return json ? eval('(' + json + ')') : null;
972    } catch (e) { return null }
973  },
974
975  evalResponse: function() {
976    try {
977      return eval(this.transport.responseText);
978    } catch (e) {
979      this.dispatchException(e);
980    }
981  },
982
983  dispatchException: function(exception) {
984    (this.options.onException || Prototype.emptyFunction)(this, exception);
985    Ajax.Responders.dispatch('onException', this, exception);
986  }
987});
988
989Ajax.Updater = Class.create();
990
991Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
992  initialize: function(container, url, options) {
993    this.container = {
994      success: (container.success || container),
995      failure: (container.failure || (container.success ? null : container))
996    };
997
998    this.transport = Ajax.getTransport();
999    this.setOptions(options);
1000
1001    var onComplete = this.options.onComplete || Prototype.emptyFunction;
1002    this.options.onComplete = (function(transport, param) {
1003      this.updateContent();
1004      onComplete(transport, param);
1005    }).bind(this);
1006
1007    this.request(url);
1008  },
1009
1010  updateContent: function() {
1011    var receiver = this.container[this.success() ? 'success' : 'failure'];
1012    var response = this.transport.responseText;
1013
1014    if (!this.options.evalScripts) response = response.stripScripts();
1015
1016    if (receiver = $(receiver)) {
1017      if (this.options.insertion)
1018        new this.options.insertion(receiver, response);
1019      else
1020        receiver.update(response);
1021    }
1022
1023    if (this.success()) {
1024      if (this.onComplete)
1025        setTimeout(this.onComplete.bind(this), 10);
1026    }
1027  }
1028});
1029
1030Ajax.PeriodicalUpdater = Class.create();
1031Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1032  initialize: function(container, url, options) {
1033    this.setOptions(options);
1034    this.onComplete = this.options.onComplete;
1035
1036    this.frequency = (this.options.frequency || 2);
1037    this.decay = (this.options.decay || 1);
1038
1039    this.updater = {};
1040    this.container = container;
1041    this.url = url;
1042
1043    this.start();
1044  },
1045
1046  start: function() {
1047    this.options.onComplete = this.updateComplete.bind(this);
1048    this.onTimerEvent();
1049  },
1050
1051  stop: function() {
1052    this.updater.options.onComplete = undefined;
1053    clearTimeout(this.timer);
1054    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1055  },
1056
1057  updateComplete: function(request) {
1058    if (this.options.decay) {
1059      this.decay = (request.responseText == this.lastText ?
1060        this.decay * this.options.decay : 1);
1061
1062      this.lastText = request.responseText;
1063    }
1064    this.timer = setTimeout(this.onTimerEvent.bind(this),
1065      this.decay * this.frequency * 1000);
1066  },
1067
1068  onTimerEvent: function() {
1069    this.updater = new Ajax.Updater(this.container, this.url, this.options);
1070  }
1071});
1072function $(element) {
1073  if (arguments.length > 1) {
1074    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1075      elements.push($(arguments[i]));
1076    return elements;
1077  }
1078  if (typeof element == 'string')
1079    element = document.getElementById(element);
1080  return Element.extend(element);
1081};
1082
1083if (Prototype.BrowserFeatures.XPath) {
1084  document._getElementsByXPath = function(expression, parentElement) {
1085    var results = [];
1086    var query = document.evaluate(expression, $(parentElement) || document,
1087      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1088    for (var i = 0, length = query.snapshotLength; i < length; i++)
1089      results.push(query.snapshotItem(i));
1090    return results;
1091  };
1092};
1093
1094document.getElementsByClassName = function(className, parentElement) {
1095  if (Prototype.BrowserFeatures.XPath) {
1096    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1097    return document._getElementsByXPath(q, parentElement);
1098  } else {
1099    var children = ($(parentElement) || document.body).getElementsByTagName('*');
1100    var elements = [], child;
1101    for (var i = 0, length = children.length; i < length; i++) {
1102      child = children[i];
1103      if (Element.hasClassName(child, className))
1104        elements.push(Element.extend(child));
1105    }
1106    return elements;
1107  }
1108};
1109
1110/*--------------------------------------------------------------------------*/
1111
1112if (!window.Element)
1113  var Element = new Object();
1114
1115Element.extend = function(element) {
1116  if (!element || _nativeExtensions || element.nodeType == 3) return element;
1117
1118  if (!element._extended && element.tagName && element != window) {
1119    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1120
1121    if (element.tagName == 'FORM')
1122      Object.extend(methods, Form.Methods);
1123    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1124      Object.extend(methods, Form.Element.Methods);
1125
1126    Object.extend(methods, Element.Methods.Simulated);
1127
1128    for (var property in methods) {
1129      var value = methods[property];
1130      if (typeof value == 'function' && !(property in element))
1131        element[property] = cache.findOrStore(value);
1132    }
1133  }
1134
1135  element._extended = true;
1136  return element;
1137};
1138
1139Element.extend.cache = {
1140  findOrStore: function(value) {
1141    return this[value] = this[value] || function() {
1142      return value.apply(null, [this].concat($A(arguments)));
1143    }
1144  }
1145};
1146
1147Element.Methods = {
1148  visible: function(element) {
1149    return $(element).style.display != 'none';
1150  },
1151
1152  toggle: function(element) {
1153    element = $(element);
1154    Element[Element.visible(element) ? 'hide' : 'show'](element);
1155    return element;
1156  },
1157
1158  hide: function(element) {
1159    $(element).style.display = 'none';
1160    return element;
1161  },
1162
1163  show: function(element) {
1164    $(element).style.display = '';
1165    return element;
1166  },
1167
1168  remove: function(element) {
1169    element = $(element);
1170    element.parentNode.removeChild(element);
1171    return element;
1172  },
1173
1174  update: function(element, html) {
1175    html = typeof html == 'undefined' ? '' : html.toString();
1176    $(element).innerHTML = html.stripScripts();
1177    setTimeout(function() {html.evalScripts()}, 10);
1178    return element;
1179  },
1180
1181  replace: function(element, html) {
1182    element = $(element);
1183    html = typeof html == 'undefined' ? '' : html.toString();
1184    if (element.outerHTML) {
1185      element.outerHTML = html.stripScripts();
1186    } else {
1187      var range = element.ownerDocument.createRange();
1188      range.selectNodeContents(element);
1189      element.parentNode.replaceChild(
1190        range.createContextualFragment(html.stripScripts()), element);
1191    }
1192    setTimeout(function() {html.evalScripts()}, 10);
1193    return element;
1194  },
1195
1196  inspect: function(element) {
1197    element = $(element);
1198    var result = '<' + element.tagName.toLowerCase();
1199    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1200      var property = pair.first(), attribute = pair.last();
1201      var value = (element[property] || '').toString();
1202      if (value) result += ' ' + attribute + '=' + value.inspect(true);
1203    });
1204    return result + '>';
1205  },
1206
1207  recursivelyCollect: function(element, property) {
1208    element = $(element);
1209    var elements = [];
1210    while (element = element[property])
1211      if (element.nodeType == 1)
1212        elements.push(Element.extend(element));
1213    return elements;
1214  },
1215
1216  ancestors: function(element) {
1217    return $(element).recursivelyCollect('parentNode');
1218  },
1219
1220  descendants: function(element) {
1221    return $A($(element).getElementsByTagName('*'));
1222  },
1223
1224  immediateDescendants: function(element) {
1225    if (!(element = $(element).firstChild)) return [];
1226    while (element && element.nodeType != 1) element = element.nextSibling;
1227    if (element) return [element].concat($(element).nextSiblings());
1228    return [];
1229  },
1230
1231  previousSiblings: function(element) {
1232    return $(element).recursivelyCollect('previousSibling');
1233  },
1234
1235  nextSiblings: function(element) {
1236    return $(element).recursivelyCollect('nextSibling');
1237  },
1238
1239  siblings: function(element) {
1240    element = $(element);
1241    return element.previousSiblings().reverse().concat(element.nextSiblings());
1242  },
1243
1244  match: function(element, selector) {
1245    if (typeof selector == 'string')
1246      selector = new Selector(selector);
1247    return selector.match($(element));
1248  },
1249
1250  up: function(element, expression, index) {
1251    return Selector.findElement($(element).ancestors(), expression, index);
1252  },
1253
1254  down: function(element, expression, index) {
1255    return Selector.findElement($(element).descendants(), expression, index);
1256  },
1257
1258  previous: function(element, expression, index) {
1259    return Selector.findElement($(element).previousSiblings(), expression, index);
1260  },
1261
1262  next: function(element, expression, index) {
1263    return Selector.findElement($(element).nextSiblings(), expression, index);
1264  },
1265
1266  getElementsBySelector: function() {
1267    var args = $A(arguments), element = $(args.shift());
1268    return Selector.findChildElements(element, args);
1269  },
1270
1271  getElementsByClassName: function(element, className) {
1272    return document.getElementsByClassName(className, element);
1273  },
1274
1275  readAttribute: function(element, name) {
1276    element = $(element);
1277    if (document.all && !window.opera) {
1278      var t = Element._attributeTranslations;
1279      if (t.values[name]) return t.values[name](element, name);
1280      if (t.names[name])  name = t.names[name];
1281      var attribute = element.attributes[name];
1282      if(attribute) return attribute.nodeValue;
1283    }
1284    return element.getAttribute(name);
1285  },
1286
1287  getHeight: function(element) {
1288    return $(element).getDimensions().height;
1289  },
1290
1291  getWidth: function(element) {
1292    return $(element).getDimensions().width;
1293  },
1294
1295  classNames: function(element) {
1296    return new Element.ClassNames(element);
1297  },
1298
1299  hasClassName: function(element, className) {
1300    if (!(element = $(element))) return;
1301    var elementClassName = element.className;
1302    if (elementClassName.length == 0) return false;
1303    if (elementClassName == className ||
1304        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1305      return true;
1306    return false;
1307  },
1308
1309  addClassName: function(element, className) {
1310    if (!(element = $(element))) return;
1311    Element.classNames(element).add(className);
1312    return element;
1313  },
1314
1315  removeClassName: function(element, className) {
1316    if (!(element = $(element))) return;
1317    Element.classNames(element).remove(className);
1318    return element;
1319  },
1320
1321  toggleClassName: function(element, className) {
1322    if (!(element = $(element))) return;
1323    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1324    return element;
1325  },
1326
1327  observe: function() {
1328    Event.observe.apply(Event, arguments);
1329    return $A(arguments).first();
1330  },
1331
1332  stopObserving: function() {
1333    Event.stopObserving.apply(Event, arguments);
1334    return $A(arguments).first();
1335  },
1336
1337  // removes whitespace-only text node children
1338  cleanWhitespace: function(element) {
1339    element = $(element);
1340    var node = element.firstChild;
1341    while (node) {
1342      var nextNode = node.nextSibling;
1343      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1344        element.removeChild(node);
1345      node = nextNode;
1346    }
1347    return element;
1348  },
1349
1350  empty: function(element) {
1351    return $(element).innerHTML.match(/^\s*$/);
1352  },
1353
1354  descendantOf: function(element, ancestor) {
1355    element = $(element), ancestor = $(ancestor);
1356    while (element = element.parentNode)
1357      if (element == ancestor) return true;
1358    return false;
1359  },
1360
1361  scrollTo: function(element) {
1362    element = $(element);
1363    var pos = Position.cumulativeOffset(element);
1364    window.scrollTo(pos[0], pos[1]);
1365    return element;
1366  },
1367
1368  getStyle: function(element, style) {
1369    element = $(element);
1370    if (['float','cssFloat'].include(style))
1371      style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1372    style = style.camelize();
1373    var value = element.style[style];
1374    if (!value) {
1375      if (document.defaultView && document.defaultView.getComputedStyle) {
1376        var css = document.defaultView.getComputedStyle(element, null);
1377        value = css ? css[style] : null;
1378      } else if (element.currentStyle) {
1379        value = element.currentStyle[style];
1380      }
1381    }
1382
1383    if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1384      value = element['offset'+style.capitalize()] + 'px';
1385
1386    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1387      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1388    if(style == 'opacity') {
1389      if(value) return parseFloat(value);
1390      if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1391        if(value[1]) return parseFloat(value[1]) / 100;
1392      return 1.0;
1393    }
1394    return value == 'auto' ? null : value;
1395  },
1396
1397  setStyle: function(element, style) {
1398    element = $(element);
1399    for (var name in style) {
1400      var value = style[name];
1401      if(name == 'opacity') {
1402        if (value == 1) {
1403          value = (/Gecko/.test(navigator.userAgent) &&
1404            !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1405          if(/MSIE/.test(navigator.userAgent) && !window.opera)
1406            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1407        } else if(value == '') {
1408          if(/MSIE/.test(navigator.userAgent) && !window.opera)
1409            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1410        } else {
1411          if(value < 0.00001) value = 0;
1412          if(/MSIE/.test(navigator.userAgent) && !window.opera)
1413            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1414              'alpha(opacity='+value*100+')';
1415        }
1416      } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1417      element.style[name.camelize()] = value;
1418    }
1419    return element;
1420  },
1421
1422  getDimensions: function(element) {
1423    element = $(element);
1424    var display = $(element).getStyle('display');
1425    if (display != 'none' && display != null) // Safari bug
1426      return {width: element.offsetWidth, height: element.offsetHeight};
1427
1428    // All *Width and *Height properties give 0 on elements with display none,
1429    // so enable the element temporarily
1430    var els = element.style;
1431    var originalVisibility = els.visibility;
1432    var originalPosition = els.position;
1433    var originalDisplay = els.display;
1434    els.visibility = 'hidden';
1435    els.position = 'absolute';
1436    els.display = 'block';
1437    var originalWidth = element.clientWidth;
1438    var originalHeight = element.clientHeight;
1439    els.display = originalDisplay;
1440    els.position = originalPosition;
1441    els.visibility = originalVisibility;
1442    return {width: originalWidth, height: originalHeight};
1443  },
1444
1445  makePositioned: function(element) {
1446    element = $(element);
1447    var pos = Element.getStyle(element, 'position');
1448    if (pos == 'static' || !pos) {
1449      element._madePositioned = true;
1450      element.style.position = 'relative';
1451      // Opera returns the offset relative to the positioning context, when an
1452      // element is position relative but top and left have not been defined
1453      if (window.opera) {
1454        element.style.top = 0;
1455        element.style.left = 0;
1456      }
1457    }
1458    return element;
1459  },
1460
1461  undoPositioned: function(element) {
1462    element = $(element);
1463    if (element._madePositioned) {
1464      element._madePositioned = undefined;
1465      element.style.position =
1466        element.style.top =
1467        element.style.left =
1468        element.style.bottom =
1469        element.style.right = '';
1470    }
1471    return element;
1472  },
1473
1474  makeClipping: function(element) {
1475    element = $(element);
1476    if (element._overflow) return element;
1477    element._overflow = element.style.overflow || 'auto';
1478    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1479      element.style.overflow = 'hidden';
1480    return element;
1481  },
1482
1483  undoClipping: function(element) {
1484    element = $(element);
1485    if (!element._overflow) return element;
1486    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
1487    element._overflow = null;
1488    return element;
1489  }
1490};
1491
1492Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1493
1494Element._attributeTranslations = {};
1495
1496Element._attributeTranslations.names = {
1497  colspan:   "colSpan",
1498  rowspan:   "rowSpan",
1499  valign:    "vAlign",
1500  datetime:  "dateTime",
1501  accesskey: "accessKey",
1502  tabindex:  "tabIndex",
1503  enctype:   "encType",
1504  maxlength: "maxLength",
1505  readonly:  "readOnly",
1506  longdesc:  "longDesc"
1507};
1508
1509Element._attributeTranslations.values = {
1510  _getAttr: function(element, attribute) {
1511    return element.getAttribute(attribute, 2);
1512  },
1513
1514  _flag: function(element, attribute) {
1515    return $(element).hasAttribute(attribute) ? attribute : null;
1516  },
1517
1518  style: function(element) {
1519    return element.style.cssText.toLowerCase();
1520  },
1521
1522  title: function(element) {
1523    var node = element.getAttributeNode('title');
1524    return node.specified ? node.nodeValue : null;
1525  }
1526};
1527
1528Object.extend(Element._attributeTranslations.values, {
1529  href: Element._attributeTranslations.values._getAttr,
1530  src:  Element._attributeTranslations.values._getAttr,
1531  disabled: Element._attributeTranslations.values._flag,
1532  checked:  Element._attributeTranslations.values._flag,
1533  readonly: Element._attributeTranslations.values._flag,
1534  multiple: Element._attributeTranslations.values._flag
1535});
1536
1537Element.Methods.Simulated = {
1538  hasAttribute: function(element, attribute) {
1539    var t = Element._attributeTranslations;
1540    attribute = t.names[attribute] || attribute;
1541    return $(element).getAttributeNode(attribute).specified;
1542  }
1543};
1544
1545// IE is missing .innerHTML support for TABLE-related elements
1546if (document.all && !window.opera){
1547  Element.Methods.update = function(element, html) {
1548    element = $(element);
1549    html = typeof html == 'undefined' ? '' : html.toString();
1550    var tagName = element.tagName.toUpperCase();
1551    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1552      var div = document.createElement('div');
1553      switch (tagName) {
1554        case 'THEAD':
1555        case 'TBODY':
1556          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
1557          depth = 2;
1558          break;
1559        case 'TR':
1560          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
1561          depth = 3;
1562          break;
1563        case 'TD':
1564          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
1565          depth = 4;
1566      }
1567      $A(element.childNodes).each(function(node){
1568        element.removeChild(node)
1569      });
1570      depth.times(function(){ div = div.firstChild });
1571
1572      $A(div.childNodes).each(
1573        function(node){ element.appendChild(node) });
1574    } else {
1575      element.innerHTML = html.stripScripts();
1576    }
1577    setTimeout(function() {html.evalScripts()}, 10);
1578    return element;
1579  }
1580};
1581
1582Object.extend(Element, Element.Methods);
1583
1584var _nativeExtensions = false;
1585
1586if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1587  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
1588    var className = 'HTML' + tag + 'Element';
1589    if(window[className]) return;
1590    var klass = window[className] = {};
1591    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
1592  });
1593
1594Element.addMethods = function(methods) {
1595  Object.extend(Element.Methods, methods || {});
1596
1597  function copy(methods, destination, onlyIfAbsent) {
1598    onlyIfAbsent = onlyIfAbsent || false;
1599    var cache = Element.extend.cache;
1600    for (var property in methods) {
1601      var value = methods[property];
1602      if (!onlyIfAbsent || !(property in destination))
1603        destination[property] = cache.findOrStore(value);
1604    }
1605  }
1606
1607  if (typeof HTMLElement != 'undefined') {
1608    copy(Element.Methods, HTMLElement.prototype);
1609    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1610    copy(Form.Methods, HTMLFormElement.prototype);
1611    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
1612      copy(Form.Element.Methods, klass.prototype);
1613    });
1614    _nativeExtensions = true;
1615  }
1616};
1617
1618var Toggle = new Object();
1619Toggle.display = Element.toggle;
1620
1621/*--------------------------------------------------------------------------*/
1622
1623Abstract.Insertion = function(adjacency) {
1624  this.adjacency = adjacency;
1625};
1626
1627Abstract.Insertion.prototype = {
1628  initialize: function(element, content) {
1629    this.element = $(element);
1630    this.content = content.stripScripts();
1631
1632    if (this.adjacency && this.element.insertAdjacentHTML) {
1633      try {
1634        this.element.insertAdjacentHTML(this.adjacency, this.content);
1635      } catch (e) {
1636        var tagName = this.element.tagName.toUpperCase();
1637        if (['TBODY', 'TR'].include(tagName)) {
1638          this.insertContent(this.contentFromAnonymousTable());
1639        } else {
1640          throw e;
1641        }
1642      }
1643    } else {
1644      this.range = this.element.ownerDocument.createRange();
1645      if (this.initializeRange) this.initializeRange();
1646      this.insertContent([this.range.createContextualFragment(this.content)]);
1647    }
1648
1649    setTimeout(function() {content.evalScripts()}, 10);
1650  },
1651
1652  contentFromAnonymousTable: function() {
1653    var div = document.createElement('div');
1654    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1655    return $A(div.childNodes[0].childNodes[0].childNodes);
1656  }
1657};
1658
1659var Insertion = new Object();
1660
1661Insertion.Before = Class.create();
1662Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1663  initializeRange: function() {
1664    this.range.setStartBefore(this.element);
1665  },
1666
1667  insertContent: function(fragments) {
1668    fragments.each((function(fragment) {
1669      this.element.parentNode.insertBefore(fragment, this.element);
1670    }).bind(this));
1671  }
1672});
1673
1674Insertion.Top = Class.create();
1675Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1676  initializeRange: function() {
1677    this.range.selectNodeContents(this.element);
1678    this.range.collapse(true);
1679  },
1680
1681  insertContent: function(fragments) {
1682    fragments.reverse(false).each((function(fragment) {
1683      this.element.insertBefore(fragment, this.element.firstChild);
1684    }).bind(this));
1685  }
1686});
1687
1688Insertion.Bottom = Class.create();
1689Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1690  initializeRange: function() {
1691    this.range.selectNodeContents(this.element);
1692    this.range.collapse(this.element);
1693  },
1694
1695  insertContent: function(fragments) {
1696    fragments.each((function(fragment) {
1697      this.element.appendChild(fragment);
1698    }).bind(this));
1699  }
1700});
1701
1702Insertion.After = Class.create();
1703Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1704  initializeRange: function() {
1705    this.range.setStartAfter(this.element);
1706  },
1707
1708  insertContent: function(fragments) {
1709    fragments.each((function(fragment) {
1710      this.element.parentNode.insertBefore(fragment,
1711        this.element.nextSibling);
1712    }).bind(this));
1713  }
1714});
1715
1716/*--------------------------------------------------------------------------*/
1717
1718Element.ClassNames = Class.create();
1719Element.ClassNames.prototype = {
1720  initialize: function(element) {
1721    this.element = $(element);
1722  },
1723
1724  _each: function(iterator) {
1725    this.element.className.split(/\s+/).select(function(name) {
1726      return name.length > 0;
1727    })._each(iterator);
1728  },
1729
1730  set: function(className) {
1731    this.element.className = className;
1732  },
1733
1734  add: function(classNameToAdd) {
1735    if (this.include(classNameToAdd)) return;
1736    this.set($A(this).concat(classNameToAdd).join(' '));
1737  },
1738
1739  remove: function(classNameToRemove) {
1740    if (!this.include(classNameToRemove)) return;
1741    this.set($A(this).without(classNameToRemove).join(' '));
1742  },
1743
1744  toString: function() {
1745    return $A(this).join(' ');
1746  }
1747};
1748
1749Object.extend(Element.ClassNames.prototype, Enumerable);
1750var Selector = Class.create();
1751Selector.prototype = {
1752  initialize: function(expression) {
1753    this.params = {classNames: []};
1754    this.expression = expression.toString().strip();
1755    this.parseExpression();
1756    this.compileMatcher();
1757  },
1758
1759  parseExpression: function() {
1760    function abort(message) { throw 'Parse error in selector: ' + message; }
1761
1762    if (this.expression == '')  abort('empty expression');
1763
1764    var params = this.params, expr = this.expression, match, modifier, clause, rest;
1765    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
1766      params.attributes = params.attributes || [];
1767      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
1768      expr = match[1];
1769    }
1770
1771    if (expr == '*') return this.params.wildcard = true;
1772
1773    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
1774      modifier = match[1], clause = match[2], rest = match[3];
1775      switch (modifier) {
1776        case '#':       params.id = clause; break;
1777        case '.':       params.classNames.push(clause); break;
1778        case '':
1779        case undefined: params.tagName = clause.toUpperCase(); break;
1780        default:        abort(expr.inspect());
1781      }
1782      expr = rest;
1783    }
1784
1785    if (expr.length > 0) abort(expr.inspect());
1786  },
1787
1788  buildMatchExpression: function() {
1789    var params = this.params, conditions = [], clause;
1790
1791    if (params.wildcard)
1792      conditions.push('true');
1793    if (clause = params.id)
1794      conditions.push('element.readAttribute("id") == ' + clause.inspect());
1795    if (clause = params.tagName)
1796      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1797    if ((clause = params.classNames).length > 0)
1798      for (var i = 0, length = clause.length; i < length; i++)
1799        conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1800    if (clause = params.attributes) {
1801      clause.each(function(attribute) {
1802        var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1803        var splitValueBy = function(delimiter) {
1804          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1805        };
1806
1807        switch (attribute.operator) {
1808          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
1809          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
1810          case '|=':      conditions.push(
1811                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
1812                          ); break;
1813          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
1814          case '':
1815          case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1816          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
1817        }
1818      });
1819    }
1820
1821    return conditions.join(' && ');
1822  },
1823
1824  compileMatcher: function() {
1825    this.match = new Function('element', 'if (!element.tagName) return false; element = $(element); return ' + this.buildMatchExpression());
1826  },
1827
1828  findElements: function(scope) {
1829    var element;
1830
1831    if (element = $(this.params.id))
1832      if (this.match(element))
1833        if (!scope || Element.childOf(element, scope))
1834          return [element];
1835
1836    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
1837
1838    var results = [];
1839    for (var i = 0, length = scope.length; i < length; i++)
1840      if (this.match(element = scope[i]))
1841        results.push(Element.extend(element));
1842
1843    return results;
1844  },
1845
1846  toString: function() {
1847    return this.expression;
1848  }
1849};
1850
1851Object.extend(Selector, {
1852  matchElements: function(elements, expression) {
1853    var selector = new Selector(expression);
1854    return elements.select(selector.match.bind(selector)).map(Element.extend);
1855  },
1856
1857  findElement: function(elements, expression, index) {
1858    if (typeof expression == 'number') index = expression, expression = false;
1859    return Selector.matchElements(elements, expression || '*')[index || 0];
1860  },
1861
1862  findChildElements: function(element, expressions) {
1863    return expressions.map(function(expression) {
1864      return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1865        var selector = new Selector(expr);
1866        return results.inject([], function(elements, result) {
1867          return elements.concat(selector.findElements(result || element));
1868        });
1869      });
1870    }).flatten();
1871  }
1872});
1873
1874function $$() {
1875  return Selector.findChildElements(document, $A(arguments));
1876};
1877var Form = {
1878  reset: function(form) {
1879    $(form).reset();
1880    return form;
1881  },
1882
1883  serializeElements: function(elements, getHash) {
1884    var data = elements.inject({}, function(result, element) {
1885      if (!element.disabled && element.name) {
1886        var key = element.name, value = $(element).getValue();
1887        if (value != undefined) {
1888          value = escape(value);
1889          if (result[key]) {
1890            if (result[key].constructor != Array) result[key] = [result[key]];
1891            result[key].push(value);
1892          }
1893          else result[key] = value;
1894        }
1895      }
1896      return result;
1897    });
1898
1899    return getHash ? data : Hash.toQueryString(data);
1900  }
1901};
1902
1903Form.Methods = {
1904  serialize: function(form, getHash) {
1905    return Form.serializeElements(Form.getElements(form), getHash);
1906  },
1907
1908  getElements: function(form) {
1909    return $A($(form).getElementsByTagName('*')).inject([],
1910      function(elements, child) {
1911        if (Form.Element.Serializers[child.tagName.toLowerCase()])
1912          elements.push(Element.extend(child));
1913        return elements;
1914      }
1915    );
1916  },
1917
1918  getInputs: function(form, typeName, name) {
1919    form = $(form);
1920    var inputs = form.getElementsByTagName('input');
1921
1922    if (!typeName && !name) return $A(inputs).map(Element.extend);
1923
1924    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
1925      var input = inputs[i];
1926      if ((typeName && input.type != typeName) || (name && input.name != name))
1927        continue;
1928      matchingInputs.push(Element.extend(input));
1929    }
1930
1931    return matchingInputs;
1932  },
1933
1934  disable: function(form) {
1935    form = $(form);
1936    form.getElements().each(function(element) {
1937      element.blur();
1938      element.disabled = 'true';
1939    });
1940    return form;
1941  },
1942
1943  enable: function(form) {
1944    form = $(form);
1945    form.getElements().each(function(element) {
1946      element.disabled = '';
1947    });
1948    return form;
1949  },
1950
1951  findFirstElement: function(form) {
1952    return $(form).getElements().find(function(element) {
1953      return element.type != 'hidden' && !element.disabled &&
1954        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1955    });
1956  },
1957
1958  focusFirstElement: function(form) {
1959    form = $(form);
1960    form.findFirstElement().activate();
1961    return form;
1962  }
1963};
1964
1965Object.extend(Form, Form.Methods);
1966
1967/*--------------------------------------------------------------------------*/
1968
1969Form.Element = {
1970  focus: function(element) {
1971    $(element).focus();
1972    return element;
1973  },
1974
1975  select: function(element) {
1976    $(element).select();
1977    return element;
1978  }
1979};
1980
1981Form.Element.Methods = {
1982  serialize: function(element) {
1983    element = $(element);
1984    if (!element.disabled && element.name) {
1985      var value = element.getValue();
1986      if (value != undefined) {
1987        var pair = {};
1988        pair[element.name] = value;
1989        return Hash.toQueryString(pair);
1990      }
1991    }
1992    return '';
1993  },
1994
1995  getValue: function(element) {
1996    element = $(element);
1997    var method = element.tagName.toLowerCase();
1998    return Form.Element.Serializers[method](element);
1999  },
2000
2001  clear: function(element) {
2002    $(element).value = '';
2003    return element;
2004  },
2005
2006  present: function(element) {
2007    return $(element).value != '';
2008  },
2009
2010  activate: function(element) {
2011    element = $(element);
2012    element.focus();
2013    if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2014      !['button', 'reset', 'submit'].include(element.type) ) )
2015      element.select();
2016    return element;
2017  },
2018
2019  disable: function(element) {
2020    element = $(element);
2021    element.disabled = true;
2022    return element;
2023  },
2024
2025  enable: function(element) {
2026    element = $(element);
2027    element.blur();
2028    element.disabled = false;
2029    return element;
2030  }
2031};
2032
2033Object.extend(Form.Element, Form.Element.Methods);
2034var Field = Form.Element;
2035var $F = Form.Element.getValue;
2036
2037/*--------------------------------------------------------------------------*/
2038
2039Form.Element.Serializers = {
2040  input: function(element) {
2041    switch (element.type.toLowerCase()) {
2042      case 'checkbox':
2043      case 'radio':
2044        return Form.Element.Serializers.inputSelector(element);
2045      default:
2046        return Form.Element.Serializers.textarea(element);
2047    }
2048  },
2049
2050  inputSelector: function(element) {
2051    return element.checked ? element.value : null;
2052  },
2053
2054  textarea: function(element) {
2055    return element.value;
2056  },
2057
2058  select: function(element) {
2059    return this[element.type == 'select-one' ?
2060      'selectOne' : 'selectMany'](element);
2061  },
2062
2063  selectOne: function(element) {
2064    var index = element.selectedIndex;
2065    return index >= 0 ? this.optionValue(element.options[index]) : null;
2066  },
2067
2068  selectMany: function(element) {
2069    var values, length = element.length;
2070    if (!length) return null;
2071
2072    for (var i = 0, values = []; i < length; i++) {
2073      var opt = element.options[i];
2074      if (opt.selected) values.push(this.optionValue(opt));
2075    }
2076    return values;
2077  },
2078
2079  optionValue: function(opt) {
2080    // extend element because hasAttribute may not be native
2081    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2082  }
2083};
2084
2085/*--------------------------------------------------------------------------*/
2086
2087Abstract.TimedObserver = function() {};
2088Abstract.TimedObserver.prototype = {
2089  initialize: function(element, frequency, callback) {
2090    this.frequency = frequency;
2091    this.element   = $(element);
2092    this.callback  = callback;
2093
2094    this.lastValue = this.getValue();
2095    this.registerCallback();
2096  },
2097
2098  registerCallback: function() {
2099    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2100  },
2101
2102  onTimerEvent: function() {
2103    var value = this.getValue();
2104    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2105      ? this.lastValue != value : String(this.lastValue) != String(value));
2106    if (changed) {
2107      this.callback(this.element, value);
2108      this.lastValue = value;
2109    }
2110  }
2111};
2112
2113Form.Element.Observer = Class.create();
2114Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2115  getValue: function() {
2116    return Form.Element.getValue(this.element);
2117  }
2118});
2119
2120Form.Observer = Class.create();
2121Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2122  getValue: function() {
2123    return Form.serialize(this.element);
2124  }
2125});
2126
2127/*--------------------------------------------------------------------------*/
2128
2129Abstract.EventObserver = function() {};
2130Abstract.EventObserver.prototype = {
2131  initialize: function(element, callback) {
2132    this.element  = $(element);
2133    this.callback = callback;
2134
2135    this.lastValue = this.getValue();
2136    if (this.element.tagName.toLowerCase() == 'form')
2137      this.registerFormCallbacks();
2138    else
2139      this.registerCallback(this.element);
2140  },
2141
2142  onElementEvent: function() {
2143    var value = this.getValue();
2144    if (this.lastValue != value) {
2145      this.callback(this.element, value);
2146      this.lastValue = value;
2147    }
2148  },
2149
2150  registerFormCallbacks: function() {
2151    Form.getElements(this.element).each(this.registerCallback.bind(this));
2152  },
2153
2154  registerCallback: function(element) {
2155    if (element.type) {
2156      switch (element.type.toLowerCase()) {
2157        case 'checkbox':
2158        case 'radio':
2159          Event.observe(element, 'click', this.onElementEvent.bind(this));
2160          break;
2161        default:
2162          Event.observe(element, 'change', this.onElementEvent.bind(this));
2163          break;
2164      }
2165    }
2166  }
2167};
2168
2169Form.Element.EventObserver = Class.create();
2170Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2171  getValue: function() {
2172    return Form.Element.getValue(this.element);
2173  }
2174});
2175
2176Form.EventObserver = Class.create();
2177Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2178  getValue: function() {
2179    return Form.serialize(this.element);
2180  }
2181});
2182if (!window.Event) {
2183  var Event = new Object();
2184};
2185
2186Object.extend(Event, {
2187  KEY_BACKSPACE: 8,
2188  KEY_TAB:       9,
2189  KEY_RETURN:   13,
2190  KEY_ESC:      27,
2191  KEY_LEFT:     37,
2192  KEY_UP:       38,
2193  KEY_RIGHT:    39,
2194  KEY_DOWN:     40,
2195  KEY_DELETE:   46,
2196  KEY_HOME:     36,
2197  KEY_END:      35,
2198  KEY_PAGEUP:   33,
2199  KEY_PAGEDOWN: 34,
2200
2201  element: function(event) {
2202    return event.target || event.srcElement;
2203  },
2204
2205  isLeftClick: function(event) {
2206    return (((event.which) && (event.which == 1)) ||
2207            ((event.button) && (event.button == 1)));
2208  },
2209
2210  pointerX: function(event) {
2211    return event.pageX || (event.clientX +
2212      (document.documentElement.scrollLeft || document.body.scrollLeft));
2213  },
2214
2215  pointerY: function(event) {
2216    return event.pageY || (event.clientY +
2217      (document.documentElement.scrollTop || document.body.scrollTop));
2218  },
2219
2220  stop: function(event) {
2221    if (event.preventDefault) {
2222      event.preventDefault();
2223      event.stopPropagation();
2224    } else {
2225      event.returnValue = false;
2226      event.cancelBubble = true;
2227    }
2228  },
2229
2230  // find the first node with the given tagName, starting from the
2231  // node the event was triggered on; traverses the DOM upwards
2232  findElement: function(event, tagName) {
2233    var element = Event.element(event);
2234    while (element.parentNode && (!element.tagName ||
2235        (element.tagName.toUpperCase() != tagName.toUpperCase())))
2236      element = element.parentNode;
2237    return element;
2238  },
2239
2240  observers: false,
2241
2242  _observeAndCache: function(element, name, observer, useCapture) {
2243    if (!this.observers) this.observers = [];
2244    if (element.addEventListener) {
2245      this.observers.push([element, name, observer, useCapture]);
2246      element.addEventListener(name, observer, useCapture);
2247    } else if (element.attachEvent) {
2248      this.observers.push([element, name, observer, useCapture]);
2249      element.attachEvent('on' + name, observer);
2250    }
2251  },
2252
2253  unloadCache: function() {
2254    if (!Event.observers) return;
2255    for (var i = 0, length = Event.observers.length; i < length; i++) {
2256      Event.stopObserving.apply(this, Event.observers[i]);
2257      Event.observers[i][0] = null;
2258    }
2259    Event.observers = false;
2260  },
2261
2262  observe: function(element, name, observer, useCapture) {
2263    element = $(element);
2264    useCapture = useCapture || false;
2265
2266    if (name == 'keypress' &&
2267        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2268        || element.attachEvent))
2269      name = 'keydown';
2270
2271    Event._observeAndCache(element, name, observer, useCapture);
2272  },
2273
2274  stopObserving: function(element, name, observer, useCapture) {
2275    element = $(element);
2276    useCapture = useCapture || false;
2277
2278    if (name == 'keypress' &&
2279        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2280        || element.detachEvent))
2281      name = 'keydown';
2282
2283    if (element.removeEventListener) {
2284      element.removeEventListener(name, observer, useCapture);
2285    } else if (element.detachEvent) {
2286      try {
2287        element.detachEvent('on' + name, observer);
2288      } catch (e) {}
2289    }
2290  }
2291});
2292
2293/* prevent memory leaks in IE */
2294if (navigator.appVersion.match(/\bMSIE\b/))
2295  Event.observe(window, 'unload', Event.unloadCache, false);
2296var Position = {
2297  // set to true if needed, warning: firefox performance problems
2298  // NOT neeeded for page scrolling, only if draggable contained in
2299  // scrollable elements
2300  includeScrollOffsets: false,
2301
2302  // must be called before calling withinIncludingScrolloffset, every time the
2303  // page is scrolled
2304  prepare: function() {
2305    this.deltaX =  window.pageXOffset
2306                || document.documentElement.scrollLeft
2307                || document.body.scrollLeft
2308                || 0;
2309    this.deltaY =  window.pageYOffset
2310                || document.documentElement.scrollTop
2311                || document.body.scrollTop
2312                || 0;
2313  },
2314
2315  realOffset: function(element) {
2316    var valueT = 0, valueL = 0;
2317    do {
2318      valueT += element.scrollTop  || 0;
2319      valueL += element.scrollLeft || 0;
2320      element = element.parentNode;
2321    } while (element);
2322    return [valueL, valueT];
2323  },
2324
2325  cumulativeOffset: function(element) {
2326    var valueT = 0, valueL = 0;
2327    do {
2328      valueT += element.offsetTop  || 0;
2329      valueL += element.offsetLeft || 0;
2330      element = element.offsetParent;
2331    } while (element);
2332    return [valueL, valueT];
2333  },
2334
2335  positionedOffset: function(element) {
2336    var valueT = 0, valueL = 0;
2337    do {
2338      valueT += element.offsetTop  || 0;
2339      valueL += element.offsetLeft || 0;
2340      element = element.offsetParent;
2341      if (element) {
2342        if(element.tagName=='BODY') break;
2343        var p = Element.getStyle(element, 'position');
2344        if (p == 'relative' || p == 'absolute') break;
2345      }
2346    } while (element);
2347    return [valueL, valueT];
2348  },
2349
2350  offsetParent: function(element) {
2351    if (element.offsetParent) return element.offsetParent;
2352    if (element == document.body) return element;
2353
2354    while ((element = element.parentNode) && element != document.body)
2355      if (Element.getStyle(element, 'position') != 'static')
2356        return element;
2357
2358    return document.body;
2359  },
2360
2361  // caches x/y coordinate pair to use with overlap
2362  within: function(element, x, y) {
2363    if (this.includeScrollOffsets)
2364      return this.withinIncludingScrolloffsets(element, x, y);
2365    this.xcomp = x;
2366    this.ycomp = y;
2367    this.offset = this.cumulativeOffset(element);
2368
2369    return (y >= this.offset[1] &&
2370            y <  this.offset[1] + element.offsetHeight &&
2371            x >= this.offset[0] &&
2372            x <  this.offset[0] + element.offsetWidth);
2373  },
2374
2375  withinIncludingScrolloffsets: function(element, x, y) {
2376    var offsetcache = this.realOffset(element);
2377
2378    this.xcomp = x + offsetcache[0] - this.deltaX;
2379    this.ycomp = y + offsetcache[1] - this.deltaY;
2380    this.offset = this.cumulativeOffset(element);
2381
2382    return (this.ycomp >= this.offset[1] &&
2383            this.ycomp <  this.offset[1] + element.offsetHeight &&
2384            this.xcomp >= this.offset[0] &&
2385            this.xcomp <  this.offset[0] + element.offsetWidth);
2386  },
2387
2388  // within must be called directly before
2389  overlap: function(mode, element) {
2390    if (!mode) return 0;
2391    if (mode == 'vertical')
2392      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
2393        element.offsetHeight;
2394    if (mode == 'horizontal')
2395      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
2396        element.offsetWidth;
2397  },
2398
2399  page: function(forElement) {
2400    var valueT = 0, valueL = 0;
2401
2402    var element = forElement;
2403    do {
2404      valueT += element.offsetTop  || 0;
2405      valueL += element.offsetLeft || 0;
2406
2407      // Safari fix
2408      if (element.offsetParent==document.body)
2409        if (Element.getStyle(element,'position')=='absolute') break;
2410
2411    } while (element = element.offsetParent);
2412
2413    element = forElement;
2414    do {
2415      if (!window.opera || element.tagName=='BODY') {
2416        valueT -= element.scrollTop  || 0;
2417        valueL -= element.scrollLeft || 0;
2418      }
2419    } while (element = element.parentNode);
2420
2421    return [valueL, valueT];
2422  },
2423
2424  clone: function(source, target) {
2425    var options = Object.extend({
2426      setLeft:    true,
2427      setTop:     true,
2428      setWidth:   true,
2429      setHeight:  true,
2430      offsetTop:  0,
2431      offsetLeft: 0
2432    }, arguments[2] || {});
2433
2434    // find page position of source
2435    source = $(source);
2436    var p = Position.page(source);
2437
2438    // find coordinate system to use
2439    target = $(target);
2440    var delta = [0, 0];
2441    var parent = null;
2442    // delta [0,0] will do fine with position: fixed elements,
2443    // position:absolute needs offsetParent deltas
2444    if (Element.getStyle(target,'position') == 'absolute') {
2445      parent = Position.offsetParent(target);
2446      delta = Position.page(parent);
2447    }
2448
2449    // correct by body offsets (fixes Safari)
2450    if (parent == document.body) {
2451      delta[0] -= document.body.offsetLeft;
2452      delta[1] -= document.body.offsetTop;
2453    }
2454
2455    // set position
2456    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2457    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2458    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
2459    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
2460  },
2461
2462  absolutize: function(element) {
2463    element = $(element);
2464    if (element.style.position == 'absolute') return;
2465    Position.prepare();
2466
2467    var offsets = Position.positionedOffset(element);
2468    var top     = offsets[1];
2469    var left    = offsets[0];
2470    var width   = element.clientWidth;
2471    var height  = element.clientHeight;
2472
2473    element._originalLeft   = left - parseFloat(element.style.left  || 0);
2474    element._originalTop    = top  - parseFloat(element.style.top || 0);
2475    element._originalWidth  = element.style.width;
2476    element._originalHeight = element.style.height;
2477
2478    element.style.position = 'absolute';
2479    element.style.top    = top + 'px';
2480    element.style.left   = left + 'px';
2481    element.style.width  = width + 'px';
2482    element.style.height = height + 'px';
2483  },
2484
2485  relativize: function(element) {
2486    element = $(element);
2487    if (element.style.position == 'relative') return;
2488    Position.prepare();
2489
2490    element.style.position = 'relative';
2491    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
2492    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2493
2494    element.style.top    = top + 'px';
2495    element.style.left   = left + 'px';
2496    element.style.height = element._originalHeight;
2497    element.style.width  = element._originalWidth;
2498  }
2499};
2500
2501// Safari returns margins on body which is incorrect if the child is absolutely
2502// positioned.  For performance reasons, redefine Position.cumulativeOffset for
2503// KHTML/WebKit only.
2504if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2505  Position.cumulativeOffset = function(element) {
2506    var valueT = 0, valueL = 0;
2507    do {
2508      valueT += element.offsetTop  || 0;
2509      valueL += element.offsetLeft || 0;
2510      if (element.offsetParent == document.body)
2511        if (Element.getStyle(element, 'position') == 'absolute') break;
2512
2513      element = element.offsetParent;
2514    } while (element);
2515
2516    return [valueL, valueT];
2517  }
2518};
2519
2520Element.addMethods();
Note: See TracBrowser for help on using the repository browser.