PageSourceSearch

https://www.upv.es/intupv2k7/ajax3/prototype.js

js upv.es collected 2026-09-24 07:43:08 UTC 129,733 bytes, 4,319 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.