source: branches/TaskRewrite/gnuMims/web-app/js/prototype/prototype.js @ 56

Last change on this file since 56 was 56, checked in by gav, 15 years ago

Create Grails app (manual recursion to get everything in)

File size: 121.2 KB
Line 
1/*  Prototype JavaScript framework, version 1.6.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://www.prototypejs.org/
6 *
7 *--------------------------------------------------------------------------*/
8
9var Prototype = {
10  Version: '1.6.0',
11
12  Browser: {
13    IE:     !!(window.attachEvent && !window.opera),
14    Opera:  !!window.opera,
15    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
17    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
18  },
19
20  BrowserFeatures: {
21    XPath: !!document.evaluate,
22    ElementExtensions: !!window.HTMLElement,
23    SpecificElementExtensions:
24      document.createElement('div').__proto__ &&
25      document.createElement('div').__proto__ !==
26        document.createElement('form').__proto__
27  },
28
29  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
30  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
31
32  emptyFunction: function() { },
33  K: function(x) { return x }
34};
35
36if (Prototype.Browser.MobileSafari)
37  Prototype.BrowserFeatures.SpecificElementExtensions = false;
38
39if (Prototype.Browser.WebKit)
40  Prototype.BrowserFeatures.XPath = false;
41
42/* Based on Alex Arnell's inheritance implementation. */
43var Class = {
44  create: function() {
45    var parent = null, properties = $A(arguments);
46    if (Object.isFunction(properties[0]))
47      parent = properties.shift();
48
49    function klass() {
50      this.initialize.apply(this, arguments);
51    }
52
53    Object.extend(klass, Class.Methods);
54    klass.superclass = parent;
55    klass.subclasses = [];
56
57    if (parent) {
58      var subclass = function() { };
59      subclass.prototype = parent.prototype;
60      klass.prototype = new subclass;
61      parent.subclasses.push(klass);
62    }
63
64    for (var i = 0; i < properties.length; i++)
65      klass.addMethods(properties[i]);
66
67    if (!klass.prototype.initialize)
68      klass.prototype.initialize = Prototype.emptyFunction;
69
70    klass.prototype.constructor = klass;
71
72    return klass;
73  }
74};
75
76Class.Methods = {
77  addMethods: function(source) {
78    var ancestor   = this.superclass && this.superclass.prototype;
79    var properties = Object.keys(source);
80
81    if (!Object.keys({ toString: true }).length)
82      properties.push("toString", "valueOf");
83
84    for (var i = 0, length = properties.length; i < length; i++) {
85      var property = properties[i], value = source[property];
86      if (ancestor && Object.isFunction(value) &&
87          value.argumentNames().first() == "$super") {
88        var method = value, value = Object.extend((function(m) {
89          return function() { return ancestor[m].apply(this, arguments) };
90        })(property).wrap(method), {
91          valueOf:  function() { return method },
92          toString: function() { return method.toString() }
93        });
94      }
95      this.prototype[property] = value;
96    }
97
98    return this;
99  }
100};
101
102var Abstract = { };
103
104Object.extend = function(destination, source) {
105  for (var property in source)
106    destination[property] = source[property];
107  return destination;
108};
109
110Object.extend(Object, {
111  inspect: function(object) {
112    try {
113      if (object === undefined) return 'undefined';
114      if (object === null) return 'null';
115      return object.inspect ? object.inspect() : object.toString();
116    } catch (e) {
117      if (e instanceof RangeError) return '...';
118      throw e;
119    }
120  },
121
122  toJSON: function(object) {
123    var type = typeof object;
124    switch (type) {
125      case 'undefined':
126      case 'function':
127      case 'unknown': return;
128      case 'boolean': return object.toString();
129    }
130
131    if (object === null) return 'null';
132    if (object.toJSON) return object.toJSON();
133    if (Object.isElement(object)) return;
134
135    var results = [];
136    for (var property in object) {
137      var value = Object.toJSON(object[property]);
138      if (value !== undefined)
139        results.push(property.toJSON() + ': ' + value);
140    }
141
142    return '{' + results.join(', ') + '}';
143  },
144
145  toQueryString: function(object) {
146    return $H(object).toQueryString();
147  },
148
149  toHTML: function(object) {
150    return object && object.toHTML ? object.toHTML() : String.interpret(object);
151  },
152
153  keys: function(object) {
154    var keys = [];
155    for (var property in object)
156      keys.push(property);
157    return keys;
158  },
159
160  values: function(object) {
161    var values = [];
162    for (var property in object)
163      values.push(object[property]);
164    return values;
165  },
166
167  clone: function(object) {
168    return Object.extend({ }, object);
169  },
170
171  isElement: function(object) {
172    return object && object.nodeType == 1;
173  },
174
175  isArray: function(object) {
176    return object && object.constructor === Array;
177  },
178
179  isHash: function(object) {
180    return object instanceof Hash;
181  },
182
183  isFunction: function(object) {
184    return typeof object == "function";
185  },
186
187  isString: function(object) {
188    return typeof object == "string";
189  },
190
191  isNumber: function(object) {
192    return typeof object == "number";
193  },
194
195  isUndefined: function(object) {
196    return typeof object == "undefined";
197  }
198});
199
200Object.extend(Function.prototype, {
201  argumentNames: function() {
202    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
203    return names.length == 1 && !names[0] ? [] : names;
204  },
205
206  bind: function() {
207    if (arguments.length < 2 && arguments[0] === undefined) return this;
208    var __method = this, args = $A(arguments), object = args.shift();
209    return function() {
210      return __method.apply(object, args.concat($A(arguments)));
211    }
212  },
213
214  bindAsEventListener: function() {
215    var __method = this, args = $A(arguments), object = args.shift();
216    return function(event) {
217      return __method.apply(object, [event || window.event].concat(args));
218    }
219  },
220
221  curry: function() {
222    if (!arguments.length) return this;
223    var __method = this, args = $A(arguments);
224    return function() {
225      return __method.apply(this, args.concat($A(arguments)));
226    }
227  },
228
229  delay: function() {
230    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
231    return window.setTimeout(function() {
232      return __method.apply(__method, args);
233    }, timeout);
234  },
235
236  wrap: function(wrapper) {
237    var __method = this;
238    return function() {
239      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
240    }
241  },
242
243  methodize: function() {
244    if (this._methodized) return this._methodized;
245    var __method = this;
246    return this._methodized = function() {
247      return __method.apply(null, [this].concat($A(arguments)));
248    };
249  }
250});
251
252Function.prototype.defer = Function.prototype.delay.curry(0.01);
253
254Date.prototype.toJSON = function() {
255  return '"' + this.getUTCFullYear() + '-' +
256    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
257    this.getUTCDate().toPaddedString(2) + 'T' +
258    this.getUTCHours().toPaddedString(2) + ':' +
259    this.getUTCMinutes().toPaddedString(2) + ':' +
260    this.getUTCSeconds().toPaddedString(2) + 'Z"';
261};
262
263var Try = {
264  these: function() {
265    var returnValue;
266
267    for (var i = 0, length = arguments.length; i < length; i++) {
268      var lambda = arguments[i];
269      try {
270        returnValue = lambda();
271        break;
272      } catch (e) { }
273    }
274
275    return returnValue;
276  }
277};
278
279RegExp.prototype.match = RegExp.prototype.test;
280
281RegExp.escape = function(str) {
282  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
283};
284
285/*--------------------------------------------------------------------------*/
286
287var PeriodicalExecuter = Class.create({
288  initialize: function(callback, frequency) {
289    this.callback = callback;
290    this.frequency = frequency;
291    this.currentlyExecuting = false;
292
293    this.registerCallback();
294  },
295
296  registerCallback: function() {
297    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
298  },
299
300  execute: function() {
301    this.callback(this);
302  },
303
304  stop: function() {
305    if (!this.timer) return;
306    clearInterval(this.timer);
307    this.timer = null;
308  },
309
310  onTimerEvent: function() {
311    if (!this.currentlyExecuting) {
312      try {
313        this.currentlyExecuting = true;
314        this.execute();
315      } finally {
316        this.currentlyExecuting = false;
317      }
318    }
319  }
320});
321Object.extend(String, {
322  interpret: function(value) {
323    return value == null ? '' : String(value);
324  },
325  specialChar: {
326    '\b': '\\b',
327    '\t': '\\t',
328    '\n': '\\n',
329    '\f': '\\f',
330    '\r': '\\r',
331    '\\': '\\\\'
332  }
333});
334
335Object.extend(String.prototype, {
336  gsub: function(pattern, replacement) {
337    var result = '', source = this, match;
338    replacement = arguments.callee.prepareReplacement(replacement);
339
340    while (source.length > 0) {
341      if (match = source.match(pattern)) {
342        result += source.slice(0, match.index);
343        result += String.interpret(replacement(match));
344        source  = source.slice(match.index + match[0].length);
345      } else {
346        result += source, source = '';
347      }
348    }
349    return result;
350  },
351
352  sub: function(pattern, replacement, count) {
353    replacement = this.gsub.prepareReplacement(replacement);
354    count = count === undefined ? 1 : count;
355
356    return this.gsub(pattern, function(match) {
357      if (--count < 0) return match[0];
358      return replacement(match);
359    });
360  },
361
362  scan: function(pattern, iterator) {
363    this.gsub(pattern, iterator);
364    return String(this);
365  },
366
367  truncate: function(length, truncation) {
368    length = length || 30;
369    truncation = truncation === undefined ? '...' : truncation;
370    return this.length > length ?
371      this.slice(0, length - truncation.length) + truncation : String(this);
372  },
373
374  strip: function() {
375    return this.replace(/^\s+/, '').replace(/\s+$/, '');
376  },
377
378  stripTags: function() {
379    return this.replace(/<\/?[^>]+>/gi, '');
380  },
381
382  stripScripts: function() {
383    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
384  },
385
386  extractScripts: function() {
387    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
388    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
389    return (this.match(matchAll) || []).map(function(scriptTag) {
390      return (scriptTag.match(matchOne) || ['', ''])[1];
391    });
392  },
393
394  evalScripts: function() {
395    return this.extractScripts().map(function(script) { return eval(script) });
396  },
397
398  escapeHTML: function() {
399    var self = arguments.callee;
400    self.text.data = this;
401    return self.div.innerHTML;
402  },
403
404  unescapeHTML: function() {
405    var div = new Element('div');
406    div.innerHTML = this.stripTags();
407    return div.childNodes[0] ? (div.childNodes.length > 1 ?
408      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
409      div.childNodes[0].nodeValue) : '';
410  },
411
412  toQueryParams: function(separator) {
413    var match = this.strip().match(/([^?#]*)(#.*)?$/);
414    if (!match) return { };
415
416    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
417      if ((pair = pair.split('='))[0]) {
418        var key = decodeURIComponent(pair.shift());
419        var value = pair.length > 1 ? pair.join('=') : pair[0];
420        if (value != undefined) value = decodeURIComponent(value);
421
422        if (key in hash) {
423          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
424          hash[key].push(value);
425        }
426        else hash[key] = value;
427      }
428      return hash;
429    });
430  },
431
432  toArray: function() {
433    return this.split('');
434  },
435
436  succ: function() {
437    return this.slice(0, this.length - 1) +
438      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
439  },
440
441  times: function(count) {
442    return count < 1 ? '' : new Array(count + 1).join(this);
443  },
444
445  camelize: function() {
446    var parts = this.split('-'), len = parts.length;
447    if (len == 1) return parts[0];
448
449    var camelized = this.charAt(0) == '-'
450      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
451      : parts[0];
452
453    for (var i = 1; i < len; i++)
454      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
455
456    return camelized;
457  },
458
459  capitalize: function() {
460    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
461  },
462
463  underscore: function() {
464    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
465  },
466
467  dasherize: function() {
468    return this.gsub(/_/,'-');
469  },
470
471  inspect: function(useDoubleQuotes) {
472    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
473      var character = String.specialChar[match[0]];
474      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
475    });
476    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
477    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
478  },
479
480  toJSON: function() {
481    return this.inspect(true);
482  },
483
484  unfilterJSON: function(filter) {
485    return this.sub(filter || Prototype.JSONFilter, '#{1}');
486  },
487
488  isJSON: function() {
489    var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
490    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
491  },
492
493  evalJSON: function(sanitize) {
494    var json = this.unfilterJSON();
495    try {
496      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
497    } catch (e) { }
498    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
499  },
500
501  include: function(pattern) {
502    return this.indexOf(pattern) > -1;
503  },
504
505  startsWith: function(pattern) {
506    return this.indexOf(pattern) === 0;
507  },
508
509  endsWith: function(pattern) {
510    var d = this.length - pattern.length;
511    return d >= 0 && this.lastIndexOf(pattern) === d;
512  },
513
514  empty: function() {
515    return this == '';
516  },
517
518  blank: function() {
519    return /^\s*$/.test(this);
520  },
521
522  interpolate: function(object, pattern) {
523    return new Template(this, pattern).evaluate(object);
524  }
525});
526
527if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
528  escapeHTML: function() {
529    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
530  },
531  unescapeHTML: function() {
532    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
533  }
534});
535
536String.prototype.gsub.prepareReplacement = function(replacement) {
537  if (Object.isFunction(replacement)) return replacement;
538  var template = new Template(replacement);
539  return function(match) { return template.evaluate(match) };
540};
541
542String.prototype.parseQuery = String.prototype.toQueryParams;
543
544Object.extend(String.prototype.escapeHTML, {
545  div:  document.createElement('div'),
546  text: document.createTextNode('')
547});
548
549with (String.prototype.escapeHTML) div.appendChild(text);
550
551var Template = Class.create({
552  initialize: function(template, pattern) {
553    this.template = template.toString();
554    this.pattern = pattern || Template.Pattern;
555  },
556
557  evaluate: function(object) {
558    if (Object.isFunction(object.toTemplateReplacements))
559      object = object.toTemplateReplacements();
560
561    return this.template.gsub(this.pattern, function(match) {
562      if (object == null) return '';
563
564      var before = match[1] || '';
565      if (before == '\\') return match[2];
566
567      var ctx = object, expr = match[3];
568      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/, match = pattern.exec(expr);
569      if (match == null) return before;
570
571      while (match != null) {
572        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
573        ctx = ctx[comp];
574        if (null == ctx || '' == match[3]) break;
575        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
576        match = pattern.exec(expr);
577      }
578
579      return before + String.interpret(ctx);
580    }.bind(this));
581  }
582});
583Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
584
585var $break = { };
586
587var Enumerable = {
588  each: function(iterator, context) {
589    var index = 0;
590    iterator = iterator.bind(context);
591    try {
592      this._each(function(value) {
593        iterator(value, index++);
594      });
595    } catch (e) {
596      if (e != $break) throw e;
597    }
598    return this;
599  },
600
601  eachSlice: function(number, iterator, context) {
602    iterator = iterator ? iterator.bind(context) : Prototype.K;
603    var index = -number, slices = [], array = this.toArray();
604    while ((index += number) < array.length)
605      slices.push(array.slice(index, index+number));
606    return slices.collect(iterator, context);
607  },
608
609  all: function(iterator, context) {
610    iterator = iterator ? iterator.bind(context) : Prototype.K;
611    var result = true;
612    this.each(function(value, index) {
613      result = result && !!iterator(value, index);
614      if (!result) throw $break;
615    });
616    return result;
617  },
618
619  any: function(iterator, context) {
620    iterator = iterator ? iterator.bind(context) : Prototype.K;
621    var result = false;
622    this.each(function(value, index) {
623      if (result = !!iterator(value, index))
624        throw $break;
625    });
626    return result;
627  },
628
629  collect: function(iterator, context) {
630    iterator = iterator ? iterator.bind(context) : Prototype.K;
631    var results = [];
632    this.each(function(value, index) {
633      results.push(iterator(value, index));
634    });
635    return results;
636  },
637
638  detect: function(iterator, context) {
639    iterator = iterator.bind(context);
640    var result;
641    this.each(function(value, index) {
642      if (iterator(value, index)) {
643        result = value;
644        throw $break;
645      }
646    });
647    return result;
648  },
649
650  findAll: function(iterator, context) {
651    iterator = iterator.bind(context);
652    var results = [];
653    this.each(function(value, index) {
654      if (iterator(value, index))
655        results.push(value);
656    });
657    return results;
658  },
659
660  grep: function(filter, iterator, context) {
661    iterator = iterator ? iterator.bind(context) : Prototype.K;
662    var results = [];
663
664    if (Object.isString(filter))
665      filter = new RegExp(filter);
666
667    this.each(function(value, index) {
668      if (filter.match(value))
669        results.push(iterator(value, index));
670    });
671    return results;
672  },
673
674  include: function(object) {
675    if (Object.isFunction(this.indexOf))
676      if (this.indexOf(object) != -1) return true;
677
678    var found = false;
679    this.each(function(value) {
680      if (value == object) {
681        found = true;
682        throw $break;
683      }
684    });
685    return found;
686  },
687
688  inGroupsOf: function(number, fillWith) {
689    fillWith = fillWith === undefined ? null : fillWith;
690    return this.eachSlice(number, function(slice) {
691      while(slice.length < number) slice.push(fillWith);
692      return slice;
693    });
694  },
695
696  inject: function(memo, iterator, context) {
697    iterator = iterator.bind(context);
698    this.each(function(value, index) {
699      memo = iterator(memo, value, index);
700    });
701    return memo;
702  },
703
704  invoke: function(method) {
705    var args = $A(arguments).slice(1);
706    return this.map(function(value) {
707      return value[method].apply(value, args);
708    });
709  },
710
711  max: function(iterator, context) {
712    iterator = iterator ? iterator.bind(context) : Prototype.K;
713    var result;
714    this.each(function(value, index) {
715      value = iterator(value, index);
716      if (result == undefined || value >= result)
717        result = value;
718    });
719    return result;
720  },
721
722  min: function(iterator, context) {
723    iterator = iterator ? iterator.bind(context) : Prototype.K;
724    var result;
725    this.each(function(value, index) {
726      value = iterator(value, index);
727      if (result == undefined || value < result)
728        result = value;
729    });
730    return result;
731  },
732
733  partition: function(iterator, context) {
734    iterator = iterator ? iterator.bind(context) : Prototype.K;
735    var trues = [], falses = [];
736    this.each(function(value, index) {
737      (iterator(value, index) ?
738        trues : falses).push(value);
739    });
740    return [trues, falses];
741  },
742
743  pluck: function(property) {
744    var results = [];
745    this.each(function(value) {
746      results.push(value[property]);
747    });
748    return results;
749  },
750
751  reject: function(iterator, context) {
752    iterator = iterator.bind(context);
753    var results = [];
754    this.each(function(value, index) {
755      if (!iterator(value, index))
756        results.push(value);
757    });
758    return results;
759  },
760
761  sortBy: function(iterator, context) {
762    iterator = iterator.bind(context);
763    return this.map(function(value, index) {
764      return {value: value, criteria: iterator(value, index)};
765    }).sort(function(left, right) {
766      var a = left.criteria, b = right.criteria;
767      return a < b ? -1 : a > b ? 1 : 0;
768    }).pluck('value');
769  },
770
771  toArray: function() {
772    return this.map();
773  },
774
775  zip: function() {
776    var iterator = Prototype.K, args = $A(arguments);
777    if (Object.isFunction(args.last()))
778      iterator = args.pop();
779
780    var collections = [this].concat(args).map($A);
781    return this.map(function(value, index) {
782      return iterator(collections.pluck(index));
783    });
784  },
785
786  size: function() {
787    return this.toArray().length;
788  },
789
790  inspect: function() {
791    return '#<Enumerable:' + this.toArray().inspect() + '>';
792  }
793};
794
795Object.extend(Enumerable, {
796  map:     Enumerable.collect,
797  find:    Enumerable.detect,
798  select:  Enumerable.findAll,
799  filter:  Enumerable.findAll,
800  member:  Enumerable.include,
801  entries: Enumerable.toArray,
802  every:   Enumerable.all,
803  some:    Enumerable.any
804});
805function $A(iterable) {
806  if (!iterable) return [];
807  if (iterable.toArray) return iterable.toArray();
808  var length = iterable.length, results = new Array(length);
809  while (length--) results[length] = iterable[length];
810  return results;
811}
812
813if (Prototype.Browser.WebKit) {
814  function $A(iterable) {
815    if (!iterable) return [];
816    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
817        iterable.toArray) return iterable.toArray();
818    var length = iterable.length, results = new Array(length);
819    while (length--) results[length] = iterable[length];
820    return results;
821  }
822}
823
824Array.from = $A;
825
826Object.extend(Array.prototype, Enumerable);
827
828if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
829
830Object.extend(Array.prototype, {
831  _each: function(iterator) {
832    for (var i = 0, length = this.length; i < length; i++)
833      iterator(this[i]);
834  },
835
836  clear: function() {
837    this.length = 0;
838    return this;
839  },
840
841  first: function() {
842    return this[0];
843  },
844
845  last: function() {
846    return this[this.length - 1];
847  },
848
849  compact: function() {
850    return this.select(function(value) {
851      return value != null;
852    });
853  },
854
855  flatten: function() {
856    return this.inject([], function(array, value) {
857      return array.concat(Object.isArray(value) ?
858        value.flatten() : [value]);
859    });
860  },
861
862  without: function() {
863    var values = $A(arguments);
864    return this.select(function(value) {
865      return !values.include(value);
866    });
867  },
868
869  reverse: function(inline) {
870    return (inline !== false ? this : this.toArray())._reverse();
871  },
872
873  reduce: function() {
874    return this.length > 1 ? this : this[0];
875  },
876
877  uniq: function(sorted) {
878    return this.inject([], function(array, value, index) {
879      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
880        array.push(value);
881      return array;
882    });
883  },
884
885  intersect: function(array) {
886    return this.uniq().findAll(function(item) {
887      return array.detect(function(value) { return item === value });
888    });
889  },
890
891  clone: function() {
892    return [].concat(this);
893  },
894
895  size: function() {
896    return this.length;
897  },
898
899  inspect: function() {
900    return '[' + this.map(Object.inspect).join(', ') + ']';
901  },
902
903  toJSON: function() {
904    var results = [];
905    this.each(function(object) {
906      var value = Object.toJSON(object);
907      if (value !== undefined) results.push(value);
908    });
909    return '[' + results.join(', ') + ']';
910  }
911});
912
913// use native browser JS 1.6 implementation if available
914if (Object.isFunction(Array.prototype.forEach))
915  Array.prototype._each = Array.prototype.forEach;
916
917if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
918  i || (i = 0);
919  var length = this.length;
920  if (i < 0) i = length + i;
921  for (; i < length; i++)
922    if (this[i] === item) return i;
923  return -1;
924};
925
926if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
927  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
928  var n = this.slice(0, i).reverse().indexOf(item);
929  return (n < 0) ? n : i - n - 1;
930};
931
932Array.prototype.toArray = Array.prototype.clone;
933
934function $w(string) {
935  if (!Object.isString(string)) return [];
936  string = string.strip();
937  return string ? string.split(/\s+/) : [];
938}
939
940if (Prototype.Browser.Opera){
941  Array.prototype.concat = function() {
942    var array = [];
943    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
944    for (var i = 0, length = arguments.length; i < length; i++) {
945      if (Object.isArray(arguments[i])) {
946        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
947          array.push(arguments[i][j]);
948      } else {
949        array.push(arguments[i]);
950      }
951    }
952    return array;
953  };
954}
955Object.extend(Number.prototype, {
956  toColorPart: function() {
957    return this.toPaddedString(2, 16);
958  },
959
960  succ: function() {
961    return this + 1;
962  },
963
964  times: function(iterator) {
965    $R(0, this, true).each(iterator);
966    return this;
967  },
968
969  toPaddedString: function(length, radix) {
970    var string = this.toString(radix || 10);
971    return '0'.times(length - string.length) + string;
972  },
973
974  toJSON: function() {
975    return isFinite(this) ? this.toString() : 'null';
976  }
977});
978
979$w('abs round ceil floor').each(function(method){
980  Number.prototype[method] = Math[method].methodize();
981});
982function $H(object) {
983  return new Hash(object);
984};
985
986var Hash = Class.create(Enumerable, (function() {
987  if (function() {
988    var i = 0, Test = function(value) { this.key = value };
989    Test.prototype.key = 'foo';
990    for (var property in new Test('bar')) i++;
991    return i > 1;
992  }()) {
993    function each(iterator) {
994      var cache = [];
995      for (var key in this._object) {
996        var value = this._object[key];
997        if (cache.include(key)) continue;
998        cache.push(key);
999        var pair = [key, value];
1000        pair.key = key;
1001        pair.value = value;
1002        iterator(pair);
1003      }
1004    }
1005  } else {
1006    function each(iterator) {
1007      for (var key in this._object) {
1008        var value = this._object[key], pair = [key, value];
1009        pair.key = key;
1010        pair.value = value;
1011        iterator(pair);
1012      }
1013    }
1014  }
1015
1016  function toQueryPair(key, value) {
1017    if (Object.isUndefined(value)) return key;
1018    return key + '=' + encodeURIComponent(String.interpret(value));
1019  }
1020
1021  return {
1022    initialize: function(object) {
1023      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1024    },
1025
1026    _each: each,
1027
1028    set: function(key, value) {
1029      return this._object[key] = value;
1030    },
1031
1032    get: function(key) {
1033      return this._object[key];
1034    },
1035
1036    unset: function(key) {
1037      var value = this._object[key];
1038      delete this._object[key];
1039      return value;
1040    },
1041
1042    toObject: function() {
1043      return Object.clone(this._object);
1044    },
1045
1046    keys: function() {
1047      return this.pluck('key');
1048    },
1049
1050    values: function() {
1051      return this.pluck('value');
1052    },
1053
1054    index: function(value) {
1055      var match = this.detect(function(pair) {
1056        return pair.value === value;
1057      });
1058      return match && match.key;
1059    },
1060
1061    merge: function(object) {
1062      return this.clone().update(object);
1063    },
1064
1065    update: function(object) {
1066      return new Hash(object).inject(this, function(result, pair) {
1067        result.set(pair.key, pair.value);
1068        return result;
1069      });
1070    },
1071
1072    toQueryString: function() {
1073      return this.map(function(pair) {
1074        var key = encodeURIComponent(pair.key), values = pair.value;
1075
1076        if (values && typeof values == 'object') {
1077          if (Object.isArray(values))
1078            return values.map(toQueryPair.curry(key)).join('&');
1079        }
1080        return toQueryPair(key, values);
1081      }).join('&');
1082    },
1083
1084    inspect: function() {
1085      return '#<Hash:{' + this.map(function(pair) {
1086        return pair.map(Object.inspect).join(': ');
1087      }).join(', ') + '}>';
1088    },
1089
1090    toJSON: function() {
1091      return Object.toJSON(this.toObject());
1092    },
1093
1094    clone: function() {
1095      return new Hash(this);
1096    }
1097  }
1098})());
1099
1100Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
1101Hash.from = $H;
1102var ObjectRange = Class.create(Enumerable, {
1103  initialize: function(start, end, exclusive) {
1104    this.start = start;
1105    this.end = end;
1106    this.exclusive = exclusive;
1107  },
1108
1109  _each: function(iterator) {
1110    var value = this.start;
1111    while (this.include(value)) {
1112      iterator(value);
1113      value = value.succ();
1114    }
1115  },
1116
1117  include: function(value) {
1118    if (value < this.start)
1119      return false;
1120    if (this.exclusive)
1121      return value < this.end;
1122    return value <= this.end;
1123  }
1124});
1125
1126var $R = function(start, end, exclusive) {
1127  return new ObjectRange(start, end, exclusive);
1128};
1129
1130var Ajax = {
1131  getTransport: function() {
1132    return Try.these(
1133      function() {return new XMLHttpRequest()},
1134      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1135      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1136    ) || false;
1137  },
1138
1139  activeRequestCount: 0
1140};
1141
1142Ajax.Responders = {
1143  responders: [],
1144
1145  _each: function(iterator) {
1146    this.responders._each(iterator);
1147  },
1148
1149  register: function(responder) {
1150    if (!this.include(responder))
1151      this.responders.push(responder);
1152  },
1153
1154  unregister: function(responder) {
1155    this.responders = this.responders.without(responder);
1156  },
1157
1158  dispatch: function(callback, request, transport, json) {
1159    this.each(function(responder) {
1160      if (Object.isFunction(responder[callback])) {
1161        try {
1162          responder[callback].apply(responder, [request, transport, json]);
1163        } catch (e) { }
1164      }
1165    });
1166  }
1167};
1168
1169Object.extend(Ajax.Responders, Enumerable);
1170
1171Ajax.Responders.register({
1172  onCreate:   function() { Ajax.activeRequestCount++ },
1173  onComplete: function() { Ajax.activeRequestCount-- }
1174});
1175
1176Ajax.Base = Class.create({
1177  initialize: function(options) {
1178    this.options = {
1179      method:       'post',
1180      asynchronous: true,
1181      contentType:  'application/x-www-form-urlencoded',
1182      encoding:     'UTF-8',
1183      parameters:   '',
1184      evalJSON:     true,
1185      evalJS:       true
1186    };
1187    Object.extend(this.options, options || { });
1188
1189    this.options.method = this.options.method.toLowerCase();
1190    if (Object.isString(this.options.parameters))
1191      this.options.parameters = this.options.parameters.toQueryParams();
1192  }
1193});
1194
1195Ajax.Request = Class.create(Ajax.Base, {
1196  _complete: false,
1197
1198  initialize: function($super, url, options) {
1199    $super(options);
1200    this.transport = Ajax.getTransport();
1201    this.request(url);
1202  },
1203
1204  request: function(url) {
1205    this.url = url;
1206    this.method = this.options.method;
1207    var params = Object.clone(this.options.parameters);
1208
1209    if (!['get', 'post'].include(this.method)) {
1210      // simulate other verbs over post
1211      params['_method'] = this.method;
1212      this.method = 'post';
1213    }
1214
1215    this.parameters = params;
1216
1217    if (params = Object.toQueryString(params)) {
1218      // when GET, append parameters to URL
1219      if (this.method == 'get')
1220        this.url += (this.url.include('?') ? '&' : '?') + params;
1221      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1222        params += '&_=';
1223    }
1224
1225    try {
1226      var response = new Ajax.Response(this);
1227      if (this.options.onCreate) this.options.onCreate(response);
1228      Ajax.Responders.dispatch('onCreate', this, response);
1229
1230      this.transport.open(this.method.toUpperCase(), this.url,
1231        this.options.asynchronous);
1232
1233      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1234
1235      this.transport.onreadystatechange = this.onStateChange.bind(this);
1236      this.setRequestHeaders();
1237
1238      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1239      this.transport.send(this.body);
1240
1241      /* Force Firefox to handle ready state 4 for synchronous requests */
1242      if (!this.options.asynchronous && this.transport.overrideMimeType)
1243        this.onStateChange();
1244
1245    }
1246    catch (e) {
1247      this.dispatchException(e);
1248    }
1249  },
1250
1251  onStateChange: function() {
1252    var readyState = this.transport.readyState;
1253    if (readyState > 1 && !((readyState == 4) && this._complete))
1254      this.respondToReadyState(this.transport.readyState);
1255  },
1256
1257  setRequestHeaders: function() {
1258    var headers = {
1259      'X-Requested-With': 'XMLHttpRequest',
1260      'X-Prototype-Version': Prototype.Version,
1261      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1262    };
1263
1264    if (this.method == 'post') {
1265      headers['Content-type'] = this.options.contentType +
1266        (this.options.encoding ? '; charset=' + this.options.encoding : '');
1267
1268      /* Force "Connection: close" for older Mozilla browsers to work
1269       * around a bug where XMLHttpRequest sends an incorrect
1270       * Content-length header. See Mozilla Bugzilla #246651.
1271       */
1272      if (this.transport.overrideMimeType &&
1273          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1274            headers['Connection'] = 'close';
1275    }
1276
1277    // user-defined headers
1278    if (typeof this.options.requestHeaders == 'object') {
1279      var extras = this.options.requestHeaders;
1280
1281      if (Object.isFunction(extras.push))
1282        for (var i = 0, length = extras.length; i < length; i += 2)
1283          headers[extras[i]] = extras[i+1];
1284      else
1285        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1286    }
1287
1288    for (var name in headers)
1289      this.transport.setRequestHeader(name, headers[name]);
1290  },
1291
1292  success: function() {
1293    var status = this.getStatus();
1294    return !status || (status >= 200 && status < 300);
1295  },
1296
1297  getStatus: function() {
1298    try {
1299      return this.transport.status || 0;
1300    } catch (e) { return 0 }
1301  },
1302
1303  respondToReadyState: function(readyState) {
1304    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1305
1306    if (state == 'Complete') {
1307      try {
1308        this._complete = true;
1309        (this.options['on' + response.status]
1310         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1311         || Prototype.emptyFunction)(response, response.headerJSON);
1312      } catch (e) {
1313        this.dispatchException(e);
1314      }
1315
1316      var contentType = response.getHeader('Content-type');
1317      if (this.options.evalJS == 'force'
1318          || (this.options.evalJS && contentType
1319          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1320        this.evalResponse();
1321    }
1322
1323    try {
1324      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1325      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1326    } catch (e) {
1327      this.dispatchException(e);
1328    }
1329
1330    if (state == 'Complete') {
1331      // avoid memory leak in MSIE: clean up
1332      this.transport.onreadystatechange = Prototype.emptyFunction;
1333    }
1334  },
1335
1336  getHeader: function(name) {
1337    try {
1338      return this.transport.getResponseHeader(name);
1339    } catch (e) { return null }
1340  },
1341
1342  evalResponse: function() {
1343    try {
1344      return eval((this.transport.responseText || '').unfilterJSON());
1345    } catch (e) {
1346      this.dispatchException(e);
1347    }
1348  },
1349
1350  dispatchException: function(exception) {
1351    (this.options.onException || Prototype.emptyFunction)(this, exception);
1352    Ajax.Responders.dispatch('onException', this, exception);
1353  }
1354});
1355
1356Ajax.Request.Events =
1357  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1358
1359Ajax.Response = Class.create({
1360  initialize: function(request){
1361    this.request = request;
1362    var transport  = this.transport  = request.transport,
1363        readyState = this.readyState = transport.readyState;
1364
1365    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1366      this.status       = this.getStatus();
1367      this.statusText   = this.getStatusText();
1368      this.responseText = String.interpret(transport.responseText);
1369      this.headerJSON   = this._getHeaderJSON();
1370    }
1371
1372    if(readyState == 4) {
1373      var xml = transport.responseXML;
1374      this.responseXML  = xml === undefined ? null : xml;
1375      this.responseJSON = this._getResponseJSON();
1376    }
1377  },
1378
1379  status:      0,
1380  statusText: '',
1381
1382  getStatus: Ajax.Request.prototype.getStatus,
1383
1384  getStatusText: function() {
1385    try {
1386      return this.transport.statusText || '';
1387    } catch (e) { return '' }
1388  },
1389
1390  getHeader: Ajax.Request.prototype.getHeader,
1391
1392  getAllHeaders: function() {
1393    try {
1394      return this.getAllResponseHeaders();
1395    } catch (e) { return null }
1396  },
1397
1398  getResponseHeader: function(name) {
1399    return this.transport.getResponseHeader(name);
1400  },
1401
1402  getAllResponseHeaders: function() {
1403    return this.transport.getAllResponseHeaders();
1404  },
1405
1406  _getHeaderJSON: function() {
1407    var json = this.getHeader('X-JSON');
1408    if (!json) return null;
1409    json = decodeURIComponent(escape(json));
1410    try {
1411      return json.evalJSON(this.request.options.sanitizeJSON);
1412    } catch (e) {
1413      this.request.dispatchException(e);
1414    }
1415  },
1416
1417  _getResponseJSON: function() {
1418    var options = this.request.options;
1419    if (!options.evalJSON || (options.evalJSON != 'force' &&
1420      !(this.getHeader('Content-type') || '').include('application/json')))
1421        return null;
1422    try {
1423      return this.transport.responseText.evalJSON(options.sanitizeJSON);
1424    } catch (e) {
1425      this.request.dispatchException(e);
1426    }
1427  }
1428});
1429
1430Ajax.Updater = Class.create(Ajax.Request, {
1431  initialize: function($super, container, url, options) {
1432    this.container = {
1433      success: (container.success || container),
1434      failure: (container.failure || (container.success ? null : container))
1435    };
1436
1437    options = options || { };
1438    var onComplete = options.onComplete;
1439    options.onComplete = (function(response, param) {
1440      this.updateContent(response.responseText);
1441      if (Object.isFunction(onComplete)) onComplete(response, param);
1442    }).bind(this);
1443
1444    $super(url, options);
1445  },
1446
1447  updateContent: function(responseText) {
1448    var receiver = this.container[this.success() ? 'success' : 'failure'],
1449        options = this.options;
1450
1451    if (!options.evalScripts) responseText = responseText.stripScripts();
1452
1453    if (receiver = $(receiver)) {
1454      if (options.insertion) {
1455        if (Object.isString(options.insertion)) {
1456          var insertion = { }; insertion[options.insertion] = responseText;
1457          receiver.insert(insertion);
1458        }
1459        else options.insertion(receiver, responseText);
1460      }
1461      else receiver.update(responseText);
1462    }
1463
1464    if (this.success()) {
1465      if (this.onComplete) this.onComplete.bind(this).defer();
1466    }
1467  }
1468});
1469
1470Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1471  initialize: function($super, container, url, options) {
1472    $super(options);
1473    this.onComplete = this.options.onComplete;
1474
1475    this.frequency = (this.options.frequency || 2);
1476    this.decay = (this.options.decay || 1);
1477
1478    this.updater = { };
1479    this.container = container;
1480    this.url = url;
1481
1482    this.start();
1483  },
1484
1485  start: function() {
1486    this.options.onComplete = this.updateComplete.bind(this);
1487    this.onTimerEvent();
1488  },
1489
1490  stop: function() {
1491    this.updater.options.onComplete = undefined;
1492    clearTimeout(this.timer);
1493    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1494  },
1495
1496  updateComplete: function(response) {
1497    if (this.options.decay) {
1498      this.decay = (response.responseText == this.lastText ?
1499        this.decay * this.options.decay : 1);
1500
1501      this.lastText = response.responseText;
1502    }
1503    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
1504  },
1505
1506  onTimerEvent: function() {
1507    this.updater = new Ajax.Updater(this.container, this.url, this.options);
1508  }
1509});
1510function $(element) {
1511  if (arguments.length > 1) {
1512    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1513      elements.push($(arguments[i]));
1514    return elements;
1515  }
1516  if (Object.isString(element))
1517    element = document.getElementById(element);
1518  return Element.extend(element);
1519}
1520
1521if (Prototype.BrowserFeatures.XPath) {
1522  document._getElementsByXPath = function(expression, parentElement) {
1523    var results = [];
1524    var query = document.evaluate(expression, $(parentElement) || document,
1525      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1526    for (var i = 0, length = query.snapshotLength; i < length; i++)
1527      results.push(Element.extend(query.snapshotItem(i)));
1528    return results;
1529  };
1530}
1531
1532/*--------------------------------------------------------------------------*/
1533
1534if (!window.Node) var Node = { };
1535
1536if (!Node.ELEMENT_NODE) {
1537  // DOM level 2 ECMAScript Language Binding
1538  Object.extend(Node, {
1539    ELEMENT_NODE: 1,
1540    ATTRIBUTE_NODE: 2,
1541    TEXT_NODE: 3,
1542    CDATA_SECTION_NODE: 4,
1543    ENTITY_REFERENCE_NODE: 5,
1544    ENTITY_NODE: 6,
1545    PROCESSING_INSTRUCTION_NODE: 7,
1546    COMMENT_NODE: 8,
1547    DOCUMENT_NODE: 9,
1548    DOCUMENT_TYPE_NODE: 10,
1549    DOCUMENT_FRAGMENT_NODE: 11,
1550    NOTATION_NODE: 12
1551  });
1552}
1553
1554(function() {
1555  var element = this.Element;
1556  this.Element = function(tagName, attributes) {
1557    attributes = attributes || { };
1558    tagName = tagName.toLowerCase();
1559    var cache = Element.cache;
1560    if (Prototype.Browser.IE && attributes.name) {
1561      tagName = '<' + tagName + ' name="' + attributes.name + '">';
1562      delete attributes.name;
1563      return Element.writeAttribute(document.createElement(tagName), attributes);
1564    }
1565    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
1566    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
1567  };
1568  Object.extend(this.Element, element || { });
1569}).call(window);
1570
1571Element.cache = { };
1572
1573Element.Methods = {
1574  visible: function(element) {
1575    return $(element).style.display != 'none';
1576  },
1577
1578  toggle: function(element) {
1579    element = $(element);
1580    Element[Element.visible(element) ? 'hide' : 'show'](element);
1581    return element;
1582  },
1583
1584  hide: function(element) {
1585    $(element).style.display = 'none';
1586    return element;
1587  },
1588
1589  show: function(element) {
1590    $(element).style.display = '';
1591    return element;
1592  },
1593
1594  remove: function(element) {
1595    element = $(element);
1596    element.parentNode.removeChild(element);
1597    return element;
1598  },
1599
1600  update: function(element, content) {
1601    element = $(element);
1602    if (content && content.toElement) content = content.toElement();
1603    if (Object.isElement(content)) return element.update().insert(content);
1604    content = Object.toHTML(content);
1605    element.innerHTML = content.stripScripts();
1606    content.evalScripts.bind(content).defer();
1607    return element;
1608  },
1609
1610  replace: function(element, content) {
1611    element = $(element);
1612    if (content && content.toElement) content = content.toElement();
1613    else if (!Object.isElement(content)) {
1614      content = Object.toHTML(content);
1615      var range = element.ownerDocument.createRange();
1616      range.selectNode(element);
1617      content.evalScripts.bind(content).defer();
1618      content = range.createContextualFragment(content.stripScripts());
1619    }
1620    element.parentNode.replaceChild(content, element);
1621    return element;
1622  },
1623
1624  insert: function(element, insertions) {
1625    element = $(element);
1626
1627    if (Object.isString(insertions) || Object.isNumber(insertions) ||
1628        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
1629          insertions = {bottom:insertions};
1630
1631    var content, t, range;
1632
1633    for (position in insertions) {
1634      content  = insertions[position];
1635      position = position.toLowerCase();
1636      t = Element._insertionTranslations[position];
1637
1638      if (content && content.toElement) content = content.toElement();
1639      if (Object.isElement(content)) {
1640        t.insert(element, content);
1641        continue;
1642      }
1643
1644      content = Object.toHTML(content);
1645
1646      range = element.ownerDocument.createRange();
1647      t.initializeRange(element, range);
1648      t.insert(element, range.createContextualFragment(content.stripScripts()));
1649
1650      content.evalScripts.bind(content).defer();
1651    }
1652
1653    return element;
1654  },
1655
1656  wrap: function(element, wrapper, attributes) {
1657    element = $(element);
1658    if (Object.isElement(wrapper))
1659      $(wrapper).writeAttribute(attributes || { });
1660    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
1661    else wrapper = new Element('div', wrapper);
1662    if (element.parentNode)
1663      element.parentNode.replaceChild(wrapper, element);
1664    wrapper.appendChild(element);
1665    return wrapper;
1666  },
1667
1668  inspect: function(element) {
1669    element = $(element);
1670    var result = '<' + element.tagName.toLowerCase();
1671    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1672      var property = pair.first(), attribute = pair.last();
1673      var value = (element[property] || '').toString();
1674      if (value) result += ' ' + attribute + '=' + value.inspect(true);
1675    });
1676    return result + '>';
1677  },
1678
1679  recursivelyCollect: function(element, property) {
1680    element = $(element);
1681    var elements = [];
1682    while (element = element[property])
1683      if (element.nodeType == 1)
1684        elements.push(Element.extend(element));
1685    return elements;
1686  },
1687
1688  ancestors: function(element) {
1689    return $(element).recursivelyCollect('parentNode');
1690  },
1691
1692  descendants: function(element) {
1693    return $A($(element).getElementsByTagName('*')).each(Element.extend);
1694  },
1695
1696  firstDescendant: function(element) {
1697    element = $(element).firstChild;
1698    while (element && element.nodeType != 1) element = element.nextSibling;
1699    return $(element);
1700  },
1701
1702  immediateDescendants: function(element) {
1703    if (!(element = $(element).firstChild)) return [];
1704    while (element && element.nodeType != 1) element = element.nextSibling;
1705    if (element) return [element].concat($(element).nextSiblings());
1706    return [];
1707  },
1708
1709  previousSiblings: function(element) {
1710    return $(element).recursivelyCollect('previousSibling');
1711  },
1712
1713  nextSiblings: function(element) {
1714    return $(element).recursivelyCollect('nextSibling');
1715  },
1716
1717  siblings: function(element) {
1718    element = $(element);
1719    return element.previousSiblings().reverse().concat(element.nextSiblings());
1720  },
1721
1722  match: function(element, selector) {
1723    if (Object.isString(selector))
1724      selector = new Selector(selector);
1725    return selector.match($(element));
1726  },
1727
1728  up: function(element, expression, index) {
1729    element = $(element);
1730    if (arguments.length == 1) return $(element.parentNode);
1731    var ancestors = element.ancestors();
1732    return expression ? Selector.findElement(ancestors, expression, index) :
1733      ancestors[index || 0];
1734  },
1735
1736  down: function(element, expression, index) {
1737    element = $(element);
1738    if (arguments.length == 1) return element.firstDescendant();
1739    var descendants = element.descendants();
1740    return expression ? Selector.findElement(descendants, expression, index) :
1741      descendants[index || 0];
1742  },
1743
1744  previous: function(element, expression, index) {
1745    element = $(element);
1746    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
1747    var previousSiblings = element.previousSiblings();
1748    return expression ? Selector.findElement(previousSiblings, expression, index) :
1749      previousSiblings[index || 0];
1750  },
1751
1752  next: function(element, expression, index) {
1753    element = $(element);
1754    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
1755    var nextSiblings = element.nextSiblings();
1756    return expression ? Selector.findElement(nextSiblings, expression, index) :
1757      nextSiblings[index || 0];
1758  },
1759
1760  select: function() {
1761    var args = $A(arguments), element = $(args.shift());
1762    return Selector.findChildElements(element, args);
1763  },
1764
1765  adjacent: function() {
1766    var args = $A(arguments), element = $(args.shift());
1767    return Selector.findChildElements(element.parentNode, args).without(element);
1768  },
1769
1770  identify: function(element) {
1771    element = $(element);
1772    var id = element.readAttribute('id'), self = arguments.callee;
1773    if (id) return id;
1774    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
1775    element.writeAttribute('id', id);
1776    return id;
1777  },
1778
1779  readAttribute: function(element, name) {
1780    element = $(element);
1781    if (Prototype.Browser.IE) {
1782      var t = Element._attributeTranslations.read;
1783      if (t.values[name]) return t.values[name](element, name);
1784      if (t.names[name]) name = t.names[name];
1785      if (name.include(':')) {
1786        return (!element.attributes || !element.attributes[name]) ? null :
1787         element.attributes[name].value;
1788      }
1789    }
1790    return element.getAttribute(name);
1791  },
1792
1793  writeAttribute: function(element, name, value) {
1794    element = $(element);
1795    var attributes = { }, t = Element._attributeTranslations.write;
1796
1797    if (typeof name == 'object') attributes = name;
1798    else attributes[name] = value === undefined ? true : value;
1799
1800    for (var attr in attributes) {
1801      var name = t.names[attr] || attr, value = attributes[attr];
1802      if (t.values[attr]) name = t.values[attr](element, value);
1803      if (value === false || value === null)
1804        element.removeAttribute(name);
1805      else if (value === true)
1806        element.setAttribute(name, name);
1807      else element.setAttribute(name, value);
1808    }
1809    return element;
1810  },
1811
1812  getHeight: function(element) {
1813    return $(element).getDimensions().height;
1814  },
1815
1816  getWidth: function(element) {
1817    return $(element).getDimensions().width;
1818  },
1819
1820  classNames: function(element) {
1821    return new Element.ClassNames(element);
1822  },
1823
1824  hasClassName: function(element, className) {
1825    if (!(element = $(element))) return;
1826    var elementClassName = element.className;
1827    return (elementClassName.length > 0 && (elementClassName == className ||
1828      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
1829  },
1830
1831  addClassName: function(element, className) {
1832    if (!(element = $(element))) return;
1833    if (!element.hasClassName(className))
1834      element.className += (element.className ? ' ' : '') + className;
1835    return element;
1836  },
1837
1838  removeClassName: function(element, className) {
1839    if (!(element = $(element))) return;
1840    element.className = element.className.replace(
1841      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
1842    return element;
1843  },
1844
1845  toggleClassName: function(element, className) {
1846    if (!(element = $(element))) return;
1847    return element[element.hasClassName(className) ?
1848      'removeClassName' : 'addClassName'](className);
1849  },
1850
1851  // removes whitespace-only text node children
1852  cleanWhitespace: function(element) {
1853    element = $(element);
1854    var node = element.firstChild;
1855    while (node) {
1856      var nextNode = node.nextSibling;
1857      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1858        element.removeChild(node);
1859      node = nextNode;
1860    }
1861    return element;
1862  },
1863
1864  empty: function(element) {
1865    return $(element).innerHTML.blank();
1866  },
1867
1868  descendantOf: function(element, ancestor) {
1869    element = $(element), ancestor = $(ancestor);
1870
1871    if (element.compareDocumentPosition)
1872      return (element.compareDocumentPosition(ancestor) & 8) === 8;
1873
1874    if (element.sourceIndex && !Prototype.Browser.Opera) {
1875      var e = element.sourceIndex, a = ancestor.sourceIndex,
1876       nextAncestor = ancestor.nextSibling;
1877      if (!nextAncestor) {
1878        do { ancestor = ancestor.parentNode; }
1879        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
1880      }
1881      if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex);
1882    }
1883
1884    while (element = element.parentNode)
1885      if (element == ancestor) return true;
1886    return false;
1887  },
1888
1889  scrollTo: function(element) {
1890    element = $(element);
1891    var pos = element.cumulativeOffset();
1892    window.scrollTo(pos[0], pos[1]);
1893    return element;
1894  },
1895
1896  getStyle: function(element, style) {
1897    element = $(element);
1898    style = style == 'float' ? 'cssFloat' : style.camelize();
1899    var value = element.style[style];
1900    if (!value) {
1901      var css = document.defaultView.getComputedStyle(element, null);
1902      value = css ? css[style] : null;
1903    }
1904    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
1905    return value == 'auto' ? null : value;
1906  },
1907
1908  getOpacity: function(element) {
1909    return $(element).getStyle('opacity');
1910  },
1911
1912  setStyle: function(element, styles) {
1913    element = $(element);
1914    var elementStyle = element.style, match;
1915    if (Object.isString(styles)) {
1916      element.style.cssText += ';' + styles;
1917      return styles.include('opacity') ?
1918        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
1919    }
1920    for (var property in styles)
1921      if (property == 'opacity') element.setOpacity(styles[property]);
1922      else
1923        elementStyle[(property == 'float' || property == 'cssFloat') ?
1924          (elementStyle.styleFloat === undefined ? 'cssFloat' : 'styleFloat') :
1925            property] = styles[property];
1926
1927    return element;
1928  },
1929
1930  setOpacity: function(element, value) {
1931    element = $(element);
1932    element.style.opacity = (value == 1 || value === '') ? '' :
1933      (value < 0.00001) ? 0 : value;
1934    return element;
1935  },
1936
1937  getDimensions: function(element) {
1938    element = $(element);
1939    var display = $(element).getStyle('display');
1940    if (display != 'none' && display != null) // Safari bug
1941      return {width: element.offsetWidth, height: element.offsetHeight};
1942
1943    // All *Width and *Height properties give 0 on elements with display none,
1944    // so enable the element temporarily
1945    var els = element.style;
1946    var originalVisibility = els.visibility;
1947    var originalPosition = els.position;
1948    var originalDisplay = els.display;
1949    els.visibility = 'hidden';
1950    els.position = 'absolute';
1951    els.display = 'block';
1952    var originalWidth = element.clientWidth;
1953    var originalHeight = element.clientHeight;
1954    els.display = originalDisplay;
1955    els.position = originalPosition;
1956    els.visibility = originalVisibility;
1957    return {width: originalWidth, height: originalHeight};
1958  },
1959
1960  makePositioned: function(element) {
1961    element = $(element);
1962    var pos = Element.getStyle(element, 'position');
1963    if (pos == 'static' || !pos) {
1964      element._madePositioned = true;
1965      element.style.position = 'relative';
1966      // Opera returns the offset relative to the positioning context, when an
1967      // element is position relative but top and left have not been defined
1968      if (window.opera) {
1969        element.style.top = 0;
1970        element.style.left = 0;
1971      }
1972    }
1973    return element;
1974  },
1975
1976  undoPositioned: function(element) {
1977    element = $(element);
1978    if (element._madePositioned) {
1979      element._madePositioned = undefined;
1980      element.style.position =
1981        element.style.top =
1982        element.style.left =
1983        element.style.bottom =
1984        element.style.right = '';
1985    }
1986    return element;
1987  },
1988
1989  makeClipping: function(element) {
1990    element = $(element);
1991    if (element._overflow) return element;
1992    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
1993    if (element._overflow !== 'hidden')
1994      element.style.overflow = 'hidden';
1995    return element;
1996  },
1997
1998  undoClipping: function(element) {
1999    element = $(element);
2000    if (!element._overflow) return element;
2001    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
2002    element._overflow = null;
2003    return element;
2004  },
2005
2006  cumulativeOffset: function(element) {
2007    var valueT = 0, valueL = 0;
2008    do {
2009      valueT += element.offsetTop  || 0;
2010      valueL += element.offsetLeft || 0;
2011      element = element.offsetParent;
2012    } while (element);
2013    return Element._returnOffset(valueL, valueT);
2014  },
2015
2016  positionedOffset: function(element) {
2017    var valueT = 0, valueL = 0;
2018    do {
2019      valueT += element.offsetTop  || 0;
2020      valueL += element.offsetLeft || 0;
2021      element = element.offsetParent;
2022      if (element) {
2023        if (element.tagName == 'BODY') break;
2024        var p = Element.getStyle(element, 'position');
2025        if (p == 'relative' || p == 'absolute') break;
2026      }
2027    } while (element);
2028    return Element._returnOffset(valueL, valueT);
2029  },
2030
2031  absolutize: function(element) {
2032    element = $(element);
2033    if (element.getStyle('position') == 'absolute') return;
2034    // Position.prepare(); // To be done manually by Scripty when it needs it.
2035
2036    var offsets = element.positionedOffset();
2037    var top     = offsets[1];
2038    var left    = offsets[0];
2039    var width   = element.clientWidth;
2040    var height  = element.clientHeight;
2041
2042    element._originalLeft   = left - parseFloat(element.style.left  || 0);
2043    element._originalTop    = top  - parseFloat(element.style.top || 0);
2044    element._originalWidth  = element.style.width;
2045    element._originalHeight = element.style.height;
2046
2047    element.style.position = 'absolute';
2048    element.style.top    = top + 'px';
2049    element.style.left   = left + 'px';
2050    element.style.width  = width + 'px';
2051    element.style.height = height + 'px';
2052    return element;
2053  },
2054
2055  relativize: function(element) {
2056    element = $(element);
2057    if (element.getStyle('position') == 'relative') return;
2058    // Position.prepare(); // To be done manually by Scripty when it needs it.
2059
2060    element.style.position = 'relative';
2061    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
2062    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
2063
2064    element.style.top    = top + 'px';
2065    element.style.left   = left + 'px';
2066    element.style.height = element._originalHeight;
2067    element.style.width  = element._originalWidth;
2068    return element;
2069  },
2070
2071  cumulativeScrollOffset: function(element) {
2072    var valueT = 0, valueL = 0;
2073    do {
2074      valueT += element.scrollTop  || 0;
2075      valueL += element.scrollLeft || 0;
2076      element = element.parentNode;
2077    } while (element);
2078    return Element._returnOffset(valueL, valueT);
2079  },
2080
2081  getOffsetParent: function(element) {
2082    if (element.offsetParent) return $(element.offsetParent);
2083    if (element == document.body) return $(element);
2084
2085    while ((element = element.parentNode) && element != document.body)
2086      if (Element.getStyle(element, 'position') != 'static')
2087        return $(element);
2088
2089    return $(document.body);
2090  },
2091
2092  viewportOffset: function(forElement) {
2093    var valueT = 0, valueL = 0;
2094
2095    var element = forElement;
2096    do {
2097      valueT += element.offsetTop  || 0;
2098      valueL += element.offsetLeft || 0;
2099
2100      // Safari fix
2101      if (element.offsetParent == document.body &&
2102        Element.getStyle(element, 'position') == 'absolute') break;
2103
2104    } while (element = element.offsetParent);
2105
2106    element = forElement;
2107    do {
2108      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
2109        valueT -= element.scrollTop  || 0;
2110        valueL -= element.scrollLeft || 0;
2111      }
2112    } while (element = element.parentNode);
2113
2114    return Element._returnOffset(valueL, valueT);
2115  },
2116
2117  clonePosition: function(element, source) {
2118    var options = Object.extend({
2119      setLeft:    true,
2120      setTop:     true,
2121      setWidth:   true,
2122      setHeight:  true,
2123      offsetTop:  0,
2124      offsetLeft: 0
2125    }, arguments[2] || { });
2126
2127    // find page position of source
2128    source = $(source);
2129    var p = source.viewportOffset();
2130
2131    // find coordinate system to use
2132    element = $(element);
2133    var delta = [0, 0];
2134    var parent = null;
2135    // delta [0,0] will do fine with position: fixed elements,
2136    // position:absolute needs offsetParent deltas
2137    if (Element.getStyle(element, 'position') == 'absolute') {
2138      parent = element.getOffsetParent();
2139      delta = parent.viewportOffset();
2140    }
2141
2142    // correct by body offsets (fixes Safari)
2143    if (parent == document.body) {
2144      delta[0] -= document.body.offsetLeft;
2145      delta[1] -= document.body.offsetTop;
2146    }
2147
2148    // set position
2149    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
2150    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
2151    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
2152    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
2153    return element;
2154  }
2155};
2156
2157Element.Methods.identify.counter = 1;
2158
2159Object.extend(Element.Methods, {
2160  getElementsBySelector: Element.Methods.select,
2161  childElements: Element.Methods.immediateDescendants
2162});
2163
2164Element._attributeTranslations = {
2165  write: {
2166    names: {
2167      className: 'class',
2168      htmlFor:   'for'
2169    },
2170    values: { }
2171  }
2172};
2173
2174
2175if (!document.createRange || Prototype.Browser.Opera) {
2176  Element.Methods.insert = function(element, insertions) {
2177    element = $(element);
2178
2179    if (Object.isString(insertions) || Object.isNumber(insertions) ||
2180        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
2181          insertions = { bottom: insertions };
2182
2183    var t = Element._insertionTranslations, content, position, pos, tagName;
2184
2185    for (position in insertions) {
2186      content  = insertions[position];
2187      position = position.toLowerCase();
2188      pos      = t[position];
2189
2190      if (content && content.toElement) content = content.toElement();
2191      if (Object.isElement(content)) {
2192        pos.insert(element, content);
2193        continue;
2194      }
2195
2196      content = Object.toHTML(content);
2197      tagName = ((position == 'before' || position == 'after')
2198        ? element.parentNode : element).tagName.toUpperCase();
2199
2200      if (t.tags[tagName]) {
2201        var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2202        if (position == 'top' || position == 'after') fragments.reverse();
2203        fragments.each(pos.insert.curry(element));
2204      }
2205      else element.insertAdjacentHTML(pos.adjacency, content.stripScripts());
2206
2207      content.evalScripts.bind(content).defer();
2208    }
2209
2210    return element;
2211  };
2212}
2213
2214if (Prototype.Browser.Opera) {
2215  Element.Methods._getStyle = Element.Methods.getStyle;
2216  Element.Methods.getStyle = function(element, style) {
2217    switch(style) {
2218      case 'left':
2219      case 'top':
2220      case 'right':
2221      case 'bottom':
2222        if (Element._getStyle(element, 'position') == 'static') return null;
2223      default: return Element._getStyle(element, style);
2224    }
2225  };
2226  Element.Methods._readAttribute = Element.Methods.readAttribute;
2227  Element.Methods.readAttribute = function(element, attribute) {
2228    if (attribute == 'title') return element.title;
2229    return Element._readAttribute(element, attribute);
2230  };
2231}
2232
2233else if (Prototype.Browser.IE) {
2234  $w('positionedOffset getOffsetParent viewportOffset').each(function(method) {
2235    Element.Methods[method] = Element.Methods[method].wrap(
2236      function(proceed, element) {
2237        element = $(element);
2238        var position = element.getStyle('position');
2239        if (position != 'static') return proceed(element);
2240        element.setStyle({ position: 'relative' });
2241        var value = proceed(element);
2242        element.setStyle({ position: position });
2243        return value;
2244      }
2245    );
2246  });
2247
2248  Element.Methods.getStyle = function(element, style) {
2249    element = $(element);
2250    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
2251    var value = element.style[style];
2252    if (!value && element.currentStyle) value = element.currentStyle[style];
2253
2254    if (style == 'opacity') {
2255      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
2256        if (value[1]) return parseFloat(value[1]) / 100;
2257      return 1.0;
2258    }
2259
2260    if (value == 'auto') {
2261      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
2262        return element['offset' + style.capitalize()] + 'px';
2263      return null;
2264    }
2265    return value;
2266  };
2267
2268  Element.Methods.setOpacity = function(element, value) {
2269    function stripAlpha(filter){
2270      return filter.replace(/alpha\([^\)]*\)/gi,'');
2271    }
2272    element = $(element);
2273    var currentStyle = element.currentStyle;
2274    if ((currentStyle && !currentStyle.hasLayout) ||
2275      (!currentStyle && element.style.zoom == 'normal'))
2276        element.style.zoom = 1;
2277
2278    var filter = element.getStyle('filter'), style = element.style;
2279    if (value == 1 || value === '') {
2280      (filter = stripAlpha(filter)) ?
2281        style.filter = filter : style.removeAttribute('filter');
2282      return element;
2283    } else if (value < 0.00001) value = 0;
2284    style.filter = stripAlpha(filter) +
2285      'alpha(opacity=' + (value * 100) + ')';
2286    return element;
2287  };
2288
2289  Element._attributeTranslations = {
2290    read: {
2291      names: {
2292        'class': 'className',
2293        'for':   'htmlFor'
2294      },
2295      values: {
2296        _getAttr: function(element, attribute) {
2297          return element.getAttribute(attribute, 2);
2298        },
2299        _getAttrNode: function(element, attribute) {
2300          var node = element.getAttributeNode(attribute);
2301          return node ? node.value : "";
2302        },
2303        _getEv: function(element, attribute) {
2304          var attribute = element.getAttribute(attribute);
2305          return attribute ? attribute.toString().slice(23, -2) : null;
2306        },
2307        _flag: function(element, attribute) {
2308          return $(element).hasAttribute(attribute) ? attribute : null;
2309        },
2310        style: function(element) {
2311          return element.style.cssText.toLowerCase();
2312        },
2313        title: function(element) {
2314          return element.title;
2315        }
2316      }
2317    }
2318  };
2319
2320  Element._attributeTranslations.write = {
2321    names: Object.clone(Element._attributeTranslations.read.names),
2322    values: {
2323      checked: function(element, value) {
2324        element.checked = !!value;
2325      },
2326
2327      style: function(element, value) {
2328        element.style.cssText = value ? value : '';
2329      }
2330    }
2331  };
2332
2333  Element._attributeTranslations.has = {};
2334
2335  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
2336      'encType maxLength readOnly longDesc').each(function(attr) {
2337    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
2338    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
2339  });
2340
2341  (function(v) {
2342    Object.extend(v, {
2343      href:        v._getAttr,
2344      src:         v._getAttr,
2345      type:        v._getAttr,
2346      action:      v._getAttrNode,
2347      disabled:    v._flag,
2348      checked:     v._flag,
2349      readonly:    v._flag,
2350      multiple:    v._flag,
2351      onload:      v._getEv,
2352      onunload:    v._getEv,
2353      onclick:     v._getEv,
2354      ondblclick:  v._getEv,
2355      onmousedown: v._getEv,
2356      onmouseup:   v._getEv,
2357      onmouseover: v._getEv,
2358      onmousemove: v._getEv,
2359      onmouseout:  v._getEv,
2360      onfocus:     v._getEv,
2361      onblur:      v._getEv,
2362      onkeypress:  v._getEv,
2363      onkeydown:   v._getEv,
2364      onkeyup:     v._getEv,
2365      onsubmit:    v._getEv,
2366      onreset:     v._getEv,
2367      onselect:    v._getEv,
2368      onchange:    v._getEv
2369    });
2370  })(Element._attributeTranslations.read.values);
2371}
2372
2373else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
2374  Element.Methods.setOpacity = function(element, value) {
2375    element = $(element);
2376    element.style.opacity = (value == 1) ? 0.999999 :
2377      (value === '') ? '' : (value < 0.00001) ? 0 : value;
2378    return element;
2379  };
2380}
2381
2382else if (Prototype.Browser.WebKit) {
2383  Element.Methods.setOpacity = function(element, value) {
2384    element = $(element);
2385    element.style.opacity = (value == 1 || value === '') ? '' :
2386      (value < 0.00001) ? 0 : value;
2387
2388    if (value == 1)
2389      if(element.tagName == 'IMG' && element.width) {
2390        element.width++; element.width--;
2391      } else try {
2392        var n = document.createTextNode(' ');
2393        element.appendChild(n);
2394        element.removeChild(n);
2395      } catch (e) { }
2396
2397    return element;
2398  };
2399
2400  // Safari returns margins on body which is incorrect if the child is absolutely
2401  // positioned.  For performance reasons, redefine Position.cumulativeOffset for
2402  // KHTML/WebKit only.
2403  Element.Methods.cumulativeOffset = function(element) {
2404    var valueT = 0, valueL = 0;
2405    do {
2406      valueT += element.offsetTop  || 0;
2407      valueL += element.offsetLeft || 0;
2408      if (element.offsetParent == document.body)
2409        if (Element.getStyle(element, 'position') == 'absolute') break;
2410
2411      element = element.offsetParent;
2412    } while (element);
2413
2414    return Element._returnOffset(valueL, valueT);
2415  };
2416}
2417
2418if (Prototype.Browser.IE || Prototype.Browser.Opera) {
2419  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
2420  Element.Methods.update = function(element, content) {
2421    element = $(element);
2422
2423    if (content && content.toElement) content = content.toElement();
2424    if (Object.isElement(content)) return element.update().insert(content);
2425
2426    content = Object.toHTML(content);
2427    var tagName = element.tagName.toUpperCase();
2428
2429    if (tagName in Element._insertionTranslations.tags) {
2430      $A(element.childNodes).each(function(node) { element.removeChild(node) });
2431      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
2432        .each(function(node) { element.appendChild(node) });
2433    }
2434    else element.innerHTML = content.stripScripts();
2435
2436    content.evalScripts.bind(content).defer();
2437    return element;
2438  };
2439}
2440
2441if (document.createElement('div').outerHTML) {
2442  Element.Methods.replace = function(element, content) {
2443    element = $(element);
2444
2445    if (content && content.toElement) content = content.toElement();
2446    if (Object.isElement(content)) {
2447      element.parentNode.replaceChild(content, element);
2448      return element;
2449    }
2450
2451    content = Object.toHTML(content);
2452    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2453
2454    if (Element._insertionTranslations.tags[tagName]) {
2455      var nextSibling = element.next();
2456      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
2457      parent.removeChild(element);
2458      if (nextSibling)
2459        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
2460      else
2461        fragments.each(function(node) { parent.appendChild(node) });
2462    }
2463    else element.outerHTML = content.stripScripts();
2464
2465    content.evalScripts.bind(content).defer();
2466    return element;
2467  };
2468}
2469
2470Element._returnOffset = function(l, t) {
2471  var result = [l, t];
2472  result.left = l;
2473  result.top = t;
2474  return result;
2475};
2476
2477Element._getContentFromAnonymousElement = function(tagName, html) {
2478  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
2479  div.innerHTML = t[0] + html + t[1];
2480  t[2].times(function() { div = div.firstChild });
2481  return $A(div.childNodes);
2482};
2483
2484Element._insertionTranslations = {
2485  before: {
2486    adjacency: 'beforeBegin',
2487    insert: function(element, node) {
2488      element.parentNode.insertBefore(node, element);
2489    },
2490    initializeRange: function(element, range) {
2491      range.setStartBefore(element);
2492    }
2493  },
2494  top: {
2495    adjacency: 'afterBegin',
2496    insert: function(element, node) {
2497      element.insertBefore(node, element.firstChild);
2498    },
2499    initializeRange: function(element, range) {
2500      range.selectNodeContents(element);
2501      range.collapse(true);
2502    }
2503  },
2504  bottom: {
2505    adjacency: 'beforeEnd',
2506    insert: function(element, node) {
2507      element.appendChild(node);
2508    }
2509  },
2510  after: {
2511    adjacency: 'afterEnd',
2512    insert: function(element, node) {
2513      element.parentNode.insertBefore(node, element.nextSibling);
2514    },
2515    initializeRange: function(element, range) {
2516      range.setStartAfter(element);
2517    }
2518  },
2519  tags: {
2520    TABLE:  ['<table>',                '</table>',                   1],
2521    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
2522    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
2523    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2524    SELECT: ['<select>',               '</select>',                  1]
2525  }
2526};
2527
2528(function() {
2529  this.bottom.initializeRange = this.top.initializeRange;
2530  Object.extend(this.tags, {
2531    THEAD: this.tags.TBODY,
2532    TFOOT: this.tags.TBODY,
2533    TH:    this.tags.TD
2534  });
2535}).call(Element._insertionTranslations);
2536
2537Element.Methods.Simulated = {
2538  hasAttribute: function(element, attribute) {
2539    attribute = Element._attributeTranslations.has[attribute] || attribute;
2540    var node = $(element).getAttributeNode(attribute);
2541    return node && node.specified;
2542  }
2543};
2544
2545Element.Methods.ByTag = { };
2546
2547Object.extend(Element, Element.Methods);
2548
2549if (!Prototype.BrowserFeatures.ElementExtensions &&
2550    document.createElement('div').__proto__) {
2551  window.HTMLElement = { };
2552  window.HTMLElement.prototype = document.createElement('div').__proto__;
2553  Prototype.BrowserFeatures.ElementExtensions = true;
2554}
2555
2556Element.extend = (function() {
2557  if (Prototype.BrowserFeatures.SpecificElementExtensions)
2558    return Prototype.K;
2559
2560  var Methods = { }, ByTag = Element.Methods.ByTag;
2561
2562  var extend = Object.extend(function(element) {
2563    if (!element || element._extendedByPrototype ||
2564        element.nodeType != 1 || element == window) return element;
2565
2566    var methods = Object.clone(Methods),
2567      tagName = element.tagName, property, value;
2568
2569    // extend methods for specific tags
2570    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
2571
2572    for (property in methods) {
2573      value = methods[property];
2574      if (Object.isFunction(value) && !(property in element))
2575        element[property] = value.methodize();
2576    }
2577
2578    element._extendedByPrototype = Prototype.emptyFunction;
2579    return element;
2580
2581  }, {
2582    refresh: function() {
2583      // extend methods for all tags (Safari doesn't need this)
2584      if (!Prototype.BrowserFeatures.ElementExtensions) {
2585        Object.extend(Methods, Element.Methods);
2586        Object.extend(Methods, Element.Methods.Simulated);
2587      }
2588    }
2589  });
2590
2591  extend.refresh();
2592  return extend;
2593})();
2594
2595Element.hasAttribute = function(element, attribute) {
2596  if (element.hasAttribute) return element.hasAttribute(attribute);
2597  return Element.Methods.Simulated.hasAttribute(element, attribute);
2598};
2599
2600Element.addMethods = function(methods) {
2601  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
2602
2603  if (!methods) {
2604    Object.extend(Form, Form.Methods);
2605    Object.extend(Form.Element, Form.Element.Methods);
2606    Object.extend(Element.Methods.ByTag, {
2607      "FORM":     Object.clone(Form.Methods),
2608      "INPUT":    Object.clone(Form.Element.Methods),
2609      "SELECT":   Object.clone(Form.Element.Methods),
2610      "TEXTAREA": Object.clone(Form.Element.Methods)
2611    });
2612  }
2613
2614  if (arguments.length == 2) {
2615    var tagName = methods;
2616    methods = arguments[1];
2617  }
2618
2619  if (!tagName) Object.extend(Element.Methods, methods || { });
2620  else {
2621    if (Object.isArray(tagName)) tagName.each(extend);
2622    else extend(tagName);
2623  }
2624
2625  function extend(tagName) {
2626    tagName = tagName.toUpperCase();
2627    if (!Element.Methods.ByTag[tagName])
2628      Element.Methods.ByTag[tagName] = { };
2629    Object.extend(Element.Methods.ByTag[tagName], methods);
2630  }
2631
2632  function copy(methods, destination, onlyIfAbsent) {
2633    onlyIfAbsent = onlyIfAbsent || false;
2634    for (var property in methods) {
2635      var value = methods[property];
2636      if (!Object.isFunction(value)) continue;
2637      if (!onlyIfAbsent || !(property in destination))
2638        destination[property] = value.methodize();
2639    }
2640  }
2641
2642  function findDOMClass(tagName) {
2643    var klass;
2644    var trans = {
2645      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
2646      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
2647      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
2648      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
2649      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
2650      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
2651      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
2652      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
2653      "FrameSet", "IFRAME": "IFrame"
2654    };
2655    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
2656    if (window[klass]) return window[klass];
2657    klass = 'HTML' + tagName + 'Element';
2658    if (window[klass]) return window[klass];
2659    klass = 'HTML' + tagName.capitalize() + 'Element';
2660    if (window[klass]) return window[klass];
2661
2662    window[klass] = { };
2663    window[klass].prototype = document.createElement(tagName).__proto__;
2664    return window[klass];
2665  }
2666
2667  if (F.ElementExtensions) {
2668    copy(Element.Methods, HTMLElement.prototype);
2669    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
2670  }
2671
2672  if (F.SpecificElementExtensions) {
2673    for (var tag in Element.Methods.ByTag) {
2674      var klass = findDOMClass(tag);
2675      if (Object.isUndefined(klass)) continue;
2676      copy(T[tag], klass.prototype);
2677    }
2678  }
2679
2680  Object.extend(Element, Element.Methods);
2681  delete Element.ByTag;
2682
2683  if (Element.extend.refresh) Element.extend.refresh();
2684  Element.cache = { };
2685};
2686
2687document.viewport = {
2688  getDimensions: function() {
2689    var dimensions = { };
2690    $w('width height').each(function(d) {
2691      var D = d.capitalize();
2692      dimensions[d] = self['inner' + D] ||
2693       (document.documentElement['client' + D] || document.body['client' + D]);
2694    });
2695    return dimensions;
2696  },
2697
2698  getWidth: function() {
2699    return this.getDimensions().width;
2700  },
2701
2702  getHeight: function() {
2703    return this.getDimensions().height;
2704  },
2705
2706  getScrollOffsets: function() {
2707    return Element._returnOffset(
2708      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
2709      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
2710  }
2711};
2712/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
2713 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
2714 * license.  Please see http://www.yui-ext.com/ for more information. */
2715
2716var Selector = Class.create({
2717  initialize: function(expression) {
2718    this.expression = expression.strip();
2719    this.compileMatcher();
2720  },
2721
2722  compileMatcher: function() {
2723    // Selectors with namespaced attributes can't use the XPath version
2724    if (Prototype.BrowserFeatures.XPath && !(/(\[[\w-]*?:|:checked)/).test(this.expression))
2725      return this.compileXPathMatcher();
2726
2727    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
2728        c = Selector.criteria, le, p, m;
2729
2730    if (Selector._cache[e]) {
2731      this.matcher = Selector._cache[e];
2732      return;
2733    }
2734
2735    this.matcher = ["this.matcher = function(root) {",
2736                    "var r = root, h = Selector.handlers, c = false, n;"];
2737
2738    while (e && le != e && (/\S/).test(e)) {
2739      le = e;
2740      for (var i in ps) {
2741        p = ps[i];
2742        if (m = e.match(p)) {
2743          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
2744              new Template(c[i]).evaluate(m));
2745          e = e.replace(m[0], '');
2746          break;
2747        }
2748      }
2749    }
2750
2751    this.matcher.push("return h.unique(n);\n}");
2752    eval(this.matcher.join('\n'));
2753    Selector._cache[this.expression] = this.matcher;
2754  },
2755
2756  compileXPathMatcher: function() {
2757    var e = this.expression, ps = Selector.patterns,
2758        x = Selector.xpath, le, m;
2759
2760    if (Selector._cache[e]) {
2761      this.xpath = Selector._cache[e]; return;
2762    }
2763
2764    this.matcher = ['.//*'];
2765    while (e && le != e && (/\S/).test(e)) {
2766      le = e;
2767      for (var i in ps) {
2768        if (m = e.match(ps[i])) {
2769          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
2770            new Template(x[i]).evaluate(m));
2771          e = e.replace(m[0], '');
2772          break;
2773        }
2774      }
2775    }
2776
2777    this.xpath = this.matcher.join('');
2778    Selector._cache[this.expression] = this.xpath;
2779  },
2780
2781  findElements: function(root) {
2782    root = root || document;
2783    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
2784    return this.matcher(root);
2785  },
2786
2787  match: function(element) {
2788    this.tokens = [];
2789
2790    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
2791    var le, p, m;
2792
2793    while (e && le !== e && (/\S/).test(e)) {
2794      le = e;
2795      for (var i in ps) {
2796        p = ps[i];
2797        if (m = e.match(p)) {
2798          // use the Selector.assertions methods unless the selector
2799          // is too complex.
2800          if (as[i]) {
2801            this.tokens.push([i, Object.clone(m)]);
2802            e = e.replace(m[0], '');
2803          } else {
2804            // reluctantly do a document-wide search
2805            // and look for a match in the array
2806            return this.findElements(document).include(element);
2807          }
2808        }
2809      }
2810    }
2811
2812    var match = true, name, matches;
2813    for (var i = 0, token; token = this.tokens[i]; i++) {
2814      name = token[0], matches = token[1];
2815      if (!Selector.assertions[name](element, matches)) {
2816        match = false; break;
2817      }
2818    }
2819
2820    return match;
2821  },
2822
2823  toString: function() {
2824    return this.expression;
2825  },
2826
2827  inspect: function() {
2828    return "#<Selector:" + this.expression.inspect() + ">";
2829  }
2830});
2831
2832Object.extend(Selector, {
2833  _cache: { },
2834
2835  xpath: {
2836    descendant:   "//*",
2837    child:        "/*",
2838    adjacent:     "/following-sibling::*[1]",
2839    laterSibling: '/following-sibling::*',
2840    tagName:      function(m) {
2841      if (m[1] == '*') return '';
2842      return "[local-name()='" + m[1].toLowerCase() +
2843             "' or local-name()='" + m[1].toUpperCase() + "']";
2844    },
2845    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
2846    id:           "[@id='#{1}']",
2847    attrPresence: "[@#{1}]",
2848    attr: function(m) {
2849      m[3] = m[5] || m[6];
2850      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
2851    },
2852    pseudo: function(m) {
2853      var h = Selector.xpath.pseudos[m[1]];
2854      if (!h) return '';
2855      if (Object.isFunction(h)) return h(m);
2856      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
2857    },
2858    operators: {
2859      '=':  "[@#{1}='#{3}']",
2860      '!=': "[@#{1}!='#{3}']",
2861      '^=': "[starts-with(@#{1}, '#{3}')]",
2862      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
2863      '*=': "[contains(@#{1}, '#{3}')]",
2864      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
2865      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
2866    },
2867    pseudos: {
2868      'first-child': '[not(preceding-sibling::*)]',
2869      'last-child':  '[not(following-sibling::*)]',
2870      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
2871      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
2872      'checked':     "[@checked]",
2873      'disabled':    "[@disabled]",
2874      'enabled':     "[not(@disabled)]",
2875      'not': function(m) {
2876        var e = m[6], p = Selector.patterns,
2877            x = Selector.xpath, le, m, v;
2878
2879        var exclusion = [];
2880        while (e && le != e && (/\S/).test(e)) {
2881          le = e;
2882          for (var i in p) {
2883            if (m = e.match(p[i])) {
2884              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
2885              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
2886              e = e.replace(m[0], '');
2887              break;
2888            }
2889          }
2890        }
2891        return "[not(" + exclusion.join(" and ") + ")]";
2892      },
2893      'nth-child':      function(m) {
2894        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
2895      },
2896      'nth-last-child': function(m) {
2897        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
2898      },
2899      'nth-of-type':    function(m) {
2900        return Selector.xpath.pseudos.nth("position() ", m);
2901      },
2902      'nth-last-of-type': function(m) {
2903        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
2904      },
2905      'first-of-type':  function(m) {
2906        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
2907      },
2908      'last-of-type':   function(m) {
2909        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
2910      },
2911      'only-of-type':   function(m) {
2912        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
2913      },
2914      nth: function(fragment, m) {
2915        var mm, formula = m[6], predicate;
2916        if (formula == 'even') formula = '2n+0';
2917        if (formula == 'odd')  formula = '2n+1';
2918        if (mm = formula.match(/^(\d+)$/)) // digit only
2919          return '[' + fragment + "= " + mm[1] + ']';
2920        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
2921          if (mm[1] == "-") mm[1] = -1;
2922          var a = mm[1] ? Number(mm[1]) : 1;
2923          var b = mm[2] ? Number(mm[2]) : 0;
2924          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
2925          "((#{fragment} - #{b}) div #{a} >= 0)]";
2926          return new Template(predicate).evaluate({
2927            fragment: fragment, a: a, b: b });
2928        }
2929      }
2930    }
2931  },
2932
2933  criteria: {
2934    tagName:      'n = h.tagName(n, r, "#{1}", c);   c = false;',
2935    className:    'n = h.className(n, r, "#{1}", c); c = false;',
2936    id:           'n = h.id(n, r, "#{1}", c);        c = false;',
2937    attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;',
2938    attr: function(m) {
2939      m[3] = (m[5] || m[6]);
2940      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m);
2941    },
2942    pseudo: function(m) {
2943      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
2944      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
2945    },
2946    descendant:   'c = "descendant";',
2947    child:        'c = "child";',
2948    adjacent:     'c = "adjacent";',
2949    laterSibling: 'c = "laterSibling";'
2950  },
2951
2952  patterns: {
2953    // combinators must be listed first
2954    // (and descendant needs to be last combinator)
2955    laterSibling: /^\s*~\s*/,
2956    child:        /^\s*>\s*/,
2957    adjacent:     /^\s*\+\s*/,
2958    descendant:   /^\s/,
2959
2960    // selectors follow
2961    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
2962    id:           /^#([\w\-\*]+)(\b|$)/,
2963    className:    /^\.([\w\-\*]+)(\b|$)/,
2964    pseudo:       /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/,
2965    attrPresence: /^\[([\w]+)\]/,
2966    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
2967  },
2968
2969  // for Selector.match and Element#match
2970  assertions: {
2971    tagName: function(element, matches) {
2972      return matches[1].toUpperCase() == element.tagName.toUpperCase();
2973    },
2974
2975    className: function(element, matches) {
2976      return Element.hasClassName(element, matches[1]);
2977    },
2978
2979    id: function(element, matches) {
2980      return element.id === matches[1];
2981    },
2982
2983    attrPresence: function(element, matches) {
2984      return Element.hasAttribute(element, matches[1]);
2985    },
2986
2987    attr: function(element, matches) {
2988      var nodeValue = Element.readAttribute(element, matches[1]);
2989      return Selector.operators[matches[2]](nodeValue, matches[3]);
2990    }
2991  },
2992
2993  handlers: {
2994    // UTILITY FUNCTIONS
2995    // joins two collections
2996    concat: function(a, b) {
2997      for (var i = 0, node; node = b[i]; i++)
2998        a.push(node);
2999      return a;
3000    },
3001
3002    // marks an array of nodes for counting
3003    mark: function(nodes) {
3004      for (var i = 0, node; node = nodes[i]; i++)
3005        node._counted = true;
3006      return nodes;
3007    },
3008
3009    unmark: function(nodes) {
3010      for (var i = 0, node; node = nodes[i]; i++)
3011        node._counted = undefined;
3012      return nodes;
3013    },
3014
3015    // mark each child node with its position (for nth calls)
3016    // "ofType" flag indicates whether we're indexing for nth-of-type
3017    // rather than nth-child
3018    index: function(parentNode, reverse, ofType) {
3019      parentNode._counted = true;
3020      if (reverse) {
3021        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
3022          var node = nodes[i];
3023          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
3024        }
3025      } else {
3026        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
3027          if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++;
3028      }
3029    },
3030
3031    // filters out duplicates and extends all nodes
3032    unique: function(nodes) {
3033      if (nodes.length == 0) return nodes;
3034      var results = [], n;
3035      for (var i = 0, l = nodes.length; i < l; i++)
3036        if (!(n = nodes[i])._counted) {
3037          n._counted = true;
3038          results.push(Element.extend(n));
3039        }
3040      return Selector.handlers.unmark(results);
3041    },
3042
3043    // COMBINATOR FUNCTIONS
3044    descendant: function(nodes) {
3045      var h = Selector.handlers;
3046      for (var i = 0, results = [], node; node = nodes[i]; i++)
3047        h.concat(results, node.getElementsByTagName('*'));
3048      return results;
3049    },
3050
3051    child: function(nodes) {
3052      var h = Selector.handlers;
3053      for (var i = 0, results = [], node; node = nodes[i]; i++) {
3054        for (var j = 0, children = [], child; child = node.childNodes[j]; j++)
3055          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
3056      }
3057      return results;
3058    },
3059
3060    adjacent: function(nodes) {
3061      for (var i = 0, results = [], node; node = nodes[i]; i++) {
3062        var next = this.nextElementSibling(node);
3063        if (next) results.push(next);
3064      }
3065      return results;
3066    },
3067
3068    laterSibling: function(nodes) {
3069      var h = Selector.handlers;
3070      for (var i = 0, results = [], node; node = nodes[i]; i++)
3071        h.concat(results, Element.nextSiblings(node));
3072      return results;
3073    },
3074
3075    nextElementSibling: function(node) {
3076      while (node = node.nextSibling)
3077              if (node.nodeType == 1) return node;
3078      return null;
3079    },
3080
3081    previousElementSibling: function(node) {
3082      while (node = node.previousSibling)
3083        if (node.nodeType == 1) return node;
3084      return null;
3085    },
3086
3087    // TOKEN FUNCTIONS
3088    tagName: function(nodes, root, tagName, combinator) {
3089      tagName = tagName.toUpperCase();
3090      var results = [], h = Selector.handlers;
3091      if (nodes) {
3092        if (combinator) {
3093          // fastlane for ordinary descendant combinators
3094          if (combinator == "descendant") {
3095            for (var i = 0, node; node = nodes[i]; i++)
3096              h.concat(results, node.getElementsByTagName(tagName));
3097            return results;
3098          } else nodes = this[combinator](nodes);
3099          if (tagName == "*") return nodes;
3100        }
3101        for (var i = 0, node; node = nodes[i]; i++)
3102          if (node.tagName.toUpperCase() == tagName) results.push(node);
3103        return results;
3104      } else return root.getElementsByTagName(tagName);
3105    },
3106
3107    id: function(nodes, root, id, combinator) {
3108      var targetNode = $(id), h = Selector.handlers;
3109      if (!targetNode) return [];
3110      if (!nodes && root == document) return [targetNode];
3111      if (nodes) {
3112        if (combinator) {
3113          if (combinator == 'child') {
3114            for (var i = 0, node; node = nodes[i]; i++)
3115              if (targetNode.parentNode == node) return [targetNode];
3116          } else if (combinator == 'descendant') {
3117            for (var i = 0, node; node = nodes[i]; i++)
3118              if (Element.descendantOf(targetNode, node)) return [targetNode];
3119          } else if (combinator == 'adjacent') {
3120            for (var i = 0, node; node = nodes[i]; i++)
3121              if (Selector.handlers.previousElementSibling(targetNode) == node)
3122                return [targetNode];
3123          } else nodes = h[combinator](nodes);
3124        }
3125        for (var i = 0, node; node = nodes[i]; i++)
3126          if (node == targetNode) return [targetNode];
3127        return [];
3128      }
3129      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
3130    },
3131
3132    className: function(nodes, root, className, combinator) {
3133      if (nodes && combinator) nodes = this[combinator](nodes);
3134      return Selector.handlers.byClassName(nodes, root, className);
3135    },
3136
3137    byClassName: function(nodes, root, className) {
3138      if (!nodes) nodes = Selector.handlers.descendant([root]);
3139      var needle = ' ' + className + ' ';
3140      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
3141        nodeClassName = node.className;
3142        if (nodeClassName.length == 0) continue;
3143        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
3144          results.push(node);
3145      }
3146      return results;
3147    },
3148
3149    attrPresence: function(nodes, root, attr) {
3150      if (!nodes) nodes = root.getElementsByTagName("*");
3151      var results = [];
3152      for (var i = 0, node; node = nodes[i]; i++)
3153        if (Element.hasAttribute(node, attr)) results.push(node);
3154      return results;
3155    },
3156
3157    attr: function(nodes, root, attr, value, operator) {
3158      if (!nodes) nodes = root.getElementsByTagName("*");
3159      var handler = Selector.operators[operator], results = [];
3160      for (var i = 0, node; node = nodes[i]; i++) {
3161        var nodeValue = Element.readAttribute(node, attr);
3162        if (nodeValue === null) continue;
3163        if (handler(nodeValue, value)) results.push(node);
3164      }
3165      return results;
3166    },
3167
3168    pseudo: function(nodes, name, value, root, combinator) {
3169      if (nodes && combinator) nodes = this[combinator](nodes);
3170      if (!nodes) nodes = root.getElementsByTagName("*");
3171      return Selector.pseudos[name](nodes, value, root);
3172    }
3173  },
3174
3175  pseudos: {
3176    'first-child': function(nodes, value, root) {
3177      for (var i = 0, results = [], node; node = nodes[i]; i++) {
3178        if (Selector.handlers.previousElementSibling(node)) continue;
3179          results.push(node);
3180      }
3181      return results;
3182    },
3183    'last-child': function(nodes, value, root) {
3184      for (var i = 0, results = [], node; node = nodes[i]; i++) {
3185        if (Selector.handlers.nextElementSibling(node)) continue;
3186          results.push(node);
3187      }
3188      return results;
3189    },
3190    'only-child': function(nodes, value, root) {
3191      var h = Selector.handlers;
3192      for (var i = 0, results = [], node; node = nodes[i]; i++)
3193        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
3194          results.push(node);
3195      return results;
3196    },
3197    'nth-child':        function(nodes, formula, root) {
3198      return Selector.pseudos.nth(nodes, formula, root);
3199    },
3200    'nth-last-child':   function(nodes, formula, root) {
3201      return Selector.pseudos.nth(nodes, formula, root, true);
3202    },
3203    'nth-of-type':      function(nodes, formula, root) {
3204      return Selector.pseudos.nth(nodes, formula, root, false, true);
3205    },
3206    'nth-last-of-type': function(nodes, formula, root) {
3207      return Selector.pseudos.nth(nodes, formula, root, true, true);
3208    },
3209    'first-of-type':    function(nodes, formula, root) {
3210      return Selector.pseudos.nth(nodes, "1", root, false, true);
3211    },
3212    'last-of-type':     function(nodes, formula, root) {
3213      return Selector.pseudos.nth(nodes, "1", root, true, true);
3214    },
3215    'only-of-type':     function(nodes, formula, root) {
3216      var p = Selector.pseudos;
3217      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
3218    },
3219
3220    // handles the an+b logic
3221    getIndices: function(a, b, total) {
3222      if (a == 0) return b > 0 ? [b] : [];
3223      return $R(1, total).inject([], function(memo, i) {
3224        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
3225        return memo;
3226      });
3227    },
3228
3229    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
3230    nth: function(nodes, formula, root, reverse, ofType) {
3231      if (nodes.length == 0) return [];
3232      if (formula == 'even') formula = '2n+0';
3233      if (formula == 'odd')  formula = '2n+1';
3234      var h = Selector.handlers, results = [], indexed = [], m;
3235      h.mark(nodes);
3236      for (var i = 0, node; node = nodes[i]; i++) {
3237        if (!node.parentNode._counted) {
3238          h.index(node.parentNode, reverse, ofType);
3239          indexed.push(node.parentNode);
3240        }
3241      }
3242      if (formula.match(/^\d+$/)) { // just a number
3243        formula = Number(formula);
3244        for (var i = 0, node; node = nodes[i]; i++)
3245          if (node.nodeIndex == formula) results.push(node);
3246      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
3247        if (m[1] == "-") m[1] = -1;
3248        var a = m[1] ? Number(m[1]) : 1;
3249        var b = m[2] ? Number(m[2]) : 0;
3250        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
3251        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
3252          for (var j = 0; j < l; j++)
3253            if (node.nodeIndex == indices[j]) results.push(node);
3254        }
3255      }
3256      h.unmark(nodes);
3257      h.unmark(indexed);
3258      return results;
3259    },
3260
3261    'empty': function(nodes, value, root) {
3262      for (var i = 0, results = [], node; node = nodes[i]; i++) {
3263        // IE treats comments as element nodes
3264        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
3265        results.push(node);
3266      }
3267      return results;
3268    },
3269
3270    'not': function(nodes, selector, root) {
3271      var h = Selector.handlers, selectorType, m;
3272      var exclusions = new Selector(selector).findElements(root);
3273      h.mark(exclusions);
3274      for (var i = 0, results = [], node; node = nodes[i]; i++)
3275        if (!node._counted) results.push(node);
3276      h.unmark(exclusions);
3277      return results;
3278    },
3279
3280    'enabled': function(nodes, value, root) {
3281      for (var i = 0, results = [], node; node = nodes[i]; i++)
3282        if (!node.disabled) results.push(node);
3283      return results;
3284    },
3285
3286    'disabled': function(nodes, value, root) {
3287      for (var i = 0, results = [], node; node = nodes[i]; i++)
3288        if (node.disabled) results.push(node);
3289      return results;
3290    },
3291
3292    'checked': function(nodes, value, root) {
3293      for (var i = 0, results = [], node; node = nodes[i]; i++)
3294        if (node.checked) results.push(node);
3295      return results;
3296    }
3297  },
3298
3299  operators: {
3300    '=':  function(nv, v) { return nv == v; },
3301    '!=': function(nv, v) { return nv != v; },
3302    '^=': function(nv, v) { return nv.startsWith(v); },
3303    '$=': function(nv, v) { return nv.endsWith(v); },
3304    '*=': function(nv, v) { return nv.include(v); },
3305    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
3306    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
3307  },
3308
3309  matchElements: function(elements, expression) {
3310    var matches = new Selector(expression).findElements(), h = Selector.handlers;
3311    h.mark(matches);
3312    for (var i = 0, results = [], element; element = elements[i]; i++)
3313      if (element._counted) results.push(element);
3314    h.unmark(matches);
3315    return results;
3316  },
3317
3318  findElement: function(elements, expression, index) {
3319    if (Object.isNumber(expression)) {
3320      index = expression; expression = false;
3321    }
3322    return Selector.matchElements(elements, expression || '*')[index || 0];
3323  },
3324
3325  findChildElements: function(element, expressions) {
3326    var exprs = expressions.join(','), expressions = [];
3327    exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
3328      expressions.push(m[1].strip());
3329    });
3330    var results = [], h = Selector.handlers;
3331    for (var i = 0, l = expressions.length, selector; i < l; i++) {
3332      selector = new Selector(expressions[i].strip());
3333      h.concat(results, selector.findElements(element));
3334    }
3335    return (l > 1) ? h.unique(results) : results;
3336  }
3337});
3338
3339function $$() {
3340  return Selector.findChildElements(document, $A(arguments));
3341}
3342var Form = {
3343  reset: function(form) {
3344    $(form).reset();
3345    return form;
3346  },
3347
3348  serializeElements: function(elements, options) {
3349    if (typeof options != 'object') options = { hash: !!options };
3350    else if (options.hash === undefined) options.hash = true;
3351    var key, value, submitted = false, submit = options.submit;
3352
3353    var data = elements.inject({ }, function(result, element) {
3354      if (!element.disabled && element.name) {
3355        key = element.name; value = $(element).getValue();
3356        if (value != null && (element.type != 'submit' || (!submitted &&
3357            submit !== false && (!submit || key == submit) && (submitted = true)))) {
3358          if (key in result) {
3359            // a key is already present; construct an array of values
3360            if (!Object.isArray(result[key])) result[key] = [result[key]];
3361            result[key].push(value);
3362          }
3363          else result[key] = value;
3364        }
3365      }
3366      return result;
3367    });
3368
3369    return options.hash ? data : Object.toQueryString(data);
3370  }
3371};
3372
3373Form.Methods = {
3374  serialize: function(form, options) {
3375    return Form.serializeElements(Form.getElements(form), options);
3376  },
3377
3378  getElements: function(form) {
3379    return $A($(form).getElementsByTagName('*')).inject([],
3380      function(elements, child) {
3381        if (Form.Element.Serializers[child.tagName.toLowerCase()])
3382          elements.push(Element.extend(child));
3383        return elements;
3384      }
3385    );
3386  },
3387
3388  getInputs: function(form, typeName, name) {
3389    form = $(form);
3390    var inputs = form.getElementsByTagName('input');
3391
3392    if (!typeName && !name) return $A(inputs).map(Element.extend);
3393
3394    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
3395      var input = inputs[i];
3396      if ((typeName && input.type != typeName) || (name && input.name != name))
3397        continue;
3398      matchingInputs.push(Element.extend(input));
3399    }
3400
3401    return matchingInputs;
3402  },
3403
3404  disable: function(form) {
3405    form = $(form);
3406    Form.getElements(form).invoke('disable');
3407    return form;
3408  },
3409
3410  enable: function(form) {
3411    form = $(form);
3412    Form.getElements(form).invoke('enable');
3413    return form;
3414  },
3415
3416  findFirstElement: function(form) {
3417    var elements = $(form).getElements().findAll(function(element) {
3418      return 'hidden' != element.type && !element.disabled;
3419    });
3420    var firstByIndex = elements.findAll(function(element) {
3421      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
3422    }).sortBy(function(element) { return element.tabIndex }).first();
3423
3424    return firstByIndex ? firstByIndex : elements.find(function(element) {
3425      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
3426    });
3427  },
3428
3429  focusFirstElement: function(form) {
3430    form = $(form);
3431    form.findFirstElement().activate();
3432    return form;
3433  },
3434
3435  request: function(form, options) {
3436    form = $(form), options = Object.clone(options || { });
3437
3438    var params = options.parameters, action = form.readAttribute('action') || '';
3439    if (action.blank()) action = window.location.href;
3440    options.parameters = form.serialize(true);
3441
3442    if (params) {
3443      if (Object.isString(params)) params = params.toQueryParams();
3444      Object.extend(options.parameters, params);
3445    }
3446
3447    if (form.hasAttribute('method') && !options.method)
3448      options.method = form.method;
3449
3450    return new Ajax.Request(action, options);
3451  }
3452};
3453
3454/*--------------------------------------------------------------------------*/
3455
3456Form.Element = {
3457  focus: function(element) {
3458    $(element).focus();
3459    return element;
3460  },
3461
3462  select: function(element) {
3463    $(element).select();
3464    return element;
3465  }
3466};
3467
3468Form.Element.Methods = {
3469  serialize: function(element) {
3470    element = $(element);
3471    if (!element.disabled && element.name) {
3472      var value = element.getValue();
3473      if (value != undefined) {
3474        var pair = { };
3475        pair[element.name] = value;
3476        return Object.toQueryString(pair);
3477      }
3478    }
3479    return '';
3480  },
3481
3482  getValue: function(element) {
3483    element = $(element);
3484    var method = element.tagName.toLowerCase();
3485    return Form.Element.Serializers[method](element);
3486  },
3487
3488  setValue: function(element, value) {
3489    element = $(element);
3490    var method = element.tagName.toLowerCase();
3491    Form.Element.Serializers[method](element, value);
3492    return element;
3493  },
3494
3495  clear: function(element) {
3496    $(element).value = '';
3497    return element;
3498  },
3499
3500  present: function(element) {
3501    return $(element).value != '';
3502  },
3503
3504  activate: function(element) {
3505    element = $(element);
3506    try {
3507      element.focus();
3508      if (element.select && (element.tagName.toLowerCase() != 'input' ||
3509          !['button', 'reset', 'submit'].include(element.type)))
3510        element.select();
3511    } catch (e) { }
3512    return element;
3513  },
3514
3515  disable: function(element) {
3516    element = $(element);
3517    element.blur();
3518    element.disabled = true;
3519    return element;
3520  },
3521
3522  enable: function(element) {
3523    element = $(element);
3524    element.disabled = false;
3525    return element;
3526  }
3527};
3528
3529/*--------------------------------------------------------------------------*/
3530
3531var Field = Form.Element;
3532var $F = Form.Element.Methods.getValue;
3533
3534/*--------------------------------------------------------------------------*/
3535
3536Form.Element.Serializers = {
3537  input: function(element, value) {
3538    switch (element.type.toLowerCase()) {
3539      case 'checkbox':
3540      case 'radio':
3541        return Form.Element.Serializers.inputSelector(element, value);
3542      default:
3543        return Form.Element.Serializers.textarea(element, value);
3544    }
3545  },
3546
3547  inputSelector: function(element, value) {
3548    if (value === undefined) return element.checked ? element.value : null;
3549    else element.checked = !!value;
3550  },
3551
3552  textarea: function(element, value) {
3553    if (value === undefined) return element.value;
3554    else element.value = value;
3555  },
3556
3557  select: function(element, index) {
3558    if (index === undefined)
3559      return this[element.type == 'select-one' ?
3560        'selectOne' : 'selectMany'](element);
3561    else {
3562      var opt, value, single = !Object.isArray(index);
3563      for (var i = 0, length = element.length; i < length; i++) {
3564        opt = element.options[i];
3565        value = this.optionValue(opt);
3566        if (single) {
3567          if (value == index) {
3568            opt.selected = true;
3569            return;
3570          }
3571        }
3572        else opt.selected = index.include(value);
3573      }
3574    }
3575  },
3576
3577  selectOne: function(element) {
3578    var index = element.selectedIndex;
3579    return index >= 0 ? this.optionValue(element.options[index]) : null;
3580  },
3581
3582  selectMany: function(element) {
3583    var values, length = element.length;
3584    if (!length) return null;
3585
3586    for (var i = 0, values = []; i < length; i++) {
3587      var opt = element.options[i];
3588      if (opt.selected) values.push(this.optionValue(opt));
3589    }
3590    return values;
3591  },
3592
3593  optionValue: function(opt) {
3594    // extend element because hasAttribute may not be native
3595    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
3596  }
3597};
3598
3599/*--------------------------------------------------------------------------*/
3600
3601Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
3602  initialize: function($super, element, frequency, callback) {
3603    $super(callback, frequency);
3604    this.element   = $(element);
3605    this.lastValue = this.getValue();
3606  },
3607
3608  execute: function() {
3609    var value = this.getValue();
3610    if (Object.isString(this.lastValue) && Object.isString(value) ?
3611        this.lastValue != value : String(this.lastValue) != String(value)) {
3612      this.callback(this.element, value);
3613      this.lastValue = value;
3614    }
3615  }
3616});
3617
3618Form.Element.Observer = Class.create(Abstract.TimedObserver, {
3619  getValue: function() {
3620    return Form.Element.getValue(this.element);
3621  }
3622});
3623
3624Form.Observer = Class.create(Abstract.TimedObserver, {
3625  getValue: function() {
3626    return Form.serialize(this.element);
3627  }
3628});
3629
3630/*--------------------------------------------------------------------------*/
3631
3632Abstract.EventObserver = Class.create({
3633  initialize: function(element, callback) {
3634    this.element  = $(element);
3635    this.callback = callback;
3636
3637    this.lastValue = this.getValue();
3638    if (this.element.tagName.toLowerCase() == 'form')
3639      this.registerFormCallbacks();
3640    else
3641      this.registerCallback(this.element);
3642  },
3643
3644  onElementEvent: function() {
3645    var value = this.getValue();
3646    if (this.lastValue != value) {
3647      this.callback(this.element, value);
3648      this.lastValue = value;
3649    }
3650  },
3651
3652  registerFormCallbacks: function() {
3653    Form.getElements(this.element).each(this.registerCallback, this);
3654  },
3655
3656  registerCallback: function(element) {
3657    if (element.type) {
3658      switch (element.type.toLowerCase()) {
3659        case 'checkbox':
3660        case 'radio':
3661          Event.observe(element, 'click', this.onElementEvent.bind(this));
3662          break;
3663        default:
3664          Event.observe(element, 'change', this.onElementEvent.bind(this));
3665          break;
3666      }
3667    }
3668  }
3669});
3670
3671Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
3672  getValue: function() {
3673    return Form.Element.getValue(this.element);
3674  }
3675});
3676
3677Form.EventObserver = Class.create(Abstract.EventObserver, {
3678  getValue: function() {
3679    return Form.serialize(this.element);
3680  }
3681});
3682if (!window.Event) var Event = { };
3683
3684Object.extend(Event, {
3685  KEY_BACKSPACE: 8,
3686  KEY_TAB:       9,
3687  KEY_RETURN:   13,
3688  KEY_ESC:      27,
3689  KEY_LEFT:     37,
3690  KEY_UP:       38,
3691  KEY_RIGHT:    39,
3692  KEY_DOWN:     40,
3693  KEY_DELETE:   46,
3694  KEY_HOME:     36,
3695  KEY_END:      35,
3696  KEY_PAGEUP:   33,
3697  KEY_PAGEDOWN: 34,
3698  KEY_INSERT:   45,
3699
3700  cache: { },
3701
3702  relatedTarget: function(event) {
3703    var element;
3704    switch(event.type) {
3705      case 'mouseover': element = event.fromElement; break;
3706      case 'mouseout':  element = event.toElement;   break;
3707      default: return null;
3708    }
3709    return Element.extend(element);
3710  }
3711});
3712
3713Event.Methods = (function() {
3714  var isButton;
3715
3716  if (Prototype.Browser.IE) {
3717    var buttonMap = { 0: 1, 1: 4, 2: 2 };
3718    isButton = function(event, code) {
3719      return event.button == buttonMap[code];
3720    };
3721
3722  } else if (Prototype.Browser.WebKit) {
3723    isButton = function(event, code) {
3724      switch (code) {
3725        case 0: return event.which == 1 && !event.metaKey;
3726        case 1: return event.which == 1 && event.metaKey;
3727        default: return false;
3728      }
3729    };
3730
3731  } else {
3732    isButton = function(event, code) {
3733      return event.which ? (event.which === code + 1) : (event.button === code);
3734    };
3735  }
3736
3737  return {
3738    isLeftClick:   function(event) { return isButton(event, 0) },
3739    isMiddleClick: function(event) { return isButton(event, 1) },
3740    isRightClick:  function(event) { return isButton(event, 2) },
3741
3742    element: function(event) {
3743      var node = Event.extend(event).target;
3744      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
3745    },
3746
3747    findElement: function(event, expression) {
3748      var element = Event.element(event);
3749      return element.match(expression) ? element : element.up(expression);
3750    },
3751
3752    pointer: function(event) {
3753      return {
3754        x: event.pageX || (event.clientX +
3755          (document.documentElement.scrollLeft || document.body.scrollLeft)),
3756        y: event.pageY || (event.clientY +
3757          (document.documentElement.scrollTop || document.body.scrollTop))
3758      };
3759    },
3760
3761    pointerX: function(event) { return Event.pointer(event).x },
3762    pointerY: function(event) { return Event.pointer(event).y },
3763
3764    stop: function(event) {
3765      Event.extend(event);
3766      event.preventDefault();
3767      event.stopPropagation();
3768      event.stopped = true;
3769    }
3770  };
3771})();
3772
3773Event.extend = (function() {
3774  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
3775    m[name] = Event.Methods[name].methodize();
3776    return m;
3777  });
3778
3779  if (Prototype.Browser.IE) {
3780    Object.extend(methods, {
3781      stopPropagation: function() { this.cancelBubble = true },
3782      preventDefault:  function() { this.returnValue = false },
3783      inspect: function() { return "[object Event]" }
3784    });
3785
3786    return function(event) {
3787      if (!event) return false;
3788      if (event._extendedByPrototype) return event;
3789
3790      event._extendedByPrototype = Prototype.emptyFunction;
3791      var pointer = Event.pointer(event);
3792      Object.extend(event, {
3793        target: event.srcElement,
3794        relatedTarget: Event.relatedTarget(event),
3795        pageX:  pointer.x,
3796        pageY:  pointer.y
3797      });
3798      return Object.extend(event, methods);
3799    };
3800
3801  } else {
3802    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
3803    Object.extend(Event.prototype, methods);
3804    return Prototype.K;
3805  }
3806})();
3807
3808Object.extend(Event, (function() {
3809  var cache = Event.cache;
3810
3811  function getEventID(element) {
3812    if (element._eventID) return element._eventID;
3813    arguments.callee.id = arguments.callee.id || 1;
3814    return element._eventID = ++arguments.callee.id;
3815  }
3816
3817  function getDOMEventName(eventName) {
3818    if (eventName && eventName.include(':')) return "dataavailable";
3819    return eventName;
3820  }
3821
3822  function getCacheForID(id) {
3823    return cache[id] = cache[id] || { };
3824  }
3825
3826  function getWrappersForEventName(id, eventName) {
3827    var c = getCacheForID(id);
3828    return c[eventName] = c[eventName] || [];
3829  }
3830
3831  function createWrapper(element, eventName, handler) {
3832    var id = getEventID(element);
3833    var c = getWrappersForEventName(id, eventName);
3834    if (c.pluck("handler").include(handler)) return false;
3835
3836    var wrapper = function(event) {
3837      if (!Event || !Event.extend ||
3838        (event.eventName && event.eventName != eventName))
3839          return false;
3840
3841      Event.extend(event);
3842      handler.call(element, event)
3843    };
3844
3845    wrapper.handler = handler;
3846    c.push(wrapper);
3847    return wrapper;
3848  }
3849
3850  function findWrapper(id, eventName, handler) {
3851    var c = getWrappersForEventName(id, eventName);
3852    return c.find(function(wrapper) { return wrapper.handler == handler });
3853  }
3854
3855  function destroyWrapper(id, eventName, handler) {
3856    var c = getCacheForID(id);
3857    if (!c[eventName]) return false;
3858    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
3859  }
3860
3861  function destroyCache() {
3862    for (var id in cache)
3863      for (var eventName in cache[id])
3864        cache[id][eventName] = null;
3865  }
3866
3867  if (window.attachEvent) {
3868    window.attachEvent("onunload", destroyCache);
3869  }
3870
3871  return {
3872    observe: function(element, eventName, handler) {
3873      element = $(element);
3874      var name = getDOMEventName(eventName);
3875
3876      var wrapper = createWrapper(element, eventName, handler);
3877      if (!wrapper) return element;
3878
3879      if (element.addEventListener) {
3880        element.addEventListener(name, wrapper, false);
3881      } else {
3882        element.attachEvent("on" + name, wrapper);
3883      }
3884
3885      return element;
3886    },
3887
3888    stopObserving: function(element, eventName, handler) {
3889      element = $(element);
3890      var id = getEventID(element), name = getDOMEventName(eventName);
3891
3892      if (!handler && eventName) {
3893        getWrappersForEventName(id, eventName).each(function(wrapper) {
3894          element.stopObserving(eventName, wrapper.handler);
3895        });
3896        return element;
3897
3898      } else if (!eventName) {
3899        Object.keys(getCacheForID(id)).each(function(eventName) {
3900          element.stopObserving(eventName);
3901        });
3902        return element;
3903      }
3904
3905      var wrapper = findWrapper(id, eventName, handler);
3906      if (!wrapper) return element;
3907
3908      if (element.removeEventListener) {
3909        element.removeEventListener(name, wrapper, false);
3910      } else {
3911        element.detachEvent("on" + name, wrapper);
3912      }
3913
3914      destroyWrapper(id, eventName, handler);
3915
3916      return element;
3917    },
3918
3919    fire: function(element, eventName, memo) {
3920      element = $(element);
3921      if (element == document && document.createEvent && !element.dispatchEvent)
3922        element = document.documentElement;
3923
3924      if (document.createEvent) {
3925        var event = document.createEvent("HTMLEvents");
3926        event.initEvent("dataavailable", true, true);
3927      } else {
3928        var event = document.createEventObject();
3929        event.eventType = "ondataavailable";
3930      }
3931
3932      event.eventName = eventName;
3933      event.memo = memo || { };
3934
3935      if (document.createEvent) {
3936        element.dispatchEvent(event);
3937      } else {
3938        element.fireEvent(event.eventType, event);
3939      }
3940
3941      return event;
3942    }
3943  };
3944})());
3945
3946Object.extend(Event, Event.Methods);
3947
3948Element.addMethods({
3949  fire:          Event.fire,
3950  observe:       Event.observe,
3951  stopObserving: Event.stopObserving
3952});
3953
3954Object.extend(document, {
3955  fire:          Element.Methods.fire.methodize(),
3956  observe:       Element.Methods.observe.methodize(),
3957  stopObserving: Element.Methods.stopObserving.methodize()
3958});
3959
3960(function() {
3961  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
3962     Matthias Miller, Dean Edwards and John Resig. */
3963
3964  var timer, fired = false;
3965
3966  function fireContentLoadedEvent() {
3967    if (fired) return;
3968    if (timer) window.clearInterval(timer);
3969    document.fire("dom:loaded");
3970    fired = true;
3971  }
3972
3973  if (document.addEventListener) {
3974    if (Prototype.Browser.WebKit) {
3975      timer = window.setInterval(function() {
3976        if (/loaded|complete/.test(document.readyState))
3977          fireContentLoadedEvent();
3978      }, 0);
3979
3980      Event.observe(window, "load", fireContentLoadedEvent);
3981
3982    } else {
3983      document.addEventListener("DOMContentLoaded",
3984        fireContentLoadedEvent, false);
3985    }
3986
3987  } else {
3988    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
3989    $("__onDOMContentLoaded").onreadystatechange = function() {
3990      if (this.readyState == "complete") {
3991        this.onreadystatechange = null;
3992        fireContentLoadedEvent();
3993      }
3994    };
3995  }
3996})();
3997/*------------------------------- DEPRECATED -------------------------------*/
3998
3999Hash.toQueryString = Object.toQueryString;
4000
4001var Toggle = { display: Element.toggle };
4002
4003Element.Methods.childOf = Element.Methods.descendantOf;
4004
4005var Insertion = {
4006  Before: function(element, content) {
4007    return Element.insert(element, {before:content});
4008  },
4009
4010  Top: function(element, content) {
4011    return Element.insert(element, {top:content});
4012  },
4013
4014  Bottom: function(element, content) {
4015    return Element.insert(element, {bottom:content});
4016  },
4017
4018  After: function(element, content) {
4019    return Element.insert(element, {after:content});
4020  }
4021};
4022
4023var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
4024
4025// This should be moved to script.aculo.us; notice the deprecated methods
4026// further below, that map to the newer Element methods.
4027var Position = {
4028  // set to true if needed, warning: firefox performance problems
4029  // NOT neeeded for page scrolling, only if draggable contained in
4030  // scrollable elements
4031  includeScrollOffsets: false,
4032
4033  // must be called before calling withinIncludingScrolloffset, every time the
4034  // page is scrolled
4035  prepare: function() {
4036    this.deltaX =  window.pageXOffset
4037                || document.documentElement.scrollLeft
4038                || document.body.scrollLeft
4039                || 0;
4040    this.deltaY =  window.pageYOffset
4041                || document.documentElement.scrollTop
4042                || document.body.scrollTop
4043                || 0;
4044  },
4045
4046  // caches x/y coordinate pair to use with overlap
4047  within: function(element, x, y) {
4048    if (this.includeScrollOffsets)
4049      return this.withinIncludingScrolloffsets(element, x, y);
4050    this.xcomp = x;
4051    this.ycomp = y;
4052    this.offset = Element.cumulativeOffset(element);
4053
4054    return (y >= this.offset[1] &&
4055            y <  this.offset[1] + element.offsetHeight &&
4056            x >= this.offset[0] &&
4057            x <  this.offset[0] + element.offsetWidth);
4058  },
4059
4060  withinIncludingScrolloffsets: function(element, x, y) {
4061    var offsetcache = Element.cumulativeScrollOffset(element);
4062
4063    this.xcomp = x + offsetcache[0] - this.deltaX;
4064    this.ycomp = y + offsetcache[1] - this.deltaY;
4065    this.offset = Element.cumulativeOffset(element);
4066
4067    return (this.ycomp >= this.offset[1] &&
4068            this.ycomp <  this.offset[1] + element.offsetHeight &&
4069            this.xcomp >= this.offset[0] &&
4070            this.xcomp <  this.offset[0] + element.offsetWidth);
4071  },
4072
4073  // within must be called directly before
4074  overlap: function(mode, element) {
4075    if (!mode) return 0;
4076    if (mode == 'vertical')
4077      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
4078        element.offsetHeight;
4079    if (mode == 'horizontal')
4080      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
4081        element.offsetWidth;
4082  },
4083
4084  // Deprecation layer -- use newer Element methods now (1.5.2).
4085
4086  cumulativeOffset: Element.Methods.cumulativeOffset,
4087
4088  positionedOffset: Element.Methods.positionedOffset,
4089
4090  absolutize: function(element) {
4091    Position.prepare();
4092    return Element.absolutize(element);
4093  },
4094
4095  relativize: function(element) {
4096    Position.prepare();
4097    return Element.relativize(element);
4098  },
4099
4100  realOffset: Element.Methods.cumulativeScrollOffset,
4101
4102  offsetParent: Element.Methods.getOffsetParent,
4103
4104  page: Element.Methods.viewportOffset,
4105
4106  clone: function(source, target, options) {
4107    options = options || { };
4108    return Element.clonePosition(target, source, options);
4109  }
4110};
4111
4112/*--------------------------------------------------------------------------*/
4113
4114if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
4115  function iter(name) {
4116    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
4117  }
4118
4119  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
4120  function(element, className) {
4121    className = className.toString().strip();
4122    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
4123    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
4124  } : function(element, className) {
4125    className = className.toString().strip();
4126    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
4127    if (!classNames && !className) return elements;
4128
4129    var nodes = $(element).getElementsByTagName('*');
4130    className = ' ' + className + ' ';
4131
4132    for (var i = 0, child, cn; child = nodes[i]; i++) {
4133      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
4134          (classNames && classNames.all(function(name) {
4135            return !name.toString().blank() && cn.include(' ' + name + ' ');
4136          }))))
4137        elements.push(Element.extend(child));
4138    }
4139    return elements;
4140  };
4141
4142  return function(className, parentElement) {
4143    return $(parentElement || document.body).getElementsByClassName(className);
4144  };
4145}(Element.Methods);
4146
4147/*--------------------------------------------------------------------------*/
4148
4149Element.ClassNames = Class.create();
4150Element.ClassNames.prototype = {
4151  initialize: function(element) {
4152    this.element = $(element);
4153  },
4154
4155  _each: function(iterator) {
4156    this.element.className.split(/\s+/).select(function(name) {
4157      return name.length > 0;
4158    })._each(iterator);
4159  },
4160
4161  set: function(className) {
4162    this.element.className = className;
4163  },
4164
4165  add: function(classNameToAdd) {
4166    if (this.include(classNameToAdd)) return;
4167    this.set($A(this).concat(classNameToAdd).join(' '));
4168  },
4169
4170  remove: function(classNameToRemove) {
4171    if (!this.include(classNameToRemove)) return;
4172    this.set($A(this).without(classNameToRemove).join(' '));
4173  },
4174
4175  toString: function() {
4176    return $A(this).join(' ');
4177  }
4178};
4179
4180Object.extend(Element.ClassNames.prototype, Enumerable);
4181
4182/*--------------------------------------------------------------------------*/
4183
4184Element.addMethods();
Note: See TracBrowser for help on using the repository browser.