PageSourceSearch

https://www.idref.fr/js/prototype/prototype.js

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