PageSourceSearch

https://www.poznan.pl/mim/wspolne/.js/scriptaculous/lib/prototype-scriptaculous.js?_sst=1791469096179

js www.poznan.pl collected 2026-10-09 04:38:49 UTC 324,669 bytes, 11,211 lines download raw bytes

1/*  Prototype JavaScript framework, version 1.7.3
2 *  (c) 2005-2010 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
11  Version: '1.7.3',
12
13  Browser: (function(){
14    var ua = navigator.userAgent;
15    var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
16    return {
17      IE:             !!window.attachEvent && !isOpera,
18      Opera:          isOpera,
19      WebKit:         ua.indexOf('AppleWebKit/') > -1,
20      Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
21      MobileSafari:   /Apple.*Mobile/.test(ua)
22    }
23  })(),
24
25  BrowserFeatures: {
26    XPath: !!document.evaluate,
27
28    SelectorsAPI: !!document.querySelector,
29
30    ElementExtensions: (function() {
31      var constructor = window.Element || window.HTMLElement;
32      return !!(constructor && constructor.prototype);
33    })(),
34    SpecificElementExtensions: (function() {
35      if (typeof window.HTMLDivElement !== 'undefined')
36        return true;
37
38      var div = document.createElement('div'),
39          form = document.createElement('form'),
40          isSupported = false;
41
42      if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
43        isSupported = true;
44      }
45
46      div = form = null;
47
48      return isSupported;
49    })()
50  },
51
52  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script\\s*>',
53  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
54
55  emptyFunction: function() { },
56
57  K: function(x) { return x }
58};
59
60if (Prototype.Browser.MobileSafari)
61  Prototype.BrowserFeatures.SpecificElementExtensions = false;
62/* Based on Alex Arnell's inheritance implementation. */
63
64var Class = (function() {
65
66  var IS_DONTENUM_BUGGY = (function(){
67    for (var p in { toString: 1 }) {
68      if (p === 'toString') return false;
69    }
70    return true;
71  })();
72
73  function subclass() {};
74  function create() {
75    var parent = null, properties = $A(arguments);
76    if (Object.isFunction(properties[0]))
77      parent = properties.shift();
78
79    function klass() {
80      this.initialize.apply(this, arguments);
81    }
82
83    Object.extend(klass, Class.Methods);
84    klass.superclass = parent;
85    klass.subclasses = [];
86
87    if (parent) {
88      subclass.prototype = parent.prototype;
89      klass.prototype = new subclass;
90      parent.subclasses.push(klass);
91    }
92
93    for (var i = 0, length = properties.length; i < length; i++)
94      klass.addMethods(properties[i]);
95
96    if (!klass.prototype.initialize)
97      klass.prototype.initialize = Prototype.emptyFunction;
98
99    klass.prototype.constructor = klass;
100    return klass;
101  }
102
103  function addMethods(source) {
104    var ancestor   = this.superclass && this.superclass.prototype,
105        properties = Object.keys(source);
106
107    if (IS_DONTENUM_BUGGY) {
108      if (source.toString != Object.prototype.toString)
109        properties.push("toString");
110      if (source.valueOf != Object.prototype.valueOf)
111        properties.push("valueOf");
112    }
113
114    for (var i = 0, length = properties.length; i < length; i++) {
115      var property = properties[i], value = source[property];
116      if (ancestor && Object.isFunction(value) &&
117          value.argumentNames()[0] == "$super") {
118        var method = value;
119        value = (function(m) {
120          return function() { return ancestor[m].apply(this, arguments); };
121        })(property).wrap(method);
122
123        value.valueOf = (function(method) {
124          return function() { return method.valueOf.call(method); };
125        })(method);
126
127        value.toString = (function(method) {
128          return function() { return method.toString.call(method); };
129        })(method);
130      }
131      this.prototype[property] = value;
132    }
133
134    return this;
135  }
136
137  return {
138    create: create,
139    Methods: {
140      addMethods: addMethods
141    }
142  };
143})();
144(function() {
145
146  var _toString = Object.prototype.toString,
147      _hasOwnProperty = Object.prototype.hasOwnProperty,
148      NULL_TYPE = 'Null',
149      UNDEFINED_TYPE = 'Undefined',
150      BOOLEAN_TYPE = 'Boolean',
151      NUMBER_TYPE = 'Number',
152      STRING_TYPE = 'String',
153      OBJECT_TYPE = 'Object',
154      FUNCTION_CLASS = '[object Function]',
155      BOOLEAN_CLASS = '[object Boolean]',
156      NUMBER_CLASS = '[object Number]',
157      STRING_CLASS = '[object String]',
158      ARRAY_CLASS = '[object Array]',
159      DATE_CLASS = '[object Date]',
160      NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON &&
161        typeof JSON.stringify === 'function' &&
162        JSON.stringify(0) === '0' &&
163        typeof JSON.stringify(Prototype.K) === 'undefined';
164
165
166
167  var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf',
168   'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor'];
169
170  var IS_DONTENUM_BUGGY = (function(){
171    for (var p in { toString: 1 }) {
172      if (p === 'toString') return false;
173    }
174    return true;
175  })();
176
177  function Type(o) {
178    switch(o) {
179      case null: return NULL_TYPE;
180      case (void 0): return UNDEFINED_TYPE;
181    }
182    var type = typeof o;
183    switch(type) {
184      case 'boolean': return BOOLEAN_TYPE;
185      case 'number':  return NUMBER_TYPE;
186      case 'string':  return STRING_TYPE;
187    }
188    return OBJECT_TYPE;
189  }
190
191  function extend(destination, source) {
192    for (var property in source)
193      destination[property] = source[property];
194    return destination;
195  }
196
197  function inspect(object) {
198    try {
199      if (isUndefined(object)) return 'undefined';
200      if (object === null) return 'null';
201      return object.inspect ? object.inspect() : String(object);
202    } catch (e) {
203      if (e instanceof RangeError) return '...';
204      throw e;
205    }
206  }
207
208  function toJSON(value) {
209    return Str('', { '': value }, []);
210  }
211
212  function Str(key, holder, stack) {
213    var value = holder[key];
214    if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') {
215      value = value.toJSON(key);
216    }
217
218    var _class = _toString.call(value);
219
220    switch (_class) {
221      case NUMBER_CLASS:
222      case BOOLEAN_CLASS:
223      case STRING_CLASS:
224        value = value.valueOf();
225    }
226
227    switch (value) {
228      case null: return 'null';
229      case true: return 'true';
230      case false: return 'false';
231    }
232
233    var type = typeof value;
234    switch (type) {
235      case 'string':
236        return value.inspect(true);
237      case 'number':
238        return isFinite(value) ? String(value) : 'null';
239      case 'object':
240
241        for (var i = 0, length = stack.length; i < length; i++) {
242          if (stack[i] === value) {
243            throw new TypeError("Cyclic reference to '" + value + "' in object");
244          }
245        }
246        stack.push(value);
247
248        var partial = [];
249        if (_class === ARRAY_CLASS) {
250          for (var i = 0, length = value.length; i < length; i++) {
251            var str = Str(i, value, stack);
252            partial.push(typeof str === 'undefined' ? 'null' : str);
253          }
254          partial = '[' + partial.join(',') + ']';
255        } else {
256          var keys = Object.keys(value);
257          for (var i = 0, length = keys.length; i < length; i++) {
258            var key = keys[i], str = Str(key, value, stack);
259            if (typeof str !== "undefined") {
260               partial.push(key.inspect(true)+ ':' + str);
261             }
262          }
263          partial = '{' + partial.join(',') + '}';
264        }
265        stack.pop();
266        return partial;
267    }
268  }
269
270  function stringify(object) {
271    return JSON.stringify(object);
272  }
273
274  function toQueryString(object) {
275    return $H(object).toQueryString();
276  }
277
278  function toHTML(object) {
279    return object && object.toHTML ? object.toHTML() : String.interpret(object);
280  }
281
282  function keys(object) {
283    if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); }
284    var results = [];
285    for (var property in object) {
286      if (_hasOwnProperty.call(object, property))
287        results.push(property);
288    }
289
290    if (IS_DONTENUM_BUGGY) {
291      for (var i = 0; property = DONT_ENUMS[i]; i++) {
292        if (_hasOwnProperty.call(object, property))
293          results.push(property);
294      }
295    }
296
297    return results;
298  }
299
300  function values(object) {
301    var results = [];
302    for (var property in object)
303      results.push(object[property]);
304    return results;
305  }
306
307  function clone(object) {
308    return extend({ }, object);
309  }
310
311  function isElement(object) {
312    return !!(object && object.nodeType == 1);
313  }
314
315  function isArray(object) {
316    return _toString.call(object) === ARRAY_CLASS;
317  }
318
319  var hasNativeIsArray = (typeof Array.isArray == 'function')
320    && Array.isArray([]) && !Array.isArray({});
321
322  if (hasNativeIsArray) {
323    isArray = Array.isArray;
324  }
325
326  function isHash(object) {
327    return object instanceof Hash;
328  }
329
330  function isFunction(object) {
331    return _toString.call(object) === FUNCTION_CLASS;
332  }
333
334  function isString(object) {
335    return _toString.call(object) === STRING_CLASS;
336  }
337
338  function isNumber(object) {
339    return _toString.call(object) === NUMBER_CLASS;
340  }
341
342  function isDate(object) {
343    return _toString.call(object) === DATE_CLASS;
344  }
345
346  function isUndefined(object) {
347    return typeof object === "undefined";
348  }
349
350  extend(Object, {
351    extend:        extend,
352    inspect:       inspect,
353    toJSON:        NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON,
354    toQueryString: toQueryString,
355    toHTML:        toHTML,
356    keys:          Object.keys || keys,
357    values:        values,
358    clone:         clone,
359    isElement:     isElement,
360    isArray:       isArray,
361    isHash:        isHash,
362    isFunction:    isFunction,
363    isString:      isString,
364    isNumber:      isNumber,
365    isDate:        isDate,
366    isUndefined:   isUndefined
367  });
368})();
369Object.extend(Function.prototype, (function() {
370  var slice = Array.prototype.slice;
371
372  function update(array, args) {
373    var arrayLength = array.length, length = args.length;
374    while (length--) array[arrayLength + length] = args[length];
375    return array;
376  }
377
378  function merge(array, args) {
379    array = slice.call(array, 0);
380    return update(array, args);
381  }
382
383  function argumentNames() {
384    var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
385      .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
386      .replace(/\s+/g, '').split(',');
387    return names.length == 1 && !names[0] ? [] : names;
388  }
389
390
391  function bind(context) {
392    if (arguments.length < 2 && Object.isUndefined(arguments[0]))
393      return this;
394
395    if (!Object.isFunction(this))
396      throw new TypeError("The object is not callable.");
397
398    var nop = function() {};
399    var __method = this, args = slice.call(arguments, 1);
400
401    var bound = function() {
402      var a = merge(args, arguments);
403      var c = this instanceof bound ? this : context;
404      return __method.apply(c, a);
405    };
406
407    nop.prototype   = this.prototype;
408    bound.prototype = new nop();
409
410    return bound;
411  }
412
413  function bindAsEventListener(context) {
414    var __method = this, args = slice.call(arguments, 1);
415    return function(event) {
416      var a = update([event || window.event], args);
417      return __method.apply(context, a);
418    }
419  }
420
421  function curry() {
422    if (!arguments.length) return this;
423    var __method = this, args = slice.call(arguments, 0);
424    return function() {
425      var a = merge(args, arguments);
426      return __method.apply(this, a);
427    }
428  }
429
430  function delay(timeout) {
431    var __method = this, args = slice.call(arguments, 1);
432    timeout = timeout * 1000;
433    return window.setTimeout(function() {
434      return __method.apply(__method, args);
435    }, timeout);
436  }
437
438  function defer() {
439    var args = update([0.01], arguments);
440    return this.delay.apply(this, args);
441  }
442
443  function wrap(wrapper) {
444    var __method = this;
445    return function() {
446      var a = update([__method.bind(this)], arguments);
447      return wrapper.apply(this, a);
448    }
449  }
450
451  function methodize() {
452    if (this._methodized) return this._methodized;
453    var __method = this;
454    return this._methodized = function() {
455      var a = update([this], arguments);
456      return __method.apply(null, a);
457    };
458  }
459
460  var extensions = {
461    argumentNames:       argumentNames,
462    bindAsEventListener: bindAsEventListener,
463    curry:               curry,
464    delay:               delay,
465    defer:               defer,
466    wrap:                wrap,
467    methodize:           methodize
468  };
469
470  if (!Function.prototype.bind)
471    extensions.bind = bind;
472
473  return extensions;
474})());
475
476
477
478(function(proto) {
479
480
481  function toISOString() {
482    return this.getUTCFullYear() + '-' +
483      (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
484      this.getUTCDate().toPaddedString(2) + 'T' +
485      this.getUTCHours().toPaddedString(2) + ':' +
486      this.getUTCMinutes().toPaddedString(2) + ':' +
487      this.getUTCSeconds().toPaddedString(2) + 'Z';
488  }
489
490
491  function toJSON() {
492    return this.toISOString();
493  }
494
495  if (!proto.toISOString) proto.toISOString = toISOString;
496  if (!proto.toJSON) proto.toJSON = toJSON;
497
498})(Date.prototype);
499
500
501RegExp.prototype.match = RegExp.prototype.test;
502
503RegExp.escape = function(str) {
504  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
505};
506var PeriodicalExecuter = Class.create({
507  initialize: function(callback, frequency) {
508    this.callback = callback;
509    this.frequency = frequency;
510    this.currentlyExecuting = false;
511
512    this.registerCallback();
513  },
514
515  registerCallback: function() {
516    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
517  },
518
519  execute: function() {
520    this.callback(this);
521  },
522
523  stop: function() {
524    if (!this.timer) return;
525    clearInterval(this.timer);
526    this.timer = null;
527  },
528
529  onTimerEvent: function() {
530    if (!this.currentlyExecuting) {
531      try {
532        this.currentlyExecuting = true;
533        this.execute();
534        this.currentlyExecuting = false;
535      } catch(e) {
536        this.currentlyExecuting = false;
537        throw e;
538      }
539    }
540  }
541});
542Object.extend(String, {
543  interpret: function(value) {
544    return value == null ? '' : String(value);
545  },
546  specialChar: {
547    '\b': '\\b',
548    '\t': '\\t',
549    '\n': '\\n',
550    '\f': '\\f',
551    '\r': '\\r',
552    '\\': '\\\\'
553  }
554});
555
556Object.extend(String.prototype, (function() {
557  var NATIVE_JSON_PARSE_SUPPORT = window.JSON &&
558    typeof JSON.parse === 'function' &&
559    JSON.parse('{"test": true}').test;
560
561  function prepareReplacement(replacement) {
562    if (Object.isFunction(replacement)) return replacement;
563    var template = new Template(replacement);
564    return function(match) { return template.evaluate(match) };
565  }
566
567  function isNonEmptyRegExp(regexp) {
568    return regexp.source && regexp.source !== '(?:)';
569  }
570
571
572  function gsub(pattern, replacement) {
573    var result = '', source = this, match;
574    replacement = prepareReplacement(replacement);
575
576    if (Object.isString(pattern))
577      pattern = RegExp.escape(pattern);
578
579    if (!(pattern.length || isNonEmptyRegExp(pattern))) {
580      replacement = replacement('');
581      return replacement + source.split('').join(replacement) + replacement;
582    }
583
584    while (source.length > 0) {
585      match = source.match(pattern)
586      if (match && match[0].length > 0) {
587        result += source.slice(0, match.index);
588        result += String.interpret(replacement(match));
589        source  = source.slice(match.index + match[0].length);
590      } else {
591        result += source, source = '';
592      }
593    }
594    return result;
595  }
596
597  function sub(pattern, replacement, count) {
598    replacement = prepareReplacement(replacement);
599    count = Object.isUndefined(count) ? 1 : count;
600
601    return this.gsub(pattern, function(match) {
602      if (--count < 0) return match[0];
603      return replacement(match);
604    });
605  }
606
607  function scan(pattern, iterator) {
608    this.gsub(pattern, iterator);
609    return String(this);
610  }
611
612  function truncate(length, truncation) {
613    length = length || 30;
614    truncation = Object.isUndefined(truncation) ? '...' : truncation;
615    return this.length > length ?
616      this.slice(0, length - truncation.length) + truncation : String(this);
617  }
618
619  function strip() {
620    return this.replace(/^\s+/, '').replace(/\s+$/, '');
621  }
622
623  function stripTags() {
624    return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, '');
625  }
626
627  function stripScripts() {
628    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
629  }
630
631  function extractScripts() {
632    var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
633        matchOne = new RegExp(Prototype.ScriptFragment, 'im');
634    return (this.match(matchAll) || []).map(function(scriptTag) {
635      return (scriptTag.match(matchOne) || ['', ''])[1];
636    });
637  }
638
639  function evalScripts() {
640    return this.extractScripts().map(function(script) { return eval(script); });
641  }
642
643  function escapeHTML() {
644    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
645  }
646
647  function unescapeHTML() {
648    return this.stripTags().replace(/&lt;/g,'<').replace(/&gt;/g,'>').replace(/&amp;/g,'&');
649  }
650
651
652  function toQueryParams(separator) {
653    var match = this.strip().match(/([^?#]*)(#.*)?$/);
654    if (!match) return { };
655
656    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
657      if ((pair = pair.split('='))[0]) {
658        var key = decodeURIComponent(pair.shift()),
659            value = pair.length > 1 ? pair.join('=') : pair[0];
660
661        if (value != undefined) {
662          value = value.gsub('+', ' ');
663          value = decodeURIComponent(value);
664        }
665
666        if (key in hash) {
667          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
668          hash[key].push(value);
669        }
670        else hash[key] = value;
671      }
672      return hash;
673    });
674  }
675
676  function toArray() {
677    return this.split('');
678  }
679
680  function succ() {
681    return this.slice(0, this.length - 1) +
682      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
683  }
684
685  function times(count) {
686    return count < 1 ? '' : new Array(count + 1).join(this);
687  }
688
689  function camelize() {
690    return this.replace(/-+(.)?/g, function(match, chr) {
691      return chr ? chr.toUpperCase() : '';
692    });
693  }
694
695  function capitalize() {
696    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
697  }
698
699  function underscore() {
700    return this.replace(/::/g, '/')
701               .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
702               .replace(/([a-z\d])([A-Z])/g, '$1_$2')
703               .replace(/-/g, '_')
704               .toLowerCase();
705  }
706
707  function dasherize() {
708    return this.replace(/_/g, '-');
709  }
710
711  function inspect(useDoubleQuotes) {
712    var escapedString = this.replace(/[\x00-\x1f\\]/g, function(ch
712aracter) {
713      if (character in String.specialChar) {
714        return String.specialChar[character];
715      }
716      return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
717    });
718    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
719    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
720  }
721
722  function unfilterJSON(filter) {
723    return this.replace(filter || Prototype.JSONFilter, '$1');
724  }
725
726  function isJSON() {
727    var str = this;
728    if (str.blank()) return false;
729    str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@');
730    str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']');
731    str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
732    return (/^[\],:{}\s]*$/).test(str);
733  }
734
735  function evalJSON(sanitize) {
736    var json = this.unfilterJSON(),
737        cx = /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\u0000]/g;
738    if (cx.test(json)) {
739      json = json.replace(cx, function (a) {
740        return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
741      });
742    }
743    try {
744      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
745    } catch (e) { }
746    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
747  }
748
749  function parseJSON() {
750    var json = this.unfilterJSON();
751    return JSON.parse(json);
752  }
753
754  function include(pattern) {
755    return this.indexOf(pattern) > -1;
756  }
757
758  function startsWith(pattern, position) {
759    position = Object.isNumber(position) ? position : 0;
760    return this.lastIndexOf(pattern, position) === position;
761  }
762
763  function endsWith(pattern, position) {
764    pattern = String(pattern);
765    position = Object.isNumber(position) ? position : this.length;
766    if (position < 0) position = 0;
767    if (position > this.length) position = this.length;
768    var d = position - pattern.length;
769    return d >= 0 && this.indexOf(pattern, d) === d;
770  }
771
772  function empty() {
773    return this == '';
774  }
775
776  function blank() {
777    return /^\s*$/.test(this);
778  }
779
780  function interpolate(object, pattern) {
781    return new Template(this, pattern).evaluate(object);
782  }
783
784  return {
785    gsub:           gsub,
786    sub:            sub,
787    scan:           scan,
788    truncate:       truncate,
789    strip:          String.prototype.trim || strip,
790    stripTags:      stripTags,
791    stripScripts:   stripScripts,
792    extractScripts: extractScripts,
793    evalScripts:    evalScripts,
794    escapeHTML:     escapeHTML,
795    unescapeHTML:   unescapeHTML,
796    toQueryParams:  toQueryParams,
797    parseQuery:     toQueryParams,
798    toArray:        toArray,
799    succ:           succ,
800    times:          times,
801    camelize:       camelize,
802    capitalize:     capitalize,
803    underscore:     underscore,
804    dasherize:      dasherize,
805    inspect:        inspect,
806    unfilterJSON:   unfilterJSON,
807    isJSON:         isJSON,
808    evalJSON:       NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON,
809    include:        include,
810    startsWith:     String.prototype.startsWith || startsWith,
811    endsWith:       String.prototype.endsWith || endsWith,
812    empty:          empty,
813    blank:          blank,
814    interpolate:    interpolate
815  };
816})());
817
818var Template = Class.create({
819  initialize: function(template, pattern) {
820    this.template = template.toString();
821    this.pattern = pattern || Template.Pattern;
822  },
823
824  evaluate: function(object) {
825    if (object && Object.isFunction(object.toTemplateReplacements))
826      object = object.toTemplateReplacements();
827
828    return this.template.gsub(this.pattern, function(match) {
829      if (object == null) return (match[1] + '');
830
831      var before = match[1] || '';
832      if (before == '\\') return match[2];
833
834      var ctx = object, expr = match[3],
835          pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
836
837      match = pattern.exec(expr);
838      if (match == null) return before;
839
840      while (match != null) {
841        var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
842        ctx = ctx[comp];
843        if (null == ctx || '' == match[3]) break;
844        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
845        match = pattern.exec(expr);
846      }
847
848      return before + String.interpret(ctx);
849    });
850  }
851});
852Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
853
854var $break = { };
855
856var Enumerable = (function() {
857  function each(iterator, context) {
858    try {
859      this._each(iterator, context);
860    } catch (e) {
861      if (e != $break) throw e;
862    }
863    return this;
864  }
865
866  function eachSlice(number, iterator, context) {
867    var index = -number, slices = [], array = this.toArray();
868    if (number < 1) return array;
869    while ((index += number) < array.length)
870      slices.push(array.slice(index, index+number));
871    return slices.collect(iterator, context);
872  }
873
874  function all(iterator, context) {
875    iterator = iterator || Prototype.K;
876    var result = true;
877    this.each(function(value, index) {
878      result = result && !!iterator.call(context, value, index, this);
879      if (!result) throw $break;
880    }, this);
881    return result;
882  }
883
884  function any(iterator, context) {
885    iterator = iterator || Prototype.K;
886    var result = false;
887    this.each(function(value, index) {
888      if (result = !!iterator.call(context, value, index, this))
889        throw $break;
890    }, this);
891    return result;
892  }
893
894  function collect(iterator, context) {
895    iterator = iterator || Prototype.K;
896    var results = [];
897    this.each(function(value, index) {
898      results.push(iterator.call(context, value, index, this));
899    }, this);
900    return results;
901  }
902
903  function detect(iterator, context) {
904    var result;
905    this.each(function(value, index) {
906      if (iterator.call(context, value, index, this)) {
907        result = value;
908        throw $break;
909      }
910    }, this);
911    return result;
912  }
913
914  function findAll(iterator, context) {
915    var results = [];
916    this.each(function(value, index) {
917      if (iterator.call(context, value, index, this))
918        results.push(value);
919    }, this);
920    return results;
921  }
922
923  function grep(filter, iterator, context) {
924    iterator = iterator || Prototype.K;
925    var results = [];
926
927    if (Object.isString(filter))
928      filter = new RegExp(RegExp.escape(filter));
929
930    this.each(function(value, index) {
931      if (filter.match(value))
932        results.push(iterator.call(context, value, index, this));
933    }, this);
934    return results;
935  }
936
937  function include(object) {
938    if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1)
939      return true;
940
941    var found = false;
942    this.each(function(value) {
943      if (value == object) {
944        found = true;
945        throw $break;
946      }
947    });
948    return found;
949  }
950
951  function inGroupsOf(number, fillWith) {
952    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
953    return this.eachSlice(number, function(slice) {
954      while(slice.length < number) slice.push(fillWith);
955      return slice;
956    });
957  }
958
959  function inject(memo, iterator, context) {
960    this.each(function(value, index) {
961      memo = iterator.call(context, memo, value, index, this);
962    }, this);
963    return memo;
964  }
965
966  function invoke(method) {
967    var args = $A(arguments).slice(1);
968    return this.map(function(value) {
969      return value[method].apply(value, args);
970    });
971  }
972
973  function max(iterator, context) {
974    iterator = iterator || Prototype.K;
975    var result;
976    this.each(function(value, index) {
977      value = iterator.call(context, value, index, this);
978      if (result == null || value >= result)
979        result = value;
980    }, this);
981    return result;
982  }
983
984  function min(iterator, context) {
985    iterator = iterator || Prototype.K;
986    var result;
987    this.each(function(value, index) {
988      value = iterator.call(context, value, index, this);
989      if (result == null || value < result)
990        result = value;
991    }, this);
992    return result;
993  }
994
995  function partition(iterator, context) {
996    iterator = iterator || Prototype.K;
997    var trues = [], falses = [];
998    this.each(function(value, index) {
999      (iterator.call(context, value, index, this) ?
1000        trues : falses).push(value);
1001    }, this);
1002    return [trues, falses];
1003  }
1004
1005  function pluck(property) {
1006    var results = [];
1007    this.each(function(value) {
1008      results.push(value[property]);
1009    });
1010    return results;
1011  }
1012
1013  function reject(iterator, context) {
1014    var results = [];
1015    this.each(function(value, index) {
1016      if (!iterator.call(context, value, index, this))
1017        results.push(value);
1018    }, this);
1019    return results;
1020  }
1021
1022  function sortBy(iterator, context) {
1023    return this.map(function(value, index) {
1024      return {
1025        value: value,
1026        criteria: iterator.call(context, value, index, this)
1027      };
1028    }, this).sort(function(left, right) {
1029      var a = left.criteria, b = right.criteria;
1030      return a < b ? -1 : a > b ? 1 : 0;
1031    }).pluck('value');
1032  }
1033
1034  function toArray() {
1035    return this.map();
1036  }
1037
1038  function zip() {
1039    var iterator = Prototype.K, args = $A(arguments);
1040    if (Object.isFunction(args.last()))
1041      iterator = args.pop();
1042
1043    var collections = [this].concat(args).map($A);
1044    return this.map(function(value, index) {
1045      return iterator(collections.pluck(index));
1046    });
1047  }
1048
1049  function size() {
1050    return this.toArray().length;
1051  }
1052
1053  function inspect() {
1054    return '#<Enumerable:' + this.toArray().inspect() + '>';
1055  }
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065  return {
1066    each:       each,
1067    eachSlice:  eachSlice,
1068    all:        all,
1069    every:      all,
1070    any:        any,
1071    some:       any,
1072    collect:    collect,
1073    map:        collect,
1074    detect:     detect,
1075    findAll:    findAll,
1076    select:     findAll,
1077    filter:     findAll,
1078    grep:       grep,
1079    include:    include,
1080    member:     include,
1081    inGroupsOf: inGroupsOf,
1082    inject:     inject,
1083    invoke:     invoke,
1084    max:        max,
1085    min:        min,
1086    partition:  partition,
1087    pluck:      pluck,
1088    reject:     reject,
1089    sortBy:     sortBy,
1090    toArray:    toArray,
1091    entries:    toArray,
1092    zip:        zip,
1093    size:       size,
1094    inspect:    inspect,
1095    find:       detect
1096  };
1097})();
1098
1099function $A(iterable) {
1100  if (!iterable) return [];
1101  if ('toArray' in Object(iterable)) return iterable.toArray();
1102  var length = iterable.length || 0, results = new Array(length);
1103  while (length--) results[length] = iterable[length];
1104  return results;
1105}
1106
1107
1108function $w(string) {
1109  if (!Object.isString(string)) return [];
1110  string = string.strip();
1111  return string ? string.split(/\s+/) : [];
1112}
1113
1114//MIM-
1115Array.from = Array.from || $A;
1116
1117(function() {
1118  var arrayProto = Array.prototype,
1119      slice = arrayProto.slice,
1120      _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
1121
1122  function each(iterator, context) {
1123    for (var i = 0, length = this.length >>> 0; i < length; i++) {
1124      if (i in this) iterator.call(context, this[i], i, this);
1125    }
1126  }
1127  if (!_each) _each = each;
1128
1129  function clear() {
1130    this.length = 0;
1131    return this;
1132  }
1133
1134  function first() {
1135    return this[0];
1136  }
1137
1138  function last() {
1139    return this[this.length - 1];
1140  }
1141
1142  function compact() {
1143    return this.select(function(value) {
1144      return value != null;
1145    });
1146  }
1147
1148  function flatten() {
1149    return this.inject([], function(array, value) {
1150      if (Object.isArray(value))
1151        return array.concat(value.flatten());
1152      array.push(value);
1153      return array;
1154    });
1155  }
1156
1157  function without() {
1158    var values = slice.call(arguments, 0);
1159    return this.select(function(value) {
1160      return !values.include(value);
1161    });
1162  }
1163
1164  function reverse(inline) {
1165    return (inline === false ? this.toArray() : this)._reverse();
1166  }
1167
1168  function uniq(sorted) {
1169    return this.inject([], function(array, value, index) {
1170      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
1171        array.push(value);
1172      return array;
1173    });
1174  }
1175
1176  function intersect(array) {
1177    return this.uniq().findAll(function(item) {
1178      return array.indexOf(item) !== -1;
1179    });
1180  }
1181
1182
1183  function clone() {
1184    return slice.call(this, 0);
1185  }
1186
1187  function size() {
1188    return this.length;
1189  }
1190
1191  function inspect() {
1192    return '[' + this.map(Object.inspect).join(', ') + ']';
1193  }
1194
1195  function indexOf(item, i) {
1196    if (this == null) throw new TypeError();
1197
1198    var array = Object(this), length = array.length >>> 0;
1199    if (length === 0) return -1;
1200
1201    i = Number(i);
1202    if (isNaN(i)) {
1203      i = 0;
1204    } else if (i !== 0 && isFinite(i)) {
1205      i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
1206    }
1207
1208    if (i > length) return -1;
1209
1210    var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0);
1211    for (; k < length; k++)
1212      if (k in array && array[k] === item) return k;
1213    return -1;
1214  }
1215
1216
1217  function lastIndexOf(item, i) {
1218    if (this == null) throw new TypeError();
1219
1220    var array = Object(this), length = array.length >>> 0;
1221    if (length === 0) return -1;
1222
1223    if (!Object.isUndefined(i)) {
1224      i = Number(i);
1225      if (isNaN(i)) {
1226        i = 0;
1227      } else if (i !== 0 && isFinite(i)) {
1228        i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i));
1229      }
1230    } else {
1231      i = length;
1232    }
1233
1234    var k = i >= 0 ? Math.min(i, length - 1) :
1235     length - Math.abs(i);
1236
1237    for (; k >= 0; k--)
1238      if (k in array && array[k] === item) return k;
1239    return -1;
1240  }
1241
1242  function concat(_) {
1243    var array = [], items = slice.call(arguments, 0), item, n = 0;
1244    items.unshift(this);
1245    for (var i = 0, length = items.length; i < length; i++) {
1246      item = items[i];
1247      if (Object.isArray(item) && !('callee' in item)) {
1248        for (var j = 0, arrayLength = item.length; j < arrayLength; j++) {
1249          if (j in item) array[n] = item[j];
1250          n++;
1251        }
1252      } else {
1253        array[n++] = item;
1254      }
1255    }
1256    array.length = n;
1257    return array;
1258  }
1259
1260
1261  function wrapNative(method) {
1262    return function() {
1263      if (arguments.length === 0) {
1264        return method.call(this, Prototype.K);
1265      } else if (arguments[0] === undefined) {
1266        var args = slice.call(arguments, 1);
1267        args.unshift(Prototype.K);
1268        return method.apply(this, args);
1269      } else {
1270        return method.apply(this, arguments);
1271      }
1272    };
1273  }
1274
1275
1276  function map(iterator) {
1277    if (this == null) throw new TypeError();
1278    iterator = iterator || Prototype.K;
1279
1280    var object = Object(this);
1281    var results = [], context = arguments[1], n = 0;
1282
1283    for (var i = 0, length = object.length >>> 0; i < length; i++) {
1284      if (i in object) {
1285        results[n] = iterator.call(context, object[i], i, object);
1286      }
1287      n++;
1288    }
1289    results.length = n;
1290    return results;
1291  }
1292
1293  if (arrayProto.map) {
1294    map = wrapNative(Array.prototype.map);
1295  }
1296
1297  function filter(iterator) {
1298    if (this == null || !Object.isFunction(iterator))
1299      throw new TypeError();
1300
1301    var object = Object(this);
1302    var results = [], context = arguments[1], value;
1303
1304    for (var i = 0, length = object.length >>> 0; i < length; i++) {
1305      if (i in object) {
1306        value = object[i];
1307        if (iterator.call(context, value, i, object)) {
1308          results.push(value);
1309        }
1310      }
1311    }
1312    return results;
1313  }
1314
1315  if (arrayProto.filter) {
1316    filter = Array.prototype.filter;
1317  }
1318
1319  function some(iterator) {
1320    if (this == null) throw new TypeError();
1321    iterator = iterator || Prototype.K;
1322    var context = arguments[1];
1323
1324    var object = Object(this);
1325    for (var i = 0, length = object.length >>> 0; i < length; i++) {
1326      if (i in object && iterator.call(context, object[i], i, object)) {
1327        return true;
1328      }
1329    }
1330
1331    return false;
1332  }
1333
1334  if (arrayProto.some) {
1335    some = wrapNative(Array.prototype.some);
1336  }
1337
1338  function every(iterator) {
1339    if (this == null) throw new TypeError();
1340    iterator = iterator || Prototype.K;
1341    var context = arguments[1];
1342
1343    var object = Object(this);
1344    for (var i = 0, length = object.length >>> 0; i < length; i++) {
1345      if (i in object && !iterator.call(context, object[i], i, object)) {
1346        return false;
1347      }
1348    }
1349
1350    return true;
1351  }
1352
1353  if (arrayProto.every) {
1354    every = wrapNative(Array.prototype.every);
1355  }
1356
1357
1358  Object.extend(arrayProto, Enumerable);
1359
1360  if (arrayProto.entries === Enumerable.entries) {
1361    delete arrayProto.entries;
1362  }
1363
1364  if (!arrayProto._reverse)
1365    arrayProto._reverse = arrayProto.reverse;
1366
1367  Object.extend(arrayProto, {
1368    _each:     _each,
1369
1370    map:       map,
1371    collect:   map,
1372    select:    filter,
1373    filter:    filter,
1374    findAll:   filter,
1375    some:      some,
1376    any:       some,
1377    every:     every,
1378    all:       every,
1379
1380    clear:     clear,
1381    first:     first,
1382    last:      last,
1383    compact:   compact,
1384    flatten:   flatten,
1385    without:   without,
1386    reverse:   reverse,
1387    uniq:      uniq,
1388    intersect: intersect,
1389    clone:     clone,
1390    toArray:   clone,
1391    size:      size,
1392    inspect:   inspect
1393  });
1394
1395  var CONCAT_ARGUMENTS_BUGGY = (function() {
1396    return [].concat(arguments)[0][0] !== 1;
1397  })(1,2);
1398
1399  if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;
1400
1401  if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
1402  if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
1403})();
1404function $H(object) {
1405  return new Hash(object);
1406};
1407
1408var Hash = Class.create(Enumerable, (function() {
1409  function initialize(object) {
1410    this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
1411  }
1412
1413
1414  function _each(iterator, context) {
1415    var i = 0;
1416    for (var key in this._object) {
1417      var value = this._object[key], pair = [key, value];
1418      pair.key = key;
1419      pair.value = value;
1420      iterator.call(context, pair, i);
1421      i++;
1422    }
1423  }
1424
1425  function set(key, value) {
1426    return this._object[key] = value;
1427  }
1428
1429  function get(key) {
1430    if (this._object[key] !== Object.prototype[key])
1431      return this._object[key];
1432  }
1433
1434  function unset(key) {
1435    var value = this._object[key];
1436    delete this._object[key];
1437    return value;
1438  }
1439
1440  function toObject() {
1441    return Object.clone(this._object);
1442  }
1443
1444
1445
1446  function keys() {
1447    return this.pluck('key');
1448  }
1449
1450  function values() {
1451    return this.pluck('value');
1452  }
1453
1454  function index(value) {
1455    var match = this.detect(function(pair) {
1456      return pair.value === value;
1457    });
1458    return match && match.key;
1459  }
1460
1461  function merge(object) {
1462    return this.clone().update(object);
1463  }
1464
1465  function update(object) {
1466    return new Hash(object).inject(this, function(result, pair) {
1467      result.set(pair.key, pair.value);
1468      return result;
1469    });
1470  }
1471
1472  function toQueryPair(key, value) {
1473    if (Object.isUndefined(value)) return key;
1474
1475    value = String.interpret(value);
1476
1477    value = value.gsub(/(\r)?\n/, '\r\n');
1478    value = encodeURIComponent(value);
1479    value = value.gsub(/%20/, '+');
1480    return key + '=' + value;
1481  }
1482
1483  function toQueryString() {
1484    return this.inject([], function(results, pair) {
1485      var key = encodeURIComponent(pair.key), values = pair.value;
1486
1487      if (values && typeof values == 'object') {
1488        if (Object.isArray(values)) {
1489          var queryValues = [];
1490          for (var i = 0, len = values.length, value; i < len; i++) {
1491            value = values[i];
1492            queryValues.push(toQueryPair(key, value));
1493          }
1494          return results.concat(queryValues);
1495        }
1496      } else results.push(toQueryPair(key, values));
1497      return results;
1498    }).join('&');
1499  }
1500
1501  function inspect() {
1502    return '#<Hash:{' + this.map(function(pair) {
1503      return pair.map(Object.inspect).join(': ');
1504    }).join(', ') + '}>';
1505  }
1506
1507  function clone() {
1508    return new Hash(this);
1509  }
1510
1511  return {
1512    initialize:             initialize,
1513    _each:                  _each,
1514    set:                    set,
1515    get:                    get,
1516    unset:                  unset,
1517    toObject:               toObject,
1518    toTemplateReplacements: toObject,
1519    keys:                   keys,
1520    values:                 values,
1521    index:                  index,
1522    merge:                  merge,
1523    update:                 update,
1524    toQueryString:          toQueryString,
1525    inspect:                inspect,
1526    toJSON:                 toObject,
1527    clone:                  clone
1528  };
1529})());
1530
1531Hash.from = $H;
1532Object.extend(Number.prototype, (function() {
1533  function toColorPart() {
1534    return this.toPaddedString(2, 16);
1535  }
1536
1537  function succ() {
1538    return this + 1;
1539  }
1540
1541  function times(iterator, context) {
1542    $R(0, this, true).each(iterator, context);
1543    return this;
1544  }
1545
1546  function toPaddedString(length, radix) {
1547    var string = this.toString(radix || 10);
1548    return '0'.times(length - string.length) + string;
1549  }
1550
1551  function abs() {
1552    return Math.abs(this);
1553  }
1554
1555  function round() {
1556    return Math.round(this);
1557  }
1558
1559  function ceil() {
1560    return Math.ceil(this);
1561  }
1562
1563  function floor() {
1564    return Math.floor(this);
1565  }
1566
1567  return {
1568    toColorPart:    toColorPart,
1569    succ:           succ,
1570    times:          times,
1571    toPaddedString: toPaddedString,
1572    abs:            abs,
1573    round:          round,
1574    ceil:           ceil,
1575    floor:          floor
1576  };
1577})());
1578
1579function $R(start, end, exclusive) {
1580  return new ObjectRange(start, end, exclusive);
1581}
1582
1583var ObjectRange = Class.create(Enumerable, (function() {
1584  function initialize(start, end, exclusive) {
1585    this.start = start;
1586    this.end = end;
1587    this.exclusive = exclusive;
1588  }
1589
1590  function _each(iterator, context) {
1591    var value = this.start, i;
1592    for (i = 0; this.include(value); i++) {
1593      iterator.call(context, value, i);
1594      value = value.succ();
1595    }
1596  }
1597
1598  function include(value) {
1599    if (value < this.start)
1600      return false;
1601    if (this.exclusive)
1602      return value < this.end;
1603    return value <= this.end;
1604  }
1605
1606  return {
1607    initialize: initialize,
1608    _each:      _each,
1609    include:    include
1610  };
1611})());
1612
1613
1614
1615var Abstract = { };
1616
1617
1618var Try = {
1619  these: function() {
1620    var returnValue;
1621
1622    for (var i = 0, length = arguments.length; i < length; i++) {
1623      var lambda = arguments[i];
1624      try {
1625        returnValue = lambda();
1626        break;
1627      } catch (e) { }
1628    }
1629
1630    return returnValue;
1631  }
1632};
1633
1634var Ajax = {
1635  getTransport: function() {
1636    return Try.these(
1637      function() {return new XMLHttpRequest()},
1638      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
1639      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
1640    ) || false;
1641  },
1642
1643  activeRequestCount: 0
1644};
1645
1646Ajax.Responders = {
1647  responders: [],
1648
1649  _each: function(iterator, context) {
1650    this.responders._each(iterator, context);
1651  },
1652
1653  register: function(responder) {
1654    if (!this.include(responder))
1655      this.responders.push(responder);
1656  },
1657
1658  unregister: function(responder) {
1659    this.responders = this.responders.without(responder);
1660  },
1661
1662  dispatch: function(callback, request, transport, json) {
1663    this.each(function(responder) {
1664      if (Object.isFunction(responder[callback])) {
1665        try {
1666          responder[callback].apply(responder, [request, transport, json]);
1667        } catch (e) { }
1668      }
1669    });
1670  }
1671};
1672
1673Object.extend(Ajax.Responders, Enumerable);
1674
1675Ajax.Responders.register({
1676  onCreate:   function() { Ajax.activeRequestCount++ },
1677  onComplete: function() { Ajax.activeRequestCount-- }
1678});
1679Ajax.Base = Class.create({
1680  initialize: function(options) {
1681    this.options = {
1682      method:       'post',
1683      asynchronous: true,
1684      contentType:  'application/x-www-form-urlencoded',
1685      encoding:     'UTF-8',
1686      parameters:   '',
1687      evalJSON:     true,
1688      evalJS:       true
1689    };
1690    Object.extend(this.options, options || { });
1691
1692    this.options.method = this.options.method.toLowerCase();
1693
1694    if (Object.isHash(this.options.parameters))
1695      this.options.parameters = this.options.parameters.toObject();
1696  }
1697});
1698Ajax.Request = Class.create(Ajax.Base, {
1699  _complete: false,
1700
1701  initialize: function($super, url, options) {
1702    $super(options);
1703    this.transport = Ajax.getTransport();
1704    this.request(url);
1705  },
1706
1707  request: function(url) {
1708    this.url = url;
1709    this.method = this.options.method;
1710    var params = Object.isString(this.options.parameters) ?
1711          this.options.parameters :
1712          Object.toQueryString(this.options.parameters);
1713
1714    if (!['get', 'post'].include(this.method)) {
1715      params += (params ? '&' : '') + "_method=" + this.method;
1716      this.method = 'post';
1717    }
1718
1719    if (params && this.method === 'get') {
1720      this.url += (this.url.include('?') ? '&' : '?') + params;
1721    }
1722
1723    this.parameters = params.toQueryParams();
1724
1725    try {
1726      var response = new Ajax.Response(this);
1727      if (this.options.onCreate) this.options.onCreate(response);
1728      Ajax.Responders.dispatch('onCreate', this, response);
1729
1730      this.transport.open(this.method.toUpperCase(), this.url,
1731        this.options.asynchronous);
1732
1733      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
1734
1735      this.transport.onreadystatechange = this.onStateChange.bind(this);
1736      this.setRequestHeaders();
1737
1738      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
1739      this.transport.send(this.body);
1740
1741      /* Force Firefox to handle ready state 4 for synchronous requests */
1742      if (!this.options.asynchronous && this.transport.overrideMimeType)
1743        this.onStateChange();
1744
1745    }
1746    catch (e) {
1747      this.dispatchException(e);
1748    }
1749  },
1750
1751  onStateChange: function() {
1752    var readyState = this.transport.readyState;
1753    if (readyState > 1 && !((readyState == 4) && this._complete))
1754      this.respondToReadyState(this.transport.readyState);
1755  },
1756
1757  setRequestHeaders: function() {
1758    var headers = {
1759      'X-Requested-With': 'XMLHttpRequest',
1760      'X-Prototype-Version': Prototype.Version,
1761      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
1762    };
1763
1764    if (this.method == 'post') {
1765      headers['Content-type'] = this.options.contentType +
1766        (this.options.encoding ? '; charset=' + this.options.encoding : '');
1767
1768      /* Force "Connection: close" for older Mozilla browsers to work
1769       * around a bug where XMLHttpRequest sends an incorrect
1770       * Content-length header. See Mozilla Bugzilla #246651.
1771       */
1772      if (this.transport.overrideMimeType &&
1773          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
1774            headers['Connection'] = 'close';
1775    }
1776
1777    if (typeof this.options.requestHeaders == 'object') {
1778      var extras = this.options.requestHeaders;
1779
1780      if (Object.isFunction(extras.push))
1781        for (var i = 0, length = extras.length; i < length; i += 2)
1782          headers[extras[i]] = extras[i+1];
1783      else
1784        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
1785    }
1786
1787    for (var name in headers)
1788      if (headers[name] != null)
1789        this.transport.setRequestHeader(name, headers[name]);
1790  },
1791
1792  success: function() {
1793    var status = this.getStatus();
1794    return !status || (status >= 200 && status < 300) || status == 304;
1795  },
1796
1797  getStatus: function() {
1798    try {
1799      if (this.transport.status === 1223) return 204;
1800      return this.transport.status || 0;
1801    } catch (e) { return 0 }
1802  },
1803
1804  respondToReadyState: function(readyState) {
1805    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
1806
1807    if (state == 'Complete') {
1808      try {
1809        this._complete = true;
1810        (this.options['on' + response.status]
1811         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
1812         || Prototype.emptyFunction)(response, response.headerJSON);
1813      } catch (e) {
1814        this.dispatchException(e);
1815      }
1816
1817      var contentType = response.getHeader('Content-type');
1818      if (this.options.evalJS == 'force'
1819          || (this.options.evalJS && this.isSameOrigin() && contentType
1820          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
1821        this.evalResponse();
1822    }
1823
1824    try {
1825      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
1826      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
1827    } catch (e) {
1828      this.dispatchException(e);
1829    }
1830
1831    if (state == 'Complete') {
1832      this.transport.onreadystatechange = Prototype.emptyFunction;
1833    }
1834  },
1835
1836  isSameOrigin: function() {
1837    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
1838    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
1839      protocol: location.protocol,
1840      domain: document.domain,
1841      port: location.port ? ':' + location.port : ''
1842    }));
1843  },
1844
1845  getHeader: function(name) {
1846    try {
1847      return this.transport.getResponseHeader(name) || null;
1848    } catch (e) { return null; }
1849  },
1850
1851  evalResponse: function() {
1852    try {
1853      return eval((this.transport.responseText || '').unfilterJSON());
1854    } catch (e) {
1855      this.dispatchException(e);
1856    }
1857  },
1858
1859  dispatchException: function(exception) {
1860    (this.options.onException || Prototype.emptyFunction)(this, exception);
1861    Ajax.Responders.dispatch('onException', this, exception);
1862  }
1863});
1864
1865Ajax.Request.Events =
1866  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
1867
1868
1869
1870
1871
1872
1873
1874
1875Ajax.Response = Class.create({
1876  initialize: function(request){
1877    this.request = request;
1878    var transport  = this.transport  = request.transport,
1879        readyState = this.readyState = transport.readyState;
1880
1881    if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
1882      this.status       = this.getStatus();
1883      this.statusText   = this.getStatusText();
1884      this.responseText = String.interpret(transport.responseText);
1885      this.headerJSON   = this._getHeaderJSON();
1886    }
1887
1888    if (readyState == 4) {
1889      var xml = transport.responseXML;
1890      this.responseXML  = Object.isUndefined(xml) ? null : xml;
1891      this.responseJSON = this._getResponseJSON();
1892    }
1893  },
1894
1895  status:      0,
1896
1897  statusText: '',
1898
1899  getStatus: Ajax.Request.prototype.getStatus,
1900
1901  getStatusText: function() {
1902    try {
1903      return this.transport.statusText || '';
1904    } catch (e) { return '' }
1905  },
1906
1907  getHeader: Ajax.Request.prototype.getHeader,
1908
1909  getAllHeaders: function() {
1910    try {
1911      return this.getAllResponseHeaders();
1912    } catch (e) { return null }
1913  },
1914
1915  getResponseHeader: function(name) {
1916    return this.transport.getResponseHeader(name);
1917  },
1918
1919  getAllResponseHeaders: function() {
1920    return this.transport.getAllResponseHeaders();
1921  },
1922
1923  _getHeaderJSON: function() {
1924    var json = this.getHeader('X-JSON');
1925    if (!json) return null;
1926
1927    try {
1928      json = decodeURIComponent(escape(json));
1929    } catch(e) {
1930    }
1931
1932    try {
1933      return json.evalJSON(this.request.options.sanitizeJSON ||
1934        !this.request.isSameOrigin());
1935    } catch (e) {
1936      this.request.dispatchException(e);
1937    }
1938  },
1939
1940  _getResponseJSON: function() {
1941    var options = this.request.options;
1942    if (!options.evalJSON || (options.evalJSON != 'force' &&
1943      !(this.getHeader('Content-type') || '').include('application/json')) ||
1944        this.responseText.blank())
1945          return null;
1946    try {
1947      return this.responseText.evalJSON(options.sanitizeJSON ||
1948        !this.request.isSameOrigin());
1949    } catch (e) {
1950      this.request.dispatchException(e);
1951    }
1952  }
1953});
1954
1955Ajax.Updater = Class.create(Ajax.Request, {
1956  initialize: function($super, container, url, options) {
1957    this.container = {
1958      success: (container.success || container),
1959      failure: (container.failure || (container.success ? null : container))
1960    };
1961
1962    options = Object.clone(options);
1963    var onComplete = options.onComplete;
1964    options.onComplete = (function(response, json) {
1965      this.updateContent(response.responseText);
1966      if (Object.isFunction(onComplete)) onComplete(response, json);
1967    }).bind(this);
1968
1969    $super(url, options);
1970  },
1971
1972  updateContent: function(responseText) {
1973    var receiver = this.container[this.success() ? 'success' : 'failure'],
1974        options = this.options;
1975
1976    if (!options.evalScripts) responseText = responseText.stripScripts();
1977
1978    if (receiver = $(receiver)) {
1979      if (options.insertion) {
1980        if (Object.isString(options.insertion)) {
1981          var insertion = { }; insertion[options.insertion] = responseText;
1982          receiver.insert(insertion);
1983        }
1984        else options.insertion(receiver, responseText);
1985      }
1986      else receiver.update(responseText);
1987    }
1988  }
1989});
1990
1991Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
1992  initialize: function($super, container, url, options) {
1993    $super(options);
1994    this.onComplete = this.options.onComplete;
1995
1996    this.frequency = (this.options.frequency || 2);
1997    this.decay = (this.options.decay || 1);
1998
1999    this.updater = { };
2000    this.container = container;
2001    this.url = url;
2002
2003    this.start();
2004  },
2005
2006  start: function() {
2007    this.options.onComplete = this.updateComplete.bind(this);
2008    this.onTimerEvent();
2009  },
2010
2011  stop: function() {
2012    this.updater.options.onComplete = undefined;
2013    clearTimeout(this.timer);
2014    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
2015  },
2016
2017  updateComplete: function(response) {
2018    if (this.options.decay) {
2019      this.decay = (response.responseText == this.lastText ?
2020        this.decay * this.options.decay : 1);
2021
2022      this.lastText = response.responseText;
2023    }
2024    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
2025  },
2026
2027  onTimerEvent: function() {
2028    this.updater = new Ajax.Updater(this.container, this.url, this.options);
2029  }
2030});
2031
2032(function(GLOBAL) {
2033
2034  var UNDEFINED;
2035  var SLICE = Array.prototype.slice;
2036
2037  var DIV = document.createElement('div');
2038
2039
2040  function $(element) {
2041    if (arguments.length > 1) {
2042      for (var i = 0, elements = [], length = arguments.length; i < length; i++)
2043        elements.push($(arguments[i]));
2044      return elements;
2045    }
2046
2047    if (Object.isString(element))
2048      element = document.getElementById(element);
2049    return Element.extend(element);
2050  }
2051
2052  GLOBAL.$ = $;
2053
2054
2055  if (!GLOBAL.Node) GLOBAL.Node = {};
2056
2057  if (!GLOBAL.Node.ELEMENT_NODE) {
2058    Object.extend(GLOBAL.Node, {
2059      ELEMENT_NODE:                1,
2060      ATTRIBUTE_NODE:              2,
2061      TEXT_NODE:                   3,
2062      CDATA_SECTION_NODE:          4,
2063      ENTITY_REFERENCE_NODE:       5,
2064      ENTITY_NODE:                 6,
2065      PROCESSING_INSTRUCTION_NODE: 7,
2066      COMMENT_NODE:                8,
2067      DOCUMENT_NODE:               9,
2068      DOCUMENT_TYPE_NODE:         10,
2069      DOCUMENT_FRAGMENT_NODE:     11,
2070      NOTATION_NODE:              12
2071    });
2072  }
2073
2074  var ELEMENT_CACHE = {};
2075
2076  function shouldUseCreationCache(tagName, attributes) {
2077    if (tagName === 'select') return false;
2078    if ('type' in attributes) return false;
2079    return true;
2080  }
2081
2082  var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
2083    try {
2084      var el = document.createElement('<input name="x">');
2085      return el.tagName.toLowerCase() === 'input' && el.name === 'x';
2086    }
2087    catch(err) {
2088      return false;
2089    }
2090  })();
2091
2092
2093  var oldElement = GLOBAL.Element;
2094  function Element(tagName, attributes) {
2095    attributes = attributes || {};
2096    tagName = tagName.toLowerCase();
2097
2098    if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
2099      tagName = '<' + tagName + ' name="' + attributes.name + '">';
2100      delete attributes.name;
2101      return Element.writeAttribute(document.createElement(tagName), attributes);
2102    }
2103
2104    if (!ELEMENT_CACHE[tagName])
2105      ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName));
2106
2107    var node = shouldUseCreationCache(tagName, attributes) ?
2108     ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName);
2109
2110    return Element.writeAttribute(node, attributes);
2111  }
2112
2113  GLOBAL.Element = Element;
2114
2115  Object.extend(GLOBAL.Element, oldElement || {});
2116  if (oldElement) GLOBAL.Element.prototype = oldElement.prototype;
2117
2118  Element.Methods = { ByTag: {}, Simulated: {} };
2119
2120  var methods = {};
2121
2122  var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' };
2123  function inspect(element) {
2124    element = $(element);
2125    var result = '<' + element.tagName.toLowerCase();
2126
2127    var attribute, value;
2128    for (var property in INSPECT_ATTRIBUTES) {
2129      attribute = INSPECT_ATTRIBUTES[property];
2130      value = (element[property] || '').toString();
2131      if (value) result += ' ' + attribute + '=' + value.inspect(true);
2132    }
2133
2134    return result + '>';
2135  }
2136
2137  methods.inspect = inspect;
2138
2139
2140  function visible(element) {
2141    return $(element).getStyle('display') !== 'none';
2142  }
2143
2144  function toggle(element, bool) {
2145    element = $(element);
2146    if (typeof bool !== 'boolean')
2147      bool = !Element.visible(element);
2148    Element[bool ? 'show' : 'hide'](element);
2149
2150    return element;
2151  }
2152
2153  function hide(element) {
2154    element = $(element);
2155    element.style.display = 'none';
2156    return element;
2157  }
2158
2159  function show(element) {
2160    element = $(element);
2161    element.style.display = '';
2162    return element;
2163  }
2164
2165
2166  Object.extend(methods, {
2167    visible: visible,
2168    toggle:  toggle,
2169    hide:    hide,
2170    show:    show
2171  });
2172
2173
2174  function remove(element) {
2175    element = $(element);
2176    element.parentNode.removeChild(element);
2177    return element;
2178  }
2179
2180  var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
2181    var el = document.createElement("select"),
2182        isBuggy = true;
2183    el.innerHTML = "<option value=\"test\">test</option>";
2184    if (el.options && el.options[0]) {
2185      isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
2186    }
2187    el = null;
2188    return isBuggy;
2189  })();
2190
2191  var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
2192    try {
2193      var el = document.createElement("table");
2194      if (el && el.tBodies) {
2195        el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
2196        var isBuggy = typeof el.tBodies[0] == "undefined";
2197        el = null;
2198        return isBuggy;
2199      }
2200    } catch (e) {
2201      return true;
2202    }
2203  })();
2204
2205  var LINK_ELEMENT_INNERHTML_BUGGY = (function() {
2206    try {
2207      var el = document.createElement('div');
2208      el.innerHTML = "<link />";
2209      var isBuggy = (el.childNodes.length === 0);
2210      el = null;
2211      return isBuggy;
2212    } catch(e) {
2213      return true;
2214    }
2215  })();
2216
2217  var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY ||
2218   TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY;
2219
2220  var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
2221    var s = document.createElement("script"),
2222        isBuggy = false;
2223    try {
2224      s.appendChild(document.createTextNode(""));
2225      isBuggy = !s.firstChild ||
2226        s.firstChild && s.firstChild.nodeType !== 3;
2227    } catch (e) {
2228      isBuggy = true;
2229    }
2230    s = null;
2231    return isBuggy;
2232  })();
2233
2234  function update(element, content) {
2235    element = $(element);
2236
2237    var descendants = element.getElementsByTagName('*'),
2238     i = descendants.length;
2239    while (i--) purgeElement(descendants[i]);
2240
2241    if (content && content.toElement)
2242      content = content.toElement();
2243
2244    if (Object.isElement(content))
2245      return element.update().insert(content);
2246
2247
2248    content = Object.toHTML(content);
2249    var tagName = element.tagName.toUpperCase();
2250
2251    if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
2252      element.text = content;
2253      return element;
2254    }
2255
2256    if (ANY_INNERHTML_BUGGY) {
2257      if (tagName in INSERTION_TRANSLATIONS.tags) {
2258        while (element.firstChild)
2259          element.removeChild(element.firstChild);
2260
2261        var nodes = getContentFromAnonymousElement(tagName, content.stripScripts());
2262        for (var i = 0, node; node = nodes[i]; i++)
2263          element.appendChild(node);
2264
2265      } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) {
2266        while (element.firstChild)
2267          element.removeChild(element.firstChild);
2268
2269        var nodes = getContentFromAnonymousElement(tagName,
2270         content.stripScripts(), true);
2271
2272        for (var i = 0, node; node = nodes[i]; i++)
2273          element.appendChild(node);
2274      } else {
2275        element.innerHTML = content.stripScripts();
2276      }
2277    } else {
2278      element.innerHTML = content.stripScripts();
2279    }
2280
2281    content.evalScripts.bind(content).defer();
2282    return element;
2283  }
2284
2285  function replace(element, content) {
2286    element = $(element);
2287
2288    if (content && content.toElement) {
2289      content = content.toElement();
2290    } else if (!Object.isElement(content)) {
2291      content = Object.toHTML(content);
2292      var range = element.ownerDocument.createRange();
2293      range.selectNode(element);
2294      content.evalScripts.bind(content).defer();
2295      content = range.createContextualFragment(content.stripScripts());
2296    }
2297
2298    element.parentNode.replaceChild(content, element);
2299    return element;
2300  }
2301
2302  var INSERTION_TRANSLATIONS = {
2303    before: function(element, node) {
2304      element.parentNode.insertBefore(node, element);
2305    },
2306    top: function(element, node) {
2307      element.insertBefore(node, element.firstChild);
2308    },
2309    bottom: function(element, node) {
2310      element.appendChild(node);
2311    },
2312    after: function(element, node) {
2313      element.parentNode.insertBefore(node, element.nextSibling);
2314    },
2315
2316    tags: {
2317      TABLE:  ['<table>',                '</table>',                   1],
2318      TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
2319      TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
2320      TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
2321      SELECT: ['<select>',               '</select>',                  1]
2322    }
2323  };
2324
2325  var tags = INSERTION_TRANSLATIONS.tags;
2326
2327  Object.extend(tags, {
2328    THEAD: tags.TBODY,
2329    TFOOT: tags.TBODY,
2330    TH:    tags.TD
2331  });
2332
2333  function replace_IE(element, content) {
2334    element = $(element);
2335    if (content && content.toElement)
2336      content = content.toElement();
2337    if (Object.isElement(content)) {
2338      element.parentNode.replaceChild(content, element);
2339      return element;
2340    }
2341
2342    content = Object.toHTML(content);
2343    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
2344
2345    if (tagName in INSERTION_TRANSLATIONS.tags) {
2346      var nextSibling = Element.next(element);
2347      var fragments = getContentFromAnonymousElement(
2348       tagName, content.stripScripts());
2349
2350      parent.removeChild(element);
2351
2352      var iterator;
2353      if (nextSibling)
2354        iterator = function(node) { parent.insertBefore(node, nextSibling) };
2355      else
2356        iterator = function(node) { parent.appendChild(node); }
2357
2358      fragments.each(iterator);
2359    } else {
2360      element.outerHTML = content.stripScripts();
2361    }
2362
2363    content.evalScripts.bind(content).defer();
2364    return element;
2365  }
2366
2367  if ('outerHTML' in document.documentElement)
2368    replace = replace_IE;
2369
2370  function isContent(content) {
2371    if (Object.isUndefined(content) || content === null) return false;
2372
2373    if (Object.isString(content) || Object.isNumber(content)) return true;
2374    if (Object.isElement(content)) return true;
2375    if (content.toElement || content.toHTML) return true;
2376
2377    return false;
2378  }
2379
2380  function insertContentAt(element, content, position) {
2381    position   = position.toLowerCase();
2382    var method = INSERTION_TRANSLATIONS[position];
2383
2384    if (content && content.toElement) content = content.toElement();
2385    if (Object.isElement(content)) {
2386      method(element, content);
2387      return element;
2388    }
2389
2390    content = Object.toHTML(content);
2391    var tagName = ((position === 'before' || position === 'after') ?
2392     element.parentNode : element).tagName.toUpperCase();
2393
2394    var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts());
2395
2396    if (position === 'top' || position === 'after') childNodes.reverse();
2397
2398    for (var i = 0, node; node = childNodes[i]; i++)
2399      method(element, node);
2400
2401    content.evalScripts.bind(content).defer();
2402  }
2403
2404  function insert(element, insertions) {
2405    element = $(element);
2406
2407    if (isContent(insertions))
2408      insertions = { bottom: insertions };
2409
2410    for (var position in insertions)
2411      insertContentAt(element, insertions[position], position);
2412
2413    return element;
2414  }
2415
2416  function wrap(element, wrapper, attributes) {
2417    element = $(element);
2418
2419    if (Object.isElement(wrapper)) {
2420      $(wrapper).writeAttribute(attributes || {});
2421    } else if (Object.isString(wrapper)) {
2422      wrapper = new Element(wrapper, attributes);
2423    } else {
2424      wrapper = new Element('div', wrapper);
2425    }
2426
2427    if (element.parentNode)
2428      element.parentNode.replaceChild(wrapper, element);
2429
2430    wrapper.appendChild(element);
2431
2432    return wrapper;
2433  }
2434
2435  function cleanWhitespace(element) {
2436    element = $(element);
2437    var node = element.firstChild;
2438
2439    while (node) {
2440      var nextNode = node.nextSibling;
2441      if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue))
2442        element.removeChild(node);
2443      node = nextNode;
2444    }
2445    return element;
2446  }
2447
2448  function empty(element) {
2449    return $(element).innerHTML.blank();
2450  }
2451
2452  function getContentFromAnonymousElement(tagName, html, force) {
2453    var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV;
2454
2455    var workaround = !!t;
2456    if (!workaround && force) {
2457      workaround = true;
2458      t = ['', '', 0];
2459    }
2460
2461    if (workaround) {
2462      div.innerHTML = '&#160;' + t[0] + html + t[1];
2463      div.removeChild(div.firstChild);
2464      for (var i = t[2]; i--; )
2465        div = div.firstChild;
2466    } else {
2467      div.innerHTML = html;
2468    }
2469
2470    return $A(div.childNodes);
2471  }
2472
2473  function clone(element, deep) {
2474    if (!(element = $(element))) return;
2475    var clone = element.cloneNode(deep);
2476    if (!HAS_UNIQUE_ID_PROPERTY) {
2477      clone._prototypeUID = UNDEFINED;
2478      if (deep) {
2479        var descendants = Element.select(clone, '*'),
2480         i = descendants.length;
2481        while (i--)
2482          descendants[i]._prototypeUID = UNDEFINED;
2483      }
2484    }
2485    return Element.extend(clone);
2486  }
2487
2488  function purgeElement(element) {
2489    var uid = getUniqueElementID(element);
2490    if (uid) {
2491      Element.stopObserving(element);
2492      if (!HAS_UNIQUE_ID_PROPERTY)
2493        element._prototypeUID = UNDEFINED;
2494      delete Element.Storage[uid];
2495    }
2496  }
2497
2498  function purgeCollection(elements) {
2499    var i = elements.length;
2500    while (i--)
2501      purgeElement(elements[i]);
2502  }
2503
2504  function purgeCollection_IE(elements) {
2505    var i = elements.length, element, uid;
2506    while (i--) {
2507      element = elements[i];
2508      uid = getUniqueElementID(element);
2509      delete Element.Storage[uid];
2510      delete Event.cache[uid];
2511    }
2512  }
2513
2514  if (HAS_UNIQUE_ID_PROPERTY) {
2515    purgeCollection = purgeCollection_IE;
2516  }
2517
2518
2519  function purge(element) {
2520    if (!(element = $(element))) return;
2521    purgeElement(element);
2522
2523    var descendants = element.getElementsByTagName('*'),
2524     i = descendants.length;
2525
2526    while (i--) purgeElement(descendants[i]);
2527
2528    return null;
2529  }
2530
2531  Object.extend(methods, {
2532    remove:  remove,
2533    update:  update,
2534    replace: replace,
2535    insert:  insert,
2536    wrap:    wrap,
2537    cleanWhitespace: cleanWhitespace,
2538    empty:   empty,
2539    clone:   clone,
2540    purge:   purge
2541  });
2542
2543
2544
2545  function recursivelyCollect(element, property, maximumLength) {
2546    element = $(element);
2547    maximumLength = maximumLength || -1;
2548    var elements = [];
2549
2550    while (element = element[property]) {
2551      if (element.nodeType === Node.ELEMENT_NODE)
2552        elements.push(Element.extend(element));
2553
2554      if (elements.length === maximumLength) break;
2555    }
2556
2557    return elements;
2558  }
2559
2560
2561  function ancestors(element) {
2562    return recursivelyCollect(element, 'parentNode');
2563  }
2564
2565  function descendants(element) {
2566    return Element.select(element, '*');
2567  }
2568
2569  function firstDescendant(element) {
2570    element = $(element).firstChild;
2571    while (element && element.nodeType !== Node.ELEMENT_NODE)
2572      element = element.nextSibling;
2573
2574    return $(element);
2575  }
2576
2577  function immediateDescendants(element) {
2578    var results = [], child = $(element).firstChild;
2579
2580    while (child) {
2581      if (child.nodeType === Node.ELEMENT_NODE)
2582        results.push(Element.extend(child));
2583
2584      child = child.nextSibling;
2585    }
2586
2587    return results;
2588  }
2589
2590  function previousSiblings(element) {
2591    return recursivelyCollect(element, 'previousSibling');
2592  }
2593
2594  function nextSiblings(element) {
2595    return recursivelyCollect(element, 'nextSibling');
2596  }
2597
2598  function siblings(element) {
2599    element = $(element);
2600    var previous = previousSiblings(element),
2601     next = nextSiblings(element);
2602    return previous.reverse().concat(next);
2603  }
2604
2605  function match(element, selector) {
2606    element = $(element);
2607
2608    if (Object.isString(selector))
2609      return Prototype.Selector.match(element, selector);
2610
2611    return selector.match(element);
2612  }
2613
2614
2615  function _recursivelyFind(element, property, expression, index) {
2616    element = $(element), expression = expression || 0, index = index || 0;
2617    if (Object.isNumber(expression)) {
2618      index = expression, expression = null;
2619    }
2620
2621    while (element = element[property]) {
2622      if (element.nodeType !== 1) continue;
2623      if (expression && !Prototype.Selector.match(element, expression))
2624        continue;
2625      if (--index >= 0) continue;
2626
2627      return Element.extend(element);
2628    }
2629  }
2630
2631
2632  function up(element, expression, index) {
2633    element = $(element);
2634
2635    if (arguments.length === 1) return $(element.parentNode);
2636    return _recursivelyFind(element, 'parentNode', expression, index);
2637  }
2638
2639  function down(element, expression, index) {
2640    if (arguments.length === 1) return firstDescendant(element);
2641    element = $(element), expression = expression || 0, index = index || 0;
2642
2643    if (Object.isNumber(expression))
2644      index = expression, expression = '*';
2645
2646    var node = Prototype.Selector.select(expression, element)[index];
2647    return Element.extend(node);
2648  }
2649
2650  function previous(element, expression, index) {
2651    return _recursivelyFind(element, 'previousSibling', expression, index);
2652  }
2653
2654  function next(element, expression, index) {
2655    return _recursivelyFind(element, 'nextSibling', expression, index);
2656  }
2657
2658  function select(element) {
2659    element = $(element);
2660    var expressions = SLICE.call(arguments, 1).join(', ');
2661    return Prototype.Selector.select(expressions, element);
2662  }
2663
2664  function adjacent(element) {
2665    element = $(element);
2666    var expressions = SLICE.call(arguments, 1).join(', ');
2667    var siblings = Element.siblings(element), results = [];
2668    for (var i = 0, sibling; sibling = siblings[i]; i++) {
2669      if (Prototype.Selector.match(sibling, expressions))
2670        results.push(sibling);
2671    }
2672
2673    return results;
2674  }
2675
2676  function descendantOf_DOM(element, ancestor) {
2677    element = $(element), ancestor = $(ancestor);
2678    if (!element || !ancestor) return false;
2679    while (element = element.parentNode)
2680      if (element === ancestor) return true;
2681    return false;
2682  }
2683
2684  function descendantOf_contains(element, ancestor) {
2685    element = $(element), ancestor = $(ancestor);
2686    if (!element || !ancestor) return false;
2687    if (!ancestor.contains) return descendantOf_DOM(element, ancestor);
2688    return ancestor.contains(element) && ancestor !== element;
2689  }
2690
2691  function descendantOf_compareDocumentPosition(element, ancestor) {
2692    element = $(element), ancestor = $(ancestor);
2693    if (!element || !ancestor) return false;
2694    return (element.compareDocumentPosition(ancestor) & 8) === 8;
2695  }
2696
2697  var descendantOf;
2698  if (DIV.compareDocumentPosition) {
2699    descendantOf = descendantOf_compareDocumentPosition;
2700  } else if (DIV.contains) {
2701    descendantOf = descendantOf_contains;
2702  } else {
2703    descendantOf = descendantOf_DOM;
2704  }
2705
2706
2707  Object.extend(methods, {
2708    recursivelyCollect:   recursivelyCollect,
2709    ancestors:            ancestors,
2710    descendants:          descendants,
2711    firstDescendant:      firstDescendant,
2712    immediateDescendants: immediateDescendants,
2713    previousSiblings:     previousSiblings,
2714    nextSiblings:         nextSiblings,
2715    siblings:             siblings,
2716    match:                match,
2717    up:                   up,
2718    down:                 down,
2719    previous:             previous,
2720    next:                 next,
2721    select:               select,
2722    adjacent:             adjacent,
2723    descendantOf:         descendantOf,
2724
2725    getElementsBySelector: select,
2726
2727    childElements:         immediateDescendants
2728  });
2729
2730
2731  var idCounter = 1;
2732  function identify(element) {
2733    element = $(element);
2734    var id = Element.readAttribute(element, 'id');
2735    if (id) return id;
2736
2737    do { id = 'anonymous_element_' + idCounter++ } while ($(id));
2738
2739    Element.writeAttribute(element, 'id', id);
2740    return id;
2741  }
2742
2743
2744  function readAttribute(element, name) {
2745    return $(element).getAttribute(name);
2746  }
2747
2748  function readAttribute_IE(element, name) {
2749    element = $(element);
2750
2751    var table = ATTRIBUTE_TRANSLATIONS.read;
2752    if (table.values[name])
2753      return table.values[name](element, name);
2754
2755    if (table.names[name]) name = table.names[name];
2756
2757    if (name.include(':')) {
2758      if (!element.attributes || !element.attributes[name]) return null;
2759      return element.attributes[name].value;
2760    }
2761
2762    return element.getAttribute(name);
2763  }
2764
2765  function readAttribute_Opera(element, name) {
2766    if (name === 'title') return element.title;
2767    return element.getAttribute(name);
2768  }
2769
2770  var PROBLEMATIC_ATTRIBUTE_READING = (function() {
2771    DIV.setAttribute('onclick', []);
2772    var value = DIV.getAttribute('onclick');
2773    var isFunction = Object.isArray(value);
2774    DIV.removeAttribute('onclick');
2775    return isFunction;
2776  })();
2777
2778  if (PROBLEMATIC_ATTRIBUTE_READING) {
2779    readAttribute = readAttribute_IE;
2780  } else if (Prototype.Browser.Opera) {
2781    readAttribute = readAttribute_Opera;
2782  }
2783
2784
2785  function writeAttribute(element, name, value) {
2786    element = $(element);
2787    var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write;
2788
2789    if (typeof name === 'object') {
2790      attributes = name;
2791    } else {
2792      attributes[name] = Object.isUndefined(value) ? true : value;
2793    }
2794
2795    for (var attr in attributes) {
2796      name = table.names[attr] || attr;
2797      value = attributes[attr];
2798      if (table.values[attr]) {
2799        value = table.values[attr](element, value);
2800        if (Object.isUndefined(value)) continue;
2801      }
2802      if (value === false || value === null)
2803        element.removeAttribute(name);
2804      else if (value === true)
2805        element.setAttribute(name, name);
2806      else element.setAttribute(name, value);
2807    }
2808
2809    return element;
2810  }
2811
2812  var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () {
2813    if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) {
2814      return false;
2815    }
2816    var checkbox = document.createElement('<input type="checkbox">');
2817    checkbox.checked = true;
2818    var node = checkbox.getAttributeNode('checked');
2819    return !node || !node.specified;
2820  })();
2821
2822  function hasAttribute(element, attribute) {
2823    attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute;
2824    var node = $(element).getAttributeNode(attribute);
2825    return !!(node && node.specified);
2826  }
2827
2828  function hasAttribute_IE(element, attribute) {
2829    if (attribute === 'checked') {
2830      return element.checked;
2831    }
2832    return hasAttribute(element, attribute);
2833  }
2834
2835  GLOBAL.Element.Methods.Simulated.hasAttribute =
2836   PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ?
2837   hasAttribute_IE : hasAttribute;
2838
2839  function classNames(element) {
2840    return new Element.ClassNames(element);
2841  }
2842
2843  var regExpCache = {};
2844  function getRegExpForClassName(className) {
2845    if (regExpCache[className]) return regExpCache[className];
2846
2847    var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)");
2848    regExpCache[className] = re;
2849    return re;
2850  }
2851
2852  function hasClassName(element, className) {
2853    if (!(element = $(element))) return;
2854
2855    var elementClassName = element.className;
2856
2857    if (elementClassName.length === 0) return false;
2858    if (elementClassName === className) return true;
2859
2860    return getRegExpForClassName(className).test(elementClassName);
2861  }
2862
2863  function addClassName(element, className) {
2864    if (!(element = $(element))) return;
2865
2866    if (!hasClassName(element, className))
2867      element.className += (element.className ? ' ' : '') + className;
2868
2869    return element;
2870  }
2871
2872  function removeClassName(element, className) {
2873    if (!(element = $(element))) return;
2874
2875    element.className = element.className.replace(
2876     getRegExpForClassName(className), ' ').strip();
2877
2878    return element;
2879  }
2880
2881  function toggleClassName(element, className, bool) {
2882    if (!(element = $(element))) return;
2883
2884    if (Object.isUndefined(bool))
2885      bool = !hasClassName(element, className);
2886
2887    var method = Element[bool ? 'addClassName' : 'removeClassName'];
2888    return method(element, className);
2889  }
2890
2891  var ATTRIBUTE_TRANSLATIONS = {};
2892
2893  var classProp = 'className', forProp = 'for';
2894
2895  DIV.setAttribute(classProp, 'x');
2896  if (DIV.className !== 'x') {
2897    DIV.setAttribute('class', 'x');
2898    if (DIV.className === 'x')
2899      classProp = 'class';
2900  }
2901
2902  var LABEL = document.createElement('label');
2903  LABEL.setAttribute(forProp, 'x');
2904  if (LABEL.htmlFor !== 'x') {
2905    LABEL.setAttribute('htmlFor', 'x');
2906    if (LABEL.htmlFor === 'x')
2907      forProp = 'htmlFor';
2908  }
2909  LABEL = null;
2910
2911  function _getAttr(element, attribute) {
2912    return element.getAttribute(attribute);
2913  }
2914
2915  function _getAttr2(element, attribute) {
2916    return element.getAttribute(attribute, 2);
2917  }
2918
2919  function _getAttrNode(element, attribute) {
2920    var node = element.getAttributeNode(attribute);
2921    return node ? node.value : '';
2922  }
2923
2924  function _getFlag(element, attribute) {
2925    return $(element).hasAttribute(attribute) ? attribute : null;
2926  }
2927
2928  DIV.onclick = Prototype.emptyFunction;
2929  var onclickValue = DIV.getAttribute('onclick');
2930
2931  var _getEv;
2932
2933  if (String(onclickValue).indexOf('{') > -1) {
2934    _getEv = function(element, attribute) {
2935      var value = element.getAttribute(attribute);
2936      if (!value) return null;
2937      value = value.toString();
2938      value = value.split('{')[1];
2939      value = value.split('}')[0];
2940      return value.strip();
2941    };
2942  }
2943  else if (onclickValue === '') {
2944    _getEv = function(element, attribute) {
2945      var value = element.getAttribute(attribute);
2946      if (!value) return null;
2947      return value.strip();
2948    };
2949  }
2950
2951  ATTRIBUTE_TRANSLATIONS.read = {
2952    names: {
2953      'class':     classProp,
2954      'className': classProp,
2955      'for':       forProp,
2956      'htmlFor':   forProp
2957    },
2958
2959    values: {
2960      style: function(element) {
2961        return element.style.cssText.toLowerCase();
2962      },
2963      title: function(element) {
2964        return element.title;
2965      }
2966    }
2967  };
2968
2969  ATTRIBUTE_TRANSLATIONS.write = {
2970    names: {
2971      className:   'class',
2972      htmlFor:     'for',
2973      cellpadding: 'cellPadding',
2974      cellspacing: 'cellSpacing'
2975    },
2976
2977    values: {
2978      checked: function(element, value) {
2979        value = !!value;
2980        element.checked = value;
2981        return value ? 'checked' : null;
2982      },
2983
2984      style: function(element, value) {
2985        element.style.cssText = value ? value : '';
2986      }
2987    }
2988  };
2989
2990  ATTRIBUTE_TRANSLATIONS.has = { names: {} };
2991
2992  Object.extend(ATTRIBUTE_TRANSLATIONS.write.names,
2993   ATTRIBUTE_TRANSLATIONS.read.names);
2994
2995  var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' +
2996   'accessKey tabIndex encType maxLength readOnly longDesc frameBorder');
2997
2998  for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) {
2999    ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr;
3000    ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()]   = attr;
3001  }
3002
3003  Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, {
3004    href:        _getAttr2,
3005    src:         _getAttr2,
3006    type:        _getAttr,
3007    action:      _getAttrNode,
3008    disabled:    _getFlag,
3009    checked:     _getFlag,
3010    readonly:    _getFlag,
3011    multiple:    _getFlag,
3012    onload:      _getEv,
3013    onunload:    _getEv,
3014    onclick:     _getEv,
3015    ondblclick:  _getEv,
3016    onmousedown: _getEv,
3017    onmouseup:   _getEv,
3018    onmouseover: _getEv,
3019    onmousemove: _getEv,
3020    onmouseout:  _getEv,
3021    onfocus:     _getEv,
3022    onblur:      _getEv,
3023    onkeypress:  _getEv,
3024    onkeydown:   _getEv,
3025    onkeyup:     _getEv,
3026    onsubmit:    _getEv,
3027    onreset:     _getEv,
3028    onselect:    _getEv,
3029    onchange:    _getEv
3030  });
3031
3032
3033  Object.extend(methods, {
3034    identify:        identify,
3035    readAttribute:   readAttribute,
3036    writeAttribute:  writeAttribute,
3037    classNames:      classNames,
3038    hasClassName:    hasClassName,
3039    addClassName:    addClassName,
3040    removeClassName: removeClassName,
3041    toggleClassName: toggleClassName
3042  });
3043
3044
3045  function normalizeStyleName(style) {
3046    if (style === 'float' || style === 'styleFloat')
3047      return 'cssFloat';
3048    return style.camelize();
3049  }
3050
3051  function normalizeStyleName_IE(style) {
3052    if (style === 'float' || style === 'cssFloat')
3053      return 'styleFloat';
3054    return style.camelize();
3055  }
3056
3057  function setStyle(element, styles) {
3058    element = $(element);
3059    var elementStyle = element.style, match;
3060
3061    if (Object.isString(styles)) {
3062      elementStyle.cssText += ';' + styles;
3063      if (styles.include('opacity')) {
3064        var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1];
3065        Element.setOpacity(element, opacity);
3066      }
3067      return element;
3068    }
3069
3070    for (var property in styles) {
3071      if (property === 'opacity') {
3072        Element.setOpacity(element, styles[property]);
3073      } else {
3074        var value = styles[property];
3075        if (property === 'float' || property === 'cssFloat') {
3076          property = Object.isUndefined(elementStyle.styleFloat) ?
3077           'cssFloat' : 'styleFloat';
3078        }
3079        elementStyle[property] = value;
3080      }
3081    }
3082
3083    return element;
3084  }
3085
3086
3087  function getStyle(element, style) {
3088    element = $(element);
3089    style = normalizeStyleName(style);
3090
3091    var value = element.style[style];
3092    if (!value || value === 'auto') {
3093      var css = document.defaultView.getComputedStyle(element, null);
3094      value = css ? css[style] : null;
3095    }
3096
3097    if (style === 'opacity') return value ? parseFloat(value) : 1.0;
3098    return value === 'auto' ? null : value;
3099  }
3100
3101  function getStyle_Opera(element, style) {
3102    switch (style) {
3103      case 'height': case 'width':
3104        if (!Element.visible(element)) return null;
3105
3106        var dim = parseInt(getStyle(element, style), 10);
3107
3108        if (dim !== element['offset' + style.capitalize()])
3109          return dim + 'px';
3110
3111        return Element.measure(element, style);
3112
3113      default: return getStyle(element, style);
3114    }
3115  }
3116
3117  function getStyle_IE(element, style) {
3118    element = $(element);
3119    style = normalizeStyleName_IE(style);
3120
3121    var value = element.style[style];
3122    if (!value && element.currentStyle) {
3123      value = element.currentStyle[style];
3124    }
3125
3126    if (style === 'opacity') {
3127      if (!STANDARD_CSS_OPACITY_SUPPORTED)
3128        return getOpacity_IE(element);
3129      else return value ? parseFloat(value) : 1.0;
3130    }
3131
3132    if (value === 'auto') {
3133      if ((style === 'width' || style === 'height') && Element.visible(element))
3134        return Element.measure(element, style) + 'px';
3135      return null;
3136    }
3137
3138    return value;
3139  }
3140
3141  function stripAlphaFromFilter_IE(filter) {
3142    return (filter || '').replace(/alpha\([^\)]*\)/gi, '');
3143  }
3144
3145  function hasLayout_IE(element) {
3146    if (!element.currentStyle || !element.currentStyle.hasLayout)
3147      element.style.zoom = 1;
3148    return element;
3149  }
3150
3151  var STANDARD_CSS_OPACITY_SUPPORTED = (function() {
3152    DIV.style.cssText = "opacity:.55";
3153    return /^0.55/.test(DIV.style.opacity);
3154  })();
3155
3156  function setOpacity(element, value) {
3157    element = $(element);
3158    if (value == 1 || value === '') value = '';
3159    else if (value < 0.00001) value = 0;
3160    element.style.opacity = value;
3161    return element;
3162  }
3163
3164  function setOpacity_IE(element, value) {
3165    if (STANDARD_CSS_OPACITY_SUPPORTED)
3166      return setOpacity(element, value);
3167
3168    element = hasLayout_IE($(element));
3169    var filter = Element.getStyle(element, 'filter'),
3170     style = element.style;
3171
3172    if (value == 1 || value === '') {
3173      filter = stripAlphaFromFilter_IE(filter);
3174      if (filter) style.filter = filter;
3175      else style.removeAttribute('filter');
3176      return element;
3177    }
3178
3179    if (value < 0.00001) value = 0;
3180
3181    style.filter = stripAlphaFromFilter_IE(filter) +
3182     ' alpha(opacity=' + (value * 100) + ')';
3183
3184    return element;
3185  }
3186
3187
3188  function getOpacity(element) {
3189    return Element.getStyle(element, 'opacity');
3190  }
3191
3192  function getOpacity_IE(element) {
3193    if (STANDARD_CSS_OPACITY_SUPPORTED)
3194      return getOpacity(element);
3195
3196    var filter = Element.getStyle(element, 'filter');
3197    if (filter.length === 0) return 1.0;
3198    var match = (filter || '').match(/alpha\(opacity=(.*)\)/i);
3199    if (match && match[1]) return parseFloat(match[1]) / 100;
3200    return 1.0;
3201  }
3202
3203
3204  Object.extend(methods, {
3205    setStyle:   setStyle,
3206    getStyle:   getStyle,
3207    setOpacity: setOpacity,
3208    getOpacity: getOpacity
3209  });
3210
3211  if ('styleFloat' in DIV.style) {
3212    methods.getStyle = getStyle_IE;
3213    methods.setOpacity = setOpacity_IE;
3214    methods.getOpacity = getOpacity_IE;
3215  }
3216
3217  var UID = 0;
3218
3219  GLOBAL.Element.Storage = { UID: 1 };
3220
3221  function getUniqueElementID(element) {
3222    if (element === window) return 0;
3223
3224    if (typeof element._prototypeUID === 'undefined')
3225      element._prototypeUID = Element.Storage.UID++;
3226    return element._prototypeUID;
3227  }
3228
3229  function getUniqueElementID_IE(element) {
3230    if (element === window) return 0;
3231    if (element == document) return 1;
3232    return element.uniqueID;
3233  }
3234
3235  var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV);
3236  if (HAS_UNIQUE_ID_PROPERTY)
3237    getUniqueElementID = getUniqueElementID_IE;
3238
3239  function getStorage(element) {
3240    if (!(element = $(element))) return;
3241
3242    var uid = getUniqueElementID(element);
3243
3244    if (!Element.Storage[uid])
3245      Element.Storage[uid] = $H();
3246
3247    return Element.Storage[uid];
3248  }
3249
3250  function store(element, key, value) {
3251    if (!(element = $(element))) return;
3252    var storage = getStorage(element);
3253    if (arguments.length === 2) {
3254      storage.update(key);
3255    } else {
3256      storage.set(key, value);
3257    }
3258    return element;
3259  }
3260
3261  function retrieve(element, key, defaultValue) {
3262    if (!(element = $(element))) return;
3263    var storage = getStorage(element), value = storage.get(key);
3264
3265    if (Object.isUndefined(value)) {
3266      storage.set(key, defaultValue);
3267      value = defaultValue;
3268    }
3269
3270    return value;
3271  }
3272
3273
3274  Object.extend(methods, {
3275    getStorage: getStorage,
3276    store:      store,
3277    retrieve:   retrieve
3278  });
3279
3280
3281  var Methods = {}, ByTag = Element.Methods.ByTag,
3282   F = Prototype.BrowserFeatures;
3283
3284  if (!F.ElementExtensions && ('__proto__' in DIV)) {
3285    GLOBAL.HTMLElement = {};
3286    GLOBAL.HTMLElement.prototype = DIV['__proto__'];
3287    F.ElementExtensions = true;
3288  }
3289
3290  function checkElementPrototypeDeficiency(tagName) {
3291    if (typeof window.Element === 'undefined') return false;
3292    if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false;
3293    var proto = window.Element.prototype;
3294    if (proto) {
3295      var id = '_' + (Math.random() + '').slice(2),
3296       el = document.createElement(tagName);
3297      proto[id] = 'x';
3298      var isBuggy = (el[id] !== 'x');
3299      delete proto[id];
3300      el = null;
3301      return isBuggy;
3302    }
3303
3304    return false;
3305  }
3306
3307  var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY =
3308   checkElementPrototypeDeficiency('object');
3309
3310  function extendElementWith(element, methods) {
3311    for (var property in methods) {
3312      var value = methods[property];
3313      if (Object.isFunction(value) && !(property in element))
3314        element[property] = value.methodize();
3315    }
3316  }
3317
3318  var EXTENDED = {};
3319  function elementIsExtended(element) {
3320    var uid = getUniqueElementID(element);
3321    return (uid in EXTENDED);
3322  }
3323
3324  function extend(element) {
3325    if (!element || elementIsExtended(element)) return element;
3326    if (element.nodeType !== Node.ELEMENT_NODE || element == window)
3327      return element;
3328
3329    var methods = Object.clone(Methods),
3330     tagName = element.tagName.toUpperCase();
3331
3332    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
3333
3334    extendElementWith(element, methods);
3335    EXTENDED[getUniqueElementID(element)] = true;
3336    return element;
3337  }
3338
3339  function extend_IE8(element) {
3340    if (!element || elementIsExtended(element)) return element;
3341
3342    var t = element.tagName;
3343    if (t && (/^(?:object|applet|embed)$/i.test(t))) {
3344      extendElementWith(element, Element.Methods);
3345      extendElementWith(element, Element.Methods.Simulated);
3346      extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
3347    }
3348
3349    return element;
3350  }
3351
3352  if (F.SpecificElementExtensions) {
3353    extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K;
3354  }
3355
3356  function addMethodsToTagName(tagName, methods) {
3357    tagName = tagName.toUpperCase();
3358    if (!ByTag[tagName]) ByTag[tagName] = {};
3359    Object.extend(ByTag[tagName], methods);
3360  }
3361
3362  function mergeMethods(destination, methods, onlyIfAbsent) {
3363    if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false;
3364    for (var property in methods) {
3365      var value = methods[property];
3366      if (!Object.isFunction(value)) continue;
3367      if (!onlyIfAbsent || !(property in destination))
3368        destination[property] = value.methodize();
3369    }
3370  }
3371
3372  function findDOMClass(tagName) {
3373    var klass;
3374    var trans = {
3375      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
3376      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
3377      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
3378      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
3379      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
3380      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
3381      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
3382      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
3383      "FrameSet", "IFRAME": "IFrame"
3384    };
3385    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
3386    if (window[klass]) return window[klass];
3387    klass = 'HTML' + tagName + 'Element';
3388    if (window[klass]) return window[klass];
3389    klass = 'HTML' + tagName.capitalize() + 'Element';
3390    if (window[klass]) return window[klass];
3391
3392    var element = document.createElement(tagName),
3393     proto = element['__proto__'] || element.constructor.prototype;
3394
3395    element = null;
3396    return proto;
3397  }
3398
3399  function addMethods(methods) {
3400    if (arguments.length === 0) addFormMethods();
3401
3402    if (arguments.length === 2) {
3403      var tagName = methods;
3404      methods = arguments[1];
3405    }
3406
3407    if (!tagName) {
3408      Object.extend(Element.Methods, methods || {});
3409    } else {
3410      if (Object.isArray(tagName)) {
3411        for (var i = 0, tag; tag = tagName[i]; i++)
3412          addMethodsToTagName(tag, methods);
3413      } else {
3414        addMethodsToTagName(tagName, methods);
3415      }
3416    }
3417
3418    var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype :
3419     Element.prototype;
3420
3421    if (F.ElementExtensions) {
3422      mergeMethods(ELEMENT_PROTOTYPE, Element.Methods);
3423      mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true);
3424    }
3425
3426    if (F.SpecificElementExtensions) {
3427      for (var tag in Element.Methods.ByTag) {
3428        var klass = findDOMClass(tag);
3429        if (Object.isUndefined(klass)) continue;
3430        mergeMethods(klass.prototype, ByTag[tag]);
3431      }
3432    }
3433
3434    Object.extend(Element, Element.Methods);
3435    Object.extend(Element, Element.Methods.Simulated);
3436    delete Element.ByTag;
3437    delete Element.Simulated;
3438
3439    Element.extend.refresh();
3440
3441    ELEMENT_CACHE = {};
3442  }
3443
3444  Object.extend(GLOBAL.Element, {
3445    extend:     extend,
3446    addMethods: addMethods
3447  });
3448
3449  if (extend === Prototype.K) {
3450    GLOBAL.Element.extend.refresh = Prototype.emptyFunction;
3451  } else {
3452    GLOBAL.Element.extend.refresh = function() {
3453      if (Prototype.BrowserFeatures.ElementExtensions) return;
3454      Object.extend(Methods, Element.Methods);
3455      Object.extend(Methods, Element.Methods.Simulated);
3456
3457      EXTENDED = {};
3458    };
3459  }
3460
3461  function addFormMethods() {
3462    Object.extend(Form, Form.Methods);
3463    Object.extend(Form.Element, Form.Element.Methods);
3464    Object.extend(Element.Methods.ByTag, {
3465      "FORM":     Object.clone(Form.Methods),
3466      "INPUT":    Object.clone(Form.Element.Methods),
3467      "SELECT":   Object.clone(Form.Element.Methods),
3468      "TEXTAREA": Object.clone(Form.Element.Methods),
3469      "BUTTON":   Object.clone(Form.Element.Methods)
3470    });
3471  }
3472
3473  Element.addMethods(methods);
3474
3475  function destroyCache_IE() {
3476    DIV = null;
3477    ELEMENT_CACHE = null;
3478  }
3479
3480  if (window.attachEvent)
3481    window.attachEvent('onunload', destroyCache_IE);
3482
3483})(this);
3484(function() {
3485
3486  function toDecimal(pctString) {
3487    var match = pctString.match(/^(\d+)%?$/i);
3488    if (!match) return null;
3489    return (Number(match[1]) / 100);
3490  }
3491
3492  function getRawStyle(element, style) {
3493    element = $(element);
3494
3495    var value = element.style[style];
3496    if (!value || value === 'auto') {
3497      var css = document.defaultView.getComputedStyle(element, null);
3498      value = css ? css[style] : null;
3499    }
3500
3501    if (style === 'opacity') return value ? parseFloat(value) : 1.0;
3502    return value === 'auto' ? null : value;
3503  }
3504
3505  function getRawStyle_IE(element, style) {
3506    var value = element.style[style];
3507    if (!value && element.currentStyle) {
3508      value = element.currentStyle[style];
3509    }
3510    return value;
3511  }
3512
3513  function getContentWidth(element, context) {
3514    var boxWidth = element.offsetWidth;
3515
3516    var bl = getPixelValue(element, 'borderLeftWidth',  context) || 0;
3517    var br = getPixelValue(element, 'borderRightWidth', context) || 0;
3518    var pl = getPixelValue(element, 'paddingLeft',      context) || 0;
3519    var pr = getPixelValue(element, 'paddingRight',     context) || 0;
3520
3521    return boxWidth - bl - br - pl - pr;
3522  }
3523
3524  if (!Object.isUndefined(document.documentElement.currentStyle) && !Prototype.Browser.Opera) {
3525    getRawStyle = getRawStyle_IE;
3526  }
3527
3528
3529  function getPixelValue(value, property, context) {
3530    var element = null;
3531    if (Object.isElement(value)) {
3532      element = value;
3533      value = getRawStyle(element, property);
3534    }
3535
3536    if (value === null || Object.isUndefined(value)) {
3537      return null;
3538    }
3539
3540    if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) {
3541      return window.parseFloat(value);
3542    }
3543
3544    var isPercentage = value.include('%'), isViewport = (context === document.viewport);
3545
3546    if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) {
3547      var style = element.style.left, rStyle = element.runtimeStyle.left;
3548      element.runtimeStyle.left = element.currentStyle.left;
3549      element.style.left = value || 0;
3550      value = element.style.pixelLeft;
3551      element.style.left = style;
3552      element.runtimeStyle.left = rStyle;
3553
3554      return value;
3555    }
3556
3557    if (element && isPercentage) {
3558      context = context || element.parentNode;
3559      var decimal = toDecimal(value), whole = null;
3560
3561      var isHorizontal = property.include('left') || property.include('right') ||
3562       property.include('width');
3563
3564      var isVertical   = property.include('top') || property.include('bottom') ||
3565        property.include('height');
3566
3567      if (context === document.viewport) {
3568        if (isHorizontal) {
3569          whole = document.viewport.getWidth();
3570        } else if (isVertical) {
3571          whole = document.viewport.getHeight();
3572        }
3573      } else {
3574        if (isHorizontal) {
3575          whole = $(context).measure('width');
3576        } else if (isVertical) {
3577          whole = $(context).measure('height');
3578        }
3579      }
3580
3581      return (whole === null) ? 0 : whole * decimal;
3582    }
3583
3584    return 0;
3585  }
3586
3587  function toCSSPixels(number) {
3588    if (Object.isString(number) && number.endsWith('px'))
3589      return number;
3590    return number + 'px';
3591  }
3592
3593  function isDisplayed(element) {
3594    while (element && element.parentNode) {
3595      var display = element.getStyle('display');
3596      if (display === 'none') {
3597        return false;
3598      }
3599      element = $(element.parentNode);
3600    }
3601    return true;
3602  }
3603
3604  var hasLayout = Prototype.K;
3605  if ('currentStyle' in document.documentElement) {
3606    hasLayout = function(element) {
3607      if (!element.currentStyle.hasLayout) {
3608        element.style.zoom = 1;
3609      }
3610      return element;
3611    };
3612  }
3613
3614  function cssNameFor(key) {
3615    if (key.include('border')) key = key + '-width';
3616    return key.camelize();
3617  }
3618
3619  Element.Layout = Class.create(Hash, {
3620    initialize: function($super, element, preCompute) {
3621      $super();
3622      this.element = $(element);
3623
3624      Element.Layout.PROPERTIES.each( function(property) {
3625        this._set(property, null);
3626      }, this);
3627
3628      if (preCompute) {
3629        this._preComputing = true;
3630        this._begin();
3631        Element.Layout.PROPERTIES.each( this._compute, this );
3632        this._end();
3633        this._preComputing = false;
3634      }
3635    },
3636
3637    _set: function(property, value) {
3638      return Hash.prototype.set.call(this, property, value);
3639    },
3640
3641    set: function(property, value) {
3642      throw "Properties of Element.Layout are read-only.";
3643    },
3644
3645    get: function($super, property) {
3646      var value = $super(property);
3647      return value === null ? this._compute(property) : value;
3648    },
3649
3650    _begin: function() {
3651      if (this._isPrepared()) return;
3652
3653      var element = this.element;
3654      if (isDisplayed(element)) {
3655        this._setPrepared(true);
3656        return;
3657      }
3658
3659
3660      var originalStyles = {
3661        position:   element.style.position   || '',
3662        width:      element.style.width      || '',
3663        visibility: element.style.visibility || '',
3664        display:    element.style.display    || ''
3665      };
3666
3667      element.store('prototype_original_styles', originalStyles);
3668
3669      var position = getRawStyle(element, 'position'), width = element.offsetWidth;
3670
3671      if (width === 0 || width === null) {
3672        element.style.display = 'block';
3673        width = element.offsetWidth;
3674      }
3675
3676      var context = (position === 'fixed') ? document.viewport :
3677       element.parentNode;
3678
3679      var tempStyles = {
3680        visibility: 'hidden',
3681        display:    'block'
3682      };
3683
3684      if (position !== 'fixed') tempStyles.position = 'absolute';
3685
3686      element.setStyle(tempStyles);
3687
3688      var positionedWidth = element.offsetWidth, newWidth;
3689      if (width && (positionedWidth === width)) {
3690        newWidth = getContentWidth(element, context);
3691      } else if (position === 'absolute' || position === 'fixed') {
3692        newWidth = getContentWidth(element, context);
3693      } else {
3694        var parent = element.parentNode, pLayout = $(parent).getLayout();
3695
3696        newWidth = pLayout.get('width') -
3697         this.get('margin-left') -
3698         this.get('border-left') -
3699         this.get('padding-left') -
3700         this.get('padding-right') -
3701         this.get('border-right') -
3702         this.get('margin-right');
3703      }
3704
3705      element.setStyle({ width: newWidth + 'px' });
3706
3707      this._setPrepared(true);
3708    },
3709
3710    _end: function() {
3711      var element = this.element;
3712      var originalStyles = element.retrieve('prototype_original_styles');
3713      element.store('prototype_original_styles', null);
3714      element.setStyle(originalStyles);
3715      this._setPrepared(false);
3716    },
3717
3718    _compute: function(property) {
3719      var COMPUTATIONS = Element.Layout.COMPUTATIONS;
3720      if (!(property in COMPUTATIONS)) {
3721        throw "Property not found.";
3722      }
3723
3724      return this._set(property, COMPUTATIONS[property].call(this, this.element));
3725    },
3726
3727    _isPrepared: function() {
3728      return this.element.retrieve('prototype_element_layout_prepared', false);
3729    },
3730
3731    _setPrepared: function(bool) {
3732      return this.element.store('prototype_element_layout_prepared', bool);
3733    },
3734
3735    toObject: function() {
3736      var args = $A(arguments);
3737      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3738       args.join(' ').split(' ');
3739      var obj = {};
3740      keys.each( function(key) {
3741        if (!Element.Layout.PROPERTIES.include(key)) return;
3742        var value = this.get(key);
3743        if (value != null) obj[key] = value;
3744      }, this);
3745      return obj;
3746    },
3747
3748    toHash: function() {
3749      var obj = this.toObject.apply(this, arguments);
3750      return new Hash(obj);
3751    },
3752
3753    toCSS: function() {
3754      var args = $A(arguments);
3755      var keys = (args.length === 0) ? Element.Layout.PROPERTIES :
3756       args.join(' ').split(' ');
3757      var css = {};
3758
3759      keys.each( function(key) {
3760        if (!Element.Layout.PROPERTIES.include(key)) return;
3761        if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return;
3762
3763        var value = this.get(key);
3764        if (value != null) css[cssNameFor(key)] = value + 'px';
3765      }, this);
3766      return css;
3767    },
3768
3769    inspect: function() {
3770      return "#<Element.Layout>";
3771    }
3772  });
3773
3774  Object.extend(Element.Layout, {
3775    PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'),
3776
3777    COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'),
3778
3779    COMPUTATIONS: {
3780      'height': function(element) {
3781        if (!this._preComputing) this._begin();
3782
3783        var bHeight = this.get('border-box-height');
3784        if (bHeight <= 0) {
3785          if (!this._preComputing) this._end();
3786          return 0;
3787        }
3788
3789        var bTop = this.get('border-top'),
3790         bBottom = this.get('border-bottom');
3791
3792        var pTop = this.get('padding-top'),
3793         pBottom = this.get('padding-bottom');
3794
3795        if (!this._preComputing) this._end();
3796
3797        return bHeight - bTop - bBottom - pTop - pBottom;
3798      },
3799
3800      'width': function(element) {
3801        if (!this._preComputing) this._begin();
3802
3803        var bWidth = this.get('border-box-width');
3804        if (bWidth <= 0) {
3805          if (!this._preComputing) this._end();
3806          return 0;
3807        }
3808
3809        var bLeft = this.get('border-left'),
3810         bRight = this.get('border-right');
3811
3812        var pLeft = this.get('padding-left'),
3813         pRight = this.get('padding-right');
3814
3815        if (!this._preComputing) this._end();
3816        return bWidth - bLeft - bRight - pLeft - pRight;
3817      },
3818
3819      'padding-box-height': function(element) {
3820        var height = this.get('height'),
3821         pTop = this.get('padding-top'),
3822         pBottom = this.get('padding-bottom');
3823
3824        return height + pTop + pBottom;
3825      },
3826
3827      'padding-box-width': function(element) {
3828        var width = this.get('width'),
3829         pLeft = this.get('padding-left'),
3830         pRight = this.get('padding-right');
3831
3832        return width + pLeft + pRight;
3833      },
3834
3835      'border-box-height': function(element) {
3836        if (!this._preComputing) this._begin();
3837        var height = element.offsetHeight;
3838        if (!this._preComputing) this._end();
3839        return height;
3840      },
3841
3842      'border-box-width': function(element) {
3843        if (!this._preComputing) this._begin();
3844        var width = element.offsetWidth;
3845        if (!this._preComputing) this._end();
3846        return width;
3847      },
3848
3849      'margin-box-height': function(element) {
3850        var bHeight = this.get('border-box-height'),
3851         mTop = this.get('margin-top'),
3852         mBottom = this.get('margin-bottom');
3853
3854        if (bHeight <= 0) return 0;
3855
3856        return bHeight + mTop + mBottom;
3857      },
3858
3859      'margin-box-width': function(element) {
3860        var bWidth = this.get('border-box-width'),
3861         mLeft = this.get('margin-left'),
3862         mRight = this.get('margin-right');
3863
3864        if (bWidth <= 0) return 0;
3865
3866        return bWidth + mLeft + mRight;
3867      },
3868
3869      'top': function(element) {
3870        var offset = element.positionedOffset();
3871        return offset.top;
3872      },
3873
3874      'bottom': function(element) {
3875        var offset = element.positionedOffset(),
3876         parent = element.getOffsetParent(),
3877         pHeight = parent.measure('height');
3878
3879        var mHeight = this.get('border-box-height');
3880
3881        return pHeight - mHeight - offset.top;
3882      },
3883
3884      'left': function(element) {
3885        var offset = element.positionedOffset();
3886        return offset.left;
3887      },
3888
3889      'right': function(element) {
3890        var offset = element.positionedOffset(),
3891         parent = element.getOffsetParent(),
3892         pWidth = parent.measure('width');
3893
3894        var mWidth = this.get('border-box-width');
3895
3896        return pWidth - mWidth - offset.left;
3897      },
3898
3899      'padding-top': function(element) {
3900        return getPixelValue(element, 'paddingTop');
3901      },
3902
3903      'padding-bottom': function(element) {
3904        return getPixelValue(element, 'paddingBottom');
3905      },
3906
3907      'padding-left': function(element) {
3908        return getPixelValue(element, 'paddingLeft');
3909      },
3910
3911      'padding-right': function(element) {
3912        return getPixelValue(element, 'paddingRight');
3913      },
3914
3915      'border-top': function(element) {
3916        return getPixelValue(element, 'borderTopWidth');
3917      },
3918
3919      'border-bottom': function(element) {
3920        return getPixelValue(element, 'borderBottomWidth');
3921      },
3922
3923      'border-left': function(element) {
3924        return getPixelValue(element, 'borderLeftWidth');
3925      },
3926
3927      'border-right': function(element) {
3928        return getPixelValue(element, 'borderRightWidth');
3929      },
3930
3931      'margin-top': function(element) {
3932        return getPixelValue(element, 'marginTop');
3933      },
3934
3935      'margin-bottom': function(element) {
3936        return getPixelValue(element, 'marginBottom');
3937      },
3938
3939      'margin-left': function(element) {
3940        return getPixelValue(element, 'marginLeft');
3941      },
3942
3943      'margin-right': function(element) {
3944        return getPixelValue(element, 'marginRight');
3945      }
3946    }
3947  });
3948
3949  if ('getBoundingClientRect' in document.documentElement) {
3950    Object.extend(Element.Layout.COMPUTATIONS, {
3951      'right': function(element) {
3952        var parent = hasLayout(element.getOffsetParent());
3953        var rect = element.getBoundingClientRect(),
3954         pRect = parent.getBoundingClientRect();
3955
3956        return (pRect.right - rect.right).round();
3957      },
3958
3959      'bottom': function(element) {
3960        var parent = hasLayout(element.getOffsetParent());
3961        var rect = element.getBoundingClientRect(),
3962         pRect = parent.getBoundingClientRect();
3963
3964        return (pRect.bottom - rect.bottom).round();
3965      }
3966    });
3967  }
3968
3969  Element.Offset = Class.create({
3970    initialize: function(left, top) {
3971      this.left = left.round();
3972      this.top  = top.round();
3973
3974      this[0] = this.left;
3975      this[1] = this.top;
3976    },
3977
3978    relativeTo: function(offset) {
3979      return new Element.Offset(
3980        this.left - offset.left,
3981        this.top  - offset.top
3982      );
3983    },
3984
3985    inspect: function() {
3986      return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this);
3987    },
3988
3989    toString: function() {
3990      return "[#{left}, #{top}]".interpolate(this);
3991    },
3992
3993    toArray: function() {
3994      return [this.left, this.top];
3995    }
3996  });
3997
3998  function getLayout(element, preCompute) {
3999    return new Element.Layout(element, preCompute);
4000  }
4001
4002  function measure(element, property) {
4003    return $(element).getLayout().get(property);
4004  }
4005
4006  function getHeight(element) {
4007    return Element.getDimensions(element).height;
4008  }
4009
4010  function getWidth(element) {
4011    return Element.getDimensions(element).width;
4012  }
4013
4014  function getDimensions(element) {
4015    element = $(element);
4016    var display = Element.getStyle(element, 'display');
4017
4018    if (display && display !== 'none') {
4019      return { width: element.offsetWidth, height: element.offsetHeight };
4020    }
4021
4022    var style = element.style;
4023    var originalStyles = {
4024      visibility: style.visibility,
4025      position:   style.position,
4026      display:    style.display
4027    };
4028
4029    var newStyles = {
4030      visibility: 'hidden',
4031      display:    'block'
4032    };
4033
4034    if (originalStyles.position !== 'fixed')
4035      newStyles.position = 'absolute';
4036
4037    Element.setStyle(element, newStyles);
4038
4039    var dimensions = {
4040      width:  element.offsetWidth,
4041      height: element.offsetHeight
4042    };
4043    Element.setStyle(element, originalStyles);
4044
4045    return dimensions;
4046  }
4047
4048  function getOffsetParent(element) {
4049    element = $(element);
4050
4051    function selfOrBody(element) {
4052      return isHtml(element) ? $(document.body) : $(element);
4053    }
4054
4055    if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
4056      return $(document.body);
4057
4058    var isInline = (Element.getStyle(element, 'display') === 'inline');
4059    if (!isInline && element.offsetParent) return selfOrBody(element.offsetParent);
4060
4061    while ((element = element.parentNode) && element !== document.body) {
4062      if (Element.getStyle(element, 'position') !== 'static') {
4063        return selfOrBody(element);
4064      }
4065    }
4066
4067    return $(document.body);
4068  }
4069
4070
4071  function cumulativeOffset(element) {
4072    element = $(element);
4073    var valueT = 0, valueL = 0;
4074    if (element.parentNode) {
4075      do {
4076        valueT += element.offsetTop  || 0;
4077        valueL += element.offsetLeft || 0;
4078        element = element.offsetParent;
4079      } while (element);
4080    }
4081    return new Element.Offset(valueL, valueT);
4082  }
4083
4084  function positionedOffset(element) {
4085    element = $(element);
4086
4087    var layout = element.getLayout();
4088
4089    var valueT = 0, valueL = 0;
4090    do {
4091      valueT += element.offsetTop  || 0;
4092      valueL += element.offsetLeft || 0;
4093      element = element.offsetParent;
4094      if (element) {
4095        if (isBody(element)) break;
4096        var p = Element.getStyle(element, 'position');
4097        if (p !== 'static') break;
4098      }
4099    } while (element);
4100
4101    valueL -= layout.get('margin-left');
4102    valueT -= layout.get('margin-top');
4103
4104    return new Element.Offset(valueL, valueT);
4105  }
4106
4107  function cumulativeScrollOffset(element) {
4108    var valueT = 0, valueL = 0;
4109    do {
4110      if (element === document.body) {
4111        var bodyScrollNode = document.documentElement || document.body.parentNode || document.body;
4112        valueT += !Object.isUndefined(window.pageYOffset) ? window.pageYOffset : bodyScrollNode.scrollTop || 0;
4113        valueL += !Object.isUndefined(window.pageXOffset) ? window.pageXOffset : bodyScrollNode.scrollLeft || 0;
4114        break;
4115      } else {
4116        valueT += element.scrollTop  || 0;
4117        valueL += element.scrollLeft || 0;
4118        element = element.parentNode;
4119      }
4120    } while (element);
4121    return new Element.Offset(valueL, valueT);
4122  }
4123
4124  function viewportOffset(forElement) {
4125    var valueT = 0, valueL = 0, docBody = document.body;
4126
4127    forElement = $(forElement);
4128    var element = forElement;
4129    do {
4130      valueT += element.offsetTop  || 0;
4131      valueL += element.offsetLeft || 0;
4132      if (element.offsetParent == docBody &&
4133        Element.getStyle(element, 'position') == 'absolute') break;
4134    } while (element = element.offsetParent);
4135
4136    element = forElement;
4137    do {
4138      if (element != docBody) {
4139        valueT -= element.scrollTop  || 0;
4140        valueL -= element.scrollLeft || 0;
4141      }
4142    } while (element = element.parentNode);
4143    return new Element.Offset(valueL, valueT);
4144  }
4145
4146  function absolutize(element) {
4147    element = $(element);
4148
4149    if (Element.getStyle(element, 'position') === 'absolute') {
4150      return element;
4151    }
4152
4153    var offsetParent = getOffsetParent(element);
4154    var eOffset = element.viewportOffset(),
4155     pOffset = offsetParent.viewportOffset();
4156
4157    var offset = eOffset.relativeTo(pOffset);
4158    var layout = element.getLayout();
4159
4160    element.store('prototype_absolutize_original_styles', {
4161      position: element.getStyle('position'),
4162      left:     element.getStyle('left'),
4163      top:      element.getStyle('top'),
4164      width:    element.getStyle('width'),
4165      height:   element.getStyle('height')
4166    });
4167
4168    element.setStyle({
4169      position: 'absolute',
4170      top:    offset.top + 'px',
4171      left:   offset.left + 'px',
4172      width:  layout.get('width') + 'px',
4173      height: layout.get('height') + 'px'
4174    });
4175    
4176    return element;
4177  }
4178
4179  function relativize(element) {
4180    element = $(element);
4181    if (Element.getStyle(element, 'position') === 'relative') {
4182      return element;
4183    }
4184
4185    var originalStyles =
4186    element.retrieve('prototype_absolutize_original_styles');
4187
4188    if (originalStyles) element.setStyle(originalStyles);
4189    return element;
4190  }
4191
4192
4193  function scrollTo(element) {
4194    element = $(element);
4195    var pos = Element.cumulativeOffset(element);
4196    window.scrollTo(pos.left, pos.top);
4197    return element;
4198  }
4199
4200
4201  function makePositioned(element) {
4202    element = $(element);
4203    var position = Element.getStyle(element, 'position'), styles = {};
4204    if (position === 'static' || !position) {
4205      styles.position = 'relative';
4206      if (Prototype.Browser.Opera) {
4207        styles.top  = 0;
4208        styles.left = 0;
4209      }
4210      Element.setStyle(element, styles);
4211      Element.store(element, 'prototype_made_positioned', true);
4212    }
4213    return element;
4214  }
4215
4216  function undoPositioned(element) {
4217    element = $(element);
4218    var storage = Element.getStorage(element),
4219     madePositioned = storage.get('prototype_made_positioned');
4220
4221    if (madePositioned) {
4222      storage.unset('prototype_made_positioned');
4223      Element.setStyle(element, {
4224        position: '',
4225        top:      '',
4226        bottom:   '',
4227        left:     '',
4228        right:    ''
4229      });
4230    }
4231    return element;
4232  }
4233
4234  function makeClipping(element) {
4235    element = $(element);
4236
4237    var storage = Element.getStorage(element),
4238     madeClipping = storage.get('prototype_made_clipping');
4239
4240    if (Object.isUndefined(madeClipping)) {
4241      var overflow = Element.getStyle(element, 'overflow');
4242      storage.set('prototype_made_clipping', overflow);
4243      if (overflow !== 'hidden')
4244        element.style.overflow = 'hidden';
4245    }
4246
4247    return element;
4248  }
4249
4250  function undoClipping(element) {
4251    element = $(element);
4252    var storage = Element.getStorage(element),
4253     overflow = storage.get('prototype_made_clipping');
4254
4255    if (!Object.isUndefined(overflow)) {
4256      storage.unset('prototype_made_clipping');
4257      element.style.overflow = overflow || '';
4258    }
4259
4260    return element;
4261  }
4262
4263  function clonePosition(element, source, options) {
4264    options = Object.extend({
4265      setLeft:    true,
4266      setTop:     true,
4267      setWidth:   true,
4268      setHeight:  true,
4269      offsetTop:  0,
4270      offsetLeft: 0
4271    }, options || {});
4272
4273    var docEl = document.documentElement;
4274
4275    source  = $(source);
4276    element = $(element);
4277    var p, delta, layout, styles = {};
4278
4279    if (options.setLeft || options.setTop) {
4280      p = Element.viewportOffset(source);
4281      delta = [0, 0];
4282      if (Element.getStyle(element, 'position') === 'absolute') {
4283        var parent = Element.getOffsetParent(element);
4284        if (parent !== document.body) delta = Element.viewportOffset(parent);
4285      }
4286    }
4287
4288    function pageScrollXY() {
4289      var x = 0, y = 0;
4290      if (Object.isNumber(window.pageXOffset)) {
4291        x = window.pageXOffset;
4292        y = window.pageYOffset;
4293      } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
4294        x = document.body.scrollLeft;
4295        y = document.body.scrollTop;
4296      } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) {
4297        x = docEl.scrollLeft;
4298        y = docEl.scrollTop;
4299      }
4300      return { x: x, y: y };
4301    }
4302
4303
4304    var pageXY = pageScrollXY();
4305    if (options.setWidth || options.setHeight) {
4306      layout = Element.getLayout(source);
4307    }
4308	
4309    if (options.setLeft)
4310      styles.left = (p[0] + pageXY.x - delta[0] + options.offsetLeft) + 'px';
4311    if (options.setTop) {
4312	  if (Element.getStyle(element, 'position') === 'absolute') {
4313		  styles.top = '0px';
4314	  } else {
4315		styles.top  = (p[1] + pageXY.y - delta[1] + options.offsetTop)  + 'px';
4316	  }
4317    }
4318
4319    var currentLayout = element.getLayout();
4320    if (options.setWidth) {
4321	  var padding_left = layout.get('padding-left');
4322	  var padding_right = layout.get('padding-right');
4323      styles.width = (layout.get('width') + padding_left + padding_right)  + 'px';
4324    }
4325    if (options.setHeight) {
4326      styles.height = layout.get('height') + 'px';
4327    }
4328
4329    return Element.setStyle(element, styles);
4330  }
4331
4332
4333  if (Prototype.Browser.IE) {
4334    getOffsetParent = getOffsetParent.wrap(
4335      function(proceed, element) {
4336        element = $(element);
4337
4338        if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element))
4339          return $(document.body);
4340
4341        var position = element.getStyle('position');
4342        if (position !== 'static') return proceed(element);
4343
4344        element.setStyle({ position: 'relative' });
4345        var value = proceed(element);
4346        element.setStyle({ position: position });
4347        return value;
4348      }
4349    );
4350
4351    positionedOffset = positionedOffset.wrap(function(proceed, element) {
4352      element = $(element);
4353      if (!element.parentNode) return new Element.Offset(0, 0);
4354      var position = element.getStyle('position');
4355      if (position !== 'static') return proceed(element);
4356
4357      var offsetParent = element.getOffsetParent();
4358      if (offsetParent && offsetParent.getStyle('position') === 'fixed')
4359        hasLayout(offsetParent);
4360
4361      element.setStyle({ position: 'relative' });
4362      var value = proceed(element);
4363      element.setStyle({ position: position });
4364      return value;
4365    });
4366  } else if (Prototype.Browser.Webkit) {
4367    cumulativeOffset = function(element) {
4368      element = $(element);
4369      var valueT = 0, valueL = 0;
4370      do {
4371        valueT += element.offsetTop  || 0;
4372        valueL += element.offsetLeft || 0;
4373        if (element.offsetParent == document.body) {
4374          if (Element.getStyle(element, 'position') == 'absolute') break;
4375        }
4376
4377        element = element.offsetParent;
4378      } while (element);
4379
4380      return new Element.Offset(valueL, valueT);
4381    };
4382  }
4383
4384
4385  Element.addMethods({
4386    getLayout:              getLayout,
4387    measure:                measure,
4388    getWidth:               getWidth,
4389    getHeight:              getHeight,
4390    getDimensions:          getDimensions,
4391    getOffsetParent:        getOffsetParent,
4392    cumulativeOffset:       cumulativeOffset,
4393    positionedOffset:       positionedOffset,
4394    cumulativeScrollOffset: cumulativeScrollOffset,
4395    viewportOffset:         viewportOffset,
4396    absolutize:             absolutize,
4397    relativize:             relativize,
4398    scrollTo:               scrollTo,
4399    makePositioned:         makePositioned,
4400    undoPositioned:         undoPositioned,
4401    makeClipping:           makeClipping,
4402    undoClipping:           undoClipping,
4403    clonePosition:          clonePosition
4404  });
4405
4406  function isBody(element) {
4407    return element.nodeName.toUpperCase() === 'BODY';
4408  }
4409
4410  function isHtml(element) {
4411    return element.nodeName.toUpperCase() === 'HTML';
4412  }
4413
4414  function isDocument(element) {
4415    return element.nodeType === Node.DOCUMENT_NODE;
4416  }
4417
4418  function isDetached(element) {
4419    return element !== document.body &&
4420     !Element.descendantOf(element, document.body);
4421  }
4422
4423  if ('getBoundingClientRect' in document.documentElement) {
4424    Element.addMethods({
4425      viewportOffset: function(element) {
4426        element = $(element);
4427        if (isDetached(element)) return new Element.Offset(0, 0);
4428
4429        var rect = element.getBoundingClientRect(),
4430         docEl = document.documentElement;
4431        return new Element.Offset(rect.left - docEl.clientLeft,
4432         rect.top - docEl.clientTop);
4433      }
4434    });
4435  }
4436
4437
4438})();
4439
4440(function() {
4441
4442  var IS_OLD_OPERA = Prototype.Browser.Opera &&
4443   (window.parseFloat(window.opera.version()) < 9.5);
4444  var ROOT = null;
4445  function getRootElement() {
4446    if (ROOT) return ROOT;
4447    ROOT = IS_OLD_OPERA ? document.body : document.documentElement;
4448    return ROOT;
4449  }
4450
4451  function getDimensions() {
4452    return { width: this.getWidth(), height: this.getHeight() };
4453  }
4454
4455  function getWidth() {
4456    return getRootElement().clientWidth;
4457  }
4458
4459  function getHeight() {
4460    return getRootElement().clientHeight;
4461  }
4462
4463  function getScrollOffsets() {
4464    var x = window.pageXOffset || document.documentElement.scrollLeft ||
4465     document.body.scrollLeft;
4466    var y = window.pageYOffset || document.documentElement.scrollTop ||
4467     document.body.scrollTop;
4468
4469    return new Element.Offset(x, y);
4470  }
4471
4472  document.viewport = {
4473    getDimensions:    getDimensions,
4474    getWidth:         getWidth,
4475    getHeight:        getHeight,
4476    getScrollOffsets: getScrollOffsets
4477  };
4478
4479})();
4480window.$$ = function() {
4481  var expression = $A(arguments).join(', ');
4482  return Prototype.Selector.select(expression, document);
4483};
4484
4485Prototype.Selector = (function() {
4486
4487  function select() {
4488    throw new Error('Method "Prototype.Selector.select" must be defined.');
4489  }
4490
4491  function match() {
4492    throw new Error('Method "Prototype.Selector.match" must be defined.');
4493  }
4494
4495  function find(elements, expression, index) {
4496    index = index || 0;
4497    var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
4498
4499    for (i = 0; i < length; i++) {
4500      if (match(elements[i], expression) && index == matchIndex++) {
4501        return Element.extend(elements[i]);
4502      }
4503    }
4504  }
4505
4506  function extendElements(elements) {
4507    for (var i = 0, length = elements.length; i < length; i++) {
4508      Element.extend(elements[i]);
4509    }
4510    return elements;
4511  }
4512
4513
4514  var K = Prototype.K;
4515
4516  return {
4517    select: select,
4518    match: match,
4519    find: find,
4520    extendElements: (Element.extend === K) ? K : extendElements,
4521    extendElement: Element.extend
4522  };
4523})();
4524Prototype._original_property = window.Sizzle;
4525
4526;(function () {
4527  function fakeDefine(fn) {
4528    Prototype._actual_sizzle = fn();
4529  }
4530  fakeDefine.amd = true;
4531
4532  if (typeof define !== 'undefined' && define.amd) {
4533    Prototype._original_define = define;
4534    Prototype._actual_sizzle = null;
4535    window.define = fakeDefine;
4536  }
4537})();
4538
4539/*!
4540 * Sizzle CSS Selector Engine v1.10.18
4541 * http://sizzlejs.com/
4542 *
4543 * Copyright 2013 jQuery Foundation, Inc. and other contributors
4544 * Released under the MIT license
4545 * http://jquery.org/license
4546 *
4547 * Date: 2014-02-05
4548 */
4549(function( window ) {
4550
4551var i,
4552	support,
4553	Expr,
4554	getText,
4555	isXML,
4556	compile,
4557	select,
4558	outermostContext,
4559	sortInput,
4560	hasDuplicate,
4561
4562	setDocument,
4563	document,
4564	docElem,
4565	documentIsHTML,
4566	rbuggyQSA,
4567	rbuggyMatches,
4568	matches,
4569	contains,
4570
4571	expando = "sizzle" + -(new Date()),
4572	preferredDoc = window.document,
4573	dirruns = 0,
4574	done = 0,
4575	classCache = createCache(),
4576	tokenCache = createCache(),
4577	compilerCache = createCache(),
4578	sortOrder = function( a, b ) {
4579		if ( a === b ) {
4580			hasDuplicate = true;
4581		}
4582		return 0;
4583	},
4584
4585	strundefined = typeof undefined,
4586	MAX_NEGATIVE = 1 << 31,
4587
4588	hasOwn = ({}).hasOwnProperty,
4589	arr = [],
4590	pop = arr.pop,
4591	push_native = arr.push,
4592	push = arr.push,
4593	slice = arr.slice,
4594	indexOf = arr.indexOf || function( elem ) {
4595		var i = 0,
4596			len = this.length;
4597		for ( ; i < len; i++ ) {
4598			if ( this[i] === elem ) {
4599				return i;
4600			}
4601		}
4602		return -1;
4603	},
4604
4605	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
4606
4607
4608	whitespace = "[\\x20\\t\\r\\n\\f]",
4609	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
4610
4611	identifier = characterEncoding.replace( "w", "w#" ),
4612
4613	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
4614		"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
4615
4616	pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
4617
4618	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
4619
4620	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
4621	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
4622
4623	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
4624
4625	rpseudo = new RegExp( pseudos ),
4626	ridentifier = new RegExp( "^" + identifier + "$" ),
4627
4628	matchExpr = {
4629		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
4630		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
4631		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
4632		"ATTR": new RegExp( "^" + attributes ),
4633		"PSEUDO": new RegExp( "^" + pseudos ),
4634		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
4635			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
4636			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
4637		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
4638		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
4639			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-
vendor: 38,618 bytes, lines 4639-6195
4639]|$)", "i" )
4640	},
4641
4642	rinputs = /^(?:input|select|textarea|button)$/i,
4643	rheader = /^h\d$/i,
4644
4645	rnative = /^[^{]+\{\s*\[native \w/,
4646
4647	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
4648
4649	rsibling = /[+~]/,
4650	rescape = /'|\\/g,
4651
4652	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
4653	funescape = function( _, escaped, escapedWhitespace ) {
4654		var high = "0x" + escaped - 0x10000;
4655		return high !== high || escapedWhitespace ?
4656			escaped :
4657			high < 0 ?
4658				String.fromCharCode( high + 0x10000 ) :
4659				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
4660	};
4661
4662try {
4663	push.apply(
4664		(arr = slice.call( preferredDoc.childNodes )),
4665		preferredDoc.childNodes
4666	);
4667	arr[ preferredDoc.childNodes.length ].nodeType;
4668} catch ( e ) {
4669	push = { apply: arr.length ?
4670
4671		function( target, els ) {
4672			push_native.apply( target, slice.call(els) );
4673		} :
4674
4675		function( target, els ) {
4676			var j = target.length,
4677				i = 0;
4678			while ( (target[j++] = els[i++]) ) {}
4679			target.length = j - 1;
4680		}
4681	};
4682}
4683
4684function Sizzle( selector, context, results, seed ) {
4685	var match, elem, m, nodeType,
4686		i, groups, old, nid, newContext, newSelector;
4687
4688	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
4689		setDocument( context );
4690	}
4691
4692	context = context || document;
4693	results = results || [];
4694
4695	if ( !selector || typeof selector !== "string" ) {
4696		return results;
4697	}
4698
4699	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
4700		return [];
4701	}
4702
4703	if ( documentIsHTML && !seed ) {
4704
4705		if ( (match = rquickExpr.exec( selector )) ) {
4706			if ( (m = match[1]) ) {
4707				if ( nodeType === 9 ) {
4708					elem = context.getElementById( m );
4709					if ( elem && elem.parentNode ) {
4710						if ( elem.id === m ) {
4711							results.push( elem );
4712							return results;
4713						}
4714					} else {
4715						return results;
4716					}
4717				} else {
4718					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
4719						contains( context, elem ) && elem.id === m ) {
4720						results.push( elem );
4721						return results;
4722					}
4723				}
4724
4725			} else if ( match[2] ) {
4726				push.apply( results, context.getElementsByTagName( selector ) );
4727				return results;
4728
4729			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
4730				push.apply( results, context.getElementsByClassName( m ) );
4731				return results;
4732			}
4733		}
4734
4735		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
4736			nid = old = expando;
4737			newContext = context;
4738			newSelector = nodeType === 9 && selector;
4739
4740			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
4741				groups = tokenize( selector );
4742
4743				if ( (old = context.getAttribute("id")) ) {
4744					nid = old.replace( rescape, "\\$&" );
4745				} else {
4746					context.setAttribute( "id", nid );
4747				}
4748				nid = "[id='" + nid + "'] ";
4749
4750				i = groups.length;
4751				while ( i-- ) {
4752					groups[i] = nid + toSelector( groups[i] );
4753				}
4754				newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
4755				newSelector = groups.join(",");
4756			}
4757
4758			if ( newSelector ) {
4759				try {
4760					push.apply( results,
4761						newContext.querySelectorAll( newSelector )
4762					);
4763					return results;
4764				} catch(qsaError) {
4765				} finally {
4766					if ( !old ) {
4767						context.removeAttribute("id");
4768					}
4769				}
4770			}
4771		}
4772	}
4773
4774	return select( selector.replace( rtrim, "$1" ), context, results, seed );
4775}
4776
4777/**
4778 * Create key-value caches of limited size
4779 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
4780 *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
4781 *	deleting the oldest entry
4782 */
4783function createCache() {
4784	var keys = [];
4785
4786	function cache( key, value ) {
4787		if ( keys.push( key + " " ) > Expr.cacheLength ) {
4788			delete cache[ keys.shift() ];
4789		}
4790		return (cache[ key + " " ] = value);
4791	}
4792	return cache;
4793}
4794
4795/**
4796 * Mark a function for special use by Sizzle
4797 * @param {Function} fn The function to mark
4798 */
4799function markFunction( fn ) {
4800	fn[ expando ] = true;
4801	return fn;
4802}
4803
4804/**
4805 * Support testing using an element
4806 * @param {Function} fn Passed the created div and expects a boolean result
4807 */
4808function assert( fn ) {
4809	var div = document.createElement("div");
4810
4811	try {
4812		return !!fn( div );
4813	} catch (e) {
4814		return false;
4815	} finally {
4816		if ( div.parentNode ) {
4817			div.parentNode.removeChild( div );
4818		}
4819		div = null;
4820	}
4821}
4822
4823/**
4824 * Adds the same handler for all of the specified attrs
4825 * @param {String} attrs Pipe-separated list of attributes
4826 * @param {Function} handler The method that will be applied
4827 */
4828function addHandle( attrs, handler ) {
4829	var arr = attrs.split("|"),
4830		i = attrs.length;
4831
4832	while ( i-- ) {
4833		Expr.attrHandle[ arr[i] ] = handler;
4834	}
4835}
4836
4837/**
4838 * Checks document order of two siblings
4839 * @param {Element} a
4840 * @param {Element} b
4841 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
4842 */
4843function siblingCheck( a, b ) {
4844	var cur = b && a,
4845		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
4846			( ~b.sourceIndex || MAX_NEGATIVE ) -
4847			( ~a.sourceIndex || MAX_NEGATIVE );
4848
4849	if ( diff ) {
4850		return diff;
4851	}
4852
4853	if ( cur ) {
4854		while ( (cur = cur.nextSibling) ) {
4855			if ( cur === b ) {
4856				return -1;
4857			}
4858		}
4859	}
4860
4861	return a ? 1 : -1;
4862}
4863
4864/**
4865 * Returns a function to use in pseudos for input types
4866 * @param {String} type
4867 */
4868function createInputPseudo( type ) {
4869	return function( elem ) {
4870		var name = elem.nodeName.toLowerCase();
4871		return name === "input" && elem.type === type;
4872	};
4873}
4874
4875/**
4876 * Returns a function to use in pseudos for buttons
4877 * @param {String} type
4878 */
4879function createButtonPseudo( type ) {
4880	return function( elem ) {
4881		var name = elem.nodeName.toLowerCase();
4882		return (name === "input" || name === "button") && elem.type === type;
4883	};
4884}
4885
4886/**
4887 * Returns a function to use in pseudos for positionals
4888 * @param {Function} fn
4889 */
4890function createPositionalPseudo( fn ) {
4891	return markFunction(function( argument ) {
4892		argument = +argument;
4893		return markFunction(function( seed, matches ) {
4894			var j,
4895				matchIndexes = fn( [], seed.length, argument ),
4896				i = matchIndexes.length;
4897
4898			while ( i-- ) {
4899				if ( seed[ (j = matchIndexes[i]) ] ) {
4900					seed[j] = !(matches[j] = seed[j]);
4901				}
4902			}
4903		});
4904	});
4905}
4906
4907/**
4908 * Checks a node for validity as a Sizzle context
4909 * @param {Element|Object=} context
4910 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
4911 */
4912function testContext( context ) {
4913	return context && typeof context.getElementsByTagName !== strundefined && context;
4914}
4915
4916support = Sizzle.support = {};
4917
4918/**
4919 * Detects XML nodes
4920 * @param {Element|Object} elem An element or a document
4921 * @returns {Boolean} True iff elem is a non-HTML XML node
4922 */
4923isXML = Sizzle.isXML = function( elem ) {
4924	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
4925	return documentElement ? documentElement.nodeName !== "HTML" : false;
4926};
4927
4928/**
4929 * Sets document-related variables once based on the current document
4930 * @param {Element|Object} [doc] An element or document object to use to set the document
4931 * @returns {Object} Returns the current document
4932 */
4933setDocument = Sizzle.setDocument = function( node ) {
4934	var hasCompare,
4935		doc = node ? node.ownerDocument || node : preferredDoc,
4936		parent = doc.defaultView;
4937
4938	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
4939		return document;
4940	}
4941
4942	document = doc;
4943	docElem = doc.documentElement;
4944
4945	documentIsHTML = !isXML( doc );
4946
4947	if ( parent && parent !== parent.top ) {
4948		if ( parent.addEventListener ) {
4949			parent.addEventListener( "unload", function() {
4950				setDocument();
4951			}, false );
4952		} else if ( parent.attachEvent ) {
4953			parent.attachEvent( "onunload", function() {
4954				setDocument();
4955			});
4956		}
4957	}
4958
4959	/* Attributes
4960	---------------------------------------------------------------------- */
4961
4962	support.attributes = assert(function( div ) {
4963		div.className = "i";
4964		return !div.getAttribute("className");
4965	});
4966
4967	/* getElement(s)By*
4968	---------------------------------------------------------------------- */
4969
4970	support.getElementsByTagName = assert(function( div ) {
4971		div.appendChild( doc.createComment("") );
4972		return !div.getElementsByTagName("*").length;
4973	});
4974
4975	support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
4976		div.innerHTML = "<div class='a'></div><div class='a i'></div>";
4977
4978		div.firstChild.className = "i";
4979		return div.getElementsByClassName("i").length === 2;
4980	});
4981
4982	support.getById = assert(function( div ) {
4983		docElem.appendChild( div ).id = expando;
4984		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
4985	});
4986
4987	if ( support.getById ) {
4988		Expr.find["ID"] = function( id, context ) {
4989			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
4990				var m = context.getElementById( id );
4991				return m && m.parentNode ? [m] : [];
4992			}
4993		};
4994		Expr.filter["ID"] = function( id ) {
4995			var attrId = id.replace( runescape, funescape );
4996			return function( elem ) {
4997				return elem.getAttribute("id") === attrId;
4998			};
4999		};
5000	} else {
5001		delete Expr.find["ID"];
5002
5003		Expr.filter["ID"] =  function( id ) {
5004			var attrId = id.replace( runescape, funescape );
5005			return function( elem ) {
5006				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
5007				return node && node.value === attrId;
5008			};
5009		};
5010	}
5011
5012	Expr.find["TAG"] = support.getElementsByTagName ?
5013		function( tag, context ) {
5014			if ( typeof context.getElementsByTagName !== strundefined ) {
5015				return context.getElementsByTagName( tag );
5016			}
5017		} :
5018		function( tag, context ) {
5019			var elem,
5020				tmp = [],
5021				i = 0,
5022				results = context.getElementsByTagName( tag );
5023
5024			if ( tag === "*" ) {
5025				while ( (elem = results[i++]) ) {
5026					if ( elem.nodeType === 1 ) {
5027						tmp.push( elem );
5028					}
5029				}
5030
5031				return tmp;
5032			}
5033			return results;
5034		};
5035
5036	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
5037		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
5038			return context.getElementsByClassName( className );
5039		}
5040	};
5041
5042	/* QSA/matchesSelector
5043	---------------------------------------------------------------------- */
5044
5045
5046	rbuggyMatches = [];
5047
5048	rbuggyQSA = [];
5049
5050	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
5051		assert(function( div ) {
5052			div.innerHTML = "<select t=''><option selected=''></option></select>";
5053
5054			if ( div.querySelectorAll("[t^='']").length ) {
5055				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
5056			}
5057
5058			if ( !div.querySelectorAll("[selected]").length ) {
5059				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
5060			}
5061
5062			if ( !div.querySelectorAll(":checked").length ) {
5063				rbuggyQSA.push(":checked");
5064			}
5065		});
5066
5067		assert(function( div ) {
5068			var input = doc.createElement("input");
5069			input.setAttribute( "type", "hidden" );
5070			div.appendChild( input ).setAttribute( "name", "D" );
5071
5072			if ( div.querySelectorAll("[name=d]").length ) {
5073				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
5074			}
5075
5076			if ( !div.querySelectorAll(":enabled").length ) {
5077				rbuggyQSA.push( ":enabled", ":disabled" );
5078			}
5079
5080			div.querySelectorAll("*,:x");
5081			rbuggyQSA.push(",.*:");
5082		});
5083	}
5084
5085	if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
5086		docElem.mozMatchesSelector ||
5087		docElem.oMatchesSelector ||
5088		docElem.msMatchesSelector) )) ) {
5089
5090		assert(function( div ) {
5091			support.disconnectedMatch = matches.call( div, "div" );
5092
5093			matches.call( div, "[s!='']:x" );
5094			rbuggyMatches.push( "!=", pseudos );
5095		});
5096	}
5097
5098	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
5099	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
5100
5101	/* Contains
5102	---------------------------------------------------------------------- */
5103	hasCompare = rnative.test( docElem.compareDocumentPosition );
5104
5105	contains = hasCompare || rnative.test( docElem.contains ) ?
5106		function( a, b ) {
5107			var adown = a.nodeType === 9 ? a.documentElement : a,
5108				bup = b && b.parentNode;
5109			return a === bup || !!( bup && bup.nodeType === 1 && (
5110				adown.contains ?
5111					adown.contains( bup ) :
5112					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
5113			));
5114		} :
5115		function( a, b ) {
5116			if ( b ) {
5117				while ( (b = b.parentNode) ) {
5118					if ( b === a ) {
5119						return true;
5120					}
5121				}
5122			}
5123			return false;
5124		};
5125
5126	/* Sorting
5127	---------------------------------------------------------------------- */
5128
5129	sortOrder = hasCompare ?
5130	function( a, b ) {
5131
5132		if ( a === b ) {
5133			hasDuplicate = true;
5134			return 0;
5135		}
5136
5137		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
5138		if ( compare ) {
5139			return compare;
5140		}
5141
5142		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
5143			a.compareDocumentPosition( b ) :
5144
5145			1;
5146
5147		if ( compare & 1 ||
5148			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
5149
5150			if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
5151				return -1;
5152			}
5153			if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
5154				return 1;
5155			}
5156
5157			return sortInput ?
5158				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
5159				0;
5160		}
5161
5162		return compare & 4 ? -1 : 1;
5163	} :
5164	function( a, b ) {
5165		if ( a === b ) {
5166			hasDuplicate = true;
5167			return 0;
5168		}
5169
5170		var cur,
5171			i = 0,
5172			aup = a.parentNode,
5173			bup = b.parentNode,
5174			ap = [ a ],
5175			bp = [ b ];
5176
5177		if ( !aup || !bup ) {
5178			return a === doc ? -1 :
5179				b === doc ? 1 :
5180				aup ? -1 :
5181				bup ? 1 :
5182				sortInput ?
5183				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
5184				0;
5185
5186		} else if ( aup === bup ) {
5187			return siblingCheck( a, b );
5188		}
5189
5190		cur = a;
5191		while ( (cur = cur.parentNode) ) {
5192			ap.unshift( cur );
5193		}
5194		cur = b;
5195		while ( (cur = cur.parentNode) ) {
5196			bp.unshift( cur );
5197		}
5198
5199		while ( ap[i] === bp[i] ) {
5200			i++;
5201		}
5202
5203		return i ?
5204			siblingCheck( ap[i], bp[i] ) :
5205
5206			ap[i] === preferredDoc ? -1 :
5207			bp[i] === preferredDoc ? 1 :
5208			0;
5209	};
5210
5211	return doc;
5212};
5213
5214Sizzle.matches = function( expr, elements ) {
5215	return Sizzle( expr, null, null, elements );
5216};
5217
5218Sizzle.matchesSelector = function( elem, expr ) {
5219	if ( ( elem.ownerDocument || elem ) !== document ) {
5220		setDocument( elem );
5221	}
5222
5223	expr = expr.replace( rattributeQuotes, "='$1']" );
5224
5225	if ( support.matchesSelector && documentIsHTML &&
5226		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
5227		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
5228
5229		try {
5230			var ret = matches.call( elem, expr );
5231
5232			if ( ret || support.disconnectedMatch ||
5233					elem.document && elem.document.nodeType !== 11 ) {
5234				return ret;
5235			}
5236		} catch(e) {}
5237	}
5238
5239	return Sizzle( expr, document, null, [elem] ).length > 0;
5240};
5241
5242Sizzle.contains = function( context, elem ) {
5243	if ( ( context.ownerDocument || context ) !== document ) {
5244		setDocument( context );
5245	}
5246	return contains( context, elem );
5247};
5248
5249Sizzle.attr = function( elem, name ) {
5250	if ( ( elem.ownerDocument || elem ) !== document ) {
5251		setDocument( elem );
5252	}
5253
5254	var fn = Expr.attrHandle[ name.toLowerCase() ],
5255		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
5256			fn( elem, name, !documentIsHTML ) :
5257			undefined;
5258
5259	return val !== undefined ?
5260		val :
5261		support.attributes || !documentIsHTML ?
5262			elem.getAttribute( name ) :
5263			(val = elem.getAttributeNode(name)) && val.specified ?
5264				val.value :
5265				null;
5266};
5267
5268Sizzle.error = function( msg ) {
5269	throw new Error( "Syntax error, unrecognized expression: " + msg );
5270};
5271
5272/**
5273 * Document sorting and removing duplicates
5274 * @param {ArrayLike} results
5275 */
5276Sizzle.uniqueSort = function( results ) {
5277	var elem,
5278		duplicates = [],
5279		j = 0,
5280		i = 0;
5281
5282	hasDuplicate = !support.detectDuplicates;
5283	sortInput = !support.sortStable && results.slice( 0 );
5284	results.sort( sortOrder );
5285
5286	if ( hasDuplicate ) {
5287		while ( (elem = results[i++]) ) {
5288			if ( elem === results[ i ] ) {
5289				j = duplicates.push( i );
5290			}
5291		}
5292		while ( j-- ) {
5293			results.splice( duplicates[ j ], 1 );
5294		}
5295	}
5296
5297	sortInput = null;
5298
5299	return results;
5300};
5301
5302/**
5303 * Utility function for retrieving the text value of an array of DOM nodes
5304 * @param {Array|Element} elem
5305 */
5306getText = Sizzle.getText = function( elem ) {
5307	var node,
5308		ret = "",
5309		i = 0,
5310		nodeType = elem.nodeType;
5311
5312	if ( !nodeType ) {
5313		while ( (node = elem[i++]) ) {
5314			ret += getText( node );
5315		}
5316	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
5317		if ( typeof elem.textContent === "string" ) {
5318			return elem.textContent;
5319		} else {
5320			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
5321				ret += getText( elem );
5322			}
5323		}
5324	} else if ( nodeType === 3 || nodeType === 4 ) {
5325		return elem.nodeValue;
5326	}
5327
5328	return ret;
5329};
5330
5331Expr = Sizzle.selectors = {
5332
5333	cacheLength: 50,
5334
5335	createPseudo: markFunction,
5336
5337	match: matchExpr,
5338
5339	attrHandle: {},
5340
5341	find: {},
5342
5343	relative: {
5344		">": { dir: "parentNode", first: true },
5345		" ": { dir: "parentNode" },
5346		"+": { dir: "previousSibling", first: true },
5347		"~": { dir: "previousSibling" }
5348	},
5349
5350	preFilter: {
5351		"ATTR": function( match ) {
5352			match[1] = match[1].replace( runescape, funescape );
5353
5354			match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
5355
5356			if ( match[2] === "~=" ) {
5357				match[3] = " " + match[3] + " ";
5358			}
5359
5360			return match.slice( 0, 4 );
5361		},
5362
5363		"CHILD": function( match ) {
5364			/* matches from matchExpr["CHILD"]
5365				1 type (only|nth|...)
5366				2 what (child|of-type)
5367				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
5368				4 xn-component of xn+y argument ([+-]?\d*n|)
5369				5 sign of xn-component
5370				6 x of xn-component
5371				7 sign of y-component
5372				8 y of y-component
5373			*/
5374			match[1] = match[1].toLowerCase();
5375
5376			if ( match[1].slice( 0, 3 ) === "nth" ) {
5377				if ( !match[3] ) {
5378					Sizzle.error( match[0] );
5379				}
5380
5381				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
5382				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
5383
5384			} else if ( match[3] ) {
5385				Sizzle.error( match[0] );
5386			}
5387
5388			return match;
5389		},
5390
5391		"PSEUDO": function( match ) {
5392			var excess,
5393				unquoted = !match[5] && match[2];
5394
5395			if ( matchExpr["CHILD"].test( match[0] ) ) {
5396				return null;
5397			}
5398
5399			if ( match[3] && match[4] !== undefined ) {
5400				match[2] = match[4];
5401
5402			} else if ( unquoted && rpseudo.test( unquoted ) &&
5403				(excess = tokenize( unquoted, true )) &&
5404				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
5405
5406				match[0] = match[0].slice( 0, excess );
5407				match[2] = unquoted.slice( 0, excess );
5408			}
5409
5410			return match.slice( 0, 3 );
5411		}
5412	},
5413
5414	filter: {
5415
5416		"TAG": function( nodeNameSelector ) {
5417			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
5418			return nodeNameSelector === "*" ?
5419				function() { return true; } :
5420				function( elem ) {
5421					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
5422				};
5423		},
5424
5425		"CLASS": function( className ) {
5426			var pattern = classCache[ className + " " ];
5427
5428			return pattern ||
5429				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
5430				classCache( className, function( elem ) {
5431					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
5432				});
5433		},
5434
5435		"ATTR": function( name, operator, check ) {
5436			return function( elem ) {
5437				var result = Sizzle.attr( elem, name );
5438
5439				if ( result == null ) {
5440					return operator === "!=";
5441				}
5442				if ( !operator ) {
5443					return true;
5444				}
5445
5446				result += "";
5447
5448				return operator === "=" ? result === check :
5449					operator === "!=" ? result !== check :
5450					operator === "^=" ? check && result.indexOf( check ) === 0 :
5451					operator === "*=" ? check && result.indexOf( check ) > -1 :
5452					operator === "$=" ? check && result.slice( -check.length ) === check :
5453					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
5454					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
5455					false;
5456			};
5457		},
5458
5459		"CHILD": function( type, what, argument, first, last ) {
5460			var simple = type.slice( 0, 3 ) !== "nth",
5461				forward = type.slice( -4 ) !== "last",
5462				ofType = what === "of-type";
5463
5464			return first === 1 && last === 0 ?
5465
5466				function( elem ) {
5467					return !!elem.parentNode;
5468				} :
5469
5470				function( elem, context, xml ) {
5471					var cache, outerCache, node, diff, nodeIndex, start,
5472						dir = simple !== forward ? "nextSibling" : "previousSibling",
5473						parent = elem.parentNode,
5474						name = ofType && elem.nodeName.toLowerCase(),
5475						useCache = !xml && !ofType;
5476
5477					if ( parent ) {
5478
5479						if ( simple ) {
5480							while ( dir ) {
5481								node = elem;
5482								while ( (node = node[ dir ]) ) {
5483									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
5484										return false;
5485									}
5486								}
5487								start = dir = type === "only" && !start && "nextSibling";
5488							}
5489							return true;
5490						}
5491
5492						start = [ forward ? parent.firstChild : parent.lastChild ];
5493
5494						if ( forward && useCache ) {
5495							outerCache = parent[ expando ] || (parent[ expando ] = {});
5496							cache = outerCache[ type ] || [];
5497							nodeIndex = cache[0] === dirruns && cache[1];
5498							diff = cache[0] === dirruns && cache[2];
5499							node = nodeIndex && parent.childNodes[ nodeIndex ];
5500
5501							while ( (node = ++nodeIndex && node && node[ dir ] ||
5502
5503								(diff = nodeIndex = 0) || start.pop()) ) {
5504
5505								if ( node.nodeType === 1 && ++diff && node === elem ) {
5506									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
5507									break;
5508								}
5509							}
5510
5511						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
5512							diff = cache[1];
5513
5514						} else {
5515							while ( (node = ++nodeIndex && node && node[ dir ] ||
5516								(diff = nodeIndex = 0) || start.pop()) ) {
5517
5518								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
5519									if ( useCache ) {
5520										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
5521									}
5522
5523									if ( node === elem ) {
5524										break;
5525									}
5526								}
5527							}
5528						}
5529
5530						diff -= last;
5531						return diff === first || ( diff % first === 0 && diff / first >= 0 );
5532					}
5533				};
5534		},
5535
5536		"PSEUDO": function( pseudo, argument ) {
5537			var args,
5538				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
5539					Sizzle.error( "unsupported pseudo: " + pseudo );
5540
5541			if ( fn[ expando ] ) {
5542				return fn( argument );
5543			}
5544
5545			if ( fn.length > 1 ) {
5546				args = [ pseudo, pseudo, "", argument ];
5547				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
5548					markFunction(function( seed, matches ) {
5549						var idx,
5550							matched = fn( seed, argument ),
5551							i = matched.length;
5552						while ( i-- ) {
5553							idx = indexOf.call( seed, matched[i] );
5554							seed[ idx ] = !( matches[ idx ] = matched[i] );
5555						}
5556					}) :
5557					function( elem ) {
5558						return fn( elem, 0, args );
5559					};
5560			}
5561
5562			return fn;
5563		}
5564	},
5565
5566	pseudos: {
5567		"not": markFunction(function( selector ) {
5568			var input = [],
5569				results = [],
5570				matcher = compile( selector.replace( rtrim, "$1" ) );
5571
5572			return matcher[ expando ] ?
5573				markFunction(function( seed, matches, context, xml ) {
5574					var elem,
5575						unmatched = matcher( seed, null, xml, [] ),
5576						i = seed.length;
5577
5578					while ( i-- ) {
5579						if ( (elem = unmatched[i]) ) {
5580							seed[i] = !(matches[i] = elem);
5581						}
5582					}
5583				}) :
5584				function( elem, context, xml ) {
5585					input[0] = elem;
5586					matcher( input, null, xml, results );
5587					return !results.pop();
5588				};
5589		}),
5590
5591		"has": markFunction(function( selector ) {
5592			return function( elem ) {
5593				return Sizzle( selector, elem ).length > 0;
5594			};
5595		}),
5596
5597		"contains": markFunction(function( text ) {
5598			return function( elem ) {
5599				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
5600			};
5601		}),
5602
5603		"lang": markFunction( function( lang ) {
5604			if ( !ridentifier.test(lang || "") ) {
5605				Sizzle.error( "unsupported lang: " + lang );
5606			}
5607			lang = lang.replace( runescape, funescape ).toLowerCase();
5608			return function( elem ) {
5609				var elemLang;
5610				do {
5611					if ( (elemLang = documentIsHTML ?
5612						elem.lang :
5613						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
5614
5615						elemLang = elemLang.toLowerCase();
5616						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
5617					}
5618				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
5619				return false;
5620			};
5621		}),
5622
5623		"target": function( elem ) {
5624			var hash = window.location && window.location.hash;
5625			return hash && hash.slice( 1 ) === elem.id;
5626		},
5627
5628		"root": function( elem ) {
5629			return elem === docElem;
5630		},
5631
5632		"focus": function( elem ) {
5633			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
5634		},
5635
5636		"enabled": function( elem ) {
5637			return elem.disabled === false;
5638		},
5639
5640		"disabled": function( elem ) {
5641			return elem.disabled === true;
5642		},
5643
5644		"checked": function( elem ) {
5645			var nodeName = elem.nodeName.toLowerCase();
5646			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
5647		},
5648
5649		"selected": function( elem ) {
5650			if ( elem.parentNode ) {
5651				elem.parentNode.selectedIndex;
5652			}
5653
5654			return elem.selected === true;
5655		},
5656
5657		"empty": function( elem ) {
5658			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
5659				if ( elem.nodeType < 6 ) {
5660					return false;
5661				}
5662			}
5663			return true;
5664		},
5665
5666		"parent": function( elem ) {
5667			return !Expr.pseudos["empty"]( elem );
5668		},
5669
5670		"header": function( elem ) {
5671			return rheader.test( elem.nodeName );
5672		},
5673
5674		"input": function( elem ) {
5675			return rinputs.test( elem.nodeName );
5676		},
5677
5678		"button": function( elem ) {
5679			var name = elem.nodeName.toLowerCase();
5680			return name === "input" && elem.type === "button" || name === "button";
5681		},
5682
5683		"text": function( elem ) {
5684			var attr;
5685			return elem.nodeName.toLowerCase() === "input" &&
5686				elem.type === "text" &&
5687
5688				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
5689		},
5690
5691		"first": createPositionalPseudo(function() {
5692			return [ 0 ];
5693		}),
5694
5695		"last": createPositionalPseudo(function( matchIndexes, length ) {
5696			return [ length - 1 ];
5697		}),
5698
5699		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
5700			return [ argument < 0 ? argument + length : argument ];
5701		}),
5702
5703		"even": createPositionalPseudo(function( matchIndexes, length ) {
5704			var i = 0;
5705			for ( ; i < length; i += 2 ) {
5706				matchIndexes.push( i );
5707			}
5708			return matchIndexes;
5709		}),
5710
5711		"odd": createPositionalPseudo(function( matchIndexes, length ) {
5712			var i = 1;
5713			for ( ; i < length; i += 2 ) {
5714				matchIndexes.push( i );
5715			}
5716			return matchIndexes;
5717		}),
5718
5719		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
5720			var i = argument < 0 ? argument + length : argument;
5721			for ( ; --i >= 0; ) {
5722				matchIndexes.push( i );
5723			}
5724			return matchIndexes;
5725		}),
5726
5727		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
5728			var i = argument < 0 ? argument + length : argument;
5729			for ( ; ++i < length; ) {
5730				matchIndexes.push( i );
5731			}
5732			return matchIndexes;
5733		})
5734	}
5735};
5736
5737Expr.pseudos["nth"] = Expr.pseudos["eq"];
5738
5739for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
5740	Expr.pseudos[ i ] = createInputPseudo( i );
5741}
5742for ( i in { submit: true, reset: true } ) {
5743	Expr.pseudos[ i ] = createButtonPseudo( i );
5744}
5745
5746function setFilters() {}
5747setFilters.prototype = Expr.filters = Expr.pseudos;
5748Expr.setFilters = new setFilters();
5749
5750function tokenize( selector, parseOnly ) {
5751	var matched, match, tokens, type,
5752		soFar, groups, preFilters,
5753		cached = tokenCache[ selector + " " ];
5754
5755	if ( cached ) {
5756		return parseOnly ? 0 : cached.slice( 0 );
5757	}
5758
5759	soFar = selector;
5760	groups = [];
5761	preFilters = Expr.preFilter;
5762
5763	while ( soFar ) {
5764
5765		if ( !matched || (match = rcomma.exec( soFar )) ) {
5766			if ( match ) {
5767				soFar = soFar.slice( match[0].length ) || soFar;
5768			}
5769			groups.push( (tokens = []) );
5770		}
5771
5772		matched = false;
5773
5774		if ( (match = rcombinators.exec( soFar )) ) {
5775			matched = match.shift();
5776			tokens.push({
5777				value: matched,
5778				type: match[0].replace( rtrim, " " )
5779			});
5780			soFar = soFar.slice( matched.length );
5781		}
5782
5783		for ( type in Expr.filter ) {
5784			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
5785				(match = preFilters[ type ]( match ))) ) {
5786				matched = match.shift();
5787				tokens.push({
5788					value: matched,
5789					type: type,
5790					matches: match
5791				});
5792				soFar = soFar.slice( matched.length );
5793			}
5794		}
5795
5796		if ( !matched ) {
5797			break;
5798		}
5799	}
5800
5801	return parseOnly ?
5802		soFar.length :
5803		soFar ?
5804			Sizzle.error( selector ) :
5805			tokenCache( selector, groups ).slice( 0 );
5806}
5807
5808function toSelector( tokens ) {
5809	var i = 0,
5810		len = tokens.length,
5811		selector = "";
5812	for ( ; i < len; i++ ) {
5813		selector += tokens[i].value;
5814	}
5815	return selector;
5816}
5817
5818function addCombinator( matcher, combinator, base ) {
5819	var dir = combinator.dir,
5820		checkNonElements = base && dir === "parentNode",
5821		doneName = done++;
5822
5823	return combinator.first ?
5824		function( elem, context, xml ) {
5825			while ( (elem = elem[ dir ]) ) {
5826				if ( elem.nodeType === 1 || checkNonElements ) {
5827					return matcher( elem, context, xml );
5828				}
5829			}
5830		} :
5831
5832		function( elem, context, xml ) {
5833			var oldCache, outerCache,
5834				newCache = [ dirruns, doneName ];
5835
5836			if ( xml ) {
5837				while ( (elem = elem[ dir ]) ) {
5838					if ( elem.nodeType === 1 || checkNonElements ) {
5839						if ( matcher( elem, context, xml ) ) {
5840							return true;
5841						}
5842					}
5843				}
5844			} else {
5845				while ( (elem = elem[ dir ]) ) {
5846					if ( elem.nodeType === 1 || checkNonElements ) {
5847						outerCache = elem[ expando ] || (elem[ expando ] = {});
5848						if ( (oldCache = outerCache[ dir ]) &&
5849							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
5850
5851							return (newCache[ 2 ] = oldCache[ 2 ]);
5852						} else {
5853							outerCache[ dir ] = newCache;
5854
5855							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
5856								return true;
5857							}
5858						}
5859					}
5860				}
5861			}
5862		};
5863}
5864
5865function elementMatcher( matchers ) {
5866	return matchers.length > 1 ?
5867		function( elem, context, xml ) {
5868			var i = matchers.length;
5869			while ( i-- ) {
5870				if ( !matchers[i]( elem, context, xml ) ) {
5871					return false;
5872				}
5873			}
5874			return true;
5875		} :
5876		matchers[0];
5877}
5878
5879function multipleContexts( selector, contexts, results ) {
5880	var i = 0,
5881		len = contexts.length;
5882	for ( ; i < len; i++ ) {
5883		Sizzle( selector, contexts[i], results );
5884	}
5885	return results;
5886}
5887
5888function condense( unmatched, map, filter, context, xml ) {
5889	var elem,
5890		newUnmatched = [],
5891		i = 0,
5892		len = unmatched.length,
5893		mapped = map != null;
5894
5895	for ( ; i < len; i++ ) {
5896		if ( (elem = unmatched[i]) ) {
5897			if ( !filter || filter( elem, context, xml ) ) {
5898				newUnmatched.push( elem );
5899				if ( mapped ) {
5900					map.push( i );
5901				}
5902			}
5903		}
5904	}
5905
5906	return newUnmatched;
5907}
5908
5909function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
5910	if ( postFilter && !postFilter[ expando ] ) {
5911		postFilter = setMatcher( postFilter );
5912	}
5913	if ( postFinder && !postFinder[ expando ] ) {
5914		postFinder = setMatcher( postFinder, postSelector );
5915	}
5916	return markFunction(function( seed, results, context, xml ) {
5917		var temp, i, elem,
5918			preMap = [],
5919			postMap = [],
5920			preexisting = results.length,
5921
5922			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
5923
5924			matcherIn = preFilter && ( seed || !selector ) ?
5925				condense( elems, preMap, preFilter, context, xml ) :
5926				elems,
5927
5928			matcherOut = matcher ?
5929				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
5930
5931					[] :
5932
5933					results :
5934				matcherIn;
5935
5936		if ( matcher ) {
5937			matcher( matcherIn, matcherOut, context, xml );
5938		}
5939
5940		if ( postFilter ) {
5941			temp = condense( matcherOut, postMap );
5942			postFilter( temp, [], context, xml );
5943
5944			i = temp.length;
5945			while ( i-- ) {
5946				if ( (elem = temp[i]) ) {
5947					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
5948				}
5949			}
5950		}
5951
5952		if ( seed ) {
5953			if ( postFinder || preFilter ) {
5954				if ( postFinder ) {
5955					temp = [];
5956					i = matcherOut.length;
5957					while ( i-- ) {
5958						if ( (elem = matcherOut[i]) ) {
5959							temp.push( (matcherIn[i] = elem) );
5960						}
5961					}
5962					postFinder( null, (matcherOut = []), temp, xml );
5963				}
5964
5965				i = matcherOut.length;
5966				while ( i-- ) {
5967					if ( (elem = matcherOut[i]) &&
5968						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
5969
5970						seed[temp] = !(results[temp] = elem);
5971					}
5972				}
5973			}
5974
5975		} else {
5976			matcherOut = condense(
5977				matcherOut === results ?
5978					matcherOut.splice( preexisting, matcherOut.length ) :
5979					matcherOut
5980			);
5981			if ( postFinder ) {
5982				postFinder( null, results, matcherOut, xml );
5983			} else {
5984				push.apply( results, matcherOut );
5985			}
5986		}
5987	});
5988}
5989
5990function matcherFromTokens( tokens ) {
5991	var checkContext, matcher, j,
5992		len = tokens.length,
5993		leadingRelative = Expr.relative[ tokens[0].type ],
5994		implicitRelative = leadingRelative || Expr.relative[" "],
5995		i = leadingRelative ? 1 : 0,
5996
5997		matchContext = addCombinator( function( elem ) {
5998			return elem === checkContext;
5999		}, implicitRelative, true ),
6000		matchAnyContext = addCombinator( function( elem ) {
6001			return indexOf.call( checkContext, elem ) > -1;
6002		}, implicitRelative, true ),
6003		matchers = [ function( elem, context, xml ) {
6004			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
6005				(checkContext = context).nodeType ?
6006					matchContext( elem, context, xml ) :
6007					matchAnyContext( elem, context, xml ) );
6008		} ];
6009
6010	for ( ; i < len; i++ ) {
6011		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
6012			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
6013		} else {
6014			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
6015
6016			if ( matcher[ expando ] ) {
6017				j = ++i;
6018				for ( ; j < len; j++ ) {
6019					if ( Expr.relative[ tokens[j].type ] ) {
6020						break;
6021					}
6022				}
6023				return setMatcher(
6024					i > 1 && elementMatcher( matchers ),
6025					i > 1 && toSelector(
6026						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
6027					).replace( rtrim, "$1" ),
6028					matcher,
6029					i < j && matcherFromTokens( tokens.slice( i, j ) ),
6030					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
6031					j < len && toSelector( tokens )
6032				);
6033			}
6034			matchers.push( matcher );
6035		}
6036	}
6037
6038	return elementMatcher( matchers );
6039}
6040
6041function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
6042	var bySet = setMatchers.length > 0,
6043		byElement = elementMatchers.length > 0,
6044		superMatcher = function( seed, context, xml, results, outermost ) {
6045			var elem, j, matcher,
6046				matchedCount = 0,
6047				i = "0",
6048				unmatched = seed && [],
6049				setMatched = [],
6050				contextBackup = outermostContext,
6051				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
6052				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
6053				len = elems.length;
6054
6055			if ( outermost ) {
6056				outermostContext = context !== document && context;
6057			}
6058
6059			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
6060				if ( byElement && elem ) {
6061					j = 0;
6062					while ( (matcher = elementMatchers[j++]) ) {
6063						if ( matcher( elem, context, xml ) ) {
6064							results.push( elem );
6065							break;
6066						}
6067					}
6068					if ( outermost ) {
6069						dirruns = dirrunsUnique;
6070					}
6071				}
6072
6073				if ( bySet ) {
6074					if ( (elem = !matcher && elem) ) {
6075						matchedCount--;
6076					}
6077
6078					if ( seed ) {
6079						unmatched.push( elem );
6080					}
6081				}
6082			}
6083
6084			matchedCount += i;
6085			if ( bySet && i !== matchedCount ) {
6086				j = 0;
6087				while ( (matcher = setMatchers[j++]) ) {
6088					matcher( unmatched, setMatched, context, xml );
6089				}
6090
6091				if ( seed ) {
6092					if ( matchedCount > 0 ) {
6093						while ( i-- ) {
6094							if ( !(unmatched[i] || setMatched[i]) ) {
6095								setMatched[i] = pop.call( results );
6096							}
6097						}
6098					}
6099
6100					setMatched = condense( setMatched );
6101				}
6102
6103				push.apply( results, setMatched );
6104
6105				if ( outermost && !seed && setMatched.length > 0 &&
6106					( matchedCount + setMatchers.length ) > 1 ) {
6107
6108					Sizzle.uniqueSort( results );
6109				}
6110			}
6111
6112			if ( outermost ) {
6113				dirruns = dirrunsUnique;
6114				outermostContext = contextBackup;
6115			}
6116
6117			return unmatched;
6118		};
6119
6120	return bySet ?
6121		markFunction( superMatcher ) :
6122		superMatcher;
6123}
6124
6125compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
6126	var i,
6127		setMatchers = [],
6128		elementMatchers = [],
6129		cached = compilerCache[ selector + " " ];
6130
6131	if ( !cached ) {
6132		if ( !match ) {
6133			match = tokenize( selector );
6134		}
6135		i = match.length;
6136		while ( i-- ) {
6137			cached = matcherFromTokens( match[i] );
6138			if ( cached[ expando ] ) {
6139				setMatchers.push( cached );
6140			} else {
6141				elementMatchers.push( cached );
6142			}
6143		}
6144
6145		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
6146
6147		cached.selector = selector;
6148	}
6149	return cached;
6150};
6151
6152/**
6153 * A low-level selection function that works with Sizzle's compiled
6154 *  selector functions
6155 * @param {String|Function} selector A selector or a pre-compiled
6156 *  selector function built with Sizzle.compile
6157 * @param {Element} context
6158 * @param {Array} [results]
6159 * @param {Array} [seed] A set of elements to match against
6160 */
6161select = Sizzle.select = function( selector, context, results, seed ) {
6162	var i, tokens, token, type, find,
6163		compiled = typeof selector === "function" && selector,
6164		match = !seed && tokenize( (selector = compiled.selector || selector) );
6165
6166	results = results || [];
6167
6168	if ( match.length === 1 ) {
6169
6170		tokens = match[0] = match[0].slice( 0 );
6171		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
6172				support.getById && context.nodeType === 9 && documentIsHTML &&
6173				Expr.relative[ tokens[1].type ] ) {
6174
6175			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
6176			if ( !context ) {
6177				return results;
6178
6179			} else if ( compiled ) {
6180				context = context.parentNode;
6181			}
6182
6183			selector = selector.slice( tokens.shift().value.length );
6184		}
6185
6186		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
6187		while ( i-- ) {
6188			token = tokens[i];
6189
6190			if ( Expr.relative[ (type = token.type) ] ) {
6191				break;
6192			}
6193			if ( (find = Expr.find[ type ]) ) {
6194				if ( (seed = find(
6195					token.matches[0].replace( runescape, funescape ),
6196					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
6197				)) ) {
6198
6199					tokens.splice( i, 1 );
6200					selector = seed.length && toSelector( tokens );
6201					if ( !selector ) {
6202						push.apply( results, seed );
6203						return results;
6204					}
6205
6206					break;
6207				}
6208			}
6209		}
6210	}
6211
6212	( compiled || compile( selector, match ) )(
6213		seed,
6214		context,
6215		!documentIsHTML,
6216		results,
6217		rsibling.test( selector ) && testContext( context.parentNode ) || context
6218	);
6219	return results;
6220};
6221
6222
6223support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
6224
6225support.detectDuplicates = !!hasDuplicate;
6226
6227setDocument();
6228
6229support.sortDetached = assert(function( div1 ) {
6230	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
6231});
6232
6233if ( !assert(function( div ) {
6234	div.innerHTML = "<a href='#'></a>";
6235	return div.firstChild.getAttribute("href") === "#" ;
6236}) ) {
6237	addHandle( "type|href|height|width", function( elem, name, isXML ) {
6238		if ( !isXML ) {
6239			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
6240		}
6241	});
6242}
6243
6244if ( !support.attributes || !assert(function( div ) {
6245	div.innerHTML = "<input/>";
6246	div.firstChild.setAttribute( "value", "" );
6247	return div.firstChild.getAttribute( "value" ) === "";
6248}) ) {
6249	addHandle( "value", function( elem, name, isXML ) {
6250		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
6251			return elem.defaultValue;
6252		}
6253	});
6254}
6255
6256if ( !assert(function( div ) {
6257	return div.getAttribute("disabled") == null;
6258}) ) {
6259	addHandle( booleans, function( elem, name, isXML ) {
6260		var val;
6261		if ( !isXML ) {
6262			return elem[ name ] === true ? name.toLowerCase() :
6263					(val = elem.getAttributeNode( name )) && val.specified ?
6264					val.value :
6265				null;
6266		}
6267	});
6268}
6269
6270if ( typeof define === "function" && define.amd ) {
6271	define(function() { return Sizzle; });
6272} else if ( typeof module !== "undefined" && module.exports ) {
6273	module.exports = Sizzle;
6274} else {
6275	window.Sizzle = Sizzle;
6276}
6277
6278})( window );
6279
6280;(function() {
6281  if (typeof Sizzle !== 'undefined') {
6282    return;
6283  }
6284
6285  if (typeof define !== 'undefined' && define.amd) {
6286    window.Sizzle = Prototype._actual_sizzle;
6287    window.define = Prototype._original_define;
6288    delete Prototype._actual_sizzle;
6289    delete Prototype._original_define;
6290  } else if (typeof module !== 'undefined' && module.exports) {
6291    window.Sizzle = module.exports;
6292    module.exports = {};
6293  }
6294})();
6295
6296;(function(engine) {
6297  var extendElements = Prototype.Selector.extendElements;
6298
6299  function select(selector, scope) {
6300    return extendElements(engine(selector, scope || document));
6301  }
6302
6303  function match(element, selector) {
6304    return engine.matches(selector, [element]).length == 1;
6305  }
6306
6307  Prototype.Selector.engine = engine;
6308  Prototype.Selector.select = select;
6309  Prototype.Selector.match = match;
6310})(Sizzle);
6311
6312window.Sizzle = Prototype._original_property;
6313delete Prototype._original_property;
6314
6315var Form = {
6316  reset: function(form) {
6317    form = $(form);
6318    form.reset();
6319    return form;
6320  },
6321
6322  serializeElements: function(elements, options) {
6323    if (typeof options != 'object') options = { hash: !!options };
6324    else if (Object.isUndefined(options.hash)) options.hash = true;
6325    var key, value, submitted = false, submit = options.submit, accumulator, initial;
6326
6327    if (options.hash) {
6328      initial = {};
6329      accumulator = function(result, key, value) {
6330        if (key in result) {
6331          if (!Object.isArray(result[key])) result[key] = [result[key]];
6332          result[key] = result[key].concat(value);
6333        } else result[key] = value;
6334        return result;
6335      };
6336    } else {
6337      initial = '';
6338      accumulator = function(result, key, values) {
6339        if (!Object.isArray(values)) {values = [values];}
6340        if (!values.length) {return result;}
6341        var encodedKey = encodeURIComponent(key).gsub(/%20/, '+');
6342        return result + (result ? "&" : "") + values.map(function (value) {
6343          value = value.gsub(/(\r)?\n/, '\r\n');
6344          value = encodeURIComponent(value);
6345          value = value.gsub(/%20/, '+');
6346          return encodedKey + "=" + value;
6347        }).join("&");
6348      };
6349    }
6350
6351    return elements.inject(initial, function(result, element) {
6352      if (!element.disabled && element.name) {
6353        key = element.name; value = $(element).getValue();
6354        if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
6355            submit !== false && (!submit || key == submit) && (submitted = true)))) {
6356          result = accumulator(result, key, value);
6357        }
6358      }
6359      return result;
6360    });
6361  }
6362};
6363
6364Form.Methods = {
6365  serialize: function(form, options) {
6366    return Form.serializeElements(Form.getElements(form), options);
6367  },
6368
6369
6370  getElements: function(form) {
6371    var elements = $(form).getElementsByTagName('*');
6372    var element, results = [], serializers = Form.Element.Serializers;
6373
6374    for (var i = 0; element = elements[i]; i++) {
6375      if (serializers[element.tagName.toLowerCase()])
6376        results.push(Element.extend(element));
6377    }
6378    return results;
6379  },
6380
6381  getInputs: function(form, typeName, name) {
6382    form = $(form);
6383    var inputs = form.getElementsByTagName('input');
6384
6385    if (!typeName && !name) return $A(inputs).map(Element.extend);
6386
6387    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
6388      var input = inputs[i];
6389      if ((typeName && input.type != typeName) || (name && input.name != name))
6390        continue;
6391      matchingInputs.push(Element.extend(input));
6392    }
6393
6394    return matchingInputs;
6395  },
6396
6397  disable: function(form) {
6398    form = $(form);
6399    Form.getElements(form).invoke('disable');
6400    return form;
6401  },
6402
6403  enable: function(form) {
6404    form = $(form);
6405    Form.getElements(form).invoke('enable');
6406    return form;
6407  },
6408
6409  findFirstElement: function(form) {
6410    var elements = $(form).getElements().findAll(function(element) {
6411      return 'hidden' != element.type && !element.disabled;
6412    });
6413    var firstByIndex = elements.findAll(function(element) {
6414      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
6415    }).sortBy(function(element) { return element.tabIndex }).first();
6416
6417    return firstByIndex ? firstByIndex : elements.find(function(element) {
6418      return /^(?:input|select|textarea)$/i.test(element.tagName);
6419    });
6420  },
6421
6422  focusFirstElement: function(form) {
6423    form = $(form);
6424    var element = form.findFirstElement();
6425    if (element) element.activate();
6426    return form;
6427  },
6428
6429  request: function(form, options) {
6430    form = $(form), options = Object.clone(options || { });
6431
6432    var params = options.parameters, action = form.readAttribute('action') || '';
6433    if (action.blank()) action = window.location.href;
6434    options.parameters = form.serialize(true);
6435
6436    if (params) {
6437      if (Object.isString(params)) params = params.toQueryParams();
6438      Object.extend(options.parameters, params);
6439    }
6440
6441    if (form.hasAttribute('method') && !options.method)
6442      options.method = form.method;
6443
6444    return new Ajax.Request(action, options);
6445  }
6446};
6447
6448/*--------------------------------------------------------------------------*/
6449
6450
6451Form.Element = {
6452  focus: function(element) {
6453    $(element).focus();
6454    return element;
6455  },
6456
6457  select: function(element) {
6458    $(element).select();
6459    return element;
6460  }
6461};
6462
6463Form.Element.Methods = {
6464
6465  serialize: function(element) {
6466    element = $(element);
6467    if (!element.disabled && element.name) {
6468      var value = element.getValue();
6469      if (value != undefined) {
6470        var pair = { };
6471        pair[element.name] = value;
6472        return Object.toQueryString(pair);
6473      }
6474    }
6475    return '';
6476  },
6477
6478  getValue: function(element) {
6479    element = $(element);
6480    var method = element.tagName.toLowerCase();
6481    return Form.Element.Serializers[method](element);
6482  },
6483
6484  setValue: function(element, value) {
6485    element = $(element);
6486    var method = element.tagName.toLowerCase();
6487    Form.Element.Serializers[method](element, value);
6488    return element;
6489  },
6490
6491  clear: function(element) {
6492    $(element).value = '';
6493    return element;
6494  },
6495
6496  present: function(element) {
6497    return $(element).value != '';
6498  },
6499
6500  activate: function(element) {
6501    element = $(element);
6502    try {
6503      element.focus();
6504      if (element.select && (element.tagName.toLowerCase() != 'input' ||
6505          !(/^(?:button|reset|submit)$/i.test(element.type))))
6506        element.select();
6507    } catch (e) { }
6508    return element;
6509  },
6510
6511  disable: function(element) {
6512    element = $(element);
6513    element.disabled = true;
6514    return element;
6515  },
6516
6517  enable: function(element) {
6518    element = $(element);
6519    element.disabled = false;
6520    return element;
6521  }
6522};
6523
6524/*--------------------------------------------------------------------------*/
6525
6526var Field = Form.Element;
6527
6528var $F = Form.Element.Methods.getValue;
6529
6530/*--------------------------------------------------------------------------*/
6531
6532Form.Element.Serializers = (function() {
6533  function input(element, value) {
6534    switch (element.type.toLowerCase()) {
6535      case 'checkbox':
6536      case 'radio':
6537        return inputSelector(element, value);
6538      default:
6539        return valueSelector(element, value);
6540    }
6541  }
6542
6543  function inputSelector(element, value) {
6544    if (Object.isUndefined(value))
6545      return element.checked ? element.value : null;
6546    else element.checked = !!value;
6547  }
6548
6549  function valueSelector(element, value) {
6550    if (Object.isUndefined(value)) return element.value;
6551    else element.value = value;
6552  }
6553
6554  function select(element, value) {
6555    if (Object.isUndefined(value))
6556      return (element.type === 'select-one' ? selectOne : selectMany)(element);
6557
6558    var opt, currentValue, single = !Object.isArray(value);
6559    for (var i = 0, length = element.length; i < length; i++) {
6560      opt = element.options[i];
6561      currentValue = this.optionValue(opt);
6562      if (single) {
6563        if (currentValue == value) {
6564          opt.selected = true;
6565          return;
6566        }
6567      }
6568      else opt.selected = value.include(currentValue);
6569    }
6570  }
6571
6572  function selectOne(element) {
6573    var index = element.selectedIndex;
6574    return index >= 0 ? optionValue(element.options[index]) : null;
6575  }
6576
6577  function selectMany(element) {
6578    var values, length = element.length;
6579    if (!length) return null;
6580
6581    for (var i = 0, values = []; i < length; i++) {
6582      var opt = element.options[i];
6583      if (opt.selected) values.push(optionValue(opt));
6584    }
6585    return values;
6586  }
6587
6588  function optionValue(opt) {
6589    return Element.hasAttribute(opt, 'value') ? opt.value : opt.text;
6590  }
6591
6592  return {
6593    input:         input,
6594    inputSelector: inputSelector,
6595    textarea:      valueSelector,
6596    select:        select,
6597    selectOne:     selectOne,
6598    selectMany:    selectMany,
6599    optionValue:   optionValue,
6600    button:        valueSelector
6601  };
6602})();
6603
6604/*--------------------------------------------------------------------------*/
6605
6606
6607Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
6608  initialize: function($super, element, frequency, callback) {
6609    $super(callback, frequency);
6610    this.element   = $(element);
6611    this.lastValue = this.getValue();
6612  },
6613
6614  execute: function() {
6615    var value = this.getValue();
6616    if (Object.isString(this.lastValue) && Object.isString(value) ?
6617        this.lastValue != value : String(this.lastValue) != String(value)) {
6618      this.callback(this.element, value);
6619      this.lastValue = value;
6620    }
6621  }
6622});
6623
6624Form.Element.Observer = Class.create(Abstract.TimedObserver, {
6625  getValue: function() {
6626    return Form.Element.getValue(this.element);
6627  }
6628});
6629
6630Form.Observer = Class.create(Abstract.TimedObserver, {
6631  getValue: function() {
6632    return Form.serialize(this.element);
6633  }
6634});
6635
6636/*--------------------------------------------------------------------------*/
6637
6638Abstract.EventObserver = Class.create({
6639  initialize: function(element, callback) {
6640    this.element  = $(element);
6641    this.callback = callback;
6642
6643    this.lastValue = this.getValue();
6644    if (this.element.tagName.toLowerCase() == 'form')
6645      this.registerFormCallbacks();
6646    else
6647      this.registerCallback(this.element);
6648  },
6649
6650  onElementEvent: function() {
6651    var value = this.getValue();
6652    if (this.lastValue != value) {
6653      this.callback(this.element, value);
6654      this.lastValue = value;
6655    }
6656  },
6657
6658  registerFormCallbacks: function() {
6659    Form.getElements(this.element).each(this.registerCallback, this);
6660  },
6661
6662  registerCallback: function(element) {
6663    if (element.type) {
6664      switch (element.type.toLowerCase()) {
6665        case 'checkbox':
6666        case 'radio':
6667          Event.observe(element, 'click', this.onElementEvent.bind(this));
6668          break;
6669        default:
6670          Event.observe(element, 'change', this.onElementEvent.bind(this));
6671          break;
6672      }
6673    }
6674  }
6675});
6676
6677Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
6678  getValue: function() {
6679    return Form.Element.getValue(this.element);
6680  }
6681});
6682
6683Form.EventObserver = Class.create(Abstract.EventObserver, {
6684  getValue: function() {
6685    return Form.serialize(this.element);
6686  }
6687});
6688(function(GLOBAL) {
6689  var DIV = document.createElement('div');
6690  var docEl = document.documentElement;
6691  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
6692   && 'onmouseleave' in docEl;
6693
6694  var Event = {
6695    KEY_BACKSPACE: 8,
6696    KEY_TAB:       9,
6697    KEY_RETURN:   13,
6698    KEY_ESC:      27,
6699    KEY_LEFT:     37,
6700    KEY_UP:       38,
6701    KEY_RIGHT:    39,
6702    KEY_DOWN:     40,
6703    KEY_DELETE:   46,
6704    KEY_HOME:     36,
6705    KEY_END:      35,
6706    KEY_PAGEUP:   33,
6707    KEY_PAGEDOWN: 34,
6708    KEY_INSERT:   45
6709  };
6710
6711
6712  var isIELegacyEvent = function(event) { return false; };
6713
6714  if (window.attachEvent) {
6715    if (window.addEventListener) {
6716      isIELegacyEvent = function(event) {
6717        return !(event instanceof window.Event);
6718      };
6719    } else {
6720      isIELegacyEvent = function(event) { return true; };
6721    }
6722  }
6723
6724  var _isButton;
6725
6726  function _isButtonForDOMEvents(event, code) {
6727    return event.which ? (event.which === code + 1) : (event.button === code);
6728  }
6729
6730  var legacyButtonMap = { 0: 1, 1: 4, 2: 2 };
6731  function _isButtonForLegacyEvents(event, code) {
6732    return event.button === legacyButtonMap[code];
6733  }
6734
6735  function _isButtonForWebKit(event, code) {
6736    switch (code) {
6737      case 0: return event.which == 1 && !event.metaKey;
6738      case 1: return event.which == 2 || (event.which == 1 && event.metaKey);
6739      case 2: return event.which == 3;
6740      default: return false;
6741    }
6742  }
6743
6744  if (window.attachEvent) {
6745    if (!window.addEventListener) {
6746      _isButton = _isButtonForLegacyEvents;
6747    } else {
6748      _isButton = function(event, code) {
6749        return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) :
6750         _isButtonForDOMEvents(event, code);
6751      }
6752    }
6753  } else if (Prototype.Browser.WebKit) {
6754    _isButton = _isButtonForWebKit;
6755  } else {
6756    _isButton = _isButtonForDOMEvents;
6757  }
6758
6759  function isLeftClick(event)   { return _isButton(event, 0) }
6760
6761  function isMiddleClick(event) { return _isButton(event, 1) }
6762
6763  function isRightClick(event)  { return _isButton(event, 2) }
6764
6765  function element(event) {
6766    return Element.extend(_element(event));
6767  }
6768
6769  function _element(event) {
6770    event = Event.extend(event);
6771
6772    var node = event.target, type = event.type,
6773     currentTarget = event.currentTarget;
6774
6775    if (currentTarget && currentTarget.tagName) {
6776      if (type === 'load' || type === 'error' ||
6777        (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
6778          && currentTarget.type === 'radio'))
6779            node = currentTarget;
6780    }
6781
6782    return node.nodeType == Node.TEXT_NODE ? node.parentNode : node;
6783  }
6784
6785  function findElement(event, expression) {
6786    var element = _element(event), selector = Prototype.Selector;
6787    if (!expression) return Element.extend(element);
6788    while (element) {
6789      if (Object.isElement(element) && selector.match(element, expression))
6790        return Element.extend(element);
6791      element = element.parentNode;
6792    }
6793  }
6794
6795  function pointer(event) {
6796    return { x: pointerX(event), y: pointerY(event) };
6797  }
6798
6799  function pointerX(event) {
6800    var docElement = document.documentElement,
6801     body = document.body || { scrollLeft: 0 };
6802
6803    return event.pageX || (event.clientX +
6804      (docElement.scrollLeft || body.scrollLeft) -
6805      (docElement.clientLeft || 0));
6806  }
6807
6808  function pointerY(event) {
6809    var docElement = document.documentElement,
6810     body = document.body || { scrollTop: 0 };
6811
6812    return  event.pageY || (event.clientY +
6813       (docElement.scrollTop || body.scrollTop) -
6814       (docElement.clientTop || 0));
6815  }
6816
6817
6818  function stop(event) {
6819    Event.extend(event);
6820    event.preventDefault();
6821    event.stopPropagation();
6822
6823    event.stopped = true;
6824  }
6825
6826
6827  Event.Methods = {
6828    isLeftClick:   isLeftClick,
6829    isMiddleClick: isMiddleClick,
6830    isRightClick:  isRightClick,
6831
6832    element:     element,
6833    findElement: findElement,
6834
6835    pointer:  pointer,
6836    pointerX: pointerX,
6837    pointerY: pointerY,
6838
6839    stop: stop
6840  };
6841
6842  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
6843    m[name] = Event.Methods[name].methodize();
6844    return m;
6845  });
6846
6847  if (window.attachEvent) {
6848    function _relatedTarget(event) {
6849      var element;
6850      switch (event.type) {
6851        case 'mouseover':
6852        case 'mouseenter':
6853          element = event.fromElement;
6854          break;
6855        case 'mouseout':
6856        case 'mouseleave':
6857          element = event.toElement;
6858          break;
6859        default:
6860          return null;
6861      }
6862      return Element.extend(element);
6863    }
6864
6865    var additionalMethods = {
6866      stopPropagation: function() { this.cancelBubble = true },
6867      preventDefault:  function() { this.returnValue = false },
6868      inspect: function() { return '[object Event]' }
6869    };
6870
6871    Event.extend = function(event, element) {
6872      if (!event) return false;
6873
6874      if (!isIELegacyEvent(event)) return event;
6875
6876      if (event._extendedByPrototype) return event;
6877      event._extendedByPrototype = Prototype.emptyFunction;
6878
6879      var pointer = Event.pointer(event);
6880
6881      Object.extend(event, {
6882        target: event.srcElement || element,
6883        relatedTarget: _relatedTarget(event),
6884        pageX:  pointer.x,
6885        pageY:  pointer.y
6886      });
6887
6888      Object.extend(event, methods);
6889      Object.extend(event, additionalMethods);
6890
6891      return event;
6892    };
6893  } else {
6894    Event.extend = Prototype.K;
6895  }
6896
6897  if (window.addEventListener) {
6898    Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
6899    Object.extend(Event.prototype, methods);
6900  }
6901
6902  var EVENT_TRANSLATIONS = {
6903    mouseenter: 'mouseover',
6904    mouseleave: 'mouseout'
6905  };
6906
6907  function getDOMEventName(eventName) {
6908    return EVENT_TRANSLATIONS[eventName] || eventName;
6909  }
6910
6911  if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED)
6912    getDOMEventName = Prototype.K;
6913
6914  function getUniqueElementID(element) {
6915    if (element === window) return 0;
6916
6917    if (typeof element._prototypeUID === 'undefined')
6918      element._prototypeUID = Element.Storage.UID++;
6919    return element._prototypeUID;
6920  }
6921
6922  function getUniqueElementID_IE(element) {
6923    if (element === window) return 0;
6924    if (element == document) return 1;
6925    return element.uniqueID;
6926  }
6927
6928  if ('uniqueID' in DIV)
6929    getUniqueElementID = getUniqueElementID_IE;
6930
6931  function isCustomEvent(eventName) {
6932    return eventName.include(':');
6933  }
6934
6935  Event._isCustomEvent = isCustomEvent;
6936
6937  function getOrCreateRegistryFor(element, uid) {
6938    var CACHE = GLOBAL.Event.cache;
6939    if (Object.isUndefined(uid))
6940      uid = getUniqueElementID(element);
6941    if (!CACHE[uid]) CACHE[uid] = { element: element };
6942    return CACHE[uid];
6943  }
6944
6945  function destroyRegistryForElement(element, uid) {
6946    if (Object.isUndefined(uid))
6947      uid = getUniqueElementID(element);
6948    delete GLOBAL.Event.cache[uid];
6949  }
6950
6951
6952  function register(element, eventName, handler) {
6953    var registry = getOrCreateRegistryFor(element);
6954    if (!registry[eventName]) registry[eventName] = [];
6955    var entries = registry[eventName];
6956
6957    var i = entries.length;
6958    while (i--)
6959      if (entries[i].handler === handler) return null;
6960
6961    var uid = getUniqueElementID(element);
6962    var responder = GLOBAL.Event._createResponder(uid, eventName, handler);
6963    var entry = {
6964      responder: responder,
6965      handler:   handler
6966    };
6967
6968    entries.push(entry);
6969    return entry;
6970  }
6971
6972  function unregister(element, eventName, handler) {
6973    var registry = getOrCreateRegistryFor(element);
6974    var entries = registry[eventName] || [];
6975
6976    var i = entries.length, entry;
6977    while (i--) {
6978      if (entries[i].handler === handler) {
6979        entry = entries[i];
6980        break;
6981      }
6982    }
6983
6984    if (entry) {
6985      var index = entries.indexOf(entry);
6986      entries.splice(index, 1);
6987    }
6988
6989    if (entries.length === 0) {
6990      delete registry[eventName];
6991      if (Object.keys(registry).length === 1 && ('element' in registry))
6992        destroyRegistryForElement(element);
6993    }
6994
6995    return entry;
6996  }
6997
6998
6999  function observe(element, eventName, handler) {
7000    element = $(element);
7001    var entry = register(element, eventName, handler);
7002
7003    if (entry === null) return element;
7004
7005    var responder = entry.responder;
7006    if (isCustomEvent(eventName))
7007      observeCustomEvent(element, eventName, responder);
7008    else
7009      observeStandardEvent(element, eventName, responder);
7010
7011    return element;
7012  }
7013
7014  function observeStandardEvent(element, eventName, responder) {
7015    var actualEventName = getDOMEventName(eventName);
7016    if (element.addEventListener) {
7017      element.addEventListener(actualEventName, responder, false);
7018    } else {
7019      element.attachEvent('on' + actualEventName, responder);
7020    }
7021  }
7022
7023  function observeCustomEvent(element, eventName, responder) {
7024    if (element.addEventListener) {
7025      element.addEventListener('dataavailable', responder, false);
7026    } else {
7027      element.attachEvent('ondataavailable', responder);
7028      element.attachEvent('onlosecapture',   responder);
7029    }
7030  }
7031
7032  function stopObserving(element, eventName, handler) {
7033    element = $(element);
7034    var handlerGiven = !Object.isUndefined(handler),
7035     eventNameGiven = !Object.isUndefined(eventName);
7036
7037    if (!eventNameGiven && !handlerGiven) {
7038      stopObservingElement(element);
7039      return element;
7040    }
7041
7042    if (!handlerGiven) {
7043      stopObservingEventName(element, eventName);
7044      return element;
7045    }
7046
7047    var entry = unregister(element, eventName, handler);
7048
7049    if (!entry) return element;
7050    removeEvent(element, eventName, entry.responder);
7051    return element;
7052  }
7053
7054  function stopObservingStandardEvent(element, eventName, responder) {
7055    var actualEventName = getDOMEventName(eventName);
7056    if (element.removeEventListener) {
7057      element.removeEventListener(actualEventName, responder, false);
7058    } else {
7059      element.detachEvent('on' + actualEventName, responder);
7060    }
7061  }
7062
7063  function stopObservingCustomEvent(element, eventName, responder) {
7064    if (element.removeEventListener) {
7065      element.removeEventListener('dataavailable', responder, false);
7066    } else {
7067      element.detachEvent('ondataavailable', responder);
7068      element.detachEvent('onlosecapture',   responder);
7069    }
7070  }
7071
7072
7073
7074  function stopObservingElement(element) {
7075    var uid = getUniqueElementID(element), registry = GLOBAL.Event.cache[uid];
7076    if (!registry) return;
7077
7078    destroyRegistryForElement(element, uid);
7079
7080    var entries, i;
7081    for (var eventName in registry) {
7082      if (eventName === 'element') continue;
7083
7084      entries = registry[eventName];
7085      i = entries.length;
7086      while (i--)
7087        removeEvent(element, eventName, entries[i].responder);
7088    }
7089  }
7090
7091  function stopObservingEventName(element, eventName) {
7092    var registry = getOrCreateRegistryFor(element);
7093    var entries = registry[eventName];
7094    if (entries) {
7095      delete registry[eventName];
7096    }
7097
7098    entries = entries || [];
7099
7100    var i = entries.length;
7101    while (i--)
7102      removeEvent(element, eventName, entries[i].responder);
7103
7104    for (var name in registry) {
7105      if (name === 'element') continue;
7106      return; // There is another registered event
7107    }
7108
7109    destroyRegistryForElement(element);
7110  }
7111
7112
7113  function removeEvent(element, eventName, handler) {
7114    if (isCustomEvent(eventName))
7115      stopObservingCustomEvent(element, eventName, handler);
7116    else
7117      stopObservingStandardEvent(element, eventName, handler);
7118  }
7119
7120
7121
7122  function getFireTarget(element) {
7123    if (element !== document) return element;
7124    if (document.createEvent && !element.dispatchEvent)
7125      return document.documentElement;
7126    return element;
7127  }
7128
7129  function fire(element, eventName, memo, bubble) {
7130    element = getFireTarget($(element));
7131    if (Object.isUndefined(bubble)) bubble = true;
7132    memo = memo || {};
7133
7134    var event = fireEvent(element, eventName, memo, bubble);
7135    return Event.extend(event);
7136  }
7137
7138  function fireEvent_DOM(element, eventName, memo, bubble) {
7139    var event = document.createEvent('HTMLEvents');
7140    event.initEvent('dataavailable', bubble, true);
7141
7142    event.eventName = eventName;
7143    event.memo = memo;
7144
7145    element.dispatchEvent(event);
7146    return event;
7147  }
7148
7149  function fireEvent_IE(element, eventName, memo, bubble) {
7150    var event = document.createEventObject();
7151    event.eventType = bubble ? 'ondataavailable' : 'onlosecapture';
7152
7153    event.eventName = eventName;
7154    event.memo = memo;
7155
7156    element.fireEvent(event.eventType, event);
7157    return event;
7158  }
7159
7160  var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE;
7161
7162
7163
7164  Event.Handler = Class.create({
7165    initialize: function(element, eventName, selector, callback) {
7166      this.element   = $(element);
7167      this.eventName = eventName;
7168      this.selector  = selector;
7169      this.callback  = callback;
7170      this.handler   = this.handleEvent.bind(this);
7171    },
7172
7173
7174    start: function() {
7175      Event.observe(this.element, this.eventName, this.handler);
7176      return this;
7177    },
7178
7179    stop: function() {
7180      Event.stopObserving(this.element, this.eventName, this.handler);
7181      return this;
7182    },
7183
7184    handleEvent: function(event) {
7185      var element = Event.findElement(event, this.selector);
7186      if (element) this.callback.call(this.element, event, element);
7187    }
7188  });
7189
7190  function on(element, eventName, selector, callback) {
7191    element = $(element);
7192    if (Object.isFunction(selector) && Object.isUndefined(callback)) {
7193      callback = selector, selector = null;
7194    }
7195
7196    return new Event.Handler(element, eventName, selector, callback).start();
7197  }
7198
7199  Object.extend(Event, Event.Methods);
7200
7201  Object.extend(Event, {
7202    fire:          fire,
7203    observe:       observe,
7204    stopObserving: stopObserving,
7205    on:            on
7206  });
7207
7208  Element.addMethods({
7209    fire:          fire,
7210
7211    observe:       observe,
7212
7213    stopObserving: stopObserving,
7214
7215    on:            on
7216  });
7217
7218  Object.extend(document, {
7219    fire:          fire.methodize(),
7220
7221    observe:       observe.methodize(),
7222
7223    stopObserving: stopObserving.methodize(),
7224
7225    on:            on.methodize(),
7226
7227    loaded:        false
7228  });
7229
7230  if (GLOBAL.Event) Object.extend(window.Event, Event);
7231  else GLOBAL.Event = Event;
7232
7233  GLOBAL.Event.cache = {};
7234
7235  function destroyCache_IE() {
7236    GLOBAL.Event.cache = null;
7237  }
7238
7239  if (window.attachEvent)
7240    window.attachEvent('onunload', destroyCache_IE);
7241
7242  DIV = null;
7243  docEl = null;
7244})(this);
7245
7246(function(GLOBAL) {
7247  /* Code for creating leak-free event responders is based on work by
7248   John-David Dalton. */
7249
7250  var docEl = document.documentElement;
7251  var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
7252    && 'onmouseleave' in docEl;
7253
7254  function isSimulatedMouseEnterLeaveEvent(eventName) {
7255    return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
7256     (eventName === 'mouseenter' || eventName === 'mouseleave');
7257  }
7258
7259  function createResponder(uid, eventName, handler) {
7260    if (Event._isCustomEvent(eventName))
7261      return createResponderForCustomEvent(uid, eventName, handler);
7262    if (isSimulatedMouseEnterLeaveEvent(eventName))
7263      return createMouseEnterLeaveResponder(uid, eventName, handler);
7264
7265    return function(event) {
7266      if (!Event.cache) return;
7267
7268      var element = Event.cache[uid].element;
7269      Event.extend(event, element);
7270      handler.call(element, event);
7271    };
7272  }
7273
7274  function createResponderForCustomEvent(uid, eventName, handler) {
7275    return function(event) {
7276      var cache = Event.cache[uid];
7277      var element =  cache && cache.element;
7278
7279      if (Object.isUndefined(event.eventName))
7280        return false;
7281
7282      if (event.eventName !== eventName)
7283        return false;
7284
7285      Event.extend(event, element);
7286      handler.call(element, event);
7287    };
7288  }
7289
7290  function createMouseEnterLeaveResponder(uid, eventName, handler) {
7291    return function(event) {
7292      var element = Event.cache[uid].element;
7293
7294      Event.extend(event, element);
7295      var parent = event.relatedTarget;
7296
7297      while (parent && parent !== element) {
7298        try { parent = parent.parentNode; }
7299        catch(e) { parent = element; }
7300      }
7301
7302      if (parent === element) return;
7303      handler.call(element, event);
7304    }
7305  }
7306
7307  GLOBAL.Event._createResponder = createResponder;
7308  docEl = null;
7309})(this);
7310
7311(function(GLOBAL) {
7312  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
7313     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */
7314
7315  var TIMER;
7316
7317  function fireContentLoadedEvent() {
7318    if (document.loaded) return;
7319    if (TIMER) window.clearTimeout(TIMER);
7320    document.loaded = true;
7321    document.fire('dom:loaded');
7322  }
7323
7324  function checkReadyState() {
7325    if (document.readyState === 'complete') {
7326      document.detachEvent('onreadystatechange', checkReadyState);
7327      fireContentLoadedEvent();
7328    }
7329  }
7330
7331  function pollDoScroll() {
7332    try {
7333      document.documentElement.doScroll('left');
7334    } catch (e) {
7335      TIMER = pollDoScroll.defer();
7336      return;
7337    }
7338
7339    fireContentLoadedEvent();
7340  }
7341
7342
7343  if (document.readyState === 'complete') {
7344    fireContentLoadedEvent();
7345    return;
7346  }
7347
7348  if (document.addEventListener) {
7349    document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
7350  } else {
7351    document.attachEvent('onreadystatechange', checkReadyState);
7352    if (window == top) TIMER = pollDoScroll.defer();
7353  }
7354
7355  Event.observe(window, 'load', fireContentLoadedEvent);
7356})(this);
7357
7358
7359Element.addMethods();
7360/*------------------------------- DEPRECATED -------------------------------*/
7361
7362Hash.toQueryString = Object.toQueryString;
7363
7364var Toggle = { display: Element.toggle };
7365
7366Element.addMethods({
7367  childOf: Element.Methods.descendantOf
7368});
7369
7370var Insertion = {
7371  Before: function(element, content) {
7372    return Element.insert(element, {before:content});
7373  },
7374
7375  Top: function(element, content) {
7376    return Element.insert(element, {top:content});
7377  },
7378
7379  Bottom: function(element, content) {
7380    return Element.insert(element, {bottom:content});
7381  },
7382
7383  After: function(element, content) {
7384    return Element.insert(element, {after:content});
7385  }
7386};
7387
7388var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
7389
7390var Position = {
7391  includeScrollOffsets: false,
7392
7393  prepare: function() {
7394    this.deltaX =  window.pageXOffset
7395                || document.documentElement.scrollLeft
7396                || document.body.scrollLeft
7397                || 0;
7398    this.deltaY =  window.pageYOffset
7399                || document.documentElement.scrollTop
7400                || document.body.scrollTop
7401                || 0;
7402  },
7403
7404  within: function(element, x, y) {
7405    if (this.includeScrollOffsets)
7406      return this.withinIncludingScrolloffsets(element, x, y);
7407    this.xcomp = x;
7408    this.ycomp = y;
7409    this.offset = Element.cumulativeOffset(element);
7410
7411    return (y >= this.offset[1] &&
7412            y <  this.offset[1] + element.offsetHeight &&
7413            x >= this.offset[0] &&
7414            x <  this.offset[0] + element.offsetWidth);
7415  },
7416
7417  withinIncludingScrolloffsets: function(element, x, y) {
7418    var offsetcache = Element.cumulativeScrollOffset(element);
7419
7420    this.xcomp = x + offsetcache[0] - this.deltaX;
7421    this.ycomp = y + offsetcache[1] - this.deltaY;
7422    this.offset = Element.cumulativeOffset(element);
7423
7424    return (this.ycomp >= this.offset[1] &&
7425            this.ycomp <  this.offset[1] + element.offsetHeight &&
7426            this.xcomp >= this.offset[0] &&
7427            this.xcomp <  this.offset[0] + element.offsetWidth);
7428  },
7429
7430  overlap: function(mode, element) {
7431    if (!mode) return 0;
7432    if (mode == 'vertical')
7433      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
7434        element.offsetHeight;
7435    if (mode == 'horizontal')
7436      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
7437        element.offsetWidth;
7438  },
7439
7440
7441  cumulativeOffset: Element.Methods.cumulativeOffset,
7442
7443  positionedOffset: Element.Methods.positionedOffset,
7444
7445  absolutize: function(element) {
7446    Position.prepare();
7447    return Element.absolutize(element);
7448  },
7449
7450  relativize: function(element) {
7451    Position.prepare();
7452    return Element.relativize(element);
7453  },
7454
7455  realOffset: Element.Methods.cumulativeScrollOffset,
7456
7457  offsetParent: Element.Methods.getOffsetParent,
7458
7459  page: Element.Methods.viewportOffset,
7460
7461  clone: function(source, target, options) {
7462    options = options || { };
7463    return Element.clonePosition(target, source, options);
7464  }
7465};
7466
7467/*--------------------------------------------------------------------------*/
7468
7469if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
7470  function iter(name) {
7471    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
7472  }
7473
7474  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
7475  function(element, className) {
7476    className = className.toString().strip();
7477    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
7478    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
7479  } : function(element, className) {
7480    className = className.toString().strip();
7481    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
7482    if (!classNames && !className) return elements;
7483
7484    var nodes = $(element).getElementsByTagName('*');
7485    className = ' ' + className + ' ';
7486
7487    for (var i = 0, child, cn; child = nodes[i]; i++) {
7488      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
7489          (classNames && classNames.all(function(name) {
7490            return !name.toString().blank() && cn.include(' ' + name + ' ');
7491          }))))
7492        elements.push(Element.extend(child));
7493    }
7494    return elements;
7495  };
7496
7497  return function(className, parentElement) {
7498    return $(parentElement || document.body).getElementsByClassName(className);
7499  };
7500}(Element.Methods);
7501
7502/*--------------------------------------------------------------------------*/
7503
7504Element.ClassNames = Class.create();
7505Element.ClassNames.prototype = {
7506  initialize: function(element) {
7507    this.element = $(element);
7508  },
7509
7510  _each: function(iterator, context) {
7511    this.element.className.split(/\s+/).select(function(name) {
7512      return name.length > 0;
7513    })._each(iterator, context);
7514  },
7515
7516  set: function(className) {
7517    this.element.className = className;
7518  },
7519
7520  add: function(classNameToAdd) {
7521    if (this.include(classNameToAdd)) return;
7522    this.set($A(this).concat(classNameToAdd).join(' '));
7523  },
7524
7525  remove: function(classNameToRemove) {
7526    if (!this.include(classNameToRemove)) return;
7527    this.set($A(this).without(classNameToRemove).join(' '));
7528  },
7529
7530  toString: function() {
7531    return $A(this).join(' ');
7532  }
7533};
7534
7535Object.extend(Element.ClassNames.prototype, Enumerable);
7536
7537/*--------------------------------------------------------------------------*/
7538
7539(function() {
7540  window.Selector = Class.create({
7541    initialize: function(expression) {
7542      this.expression = expression.strip();
7543    },
7544
7545    findElements: function(rootElement) {
7546      return Prototype.Selector.select(this.expression, rootElement);
7547    },
7548
7549    match: function(element) {
7550      return Prototype.Selector.match(element, this.expression);
7551    },
7552
7553    toString: function() {
7554      return this.expression;
7555    },
7556
7557    inspect: function() {
7558      return "#<Selector: " + this.expression + ">";
7559    }
7560  });
7561
7562  Object.extend(Selector, {
7563    matchElements: function(elements, expression) {
7564      var match = Prototype.Selector.match,
7565          results = [];
7566
7567      for (var i = 0, length = elements.length; i < length; i++) {
7568        var element = elements[i];
7569        if (match(element, expression)) {
7570          results.push(Element.extend(element));
7571        }
7572      }
7573      return results;
7574    },
7575
7576    findElement: function(elements, expression, index) {
7577      index = index || 0;
7578      var matchIndex = 0, element;
7579      for (var i = 0, length = elements.length; i < length; i++) {
7580        element = elements[i];
7581        if (Prototype.Selector.match(element, expression) && index === matchIndex++) {
7582          return Element.extend(element);
7583        }
7584      }
7585    },
7586
7587    findChildElements: function(element, expressions) {
7588      var selector = expressions.toArray().join(', ');
7589      return Prototype.Selector.select(selector, element || document);
7590    }
7591  });
7592})();
7593// script.aculo.us builder.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
7594
7595// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
7596//
7597// script.aculo.us is freely distributable under the terms of an MIT-style license.
7598// For details, see the script.aculo.us web site: http://script.aculo.us/
7599
7600var Builder = {
7601  NODEMAP: {
7602    AREA: 'map',
7603    CAPTION: 'table',
7604    COL: 'table',
7605    COLGROUP: 'table',
7606    LEGEND: 'fieldset',
7607    OPTGROUP: 'select',
7608    OPTION: 'select',
7609    PARAM: 'object',
7610    TBODY: 'table',
7611    TD: 'table',
7612    TFOOT: 'table',
7613    TH: 'table',
7614    THEAD: 'table',
7615    TR: 'table'
7616  },
7617  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
7618  //       due to a Firefox bug
7619  node: function(elementName) {
7620    elementName = elementName.toUpperCase();
7621
7622    // try innerHTML approach
7623    var parentTag = this.NODEMAP[elementName] || 'div';
7624    var parentElement = document.createElement(parentTag);
7625    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
7626      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
7627    } catch(e) {}
7628    var element = parentElement.firstChild || null;
7629
7630    // see if browser added wrapping tags
7631    if(element && (element.tagName.toUpperCase() != elementName))
7632      element = element.getElementsByTagName(elementName)[0];
7633
7634    // fallback to createElement approach
7635    if(!element) element = document.createElement(elementName);
7636
7637    // abort if nothing could be created
7638    if(!element) return;
7639
7640    // attributes (or text)
7641    if(arguments[1])
7642      if(this._isStringOrNumber(arguments[1]) ||
7643        (arguments[1] instanceof Array) ||
7644        arguments[1].tagName) {
7645          this._children(element, arguments[1]);
7646        } else {
7647          var attrs = this._attributes(arguments[1]);
7648          if(attrs.length) {
7649            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
7650              parentElement.innerHTML = "<" +elementName + " " +
7651                attrs + "></" + elementName + ">";
7652            } catch(e) {}
7653            element = parentElement.firstChild || null;
7654            // workaround firefox 1.0.X bug
7655            if(!element) {
7656              element = document.createElement(elementName);
7657              for(attr in arguments[1])
7658                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
7659            }
7660            if(element.tagName.toUpperCase() != elementName)
7661              element = parentElement.getElementsByTagName(elementName)[0];
7662          }
7663        }
7664
7665    // text, or array of children
7666    if(arguments[2])
7667      this._children(element, arguments[2]);
7668
7669     return $(element);
7670  },
7671  _text: function(text) {
7672     return document.createTextNode(text);
7673  },
7674
7675  ATTR_MAP: {
7676    'className': 'class',
7677    'htmlFor': 'for'
7678  },
7679
7680  _attributes: function(attributes) {
7681    var attrs = [];
7682    for(attribute in attributes)
7683      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
7684          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
7685    return attrs.join(" ");
7686  },
7687  _children: function(element, children) {
7688    if(children.tagName) {
7689      element.appendChild(children);
7690      return;
7691    }
7692    if(typeof children=='object') { // array can hold nodes and text
7693      children.flatten().each( function(e) {
7694        if(typeof e=='object')
7695          element.appendChild(e);
7696        else
7697          if(Builder._isStringOrNumber(e))
7698            element.appendChild(Builder._text(e));
7699      });
7700    } else
7701      if(Builder._isStringOrNumber(children))
7702        element.appendChild(Builder._text(children));
7703  },
7704  _isStringOrNumber: function(param) {
7705    return(typeof param=='string' || typeof param=='number');
7706  },
7707  build: function(html) {
7708    var element = this.node('div');
7709    $(element).update(html.strip());
7710    return element.down();
7711  },
7712  dump: function(scope) {
7713    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
7714
7715    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
7716      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
7717      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
7718      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
7719      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
7720      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
7721
7722    tags.each( function(tag){
7723      scope[tag] = function() {
7724        return Builder.node.apply(Builder, [tag].concat($A(arguments)));
7725      };
7726    });
7727  }
7728};// script.aculo.us effects.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
7729
7730// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
7731// Contributors:
7732//  Justin Palmer (http://encytemedia.com/)
7733//  Mark Pilgrim (http://diveintomark.org/)
7734//  Martin Bialasinki
7735//
7736// script.aculo.us is freely distributable under the terms of an MIT-style license.
7737// For details, see the script.aculo.us web site: http://script.aculo.us/
7738
7739// converts rgb() and #xxx to #xxxxxx format,
7740// returns self (or first argument) if not convertable
7741String.prototype.parseColor = function() {
7742  var color = '#';
7743  if (this.slice(0,4) == 'rgb(') {
7744    var cols = this.slice(4,this.length-1).split(',');
7745    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
7746  } else {
7747    if (this.slice(0,1) == '#') {
7748      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
7749      if (this.length==7) color = this.toLowerCase();
7750    }
7751  }
7752  return (color.length==7 ? color : (arguments[0] || this));
7753};
7754
7755/*--------------------------------------------------------------------------*/
7756
7757Element.collectTextNodes = function(element) {
7758  return $A($(element).childNodes).collect( function(node) {
7759    return (node.nodeType==3 ? node.nodeValue :
7760      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
7761  }).flatten().join('');
7762};
7763
7764Element.collectTextNodesIgnoreClass = function(element, className) {
7765  return $A($(element).childNodes).collect( function(node) {
7766    return (node.nodeType==3 ? node.nodeValue :
7767      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
7768        Element.collectTextNodesIgnoreClass(node, className) : ''));
7769  }).flatten().join('');
7770};
7771
7772Element.setContentZoom = function(element, percent) {
7773  element = $(element);
7774  element.setStyle({fontSize: (percent/100) + 'em'});
7775  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
7776  return element;
7777};
7778
7779Element.getInlineOpacity = function(element){
7780  return $(element).style.opacity || '';
7781};
7782
7783Element.forceRerendering = function(element) {
7784  try {
7785    element = $(element);
7786    var n = document.createTextNode(' ');
7787    element.appendChild(n);
7788    element.removeChild(n);
7789  } catch(e) { }
7790};
7791
7792/*--------------------------------------------------------------------------*/
7793
7794var Effect = {
7795  _elementDoesNotExistError: {
7796    name: 'ElementDoesNotExistError',
7797    message: 'The specified DOM element does not exist, but is required for this effect to operate'
7798  },
7799  Transitions: {
7800    linear: Prototype.K,
7801    sinoidal: function(pos) {
7802      return (-Math.cos(pos*Math.PI)/2) + .5;
7803    },
7804    reverse: function(pos) {
7805      return 1-pos;
7806    },
7807    flicker: function(pos) {
7808      var pos = ((-Math.cos(pos*Math.PI)/4) + .75) + Math.random()/4;
7809      return pos > 1 ? 1 : pos;
7810    },
7811    wobble: function(pos) {
7812      return (-Math.cos(pos*Math.PI*(9*pos))/2) + .5;
7813    },
7814    pulse: function(pos, pulses) {
7815      return (-Math.cos((pos*((pulses||5)-.5)*2)*Math.PI)/2) + .5;
7816    },
7817    spring: function(pos) {
7818      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
7819    },
7820    none: function(pos) {
7821      return 0;
7822    },
7823    full: function(pos) {
7824      return 1;
7825    }
7826  },
7827  DefaultOptions: {
7828    duration:   1.0,   // seconds
7829    fps:        100,   // 100= assume 66fps max.
7830    sync:       false, // true for combining
7831    from:       0.0,
7832    to:         1.0,
7833    delay:      0.0,
7834    queue:      'parallel'
7835  },
7836  tagifyText: function(element) {
7837    var tagifyStyle = 'position:relative';
7838    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
7839
7840    element = $(element);
7841    $A(element.childNodes).each( function(child) {
7842      if (child.nodeType==3) {
7843        child.nodeValue.toArray().each( function(character) {
7844          element.insertBefore(
7845            new Element('span', {style: tagifyStyle}).update(
7846              character == ' ' ? String.fromCharCode(160) : character),
7847              child);
7848        });
7849        Element.remove(child);
7850      }
7851    });
7852  },
7853  multiple: function(element, effect) {
7854    var elements;
7855    if (((typeof element == 'object') ||
7856        Object.isFunction(element)) &&
7857       (element.length))
7858      elements = element;
7859    else
7860      elements = $(element).childNodes;
7861
7862    var options = Object.extend({
7863      speed: 0.1,
7864      delay: 0.0
7865    }, arguments[2] || { });
7866    var masterDelay = options.delay;
7867
7868    $A(elements).each( function(element, index) {
7869      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
7870    });
7871  },
7872  PAIRS: {
7873    'slide':  ['SlideDown','SlideUp'],
7874    'blind':  ['BlindDown','BlindUp'],
7875    'appear': ['Appear','Fade']
7876  },
7877  toggle: function(element, effect, options) {
7878    element = $(element);
7879    effect  = (effect || 'appear').toLowerCase();
7880    
7881    return Effect[ Effect.PAIRS[ effect ][ element.visible() ? 1 : 0 ] ](element, Object.extend({
7882      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
7883    }, options || {}));
7884  }
7885};
7886
7887Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
7888
7889/* ------------- core effects ------------- */
7890
7891Effect.ScopedQueue = Class.create(Enumerable, {
7892  initialize: function() {
7893    this.effects  = [];
7894    this.interval = null;
7895  },
7896  _each: function(iterator) {
7897    this.effects._each(iterator);
7898  },
7899  add: function(effect) {
7900    var timestamp = new Date().getTime();
7901
7902    var position = Object.isString(effect.options.queue) ?
7903      effect.options.queue : effect.options.queue.position;
7904
7905    switch(position) {
7906      case 'front':
7907        // move unstarted effects after this effect
7908        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
7909            e.startOn  += effect.finishOn;
7910            e.finishOn += effect.finishOn;
7911          });
7912        break;
7913      case 'with-last':
7914        timestamp = this.effects.pluck('startOn').max() || timestamp;
7915        break;
7916      case 'end':
7917        // start effect after last queued effect has finished
7918        timestamp = this.effects.pluck('finishOn').max() || timestamp;
7919        break;
7920    }
7921
7922    effect.startOn  += timestamp;
7923    effect.finishOn += timestamp;
7924
7925    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
7926      this.effects.push(effect);
7927
7928    if (!this.interval)
7929      this.interval = setInterval(this.loop.bind(this), 15);
7930  },
7931  remove: function(effect) {
7932    this.effects = this.effects.reject(function(e) { return e==effect });
7933    if (this.effects.length == 0) {
7934      clearInterval(this.interval);
7935      this.interval = null;
7936    }
7937  },
7938  loop: function() {
7939    var timePos = new Date().getTime();
7940    for(var i=0, len=this.effects.length;i<len;i++)
7941      this.effects[i] && this.effects[i].loop(timePos);
7942  }
7943});
7944
7945Effect.Queues = {
7946  instances: $H(),
7947  get: function(queueName) {
7948    if (!Object.isString(queueName)) return queueName;
7949
7950    return this.instances.get(queueName) ||
7951      this.instances.set(queueName, new Effect.ScopedQueue());
7952  }
7953};
7954Effect.Queue = Effect.Queues.get('global');
7955
7956Effect.Base = Class.create({
7957  position: null,
7958  start: function(options) {
7959    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
7960    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
7961    this.currentFrame = 0;
7962    this.state        = 'idle';
7963    this.startOn      = this.options.delay*1000;
7964    this.finishOn     = this.startOn+(this.options.duration*1000);
7965    this.fromToDelta  = this.options.to-this.options.from;
7966    this.totalTime    = this.finishOn-this.startOn;
7967    this.totalFrames  = this.options.fps*this.options.duration;
7968
7969    this.render = (function() {
7970      function dispatch(effect, eventName) {
7971        if (effect.options[eventName + 'Internal'])
7972          effect.options[eventName + 'Internal'](effect);
7973        if (effect.options[eventName])
7974          effect.options[eventName](effect);
7975      }
7976
7977      return function(pos) {
7978        if (this.state === "idle") {
7979          this.state = "running";
7980          dispatch(this, 'beforeSetup');
7981          if (this.setup) this.setup();
7982          dispatch(this, 'afterSetup');
7983        }
7984        if (this.state === "running") {
7985          pos = (this.options.transition(pos) * this.fromToDelta) + this.options.from;
7986          this.position = pos;
7987          dispatch(this, 'beforeUpdate');
7988          if (this.update) this.update(pos);
7989          dispatch(this, 'afterUpdate');
7990        }
7991      };
7992    })();
7993
7994    this.event('beforeStart');
7995    if (!this.options.sync)
7996      Effect.Queues.get(Object.isString(this.options.queue) ?
7997        'global' : this.options.queue.scope).add(this);
7998  },
7999  loop: function(timePos) {
8000    if (timePos >= this.startOn) {
8001      if (timePos >= this.finishOn) {
8002        this.render(1.0);
8003        this.cancel();
8004        this.event('beforeFinish');
8005        if (this.finish) this.finish();
8006        this.event('afterFinish');
8007        return;
8008      }
8009      var pos   = (timePos - this.startOn) / this.totalTime,
8010          frame = (pos * this.totalFrames).round();
8011      if (frame > this.currentFrame) {
8012        this.render(pos);
8013        this.currentFrame = frame;
8014      }
8015    }
8016  },
8017  cancel: function() {
8018    if (!this.options.sync)
8019      Effect.Queues.get(Object.isString(this.options.queue) ?
8020        'global' : this.options.queue.scope).remove(this);
8021    this.state = 'finished';
8022  },
8023  event: function(eventName) {
8024    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
8025    if (this.options[eventName]) this.options[eventName](this);
8026  },
8027  inspect: function() {
8028    var data = $H();
8029    for(property in this)
8030      if (!Object.isFunction(this[property])) data.set(property, this[property]);
8031    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
8032  }
8033});
8034
8035Effect.Parallel = Class.create(Effect.Base, {
8036  initialize: function(effects) {
8037    this.effects = effects || [];
8038    this.start(arguments[1]);
8039  },
8040  update: function(position) {
8041    this.effects.invoke('render', position);
8042  },
8043  finish: function(position) {
8044    this.effects.each( function(effect) {
8045      effect.render(1.0);
8046      effect.cancel();
8047      effect.event('beforeFinish');
8048      if (effect.finish) effect.finish(position);
8049      effect.event('afterFinish');
8050    });
8051  }
8052});
8053
8054Effect.Tween = Class.create(Effect.Base, {
8055  initialize: function(object, from, to) {
8056    object = Object.isString(object) ? $(object) : object;
8057    var args = $A(arguments), method = args.last(),
8058      options = args.length == 5 ? args[3] : null;
8059    this.method = Object.isFunction(method) ? method.bind(object) :
8060      Object.isFunction(object[method]) ? object[method].bind(object) :
8061      function(value) { object[method] = value };
8062    this.start(Object.extend({ from: from, to: to }, options || { }));
8063  },
8064  update: function(position) {
8065    this.method(position);
8066  }
8067});
8068
8069Effect.Event = Class.create(Effect.Base, {
8070  initialize: function() {
8071    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
8072  },
8073  update: Prototype.emptyFunction
8074});
8075
8076Effect.Opacity = Class.create(Effect.Base, {
8077  initialize: function(element) {
8078    this.element = $(element);
8079    if (!this.element) throw(Effect._elementDoesNotExistError);
8080    // make this work on IE on elements without 'layout'
8081    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
8082      this.element.setStyle({zoom: 1});
8083    var options = Object.extend({
8084      from: this.element.getOpacity() || 0.0,
8085      to:   1.0
8086    }, arguments[1] || { });
8087    this.start(options);
8088  },
8089  update: function(position) {
8090    this.element.setOpacity(position);
8091  }
8092});
8093
8094Effect.Move = Class.create(Effect.Base, {
8095  initialize: function(element) {
8096    this.element = $(element);
8097    if (!this.element) throw(Effect._elementDoesNotExistError);
8098    var options = Object.extend({
8099      x:    0,
8100      y:    0,
8101      mode: 'relative'
8102    }, arguments[1] || { });
8103    this.start(options);
8104  },
8105  setup: function() {
8106    this.element.makePositioned();
8107    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
8108    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
8109    if (this.options.mode == 'absolute') {
8110      this.options.x = this.options.x - this.originalLeft;
8111      this.options.y = this.options.y - this.originalTop;
8112    }
8113  },
8114  update: function(position) {
8115    this.element.setStyle({
8116      left: (this.options.x  * position + this.originalLeft).round() + 'px',
8117      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
8118    });
8119  }
8120});
8121
8122// for backwards compatibility
8123Effect.MoveBy = function(element, toTop, toLeft) {
8124  return new Effect.Move(element,
8125    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
8126};
8127
8128Effect.Scale = Class.create(Effect.Base, {
8129  initialize: function(element, percent) {
8130    this.element = $(element);
8131    if (!this.element) throw(Effect._elementDoesNotExistError);
8132    var options = Object.extend({
8133      scaleX: true,
8134      scaleY: true,
8135      scaleContent: true,
8136      scaleFromCenter: false,
8137      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
8138      scaleFrom: 100.0,
8139      scaleTo:   percent
8140    }, arguments[2] || { });
8141    this.start(options);
8142  },
8143  setup: function() {
8144    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
8145    this.elementPositioning = this.element.getStyle('position');
8146
8147    this.originalStyle = { };
8148    ['top','left','width','height','fontSize'].each( function(k) {
8149      this.originalStyle[k] = this.element.style[k];
8150    }.bind(this));
8151
8152    this.originalTop  = this.element.offsetTop;
8153    this.originalLeft = this.element.offsetLeft;
8154
8155    var fontSize = this.element.getStyle('font-size') || '100%';
8156    ['em','px','%','pt'].each( function(fontSizeType) {
8157      if (fontSize.indexOf(fontSizeType)>0) {
8158        this.fontSize     = parseFloat(fontSize);
8159        this.fontSizeType = fontSizeType;
8160      }
8161    }.bind(this));
8162
8163    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
8164
8165    this.dims = null;
8166    if (this.options.scaleMode=='box')
8167      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
8168    if (/^content/.test(this.options.scaleMode))
8169      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
8170    if (!this.dims)
8171      this.dims = [this.options.scaleMode.originalHeight,
8172                   this.options.scaleMode.originalWidth];
8173  },
8174  update: function(position) {
8175    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
8176    if (this.options.scaleContent && this.fontSize)
8177      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
8178    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
8179  },
8180  finish: function(position) {
8181    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
8182  },
8183  setDimensions: function(height, width) {
8184    var d = { };
8185    if (this.options.scaleX) d.width = width.round() + 'px';
8186    if (this.options.scaleY) d.height = height.round() + 'px';
8187    if (this.options.scaleFromCenter) {
8188      var topd  = (height - this.dims[0])/2;
8189      var leftd = (width  - this.dims[1])/2;
8190      if (this.elementPositioning == 'absolute') {
8191        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
8192        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
8193      } else {
8194        if (this.options.scaleY) d.top = -topd + 'px';
8195        if (this.options.scaleX) d.left = -leftd + 'px';
8196      }
8197    }
8198    this.element.setStyle(d);
8199  }
8200});
8201
8202Effect.Highlight = Class.create(Effect.Base, {
8203  initialize: function(element) {
8204    this.element = $(element);
8205    if (!this.element) throw(Effect._elementDoesNotExistError);
8206    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
8207    this.start(options);
8208  },
8209  setup: function() {
8210    // Prevent executing on elements not in the layout flow
8211    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
8212    // Disable background image during the effect
8213    this.oldStyle = { };
8214    if (!this.options.keepBackgroundImage) {
8215      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
8216      this.element.setStyle({backgroundImage: 'none'});
8217    }
8218    if (!this.options.endcolor)
8219      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
8220    if (!this.options.restorecolor)
8221      this.options.restorecolor = this.element.getStyle('background-color');
8222    // init color calculations
8223    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
8224    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
8225  },
8226  update: function(position) {
8227    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
8228      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
8229  },
8230  finish: function() {
8231    this.element.setStyle(Object.extend(this.oldStyle, {
8232      backgroundColor: this.options.restorecolor
8233    }));
8234  }
8235});
8236
8237Effect.ScrollTo = function(element) {
8238  var options = arguments[1] || { },
8239  scrollOffsets = document.viewport.getScrollOffsets(),
8240  elementOffsets = $(element).cumulativeOffset();
8241
8242  if (options.offset) elementOffsets[1] += options.offset;
8243
8244  return new Effect.Tween(null,
8245    scrollOffsets.top,
8246    elementOffsets[1],
8247    options,
8248    function(p){ scrollTo(scrollOffsets.left, p.round()); }
8249  );
8250};
8251
8252/* ------------- combination effects ------------- */
8253
8254Effect.Fade = function(element) {
8255  element = $(element);
8256  var oldOpacity = element.getInlineOpacity();
8257  var options = Object.extend({
8258    from: element.getOpacity() || 1.0,
8259    to:   0.0,
8260    afterFinishInternal: function(effect) {
8261      if (effect.options.to!=0) return;
8262      effect.element.hide().setStyle({opacity: oldOpacity});
8263    }
8264  }, arguments[1] || { });
8265  return new Effect.Opacity(element,options);
8266};
8267
8268Effect.Appear = function(element) {
8269  element = $(element);
8270  var options = Object.extend({
8271  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
8272  to:   1.0,
8273  // force Safari to render floated elements properly
8274  afterFinishInternal: function(effect) {
8275    effect.element.forceRerendering();
8276  },
8277  beforeSetup: function(effect) {
8278    effect.element.setOpacity(effect.options.from).show();
8279  }}, arguments[1] || { });
8280  return new Effect.Opacity(element,options);
8281};
8282
8283Effect.Puff = function(element) {
8284  element = $(element);
8285  var oldStyle = {
8286    opacity: element.getInlineOpacity(),
8287    position: element.getStyle('position'),
8288    top:  element.style.top,
8289    left: element.style.left,
8290    width: element.style.width,
8291    height: element.style.height
8292  };
8293  return new Effect.Parallel(
8294   [ new Effect.Scale(element, 200,
8295      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
8296     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
8297     Object.extend({ duration: 1.0,
8298      beforeSetupInternal: function(effect) {
8299        Position.absolutize(effect.effects[0].element);
8300      },
8301      afterFinishInternal: function(effect) {
8302         effect.effects[0].element.hide().setStyle(oldStyle); }
8303     }, arguments[1] || { })
8304   );
8305};
8306
8307Effect.BlindUp = function(element) {
8308  element = $(element);
8309  element.makeClipping();
8310  return new Effect.Scale(element, 0,
8311    Object.extend({ scaleContent: false,
8312      scaleX: false,
8313      restoreAfterFinish: true,
8314      afterFinishInternal: function(effect) {
8315        effect.element.hide().undoClipping();
8316      }
8317    }, arguments[1] || { })
8318  );
8319};
8320
8321Effect.BlindDown = function(element) {
8322  element = $(element);
8323  var elementDimensions = element.getDimensions();
8324  return new Effect.Scale(element, 100, Object.extend({
8325    scaleContent: false,
8326    scaleX: false,
8327    scaleFrom: 0,
8328    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
8329    restoreAfterFinish: true,
8330    afterSetup: function(effect) {
8331      effect.element.makeClipping().setStyle({height: '0px'}).show();
8332    },
8333    afterFinishInternal: function(effect) {
8334      effect.element.undoClipping();
8335    }
8336  }, arguments[1] || { }));
8337};
8338
8339Effect.SwitchOff = function(element) {
8340  element = $(element);
8341  var oldOpacity = element.getInlineOpacity();
8342  return new Effect.Appear(element, Object.extend({
8343    duration: 0.4,
8344    from: 0,
8345    transition: Effect.Transitions.flicker,
8346    afterFinishInternal: function(effect) {
8347      new Effect.Scale(effect.element, 1, {
8348        duration: 0.3, scaleFromCenter: true,
8349        scaleX: false, scaleContent: false, restoreAfterFinish: true,
8350        beforeSetup: function(effect) {
8351          effect.element.makePositioned().makeClipping();
8352        },
8353        afterFinishInternal: function(effect) {
8354          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
8355        }
8356      });
8357    }
8358  }, arguments[1] || { }));
8359};
8360
8361Effect.DropOut = function(element) {
8362  element = $(element);
8363  var oldStyle = {
8364    top: element.getStyle('top'),
8365    left: element.getStyle('left'),
8366    opacity: element.getInlineOpacity() };
8367  return new Effect.Parallel(
8368    [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
8369      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
8370    Object.extend(
8371      { duration: 0.5,
8372        beforeSetup: function(effect) {
8373          effect.effects[0].element.makePositioned();
8374        },
8375        afterFinishInternal: function(effect) {
8376          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
8377        }
8378      }, arguments[1] || { }));
8379};
8380
8381Effect.Shake = function(element) {
8382  element = $(element);
8383  var options = Object.extend({
8384    distance: 20,
8385    duration: 0.5
8386  }, arguments[1] || {});
8387  var distance = parseFloat(options.distance);
8388  var split = parseFloat(options.duration) / 10.0;
8389  var oldStyle = {
8390    top: element.getStyle('top'),
8391    left: element.getStyle('left') };
8392    return new Effect.Move(element,
8393      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
8394    new Effect.Move(effect.element,
8395      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
8396    new Effect.Move(effect.element,
8397      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
8398    new Effect.Move(effect.element,
8399      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
8400    new Effect.Move(effect.element,
8401      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
8402    new Effect.Move(effect.element,
8403      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
8404        effect.element.undoPositioned().setStyle(oldStyle);
8405  }}); }}); }}); }}); }}); }});
8406};
8407
8408Effect.SlideDown = function(element) {
8409  element = $(element).cleanWhitespace();
8410  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
8411  var oldInnerBottom = element.down().getStyle('bottom');
8412  var elementDimensions = element.getDimensions();
8413  return new Effect.Scale(element, 100, Object.extend({
8414    scaleContent: false,
8415    scaleX: false,
8416    scaleFrom: window.opera ? 0 : 1,
8417    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
8418    restoreAfterFinish: true,
8419    afterSetup: function(effect) {
8420      effect.element.makePositioned();
8421      effect.element.down().makePositioned();
8422      if (window.opera) effect.element.setStyle({top: ''});
8423      effect.element.makeClipping().setStyle({height: '0px'}).show();
8424    },
8425    afterUpdateInternal: function(effect) {
8426      effect.element.down().setStyle({bottom:
8427        (effect.dims[0] - effect.element.clientHeight) + 'px' });
8428    },
8429    afterFinishInternal: function(effect) {
8430      effect.element.undoClipping().undoPositioned();
8431      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
8432    }, arguments[1] || { })
8433  );
8434};
8435
8436Effect.SlideUp = function(element) {
8437  element = $(element).cleanWhitespace();
8438  var oldInnerBottom = element.down().getStyle('bottom');
8439  var elementDimensions = element.getDimensions();
8440  return new Effect.Scale(element, window.opera ? 0 : 1,
8441   Object.extend({ scaleContent: false,
8442    scaleX: false,
8443    scaleMode: 'box',
8444    scaleFrom: 100,
8445    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
8446    restoreAfterFinish: true,
8447    afterSetup: function(effect) {
8448      effect.element.makePositioned();
8449      effect.element.down().makePositioned();
8450      if (window.opera) effect.element.setStyle({top: ''});
8451      effect.element.makeClipping().show();
8452    },
8453    afterUpdateInternal: function(effect) {
8454      effect.element.down().setStyle({bottom:
8455        (effect.dims[0] - effect.element.clientHeight) + 'px' });
8456    },
8457    afterFinishInternal: function(effect) {
8458      effect.element.hide().undoClipping().undoPositioned();
8459      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
8460    }
8461   }, arguments[1] || { })
8462  );
8463};
8464
8465// Bug in opera makes the TD containing this element expand for a instance after finish
8466Effect.Squish = function(element) {
8467  return new Effect.Scale(element, window.opera ? 1 : 0, {
8468    restoreAfterFinish: true,
8469    beforeSetup: function(effect) {
8470      effect.element.makeClipping();
8471    },
8472    afterFinishInternal: function(effect) {
8473      effect.element.hide().undoClipping();
8474    }
8475  });
8476};
8477
8478Effect.Grow = function(element) {
8479  element = $(element);
8480  var options = Object.extend({
8481    direction: 'center',
8482    moveTransition: Effect.Transitions.sinoidal,
8483    scaleTransition: Effect.Transitions.sinoidal,
8484    opacityTransition: Effect.Transitions.full
8485  }, arguments[1] || { });
8486  var oldStyle = {
8487    top: element.style.top,
8488    left: element.style.left,
8489    height: element.style.height,
8490    width: element.style.width,
8491    opacity: element.getInlineOpacity() };
8492
8493  var dims = element.getDimensions();
8494  var initialMoveX, initialMoveY;
8495  var moveX, moveY;
8496
8497  switch (options.direction) {
8498    case 'top-left':
8499      initialMoveX = initialMoveY = moveX = moveY = 0;
8500      break;
8501    case 'top-right':
8502      initialMoveX = dims.width;
8503      initialMoveY = moveY = 0;
8504      moveX = -dims.width;
8505      break;
8506    case 'bottom-left':
8507      initialMoveX = moveX = 0;
8508      initialMoveY = dims.height;
8509      moveY = -dims.height;
8510      break;
8511    case 'bottom-right':
8512      initialMoveX = dims.width;
8513      initialMoveY = dims.height;
8514      moveX = -dims.width;
8515      moveY = -dims.height;
8516      break;
8517    case 'center':
8518      initialMoveX = dims.width / 2;
8519      initialMoveY = dims.height / 2;
8520      moveX = -dims.width / 2;
8521      moveY = -dims.height / 2;
8522      break;
8523  }
8524
8525  return new Effect.Move(element, {
8526    x: initialMoveX,
8527    y: initialMoveY,
8528    duration: 0.01,
8529    beforeSetup: function(effect) {
8530      effect.element.hide().makeClipping().makePositioned();
8531    },
8532    afterFinishInternal: function(effect) {
8533      new Effect.Parallel(
8534        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
8535          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
8536          new Effect.Scale(effect.element, 100, {
8537            scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
8538            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
8539        ], Object.extend({
8540             beforeSetup: function(effect) {
8541               effect.effects[0].element.setStyle({height: '0px'}).show();
8542             },
8543             afterFinishInternal: function(effect) {
8544               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
8545             }
8546           }, options)
8547      );
8548    }
8549  });
8550};
8551
8552Effect.Shrink = function(element) {
8553  element = $(element);
8554  var options = Object.extend({
8555    direction: 'center',
8556    moveTransition: Effect.Transitions.sinoidal,
8557    scaleTransition: Effect.Transitions.sinoidal,
8558    opacityTransition: Effect.Transitions.none
8559  }, arguments[1] || { });
8560  var oldStyle = {
8561    top: element.style.top,
8562    left: element.style.left,
8563    height: element.style.height,
8564    width: element.style.width,
8565    opacity: element.getInlineOpacity() };
8566
8567  var dims = element.getDimensions();
8568  var moveX, moveY;
8569
8570  switch (options.direction) {
8571    case 'top-left':
8572      moveX = moveY = 0;
8573      break;
8574    case 'top-right':
8575      moveX = dims.width;
8576      moveY = 0;
8577      break;
8578    case 'bottom-left':
8579      moveX = 0;
8580      moveY = dims.height;
8581      break;
8582    case 'bottom-right':
8583      moveX = dims.width;
8584      moveY = dims.height;
8585      break;
8586    case 'center':
8587      moveX = dims.width / 2;
8588      moveY = dims.height / 2;
8589      break;
8590  }
8591
8592  return new Effect.Parallel(
8593    [ new Effect.Opacity(elemen
8593t, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
8594      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
8595      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
8596    ], Object.extend({
8597         beforeStartInternal: function(effect) {
8598           effect.effects[0].element.makePositioned().makeClipping();
8599         },
8600         afterFinishInternal: function(effect) {
8601           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
8602       }, options)
8603  );
8604};
8605
8606Effect.Pulsate = function(element) {
8607  element = $(element);
8608  var options    = arguments[1] || { },
8609    oldOpacity = element.getInlineOpacity(),
8610    transition = options.transition || Effect.Transitions.linear,
8611    reverser   = function(pos){
8612      return 1 - transition((-Math.cos((pos*(options.pulses||5)*2)*Math.PI)/2) + .5);
8613    };
8614
8615  return new Effect.Opacity(element,
8616    Object.extend(Object.extend({  duration: 2.0, from: 0,
8617      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
8618    }, options), {transition: reverser}));
8619};
8620
8621Effect.Fold = function(element) {
8622  element = $(element);
8623  var oldStyle = {
8624    top: element.style.top,
8625    left: element.style.left,
8626    width: element.style.width,
8627    height: element.style.height };
8628  element.makeClipping();
8629  return new Effect.Scale(element, 5, Object.extend({
8630    scaleContent: false,
8631    scaleX: false,
8632    afterFinishInternal: function(effect) {
8633    new Effect.Scale(element, 1, {
8634      scaleContent: false,
8635      scaleY: false,
8636      afterFinishInternal: function(effect) {
8637        effect.element.hide().undoClipping().setStyle(oldStyle);
8638      } });
8639  }}, arguments[1] || { }));
8640};
8641
8642Effect.Morph = Class.create(Effect.Base, {
8643  initialize: function(element) {
8644    this.element = $(element);
8645    if (!this.element) throw(Effect._elementDoesNotExistError);
8646    var options = Object.extend({
8647      style: { }
8648    }, arguments[1] || { });
8649
8650    if (!Object.isString(options.style)) this.style = $H(options.style);
8651    else {
8652      if (options.style.include(':'))
8653        this.style = options.style.parseStyle();
8654      else {
8655        this.element.addClassName(options.style);
8656        this.style = $H(this.element.getStyles());
8657        this.element.removeClassName(options.style);
8658        var css = this.element.getStyles();
8659        this.style = this.style.reject(function(style) {
8660          return style.value == css[style.key];
8661        });
8662        options.afterFinishInternal = function(effect) {
8663          effect.element.addClassName(effect.options.style);
8664          effect.transforms.each(function(transform) {
8665            effect.element.style[transform.style] = '';
8666          });
8667        };
8668      }
8669    }
8670    this.start(options);
8671  },
8672
8673  setup: function(){
8674    function parseColor(color){
8675      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
8676      color = color.parseColor();
8677      return $R(0,2).map(function(i){
8678        return parseInt( color.slice(i*2+1,i*2+3), 16 );
8679      });
8680    }
8681    this.transforms = this.style.map(function(pair){
8682      var property = pair[0], value = pair[1], unit = null;
8683
8684      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
8685        value = value.parseColor();
8686        unit  = 'color';
8687      } else if (property == 'opacity') {
8688        value = parseFloat(value);
8689        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
8690          this.element.setStyle({zoom: 1});
8691      } else if (Element.CSS_LENGTH.test(value)) {
8692          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
8693          value = parseFloat(components[1]);
8694          unit = (components.length == 3) ? components[2] : null;
8695      }
8696
8697      var originalValue = this.element.getStyle(property);
8698      return {
8699        style: property.camelize(),
8700        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
8701        targetValue: unit=='color' ? parseColor(value) : value,
8702        unit: unit
8703      };
8704    }.bind(this)).reject(function(transform){
8705      return (
8706        (transform.originalValue == transform.targetValue) ||
8707        (
8708          transform.unit != 'color' &&
8709          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
8710        )
8711      );
8712    });
8713  },
8714  update: function(position) {
8715    var style = { }, transform, i = this.transforms.length;
8716    while(i--)
8717      style[(transform = this.transforms[i]).style] =
8718        transform.unit=='color' ? '#'+
8719          (Math.round(transform.originalValue[0]+
8720            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
8721          (Math.round(transform.originalValue[1]+
8722            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
8723          (Math.round(transform.originalValue[2]+
8724            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
8725        (transform.originalValue +
8726          (transform.targetValue - transform.originalValue) * position).toFixed(3) +
8727            (transform.unit === null ? '' : transform.unit);
8728    this.element.setStyle(style, true);
8729  }
8730});
8731
8732Effect.Transform = Class.create({
8733  initialize: function(tracks){
8734    this.tracks  = [];
8735    this.options = arguments[1] || { };
8736    this.addTracks(tracks);
8737  },
8738  addTracks: function(tracks){
8739    tracks.each(function(track){
8740      track = $H(track);
8741      var data = track.values().first();
8742      this.tracks.push($H({
8743        ids:     track.keys().first(),
8744        effect:  Effect.Morph,
8745        options: { style: data }
8746      }));
8747    }.bind(this));
8748    return this;
8749  },
8750  play: function(){
8751    return new Effect.Parallel(
8752      this.tracks.map(function(track){
8753        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
8754        var elements = [$(ids) || $$(ids)].flatten();
8755        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
8756      }).flatten(),
8757      this.options
8758    );
8759  }
8760});
8761
8762Element.CSS_PROPERTIES = $w(
8763  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
8764  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
8765  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
8766  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
8767  'fontSize fontWeight height left letterSpacing lineHeight ' +
8768  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
8769  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
8770  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
8771  'right textIndent top width wordSpacing zIndex');
8772
8773Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
8774
8775String.__parseStyleElement = document.createElement('div');
8776String.prototype.parseStyle = function(){
8777  var style, styleRules = $H();
8778  if (Prototype.Browser.WebKit)
8779    style = new Element('div',{style:this}).style;
8780  else {
8781    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
8782    style = String.__parseStyleElement.childNodes[0].style;
8783  }
8784
8785  Element.CSS_PROPERTIES.each(function(property){
8786    if (style[property]) styleRules.set(property, style[property]);
8787  });
8788
8789  if (Prototype.Browser.IE && this.include('opacity'))
8790    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
8791
8792  return styleRules;
8793};
8794
8795if (document.defaultView && document.defaultView.getComputedStyle) {
8796  Element.getStyles = function(element) {
8797    var css = document.defaultView.getComputedStyle($(element), null);
8798    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
8799      styles[property] = css[property];
8800      return styles;
8801    });
8802  };
8803} else {
8804  Element.getStyles = function(element) {
8805    element = $(element);
8806    var css = element.currentStyle, styles;
8807    styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
8808      results[property] = css[property];
8809      return results;
8810    });
8811    if (!styles.opacity) styles.opacity = element.getOpacity();
8812    return styles;
8813  };
8814}
8815
8816Effect.Methods = {
8817  morph: function(element, style) {
8818    element = $(element);
8819    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
8820    return element;
8821  },
8822  visualEffect: function(element, effect, options) {
8823    element = $(element);
8824    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
8825    new Effect[klass](element, options);
8826    return element;
8827  },
8828  highlight: function(element, options) {
8829    element = $(element);
8830    new Effect.Highlight(element, options);
8831    return element;
8832  }
8833};
8834
8835$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
8836  'pulsate shake puff squish switchOff dropOut').each(
8837  function(effect) {
8838    Effect.Methods[effect] = function(element, options){
8839      element = $(element);
8840      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
8841      return element;
8842    };
8843  }
8844);
8845
8846$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
8847  function(f) { Effect.Methods[f] = Element[f]; }
8848);
8849
8850Element.addMethods(Effect.Methods);// script.aculo.us controls.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
8851
8852// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
8853//           (c) 2005-2010 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
8854//           (c) 2005-2010 Jon Tirsen (http://www.tirsen.com)
8855// Contributors:
8856//  Richard Livsey
8857//  Rahul Bhargava
8858//  Rob Wills
8859//
8860// script.aculo.us is freely distributable under the terms of an MIT-style license.
8861// For details, see the script.aculo.us web site: http://script.aculo.us/
8862
8863// Autocompleter.Base handles all the autocompletion functionality
8864// that's independent of the data source for autocompletion. This
8865// includes drawing the autocompletion menu, observing keyboard
8866// and mouse events, and similar.
8867//
8868// Specific autocompleters need to provide, at the very least,
8869// a getUpdatedChoices function that will be invoked every time
8870// the text inside the monitored textbox changes. This method
8871// should get the text for which to provide autocompletion by
8872// invoking this.getToken(), NOT by directly accessing
8873// this.element.value. This is to allow incremental tokenized
8874// autocompletion. Specific auto-completion logic (AJAX, etc)
8875// belongs in getUpdatedChoices.
8876//
8877// Tokenized incremental autocompletion is enabled automatically
8878// when an autocompleter is instantiated with the 'tokens' option
8879// in the options parameter, e.g.:
8880// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
8881// will incrementally autocomplete with a comma as the token.
8882// Additionally, ',' in the above example can be replaced with
8883// a token array, e.g. { tokens: [',', '\n'] } which
8884// enables autocompletion on multiple tokens. This is most
8885// useful when one of the tokens is \n (a newline), as it
8886// allows smart autocompletion after linebreaks.
8887
8888if(typeof Effect == 'undefined')
8889  throw("controls.js requires including script.aculo.us' effects.js library");
8890
8891var Autocompleter = { };
8892Autocompleter.Base = Class.create({
8893  baseInitialize: function(element, update, options) {
8894    element          = $(element);
8895    this.element     = element;
8896    this.update      = $(update);
8897    this.hasFocus    = false;
8898    this.changed     = false;
8899    this.active      = false;
8900    this.index       = 0;
8901    this.entryCount  = 0;
8902    this.oldElementValue = this.element.value;
8903
8904    if(this.setOptions)
8905      this.setOptions(options);
8906    else
8907      this.options = options || { };
8908
8909    this.options.paramName    = this.options.paramName || this.element.name;
8910    this.options.tokens       = this.options.tokens || [];
8911    this.options.frequency    = this.options.frequency || 0.4;
8912    this.options.minChars     = this.options.minChars || 1;
8913    this.options.onShow       = this.options.onShow ||
8914      function(element, update){
8915        if(!update.style.position || update.style.position=='absolute') {
8916          update.style.position = 'absolute';
8917          Position.clone(element, update, {
8918            setHeight: false,
8919            offsetTop: element.offsetHeight
8920          });
8921        }
8922        Effect.Appear(update,{duration:0.15});
8923      };
8924    this.options.onHide = this.options.onHide ||
8925      function(element, update){ new Effect.Fade(update,{duration:0.15}) };
8926
8927    if(typeof(this.options.tokens) == 'string')
8928      this.options.tokens = new Array(this.options.tokens);
8929    // Force carriage returns as token delimiters anyway
8930    if (!this.options.tokens.include('\n'))
8931      this.options.tokens.push('\n');
8932
8933    this.observer = null;
8934
8935    this.element.setAttribute('autocomplete','off');
8936
8937    Element.hide(this.update);
8938
8939    Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
8940    Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
8941  },
8942
8943  show: function() {
8944    if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
8945    if(!this.iefix &&
8946      (Prototype.Browser.IE) &&
8947      (Element.getStyle(this.update, 'position')=='absolute')) {
8948      new Insertion.After(this.update,
8949       '<iframe id="' + this.update.id + '_iefix" '+
8950       'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
8951       'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
8952      this.iefix = $(this.update.id+'_iefix');
8953    }
8954    if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
8955  },
8956
8957  fixIEOverlapping: function() {
8958    Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
8959    this.iefix.style.zIndex = 1;
8960    this.update.style.zIndex = 2;
8961    Element.show(this.iefix);
8962  },
8963
8964  hide: function() {
8965    this.stopIndicator();
8966    if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
8967    if(this.iefix) Element.hide(this.iefix);
8968  },
8969
8970  startIndicator: function() {
8971    if(this.options.indicator) Element.show(this.options.indicator);
8972  },
8973
8974  stopIndicator: function() {
8975    if(this.options.indicator) Element.hide(this.options.indicator);
8976  },
8977
8978  onKeyPress: function(event) {
8979    var srcElementId = '';
8980    if (event.srcElement !== undefined && event.srcElement.id !== undefined) {
8981		srcElementId = event.srcElement.id;
8982	}
8983    if(this.active)
8984      switch(event.keyCode) {
8985       case Event.KEY_TAB:
8986       case Event.KEY_RETURN:
8987         this.selectEntry();
8988         Event.stop(event);
8989         if(srcElementId == 'search_waste'){
8990			onClickRedirectWasteList();
8991         }else if(srcElementId == 'search_text'){
8992            onEnterSelect();
8993         }
8994       case Event.KEY_ESC:
8995         this.hide();
8996         this.active = false;
8997         Event.stop(event);
8998         return;
8999       case Event.KEY_LEFT:
9000       case Event.KEY_RIGHT:
9001         return;
9002       case Event.KEY_UP:
9003         this.markPrevious();
9004         this.render();
9005
9006         Event.stop(event);
9007         return;
9008       case Event.KEY_DOWN:
9009         this.markNext();
9010         this.render();
9011
9012         Event.stop(event);
9013         return;
9014      }
9015     else
9016       if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
9017         (Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
9018
9019    this.changed = true;
9020    this.hasFocus = true;
9021
9022    if(this.observer) clearTimeout(this.observer);
9023      this.observer =
9024        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
9025  },
9026
9027  activate: function() {
9028    this.changed = false;
9029    this.hasFocus = true;
9030    this.getUpdatedChoices();
9031  },
9032
9033  onHover: function(event) {
9034    var element = Event.findElement(event, 'LI');
9035    if(element !== undefined){
9036        if(this.index != element.autocompleteIndex)
9037        {
9038            this.index = element.autocompleteIndex;
9039            this.render();
9040        }
9041    }
9042    Event.stop(event);
9043  },
9044
9045  onClick: function(event) {
9046    var element = Event.findElement(event, 'LI');
9047    if(element !== undefined){
9048        this.index = element.autocompleteIndex;
9049        this.selectEntry();
9050        this.hide();
9051    }
9052  },
9053
9054  onBlur: function(event) {
9055    // needed to make click events working
9056    setTimeout(this.hide.bind(this), 250);
9057    this.hasFocus = false;
9058    this.active = false;
9059  },
9060
9061  render: function() {
9062    if(this.entryCount > 0) {
9063      for (var i = 0; i < this.entryCount; i++)
9064        this.index==i ?
9065          Element.addClassName(this.getEntry(i),"selected") :
9066          Element.removeClassName(this.getEntry(i),"selected");
9067      if(this.hasFocus) {
9068        this.show();
9069        this.active = true;
9070      }
9071    } else {
9072      this.active = false;
9073      this.hide();
9074    }
9075  },
9076
9077  markPrevious: function() {
9078	var isNewLayout  = false;
9079	if (this.element !== undefined && this.element.id !== undefined && (this.element.id == 'search_waste' || this.element.id == 'search_text')) {
9080		isNewLayout = true;
9081	}
9082    if(this.index > 0) {
9083		this.index--;
9084    } else {
9085		 this.index = this.entryCount-1;
9086	}
9087	if (false == isNewLayout){
9088        this.getEntry(this.index).scrollIntoView(false);
9089     }
9090  },
9091
9092	markNext: function() {
9093		var isNewLayout  = false;
9094		if (this.element !== undefined && this.element.id !== undefined && (this.element.id == 'search_waste' || this.element.id == 'search_text')) {
9095			isNewLayout = true;
9096		}
9097	
9098	    if (this.index < this.entryCount-1) {
9099			this.index++;
9100	    } else {
9101	    	this.index = 0;
9102	    }
9103	    if (false == isNewLayout){
9104	        this.getEntry(this.index).scrollIntoView(false);
9105	    }
9106	},
9107	
9108	getEntry: function(index) {
9109    	return this.update.firstChild.childNodes[index];
9110	},
9111
9112  getCurrentEntry: function() {
9113    return this.getEntry(this.index);
9114  },
9115
9116  selectEntry: function() {
9117    this.active = false;
9118    this.updateElement(this.getCurrentEntry());
9119  },
9120  updateElement: function(selectedElement) {
9121    if (this.options.updateElement) {
9122      this.options.updateElement(selectedElement);
9123      return;
9124    }
9125    var value = '';
9126    if (this.options.select) {
9127      var nodes = $(selectedElement).select('.' + this.options.select) || [];
9128      if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
9129    } else
9130      value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
9131
9132    var bounds = this.getTokenBounds();
9133    if (bounds[0] != -1) {
9134      var newValue = this.element.value.substr(0, bounds[0]);
9135      var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
9136      if (whitespace)
9137        newValue += whitespace[0];
9138      this.element.value = newValue + value + this.element.value.substr(bounds[1]);
9139    } else {
9140      this.element.value = value;
9141    }
9142    this.oldElementValue = this.element.value;
9143    this.element.focus();
9144
9145    if (this.options.afterUpdateElement){
9146      this.options.afterUpdateElement(this.element, selectedElement);}
9147  },
9148
9149  updateChoices: function(choices) {
9150    if(!this.changed && this.hasFocus) {
9151      this.update.innerHTML = choices;
9152      Element.cleanWhitespace(this.update);
9153      Element.cleanWhitespace(this.update.down());
9154
9155      if(this.update.firstChild && this.update.down().childNodes) {
9156        this.entryCount =
9157          this.update.down().childNodes.length;
9158        for (var i = 0; i < this.entryCount; i++) {
9159          var entry = this.getEntry(i);
9160          entry.autocompleteIndex = i;
9161          this.addObservers(entry);
9162        }
9163      } else {
9164        this.entryCount = 0;
9165      }
9166
9167      this.stopIndicator();
9168      this.index = 0;
9169
9170      if(this.entryCount==1 && this.options.autoSelect) {
9171        this.selectEntry();
9172        this.hide();
9173      } else {
9174        this.render();
9175      }
9176    }
9177  },
9178
9179  addObservers: function(element) {
9180    Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
9181    Event.observe(element, "click", this.onClick.bindAsEventListener(this));
9182  },
9183
9184  onObserverEvent: function() {
9185    this.changed = false;
9186    this.tokenBounds = null;
9187    if(this.getToken().length>=this.options.minChars) {
9188      this.getUpdatedChoices();
9189    } else {
9190      this.active = false;
9191      this.hide();
9192    }
9193    this.oldElementValue = this.element.value;
9194  },
9195
9196  getToken: function() {
9197    var bounds = this.getTokenBounds();
9198    return this.element.value.substring(bounds[0], bounds[1]).strip();
9199  },
9200
9201  getTokenBounds: function() {
9202    if (null != this.tokenBounds) return this.tokenBounds;
9203    var value = this.element.value;
9204    if (value.strip().empty()) return [-1, 0];
9205    var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
9206    var offset = (diff == this.oldElementValue.length ? 1 : 0);
9207    var prevTokenPos = -1, nextTokenPos = value.length;
9208    var tp;
9209    for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
9210      tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
9211      if (tp > prevTokenPos) prevTokenPos = tp;
9212      tp = value.indexOf(this.options.tokens[index], diff + offset);
9213      if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
9214    }
9215    return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
9216  }
9217});
9218
9219Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
9220  var boundary = Math.min(newS.length, oldS.length);
9221  for (var index = 0; index < boundary; ++index)
9222    if (newS[index] != oldS[index])
9223      return index;
9224  return boundary;
9225};
9226
9227Ajax.Autocompleter = Class.create(Autocompleter.Base, {
9228  initialize: function(element, update, url, options) {
9229    this.baseInitialize(element, update, options);
9230    this.options.asynchronous  = true;
9231    this.options.onComplete    = this.onComplete.bind(this);
9232    this.options.defaultParams = this.options.parameters || null;
9233    this.url                   = url;
9234  },
9235
9236  getUpdatedChoices: function() {
9237    this.startIndicator();
9238
9239    var entry = encodeURIComponent(this.options.paramName) + '=' +
9240      encodeURIComponent(this.getToken());
9241
9242    this.options.parameters = this.options.callback ?
9243      this.options.callback(this.element, entry) : entry;
9244
9245    if(this.options.defaultParams)
9246      this.options.parameters += '&' + this.options.defaultParams;
9247
9248    new Ajax.Request(this.url, this.options);
9249  },
9250
9251  onComplete: function(request) {
9252    this.updateChoices(request.responseText);
9253  }
9254});
9255
9256// The local array autocompleter. Used when you'd prefer to
9257// inject an array of autocompletion options into the page, rather
9258// than sending out Ajax queries, which can be quite slow sometimes.
9259//
9260// The constructor takes four parameters. The first two are, as usual,
9261// the id of the monitored textbox, and id of the autocompletion menu.
9262// The third is the array you want to autocomplete from, and the fourth
9263// is the options block.
9264//
9265// Extra local autocompletion options:
9266// - choices - How many autocompletion choices to offer
9267//
9268// - partialSearch - If false, the autocompleter will match entered
9269//                    text only at the beginning of strings in the
9270//                    autocomplete array. Defaults to true, which will
9271//                    match text at the beginning of any *word* in the
9272//                    strings in the autocomplete array. If you want to
9273//                    search anywhere in the string, additionally set
9274//                    the option fullSearch to true (default: off).
9275//
9276// - fullSsearch - Search anywhere in autocomplete array strings.
9277//
9278// - partialChars - How many characters to enter before triggering
9279//                   a partial match (unlike minChars, which defines
9280//                   how many characters are required to do any match
9281//                   at all). Defaults to 2.
9282//
9283// - ignoreCase - Whether to ignore case when autocompleting.
9284//                 Defaults to true.
9285//
9286// It's possible to pass in a custom function as the 'selector'
9287// option, if you prefer to write your own autocompletion logic.
9288// In that case, the other options above will not apply unless
9289// you support them.
9290
9291Autocompleter.Local = Class.create(Autocompleter.Base, {
9292  initialize: function(element, update, array, options) {
9293    this.baseInitialize(element, update, options);
9294    this.options.array = array;
9295  },
9296
9297  getUpdatedChoices: function() {
9298    this.updateChoices(this.options.selector(this));
9299  },
9300
9301  setOptions: function(options) {
9302    this.options = Object.extend({
9303      choices: 10,
9304      partialSearch: true,
9305      partialChars: 2,
9306      ignoreCase: true,
9307      fullSearch: false,
9308      selector: function(instance) {
9309        var ret       = []; // Beginning matches
9310        var partial   = []; // Inside matches
9311        var entry     = instance.getToken();
9312        var count     = 0;
9313
9314        for (var i = 0; i < instance.options.array.length &&
9315          ret.length < instance.options.choices ; i++) {
9316
9317          var elem = instance.options.array[i];
9318          var foundPos = instance.options.ignoreCase ?
9319            elem.toLowerCase().indexOf(entry.toLowerCase()) :
9320            elem.indexOf(entry);
9321
9322          while (foundPos != -1) {
9323            if (foundPos == 0 && elem.length != entry.length) {
9324              ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
9325                elem.substr(entry.length) + "</li>");
9326              break;
9327            } else if (entry.length >= instance.options.partialChars &&
9328              instance.options.partialSearch && foundPos != -1) {
9329              if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
9330                partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
9331                  elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
9332                  foundPos + entry.length) + "</li>");
9333                break;
9334              }
9335            }
9336
9337            foundPos = instance.options.ignoreCase ?
9338              elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
9339              elem.indexOf(entry, foundPos + 1);
9340
9341          }
9342        }
9343        if (partial.length)
9344          ret = ret.concat(partial.slice(0, instance.options.choices - ret.length));
9345        return "<ul>" + ret.join('') + "</ul>";
9346      }
9347    }, options || { });
9348  }
9349});
9350
9351// AJAX in-place editor and collection editor
9352// Full rewrite by Christophe Porteneuve <[email protected]> (April 2007).
9353
9354// Use this if you notice weird scrolling problems on some browsers,
9355// the DOM might be a bit confused when this gets called so do this
9356// waits 1 ms (with setTimeout) until it does the activation
9357Field.scrollFreeActivate = function(field) {
9358  setTimeout(function() {
9359    Field.activate(field);
9360  }, 1);
9361};
9362
9363Ajax.InPlaceEditor = Class.create({
9364  initialize: function(element, url, options) {
9365    this.url = url;
9366    this.element = element = $(element);
9367    this.prepareOptions();
9368    this._controls = { };
9369    arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
9370    Object.extend(this.options, options || { });
9371    if (!this.options.formId && this.element.id) {
9372      this.options.formId = this.element.id + '-inplaceeditor';
9373      if ($(this.options.formId))
9374        this.options.formId = '';
9375    }
9376    if (this.options.externalControl)
9377      this.options.externalControl = $(this.options.externalControl);
9378    if (!this.options.externalControl)
9379      this.options.externalControlOnly = false;
9380    this._originalBackground = this.element.getStyle('background-color') || 'transparent';
9381    this.element.title = this.options.clickToEditText;
9382    this._boundCancelHandler = this.handleFormCancellation.bind(this);
9383    this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
9384    this._boundFailureHandler = this.handleAJAXFailure.bind(this);
9385    this._boundSubmitHandler = this.handleFormSubmission.bind(this);
9386    this._boundWrapperHandler = this.wrapUp.bind(this);
9387    this.registerListeners();
9388  },
9389  checkForEscapeOrReturn: function(e) {
9390    if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
9391    if (Event.KEY_ESC == e.keyCode)
9392      this.handleFormCancellation(e);
9393    else if (Event.KEY_RETURN == e.keyCode)
9394      this.handleFormSubmission(e);
9395  },
9396  createControl: function(mode, handler, extraClasses) {
9397    var control = this.options[mode + 'Control'];
9398    var text = this.options[mode + 'Text'];
9399    if ('button' == control) {
9400      var btn = document.createElement('input');
9401      btn.type = 'submit';
9402      btn.value = text;
9403      btn.className = 'editor_' + mode + '_button';
9404      if ('cancel' == mode)
9405        btn.onclick = this._boundCancelHandler;
9406      this._form.appendChild(btn);
9407      this._controls[mode] = btn;
9408    } else if ('link' == control) {
9409      var link = document.createElement('a');
9410      link.href = '#';
9411      link.appendChild(document.createTextNode(text));
9412      link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
9413      link.className = 'editor_' + mode + '_link';
9414      if (extraClasses)
9415        link.className += ' ' + extraClasses;
9416      this._form.appendChild(link);
9417      this._controls[mode] = link;
9418    }
9419  },
9420  createEditField: function() {
9421    var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
9422    var fld;
9423    if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
9424      fld = document.createElement('input');
9425      fld.type = 'text';
9426      var size = this.options.size || this.options.cols || 0;
9427      if (0 < size) fld.size = size;
9428    } else {
9429      fld = document.createElement('textarea');
9430      fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
9431      fld.cols = this.options.cols || 40;
9432    }
9433    fld.name = this.options.paramName;
9434    fld.value = text; // No HTML breaks conversion anymore
9435    fld.className = 'editor_field';
9436    if (this.options.submitOnBlur)
9437      fld.onblur = this._boundSubmitHandler;
9438    this._controls.editor = fld;
9439    if (this.options.loadTextURL)
9440      this.loadExternalText();
9441    this._form.appendChild(this._controls.editor);
9442  },
9443  createForm: function() {
9444    var ipe = this;
9445    function addText(mode, condition) {
9446      var text = ipe.options['text' + mode + 'Controls'];
9447      if (!text || condition === false) return;
9448      ipe._form.appendChild(document.createTextNode(text));
9449    };
9450    this._form = $(document.createElement('form'));
9451    this._form.id = this.options.formId;
9452    this._form.addClassName(this.options.formClassName);
9453    this._form.onsubmit = this._boundSubmitHandler;
9454    this.createEditField();
9455    if ('textarea' == this._controls.editor.tagName.toLowerCase())
9456      this._form.appendChild(document.createElement('br'));
9457    if (this.options.onFormCustomization)
9458      this.options.onFormCustomization(this, this._form);
9459    addText('Before', this.options.okControl || this.options.cancelControl);
9460    this.createControl('ok', this._boundSubmitHandler);
9461    addText('Between', this.options.okControl && this.options.cancelControl);
9462    this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
9463    addText('After', this.options.okControl || this.options.cancelControl);
9464  },
9465  destroy: function() {
9466    if (this._oldInnerHTML)
9467      this.element.innerHTML = this._oldInnerHTML;
9468    this.leaveEditMode();
9469    this.unregisterListeners();
9470  },
9471  enterEditMode: function(e) {
9472    if (this._saving || this._editing) return;
9473    this._editing = true;
9474    this.triggerCallback('onEnterEditMode');
9475    if (this.options.externalControl)
9476      this.options.externalControl.hide();
9477    this.element.hide();
9478    this.createForm();
9479    this.element.parentNode.insertBefore(this._form, this.element);
9480    if (!this.options.loadTextURL)
9481      this.postProcessEditField();
9482    if (e) Event.stop(e);
9483  },
9484  enterHover: function(e) {
9485    if (this.options.hoverClassName)
9486      this.element.addClassName(this.options.hoverClassName);
9487    if (this._saving) return;
9488    this.triggerCallback('onEnterHover');
9489  },
9490  getText: function() {
9491    return this.element.innerHTML.unescapeHTML();
9492  },
9493  handleAJAXFailure: function(transport) {
9494    this.triggerCallback('onFailure', transport);
9495    if (this._oldInnerHTML) {
9496      this.element.innerHTML = this._oldInnerHTML;
9497      this._oldInnerHTML = null;
9498    }
9499  },
9500  handleFormCancellation: function(e) {
9501    this.wrapUp();
9502    if (e) Event.stop(e);
9503  },
9504  handleFormSubmission: function(e) {
9505    var form = this._form;
9506    var value = $F(this._controls.editor);
9507    this.prepareSubmission();
9508    var params = this.options.callback(form, value) || '';
9509    if (Object.isString(params))
9510      params = params.toQueryParams();
9511    params.editorId = this.element.id;
9512    if (this.options.htmlResponse) {
9513      var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
9514      Object.extend(options, {
9515        parameters: params,
9516        onComplete: this._boundWrapperHandler,
9517        onFailure: this._boundFailureHandler
9518      });
9519      new Ajax.Updater({ success: this.element }, this.url, options);
9520    } else {
9521      var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
9522      Object.extend(options, {
9523        parameters: params,
9524        onComplete: this._boundWrapperHandler,
9525        onFailure: this._boundFailureHandler
9526      });
9527      new Ajax.Request(this.url, options);
9528    }
9529    if (e) Event.stop(e);
9530  },
9531  leaveEditMode: function() {
9532    this.element.removeClassName(this.options.savingClassName);
9533    this.removeForm();
9534    this.leaveHover();
9535    this.element.style.backgroundColor = this._originalBackground;
9536    this.element.show();
9537    if (this.options.externalControl)
9538      this.options.externalControl.show();
9539    this._saving = false;
9540    this._editing = false;
9541    this._oldInnerHTML = null;
9542    this.triggerCallback('onLeaveEditMode');
9543  },
9544  leaveHover: function(e) {
9545    if (this.options.hoverClassName)
9546      this.element.removeClassName(this.options.hoverClassName);
9547    if (this._saving) return;
9548    this.triggerCallback('onLeaveHover');
9549  },
9550  loadExternalText: function() {
9551    this._form.addClassName(this.options.loadingClassName);
9552    this._controls.editor.disabled = true;
9553    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
9554    Object.extend(options, {
9555      parameters: 'editorId=' + encodeURIComponent(this.element.id),
9556      onComplete: Prototype.emptyFunction,
9557      onSuccess: function(transport) {
9558        this._form.removeClassName(this.options.loadingClassName);
9559        var text = transport.responseText;
9560        if (this.options.stripLoadedTextTags)
9561          text = text.stripTags();
9562        this._controls.editor.value = text;
9563        this._controls.editor.disabled = false;
9564        this.postProcessEditField();
9565      }.bind(this),
9566      onFailure: this._boundFailureHandler
9567    });
9568    new Ajax.Request(this.options.loadTextURL, options);
9569  },
9570  postProcessEditField: function() {
9571    var fpc = this.options.fieldPostCreation;
9572    if (fpc)
9573      $(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
9574  },
9575  prepareOptions: function() {
9576    this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
9577    Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
9578    [this._extraDefaultOptions].flatten().compact().each(function(defs) {
9579      Object.extend(this.options, defs);
9580    }.bind(this));
9581  },
9582  prepareSubmission: function() {
9583    this._saving = true;
9584    this.removeForm();
9585    this.leaveHover();
9586    this.showSaving();
9587  },
9588  registerListeners: function() {
9589    this._listeners = { };
9590    var listener;
9591    $H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
9592      listener = this[pair.value].bind(this);
9593      this._listeners[pair.key] = listener;
9594      if (!this.options.externalControlOnly)
9595        this.element.observe(pair.key, listener);
9596      if (this.options.externalControl)
9597        this.options.externalControl.observe(pair.key, listener);
9598    }.bind(this));
9599  },
9600  removeForm: function() {
9601    if (!this._form) return;
9602    this._form.remove();
9603    this._form = null;
9604    this._controls = { };
9605  },
9606  showSaving: function() {
9607    this._oldInnerHTML = this.element.innerHTML;
9608    this.element.innerHTML = this.options.savingText;
9609    this.element.addClassName(this.options.savingClassName);
9610    this.element.style.backgroundColor = this._originalBackgroun
9610d;
9611    this.element.show();
9612  },
9613  triggerCallback: function(cbName, arg) {
9614    if ('function' == typeof this.options[cbName]) {
9615      this.options[cbName](this, arg);
9616    }
9617  },
9618  unregisterListeners: function() {
9619    $H(this._listeners).each(function(pair) {
9620      if (!this.options.externalControlOnly)
9621        this.element.stopObserving(pair.key, pair.value);
9622      if (this.options.externalControl)
9623        this.options.externalControl.stopObserving(pair.key, pair.value);
9624    }.bind(this));
9625  },
9626  wrapUp: function(transport) {
9627    this.leaveEditMode();
9628    // Can't use triggerCallback due to backward compatibility: requires
9629    // binding + direct element
9630    this._boundComplete(transport, this.element);
9631  }
9632});
9633
9634Object.extend(Ajax.InPlaceEditor.prototype, {
9635  dispose: Ajax.InPlaceEditor.prototype.destroy
9636});
9637
9638Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
9639  initialize: function($super, element, url, options) {
9640    this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
9641    $super(element, url, options);
9642  },
9643
9644  createEditField: function() {
9645    var list = document.createElement('select');
9646    list.name = this.options.paramName;
9647    list.size = 1;
9648    this._controls.editor = list;
9649    this._collection = this.options.collection || [];
9650    if (this.options.loadCollectionURL)
9651      this.loadCollection();
9652    else
9653      this.checkForExternalText();
9654    this._form.appendChild(this._controls.editor);
9655  },
9656
9657  loadCollection: function() {
9658    this._form.addClassName(this.options.loadingClassName);
9659    this.showLoadingText(this.options.loadingCollectionText);
9660    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
9661    Object.extend(options, {
9662      parameters: 'editorId=' + encodeURIComponent(this.element.id),
9663      onComplete: Prototype.emptyFunction,
9664      onSuccess: function(transport) {
9665        var js = transport.responseText.strip();
9666        if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
9667          throw('Server returned an invalid collection representation.');
9668        this._collection = eval(js);
9669        this.checkForExternalText();
9670      }.bind(this),
9671      onFailure: this.onFailure
9672    });
9673    new Ajax.Request(this.options.loadCollectionURL, options);
9674  },
9675
9676  showLoadingText: function(text) {
9677    this._controls.editor.disabled = true;
9678    var tempOption = this._controls.editor.firstChild;
9679    if (!tempOption) {
9680      tempOption = document.createElement('option');
9681      tempOption.value = '';
9682      this._controls.editor.appendChild(tempOption);
9683      tempOption.selected = true;
9684    }
9685    tempOption.update((text || '').stripScripts().stripTags());
9686  },
9687
9688  checkForExternalText: function() {
9689    this._text = this.getText();
9690    if (this.options.loadTextURL)
9691      this.loadExternalText();
9692    else
9693      this.buildOptionList();
9694  },
9695
9696  loadExternalText: function() {
9697    this.showLoadingText(this.options.loadingText);
9698    var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
9699    Object.extend(options, {
9700      parameters: 'editorId=' + encodeURIComponent(this.element.id),
9701      onComplete: Prototype.emptyFunction,
9702      onSuccess: function(transport) {
9703        this._text = transport.responseText.strip();
9704        this.buildOptionList();
9705      }.bind(this),
9706      onFailure: this.onFailure
9707    });
9708    new Ajax.Request(this.options.loadTextURL, options);
9709  },
9710
9711  buildOptionList: function() {
9712    this._form.removeClassName(this.options.loadingClassName);
9713    this._collection = this._collection.map(function(entry) {
9714      return 2 === entry.length ? entry : [entry, entry].flatten();
9715    });
9716    var marker = ('value' in this.options) ? this.options.value : this._text;
9717    var textFound = this._collection.any(function(entry) {
9718      return entry[0] == marker;
9719    }.bind(this));
9720    this._controls.editor.update('');
9721    var option;
9722    this._collection.each(function(entry, index) {
9723      option = document.createElement('option');
9724      option.value = entry[0];
9725      option.selected = textFound ? entry[0] == marker : 0 == index;
9726      option.appendChild(document.createTextNode(entry[1]));
9727      this._controls.editor.appendChild(option);
9728    }.bind(this));
9729    this._controls.editor.disabled = false;
9730    Field.scrollFreeActivate(this._controls.editor);
9731  }
9732});
9733
9734//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
9735//**** This only  exists for a while,  in order to  let ****
9736//**** users adapt to  the new API.  Read up on the new ****
9737//**** API and convert your code to it ASAP!            ****
9738
9739Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
9740  if (!options) return;
9741  function fallback(name, expr) {
9742    if (name in options || expr === undefined) return;
9743    options[name] = expr;
9744  };
9745  fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
9746    options.cancelLink == options.cancelButton == false ? false : undefined)));
9747  fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
9748    options.okLink == options.okButton == false ? false : undefined)));
9749  fallback('highlightColor', options.highlightcolor);
9750  fallback('highlightEndColor', options.highlightendcolor);
9751};
9752
9753Object.extend(Ajax.InPlaceEditor, {
9754  DefaultOptions: {
9755    ajaxOptions: { },
9756    autoRows: 3,                                // Use when multi-line w/ rows == 1
9757    cancelControl: 'link',                      // 'link'|'button'|false
9758    cancelText: 'cancel',
9759    clickToEditText: 'Click to edit',
9760    externalControl: null,                      // id|elt
9761    externalControlOnly: false,
9762    fieldPostCreation: 'activate',              // 'activate'|'focus'|false
9763    formClassName: 'inplaceeditor-form',
9764    formId: null,                               // id|elt
9765    highlightColor: '#ffff99',
9766    highlightEndColor: '#ffffff',
9767    hoverClassName: '',
9768    htmlResponse: true,
9769    loadingClassName: 'inplaceeditor-loading',
9770    loadingText: 'Loading...',
9771    okControl: 'button',                        // 'link'|'button'|false
9772    okText: 'ok',
9773    paramName: 'value',
9774    rows: 1,                                    // If 1 and multi-line, uses autoRows
9775    savingClassName: 'inplaceeditor-saving',
9776    savingText: 'Saving...',
9777    size: 0,
9778    stripLoadedTextTags: false,
9779    submitOnBlur: false,
9780    textAfterControls: '',
9781    textBeforeControls: '',
9782    textBetweenControls: ''
9783  },
9784  DefaultCallbacks: {
9785    callback: function(form) {
9786      return Form.serialize(form);
9787    },
9788    onComplete: function(transport, element) {
9789      // For backward compatibility, this one is bound to the IPE, and passes
9790      // the element directly.  It was too often customized, so we don't break it.
9791      new Effect.Highlight(element, {
9792        startcolor: this.options.highlightColor, keepBackgroundImage: true });
9793    },
9794    onEnterEditMode: null,
9795    onEnterHover: function(ipe) {
9796      ipe.element.style.backgroundColor = ipe.options.highlightColor;
9797      if (ipe._effect)
9798        ipe._effect.cancel();
9799    },
9800    onFailure: function(transport, ipe) {
9801      alert('Error communication with the server: ' + transport.responseText.stripTags());
9802    },
9803    onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
9804    onLeaveEditMode: null,
9805    onLeaveHover: function(ipe) {
9806      ipe._effect = new Effect.Highlight(ipe.element, {
9807        startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
9808        restorecolor: ipe._originalBackground, keepBackgroundImage: true
9809      });
9810    }
9811  },
9812  Listeners: {
9813    click: 'enterEditMode',
9814    keydown: 'checkForEscapeOrReturn',
9815    mouseover: 'enterHover',
9816    mouseout: 'leaveHover'
9817  }
9818});
9819
9820Ajax.InPlaceCollectionEditor.DefaultOptions = {
9821  loadingCollectionText: 'Loading options...'
9822};
9823
9824// Delayed observer, like Form.Element.Observer,
9825// but waits for delay after last key input
9826// Ideal for live-search fields
9827
9828Form.Element.DelayedObserver = Class.create({
9829  initialize: function(element, delay, callback) {
9830    this.delay     = delay || 0.5;
9831    this.element   = $(element);
9832    this.callback  = callback;
9833    this.timer     = null;
9834    this.lastValue = $F(this.element);
9835    Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
9836  },
9837  delayedListener: function(event) {
9838    if(this.lastValue == $F(this.element)) return;
9839    if(this.timer) clearTimeout(this.timer);
9840    this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
9841    this.lastValue = $F(this.element);
9842  },
9843  onTimerEvent: function() {
9844    this.timer = null;
9845    this.callback(this.element, $F(this.element));
9846  }
9847});// script.aculo.us dragdrop.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
9848
9849// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
9850//
9851// script.aculo.us is freely distributable under the terms of an MIT-style license.
9852// For details, see the script.aculo.us web site: http://script.aculo.us/
9853
9854if(Object.isUndefined(Effect))
9855  throw("dragdrop.js requires including script.aculo.us' effects.js library");
9856
9857var Droppables = {
9858  drops: [],
9859
9860  remove: function(element) {
9861    this.drops = this.drops.reject(function(d) { return d.element==$(element) });
9862  },
9863
9864  add: function(element) {
9865    element = $(element);
9866    var options = Object.extend({
9867      greedy:     true,
9868      hoverclass: null,
9869      tree:       false
9870    }, arguments[1] || { });
9871
9872    // cache containers
9873    if(options.containment) {
9874      options._containers = [];
9875      var containment = options.containment;
9876      if(Object.isArray(containment)) {
9877        containment.each( function(c) { options._containers.push($(c)) });
9878      } else {
9879        options._containers.push($(containment));
9880      }
9881    }
9882
9883    if(options.accept) options.accept = [options.accept].flatten();
9884
9885    Element.makePositioned(element); // fix IE
9886    options.element = element;
9887
9888    this.drops.push(options);
9889  },
9890
9891  findDeepestChild: function(drops) {
9892    deepest = drops[0];
9893
9894    for (i = 1; i < drops.length; ++i)
9895      if (Element.isParent(drops[i].element, deepest.element))
9896        deepest = drops[i];
9897
9898    return deepest;
9899  },
9900
9901  isContained: function(element, drop) {
9902    var containmentNode;
9903    if(drop.tree) {
9904      containmentNode = element.treeNode;
9905    } else {
9906      containmentNode = element.parentNode;
9907    }
9908    return drop._containers.detect(function(c) { return containmentNode == c });
9909  },
9910
9911  isAffected: function(point, element, drop) {
9912    return (
9913      (drop.element!=element) &&
9914      ((!drop._containers) ||
9915        this.isContained(element, drop)) &&
9916      ((!drop.accept) ||
9917        (Element.classNames(element).detect(
9918          function(v) { return drop.accept.include(v) } ) )) &&
9919      Position.within(drop.element, point[0], point[1]) );
9920  },
9921
9922  deactivate: function(drop) {
9923    if(drop.hoverclass)
9924      Element.removeClassName(drop.element, drop.hoverclass);
9925    this.last_active = null;
9926  },
9927
9928  activate: function(drop) {
9929    if(drop.hoverclass)
9930      Element.addClassName(drop.element, drop.hoverclass);
9931    this.last_active = drop;
9932  },
9933
9934  show: function(point, element) {
9935    if(!this.drops.length) return;
9936    var drop, affected = [];
9937
9938    this.drops.each( function(drop) {
9939      if(Droppables.isAffected(point, element, drop))
9940        affected.push(drop);
9941    });
9942
9943    if(affected.length>0)
9944      drop = Droppables.findDeepestChild(affected);
9945
9946    if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
9947    if (drop) {
9948      Position.within(drop.element, point[0], point[1]);
9949      if(drop.onHover)
9950        drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
9951
9952      if (drop != this.last_active) Droppables.activate(drop);
9953    }
9954  },
9955
9956  fire: function(event, element) {
9957    if(!this.last_active) return;
9958    Position.prepare();
9959
9960    if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
9961      if (this.last_active.onDrop) {
9962        this.last_active.onDrop(element, this.last_active.element, event);
9963        return true;
9964      }
9965  },
9966
9967  reset: function() {
9968    if(this.last_active)
9969      this.deactivate(this.last_active);
9970  }
9971};
9972
9973var Draggables = {
9974  drags: [],
9975  observers: [],
9976
9977  register: function(draggable) {
9978    if(this.drags.length == 0) {
9979      this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
9980      this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
9981      this.eventKeypress  = this.keyPress.bindAsEventListener(this);
9982
9983      Event.observe(document, "mouseup", this.eventMouseUp);
9984      Event.observe(document, "mousemove", this.eventMouseMove);
9985      Event.observe(document, "keypress", this.eventKeypress);
9986    }
9987    this.drags.push(draggable);
9988  },
9989
9990  unregister: function(draggable) {
9991    this.drags = this.drags.reject(function(d) { return d==draggable });
9992    if(this.drags.length == 0) {
9993      Event.stopObserving(document, "mouseup", this.eventMouseUp);
9994      Event.stopObserving(document, "mousemove", this.eventMouseMove);
9995      Event.stopObserving(document, "keypress", this.eventKeypress);
9996    }
9997  },
9998
9999  activate: function(draggable) {
10000    if(draggable.options.delay) {
10001      this._timeout = setTimeout(function() {
10002        Draggables._timeout = null;
10003        window.focus();
10004        Draggables.activeDraggable = draggable;
10005      }.bind(this), draggable.options.delay);
10006    } else {
10007      window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
10008      this.activeDraggable = draggable;
10009    }
10010  },
10011
10012  deactivate: function() {
10013    this.activeDraggable = null;
10014  },
10015
10016  updateDrag: function(event) {
10017    if(!this.activeDraggable) return;
10018    var pointer = [Event.pointerX(event), Event.pointerY(event)];
10019    // Mozilla-based browsers fire successive mousemove events with
10020    // the same coordinates, prevent needless redrawing (moz bug?)
10021    if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
10022    this._lastPointer = pointer;
10023
10024    this.activeDraggable.updateDrag(event, pointer);
10025  },
10026
10027  endDrag: function(event) {
10028    if(this._timeout) {
10029      clearTimeout(this._timeout);
10030      this._timeout = null;
10031    }
10032    if(!this.activeDraggable) return;
10033    this._lastPointer = null;
10034    this.activeDraggable.endDrag(event);
10035    this.activeDraggable = null;
10036  },
10037
10038  keyPress: function(event) {
10039    if(this.activeDraggable)
10040      this.activeDraggable.keyPress(event);
10041  },
10042
10043  addObserver: function(observer) {
10044    this.observers.push(observer);
10045    this._cacheObserverCallbacks();
10046  },
10047
10048  removeObserver: function(element) {  // element instead of observer fixes mem leaks
10049    this.observers = this.observers.reject( function(o) { return o.element==element });
10050    this._cacheObserverCallbacks();
10051  },
10052
10053  notify: function(eventName, draggable, event) {  // 'onStart', 'onEnd', 'onDrag'
10054    if(this[eventName+'Count'] > 0)
10055      this.observers.each( function(o) {
10056        if(o[eventName]) o[eventName](eventName, draggable, event);
10057      });
10058    if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
10059  },
10060
10061  _cacheObserverCallbacks: function() {
10062    ['onStart','onEnd','onDrag'].each( function(eventName) {
10063      Draggables[eventName+'Count'] = Draggables.observers.select(
10064        function(o) { return o[eventName]; }
10065      ).length;
10066    });
10067  }
10068};
10069
10070/*--------------------------------------------------------------------------*/
10071
10072var Draggable = Class.create({
10073  initialize: function(element) {
10074    var defaults = {
10075      handle: false,
10076      reverteffect: function(element, top_offset, left_offset) {
10077        var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
10078        new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
10079          queue: {scope:'_draggable', position:'end'}
10080        });
10081      },
10082      endeffect: function(element) {
10083        var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
10084        new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
10085          queue: {scope:'_draggable', position:'end'},
10086          afterFinish: function(){
10087            Draggable._dragging[element] = false
10088          }
10089        });
10090      },
10091      zindex: 1000,
10092      revert: false,
10093      quiet: false,
10094      scroll: false,
10095      scrollSensitivity: 20,
10096      scrollSpeed: 15,
10097      snap: false,  // false, or xy or [x,y] or function(x,y){ return [x,y] }
10098      delay: 0
10099    };
10100
10101    if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
10102      Object.extend(defaults, {
10103        starteffect: function(element) {
10104          element._opacity = Element.getOpacity(element);
10105          Draggable._dragging[element] = true;
10106          new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
10107        }
10108      });
10109
10110    var options = Object.extend(defaults, arguments[1] || { });
10111
10112    this.element = $(element);
10113
10114    if(options.handle && Object.isString(options.handle))
10115      this.handle = this.element.down('.'+options.handle, 0);
10116
10117    if(!this.handle) this.handle = $(options.handle);
10118    if(!this.handle) this.handle = this.element;
10119
10120    if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
10121      options.scroll = $(options.scroll);
10122      this._isScrollChild = Element.childOf(this.element, options.scroll);
10123    }
10124
10125    Element.makePositioned(this.element); // fix IE
10126
10127    this.options  = options;
10128    this.dragging = false;
10129
10130    this.eventMouseDown = this.initDrag.bindAsEventListener(this);
10131    Event.observe(this.handle, "mousedown", this.eventMouseDown);
10132
10133    Draggables.register(this);
10134  },
10135
10136  destroy: function() {
10137    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
10138    Draggables.unregister(this);
10139  },
10140
10141  currentDelta: function() {
10142    return([
10143      parseInt(Element.getStyle(this.element,'left') || '0'),
10144      parseInt(Element.getStyle(this.element,'top') || '0')]);
10145  },
10146
10147  initDrag: function(event) {
10148    if(!Object.isUndefined(Draggable._dragging[this.element]) &&
10149      Draggable._dragging[this.element]) return;
10150    if(Event.isLeftClick(event)) {
10151      // abort on form elements, fixes a Firefox issue
10152      var src = Event.element(event);
10153      if((tag_name = src.tagName.toUpperCase()) && (
10154        tag_name=='INPUT' ||
10155        tag_name=='SELECT' ||
10156        tag_name=='OPTION' ||
10157        tag_name=='BUTTON' ||
10158        tag_name=='TEXTAREA')) return;
10159
10160      var pointer = [Event.pointerX(event), Event.pointerY(event)];
10161      var pos     = this.element.cumulativeOffset();
10162      this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
10163
10164      Draggables.activate(this);
10165      Event.stop(event);
10166    }
10167  },
10168
10169  startDrag: function(event) {
10170    this.dragging = true;
10171    if(!this.delta)
10172      this.delta = this.currentDelta();
10173
10174    if(this.options.zindex) {
10175      this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
10176      this.element.style.zIndex = this.options.zindex;
10177    }
10178
10179    if(this.options.ghosting) {
10180      this._clone = this.element.cloneNode(true);
10181      this._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
10182      if (!this._originallyAbsolute)
10183        Position.absolutize(this.element);
10184      this.element.parentNode.insertBefore(this._clone, this.element);
10185    }
10186
10187    if(this.options.scroll) {
10188      if (this.options.scroll == window) {
10189        var where = this._getWindowScroll(this.options.scroll);
10190        this.originalScrollLeft = where.left;
10191        this.originalScrollTop = where.top;
10192      } else {
10193        this.originalScrollLeft = this.options.scroll.scrollLeft;
10194        this.originalScrollTop = this.options.scroll.scrollTop;
10195      }
10196    }
10197
10198    Draggables.notify('onStart', this, event);
10199
10200    if(this.options.starteffect) this.options.starteffect(this.element);
10201  },
10202
10203  updateDrag: function(event, pointer) {
10204    if(!this.dragging) this.startDrag(event);
10205
10206    if(!this.options.quiet){
10207      Position.prepare();
10208      Droppables.show(pointer, this.element);
10209    }
10210
10211    Draggables.notify('onDrag', this, event);
10212
10213    this.draw(pointer);
10214    if(this.options.change) this.options.change(this);
10215
10216    if(this.options.scroll) {
10217      this.stopScrolling();
10218
10219      var p;
10220      if (this.options.scroll == window) {
10221        with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
10222      } else {
10223        p = Position.page(this.options.scroll).toArray();
10224        p[0] += this.options.scroll.scrollLeft + Position.deltaX;
10225        p[1] += this.options.scroll.scrollTop + Position.deltaY;
10226        p.push(p[0]+this.options.scroll.offsetWidth);
10227        p.push(p[1]+this.options.scroll.offsetHeight);
10228      }
10229      var speed = [0,0];
10230      if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
10231      if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
10232      if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
10233      if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
10234      this.startScrolling(speed);
10235    }
10236
10237    // fix AppleWebKit rendering
10238    if(Prototype.Browser.WebKit) window.scrollBy(0,0);
10239
10240    Event.stop(event);
10241  },
10242
10243  finishDrag: function(event, success) {
10244    this.dragging = false;
10245
10246    if(this.options.quiet){
10247      Position.prepare();
10248      var pointer = [Event.pointerX(event), Event.pointerY(event)];
10249      Droppables.show(pointer, this.element);
10250    }
10251
10252    if(this.options.ghosting) {
10253      if (!this._originallyAbsolute)
10254        Position.relativize(this.element);
10255      delete this._originallyAbsolute;
10256      Element.remove(this._clone);
10257      this._clone = null;
10258    }
10259
10260    var dropped = false;
10261    if(success) {
10262      dropped = Droppables.fire(event, this.element);
10263      if (!dropped) dropped = false;
10264    }
10265    if(dropped && this.options.onDropped) this.options.onDropped(this.element);
10266    Draggables.notify('onEnd', this, event);
10267
10268    var revert = this.options.revert;
10269    if(revert && Object.isFunction(revert)) revert = revert(this.element);
10270
10271    var d = this.currentDelta();
10272    if(revert && this.options.reverteffect) {
10273      if (dropped == 0 || revert != 'failure')
10274        this.options.reverteffect(this.element,
10275          d[1]-this.delta[1], d[0]-this.delta[0]);
10276    } else {
10277      this.delta = d;
10278    }
10279
10280    if(this.options.zindex)
10281      this.element.style.zIndex = this.originalZ;
10282
10283    if(this.options.endeffect)
10284      this.options.endeffect(this.element);
10285
10286    Draggables.deactivate(this);
10287    Droppables.reset();
10288  },
10289
10290  keyPress: function(event) {
10291    if(event.keyCode!=Event.KEY_ESC) return;
10292    this.finishDrag(event, false);
10293    Event.stop(event);
10294  },
10295
10296  endDrag: function(event) {
10297    if(!this.dragging) return;
10298    this.stopScrolling();
10299    this.finishDrag(event, true);
10300    Event.stop(event);
10301  },
10302
10303  draw: function(point) {
10304    var pos = this.element.cumulativeOffset();
10305    if(this.options.ghosting) {
10306      var r   = Position.realOffset(this.element);
10307      pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
10308    }
10309
10310    var d = this.currentDelta();
10311    pos[0] -= d[0]; pos[1] -= d[1];
10312
10313    if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
10314      pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
10315      pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
10316    }
10317
10318    var p = [0,1].map(function(i){
10319      return (point[i]-pos[i]-this.offset[i])
10320    }.bind(this));
10321
10322    if(this.options.snap) {
10323      if(Object.isFunction(this.options.snap)) {
10324        p = this.options.snap(p[0],p[1],this);
10325      } else {
10326      if(Object.isArray(this.options.snap)) {
10327        p = p.map( function(v, i) {
10328          return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this));
10329      } else {
10330        p = p.map( function(v) {
10331          return (v/this.options.snap).round()*this.options.snap }.bind(this));
10332      }
10333    }}
10334
10335    var style = this.element.style;
10336    if((!this.options.constraint) || (this.options.constraint=='horizontal'))
10337      style.left = p[0] + "px";
10338    if((!this.options.constraint) || (this.options.constraint=='vertical'))
10339      style.top  = p[1] + "px";
10340
10341    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
10342  },
10343
10344  stopScrolling: function() {
10345    if(this.scrollInterval) {
10346      clearInterval(this.scrollInterval);
10347      this.scrollInterval = null;
10348      Draggables._lastScrollPointer = null;
10349    }
10350  },
10351
10352  startScrolling: function(speed) {
10353    if(!(speed[0] || speed[1])) return;
10354    this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
10355    this.lastScrolled = new Date();
10356    this.scrollInterval = setInterval(this.scroll.bind(this), 10);
10357  },
10358
10359  scroll: function() {
10360    var current = new Date();
10361    var delta = current - this.lastScrolled;
10362    this.lastScrolled = current;
10363    if(this.options.scroll == window) {
10364      with (this._getWindowScroll(this.options.scroll)) {
10365        if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
10366          var d = delta / 1000;
10367          this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
10368        }
10369      }
10370    } else {
10371      this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
10372      this.options.scroll.scrollTop  += this.scrollSpeed[1] * delta / 1000;
10373    }
10374
10375    Position.prepare();
10376    Droppables.show(Draggables._lastPointer, this.element);
10377    Draggables.notify('onDrag', this);
10378    if (this._isScrollChild) {
10379      Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
10380      Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
10381      Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
10382      if (Draggables._lastScrollPointer[0] < 0)
10383        Draggables._lastScrollPointer[0] = 0;
10384      if (Draggables._lastScrollPointer[1] < 0)
10385        Draggables._lastScrollPointer[1] = 0;
10386      this.draw(Draggables._lastScrollPointer);
10387    }
10388
10389    if(this.options.change) this.options.change(this);
10390  },
10391
10392  _getWindowScroll: function(w) {
10393    var T, L, W, H;
10394    with (w.document) {
10395      if (w.document.documentElement && documentElement.scrollTop) {
10396        T = documentElement.scrollTop;
10397        L = documentElement.scrollLeft;
10398      } else if (w.document.body) {
10399        T = body.scrollTop;
10400        L = body.scrollLeft;
10401      }
10402      if (w.innerWidth) {
10403        W = w.innerWidth;
10404        H = w.innerHeight;
10405      } else if (w.document.documentElement && documentElement.clientWidth) {
10406        W = documentElement.clientWidth;
10407        H = documentElement.clientHeight;
10408      } else {
10409        W = body.offsetWidth;
10410        H = body.offsetHeight;
10411      }
10412    }
10413    return { top: T, left: L, width: W, height: H };
10414  }
10415});
10416
10417Draggable._dragging = { };
10418
10419/*--------------------------------------------------------------------------*/
10420
10421var SortableObserver = Class.create({
10422  initialize: function(element, observer) {
10423    this.element   = $(element);
10424    this.observer  = observer;
10425    this.lastValue = Sortable.serialize(this.element);
10426  },
10427
10428  onStart: function() {
10429    this.lastValue = Sortable.serialize(this.element);
10430  },
10431
10432  onEnd: function() {
10433    Sortable.unmark();
10434    if(this.lastValue != Sortable.serialize(this.element))
10435      this.observer(this.element)
10436  }
10437});
10438
10439var Sortable = {
10440  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
10441
10442  sortables: { },
10443
10444  _findRootElement: function(element) {
10445    while (element.tagName.toUpperCase() != "BODY") {
10446      if(element.id && Sortable.sortables[element.id]) return element;
10447      element = element.parentNode;
10448    }
10449  },
10450
10451  options: function(element) {
10452    element = Sortable._findRootElement($(element));
10453    if(!element) return;
10454    return Sortable.sortables[element.id];
10455  },
10456
10457  destroy: function(element){
10458    element = $(element);
10459    var s = Sortable.sortables[element.id];
10460
10461    if(s) {
10462      Draggables.removeObserver(s.element);
10463      s.droppables.each(function(d){ Droppables.remove(d) });
10464      s.draggables.invoke('destroy');
10465
10466      delete Sortable.sortables[s.element.id];
10467    }
10468  },
10469
10470  create: function(element) {
10471    element = $(element);
10472    var options = Object.extend({
10473      element:     element,
10474      tag:         'li',       // assumes li children, override with tag: 'tagname'
10475      dropOnEmpty: false,
10476      tree:        false,
10477      treeTag:     'ul',
10478      overlap:     'vertical', // one of 'vertical', 'horizontal'
10479      constraint:  'vertical', // one of 'vertical', 'horizontal', false
10480      containment: element,    // also takes array of elements (or id's); or false
10481      handle:      false,      // or a CSS class
10482      only:        false,
10483      delay:       0,
10484      hoverclass:  null,
10485      ghosting:    false,
10486      quiet:       false,
10487      scroll:      false,
10488      scrollSensitivity: 20,
10489      scrollSpeed: 15,
10490      format:      this.SERIALIZE_RULE,
10491
10492      // these take arrays of elements or ids and can be
10493      // used for better initialization performance
10494      elements:    false,
10495      handles:     false,
10496
10497      onChange:    Prototype.emptyFunction,
10498      onUpdate:    Prototype.emptyFunction
10499    }, arguments[1] || { });
10500
10501    // clear any old sortable with same element
10502    this.destroy(element);
10503
10504    // build options for the draggables
10505    var options_for_draggable = {
10506      revert:      true,
10507      quiet:       options.quiet,
10508      scroll:      options.scroll,
10509      scrollSpeed: options.scrollSpeed,
10510      scrollSensitivity: options.scrollSensitivity,
10511      delay:       options.delay,
10512      ghosting:    options.ghosting,
10513      constraint:  options.constraint,
10514      handle:      options.handle };
10515
10516    if(options.starteffect)
10517      options_for_draggable.starteffect = options.starteffect;
10518
10519    if(options.reverteffect)
10520      options_for_draggable.reverteffect = options.reverteffect;
10521    else
10522      if(options.ghosting) options_for_draggable.reverteffect = function(element) {
10523        element.style.top  = 0;
10524        element.style.left = 0;
10525      };
10526
10527    if(options.endeffect)
10528      options_for_draggable.endeffect = options.endeffect;
10529
10530    if(options.zindex)
10531      options_for_draggable.zindex = options.zindex;
10532
10533    // build options for the droppables
10534    var options_for_droppable = {
10535      overlap:     options.overlap,
10536      containment: options.containment,
10537      tree:        options.tree,
10538      hoverclass:  options.hoverclass,
10539      onHover:     Sortable.onHover
10540    };
10541
10542    var options_for_tree = {
10543      onHover:      Sortable.onEmptyHover,
10544      overlap:      options.overlap,
10545      containment:  options.containment,
10546      hoverclass:   options.hoverclass
10547    };
10548
10549    // fix for gecko engine
10550    Element.cleanWhitespace(element);
10551
10552    options.draggables = [];
10553    options.droppables = [];
10554
10555    // drop on empty handling
10556    if(options.dropOnEmpty || options.tree) {
10557      Droppables.add(element, options_for_tree);
10558      options.droppables.push(element);
10559    }
10560
10561    (options.elements || this.findElements(element, options) || []).each( function(e,i) {
10562      var handle = options.handles ? $(options.handles[i]) :
10563        (options.handle ? $(e).select('.' + options.handle)[0] : e);
10564      options.draggables.push(
10565        new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
10566      Droppables.add(e, options_for_droppable);
10567      if(options.tree) e.treeNode = element;
10568      options.droppables.push(e);
10569    });
10570
10571    if(options.tree) {
10572      (Sortable.findTreeElements(element, options) || []).each( function(e) {
10573        Droppables.add(e, options_for_tree);
10574        e.treeNode = element;
10575        options.droppables.push(e);
10576      });
10577    }
10578
10579    // keep reference
10580    this.sortables[element.identify()] = options;
10581
10582    // for onupdate
10583    Draggables.addObserver(new SortableObserver(element, options.onUpdate));
10584
10585  },
10586
10587  // return all suitable-for-sortable elements in a guaranteed order
10588  findElements: function(element, options) {
10589    return Element.findChildren(
10590      element, options.only, options.tree ? true : false, options.tag);
10591  },
10592
10593  findTreeElements: function(element, options) {
10594    return Element.findChildren(
10595      element, options.only, options.tree ? true : false, options.treeTag);
10596  },
10597
10598  onHover: function(element, dropon, overlap) {
10599    if(Element.isParent(dropon, element)) return;
10600
10601    if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
10602      return;
10603    } else if(overlap>0.5) {
10604      Sortable.mark(dropon, 'before');
10605      if(dropon.previousSibling != element) {
10606        var oldParentNode = element.parentNode;
10607        element.style.visibility = "hidden"; // fix gecko rendering
10608        dropon.parentNode.insertBefore(element, dropon);
10609        if(dropon.parentNode!=oldParentNode)
10610          Sortable.options(oldParentNode).onChange(element);
10611        Sortable.options(dropon.parentNode).onChange(element);
10612      }
10613    } else {
10614      Sortable.mark(dropon, 'after');
10615      var nextElement = dropon.nextSibling || null;
10616      if(nextElement != element) {
10617        var oldParentNode = element.parentNode;
10618        element.style.visibility = "hidden"; // fix gecko rendering
10619        dropon.parentNode.insertBefore(element, nextElement);
10620        if(dropon.parentNode!=oldParentNode)
10621          Sortable.options(oldParentNode).onChange(element);
10622        Sortable.options(dropon.parentNode).onChange(element);
10623      }
10624    }
10625  },
10626
10627  onEmptyHover: function(element, dropon, overlap) {
10628    var oldParentNode = element.parentNode;
10629    var droponOptions = Sortable.options(dropon);
10630
10631    if(!Element.isParent(dropon, element)) {
10632      var index;
10633
10634      var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
10635      var child = null;
10636
10637      if(children) {
10638        var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
10639
10640        for (index = 0; index < children.length; index += 1) {
10641          if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
10642            offset -= Element.offsetSize (children[index], droponOptions.overlap);
10643          } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
10644            child = index + 1 < children.length ? children[index + 1] : null;
10645            break;
10646          } else {
10647            child = children[index];
10648            break;
10649          }
10650        }
10651      }
10652
10653      dropon.insertBefore(element, child);
10654
10655      Sortable.options(oldParentNode).onChange(element);
10656      droponOptions.onChange(element);
10657    }
10658  },
10659
10660  unmark: function() {
10661    if(Sortable._marker) Sortable._marker.hide();
10662  },
10663
10664  mark: function(dropon, position) {
10665    // mark on ghosting only
10666    var sortable = Sortable.options(dropon.parentNode);
10667    if(sortable && !sortable.ghosting) return;
10668
10669    if(!Sortable._marker) {
10670      Sortable._marker =
10671        ($('dropmarker') || Element.extend(document.createElement('DIV'))).
10672          hide().addClassName('dropmarker').setStyle({position:'absolute'});
10673      document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
10674    }
10675    var offsets = dropon.cumulativeOffset();
10676    Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
10677
10678    if(position=='after')
10679      if(sortable.overlap == 'horizontal')
10680        Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
10681      else
10682        Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
10683
10684    Sortable._marker.show();
10685  },
10686
10687  _tree: function(element, options, parent) {
10688    var children = Sortable.findElements(element, options) || [];
10689
10690    for (var i = 0; i < children.length; ++i) {
10691      var match = children[i].id.match(options.format);
10692
10693      if (!match) continue;
10694
10695      var child = {
10696        id: encodeURIComponent(match ? match[1] : null),
10697        element: element,
10698        parent: parent,
10699        children: [],
10700        position: parent.children.length,
10701        container: $(children[i]).down(options.treeTag)
10702      };
10703
10704      /* Get the element containing the children and recurse over it */
10705      if (child.container)
10706        this._tree(child.container, options, child);
10707
10708      parent.children.push (child);
10709    }
10710
10711    return parent;
10712  },
10713
10714  tree: function(element) {
10715    element = $(element);
10716    var sortableOptions = this.options(element);
10717    var options = Object.extend({
10718      tag: sortableOptions.tag,
10719      treeTag: sortableOptions.treeTag,
10720      only: sortableOptions.only,
10721      name: element.id,
10722      format: sortableOptions.format
10723    }, arguments[1] || { });
10724
10725    var root = {
10726      id: null,
10727      parent: null,
10728      children: [],
10729      container: element,
10730      position: 0
10731    };
10732
10733    return Sortable._tree(element, options, root);
10734  },
10735
10736  /* Construct a [i] index for a particular node */
10737  _constructIndex: function(node) {
10738    var index = '';
10739    do {
10740      if (node.id) index = '[' + node.position + ']' + index;
10741    } while ((node = node.parent) != null);
10742    return index;
10743  },
10744
10745  sequence: function(element) {
10746    element = $(element);
10747    var options = Object.extend(this.options(element), arguments[1] || { });
10748
10749    return $(this.findElements(element, options) || []).map( function(item) {
10750      return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
10751    });
10752  },
10753
10754  setSequence: function(element, new_sequence) {
10755    element = $(element);
10756    var options = Object.extend(this.options(element), arguments[2] || { });
10757
10758    var nodeMap = { };
10759    this.findElements(element, options).each( function(n) {
10760        if (n.id.match(options.format))
10761            nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
10762        n.parentNode.removeChild(n);
10763    });
10764
10765    new_sequence.each(function(ident) {
10766      var n = nodeMap[ident];
10767      if (n) {
10768        n[1].appendChild(n[0]);
10769        delete nodeMap[ident];
10770      }
10771    });
10772  },
10773
10774  serialize: function(element) {
10775    element = $(element);
10776    var options = Object.extend(Sortable.options(element), arguments[1] || { });
10777    var name = encodeURIComponent(
10778      (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
10779
10780    if (options.tree) {
10781      return Sortable.tree(element, arguments[1]).children.map( function (item) {
10782        return [name + Sortable._constructIndex(item) + "[id]=" +
10783                encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
10784      }).flatten().join('&');
10785    } else {
10786      return Sortable.sequence(element, arguments[1]).map( function(item) {
10787        return name + "[]=" + encodeURIComponent(item);
10788      }).join('&');
10789    }
10790  }
10791};
10792
10793// Returns true if child is contained within element
10794Element.isParent = function(child, element) {
10795  if (!child.parentNode || child == element) return false;
10796  if (child.parentNode == element) return true;
10797  return Element.isParent(child.parentNode, element);
10798};
10799
10800Element.findChildren = function(element, only, recursive, tagName) {
10801  if(!element.hasChildNodes()) return null;
10802  tagName = tagName.toUpperCase();
10803  if(only) only = [only].flatten();
10804  var elements = [];
10805  $A(element.childNodes).each( function(e) {
10806    if(e.tagName && e.tagName.toUpperCase()==tagName &&
10807      (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
10808        elements.push(e);
10809    if(recursive) {
10810      var grandchildren = Element.findChildren(e, only, recursive, tagName);
10811      if(grandchildren) elements.push(grandchildren);
10812    }
10813  });
10814
10815  return (elements.length>0 ? elements.flatten() : []);
10816};
10817
10818Element.offsetSize = function (element, type) {
10819  return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
10820};
10821Ajax.AutocompleterExtended = Class.create();
10822Object.extend(Object.extend(Ajax.AutocompleterExtended.prototype, Autocompleter.Base.prototype), {
10823  initialize: function(element, update, url, options, extra_params, static_params) {
10824    this.baseInitialize(element, update, options);
10825    this.options.asynchronous  = true;
10826    this.options.onComplete    = this.onComplete.bind(this);
10827    this.options.defaultParams = this.options.parameters || null;
10828    this.url                   = url;
10829    this.extra_params			= extra_params;
10830    this.static_params			= static_params;
10831  },
10832
10833  getUpdatedChoices: function() {
10834    entry = encodeURIComponent(this.options.paramName) + '=' +
10835      encodeURIComponent(this.getToken());
10836
10837    this.options.parameters = this.options.callback ?
10838      this.options.callback(this.element, entry) : entry;
10839
10840    if(this.options.defaultParams)
10841      this.options.parameters += '&' + this.options.defaultParams;
10842
10843	var tmp = "";
10844	if (this.extra_params && this.extra_params.length > 0) {
10845		//tmp += "?";
10846		for (i=0;i<this.extra_params.length;i++) {
10847			if (i > 0) {
10848				tmp+="&";
10849			}
10850			for(j=0;j<this.element.form.elements.length;j++) {
10851				if (this.element.form.elements[j].name == this.extra_params[i]) {
10852					if (this.element.form.elements[j].type == "text" || this.element.form.elements[j].type == "hidden") {
10853						tmp += encodeURIComponent(this.extra_params[i]) + "=" + encodeURIComponent(this.element.form.elements[j].value);
10854					}
10855				}
10856			}
10857		}
10858		this.options.parameters += '&' + tmp;
10859	}
10860	var tmp = "";
10861	if (this.static_params && this.static_params.length > 0) {
10862		for (sp in this.static_params) {
10863			if (this.static_params.propertyIsEnumerable(sp)) {
10864				//alert(sp);
10865				tmp += "&" + encodeURIComponent(sp) + "=" + encodeURIComponent(this.static_params[sp]);
10866			}
10867		}
10868		this.options.parameters += '&' + tmp;
10869	}
10870
10871    new Ajax.Request(this.url, this.options);
10872  },
10873
10874  onComplete: function(request) {
10875    this.updateChoices(request.responseText);
10876  }
10877});
10878
10879// script.aculo.us slider.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
10880
10881// Copyright (c) 2005-2010 Marty Haught, Thomas Fuchs
10882//
10883// script.aculo.us is freely distributable under the terms of an MIT
10883-style license.
10884// For details, see the script.aculo.us web site: http://script.aculo.us/
10885
10886if (!Control) var Control = { };
10887
10888// options:
10889//  axis: 'vertical', or 'horizontal' (default)
10890//
10891// callbacks:
10892//  onChange(value)
10893//  onSlide(value)
10894Control.Slider = Class.create({
10895  initialize: function(handle, track, options) {
10896    var slider = this;
10897
10898    if (Object.isArray(handle)) {
10899      this.handles = handle.collect( function(e) { return $(e) });
10900    } else {
10901      this.handles = [$(handle)];
10902    }
10903
10904    this.track   = $(track);
10905    this.options = options || { };
10906
10907    this.axis      = this.options.axis || 'horizontal';
10908    this.increment = this.options.increment || 1;
10909    this.step      = parseInt(this.options.step || '1');
10910    this.range     = this.options.range || $R(0,1);
10911
10912    this.value     = 0; // assure backwards compat
10913    this.values    = this.handles.map( function() { return 0 });
10914    this.spans     = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
10915    this.options.startSpan = $(this.options.startSpan || null);
10916    this.options.endSpan   = $(this.options.endSpan || null);
10917
10918    this.restricted = this.options.restricted || false;
10919
10920    this.maximum   = this.options.maximum || this.range.end;
10921    this.minimum   = this.options.minimum || this.range.start;
10922
10923    // Will be used to align the handle onto the track, if necessary
10924    this.alignX = parseInt(this.options.alignX || '0');
10925    this.alignY = parseInt(this.options.alignY || '0');
10926
10927    this.trackLength = this.maximumOffset() - this.minimumOffset();
10928
10929    this.handleLength = this.isVertical() ?
10930      (this.handles[0].offsetHeight != 0 ?
10931        this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
10932      (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
10933        this.handles[0].style.width.replace(/px$/,""));
10934
10935    this.active   = false;
10936    this.dragging = false;
10937    this.disabled = false;
10938
10939    if (this.options.disabled) this.setDisabled();
10940
10941    // Allowed values array
10942    this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
10943    if (this.allowedValues) {
10944      this.minimum = this.allowedValues.min();
10945      this.maximum = this.allowedValues.max();
10946    }
10947
10948    this.eventMouseDown = this.startDrag.bindAsEventListener(this);
10949    this.eventMouseUp   = this.endDrag.bindAsEventListener(this);
10950    this.eventMouseMove = this.update.bindAsEventListener(this);
10951
10952    // Initialize handles in reverse (make sure first handle is active)
10953    this.handles.each( function(h,i) {
10954      i = slider.handles.length-1-i;
10955      slider.setValue(parseFloat(
10956        (Object.isArray(slider.options.sliderValue) ?
10957          slider.options.sliderValue[i] : slider.options.sliderValue) ||
10958         slider.range.start), i);
10959      h.makePositioned().observe("mousedown", slider.eventMouseDown);
10960    });
10961
10962    this.track.observe("mousedown", this.eventMouseDown);
10963    document.observe("mouseup", this.eventMouseUp);
10964    document.observe("mousemove", this.eventMouseMove);
10965
10966    this.initialized = true;
10967  },
10968  dispose: function() {
10969    var slider = this;
10970    Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
10971    Event.stopObserving(document, "mouseup", this.eventMouseUp);
10972    Event.stopObserving(document, "mousemove", this.eventMouseMove);
10973    this.handles.each( function(h) {
10974      Event.stopObserving(h, "mousedown", slider.eventMouseDown);
10975    });
10976  },
10977  setDisabled: function(){
10978    this.disabled = true;
10979  },
10980  setEnabled: function(){
10981    this.disabled = false;
10982  },
10983  getNearestValue: function(value){
10984    if (this.allowedValues){
10985      if (value >= this.allowedValues.max()) return(this.allowedValues.max());
10986      if (value <= this.allowedValues.min()) return(this.allowedValues.min());
10987
10988      var offset = Math.abs(this.allowedValues[0] - value);
10989      var newValue = this.allowedValues[0];
10990      this.allowedValues.each( function(v) {
10991        var currentOffset = Math.abs(v - value);
10992        if (currentOffset <= offset){
10993          newValue = v;
10994          offset = currentOffset;
10995        }
10996      });
10997      return newValue;
10998    }
10999    if (value > this.range.end) return this.range.end;
11000    if (value < this.range.start) return this.range.start;
11001    return value;
11002  },
11003  setValue: function(sliderValue, handleIdx){
11004    if (!this.active) {
11005      this.activeHandleIdx = handleIdx || 0;
11006      this.activeHandle    = this.handles[this.activeHandleIdx];
11007      this.updateStyles();
11008    }
11009    handleIdx = handleIdx || this.activeHandleIdx || 0;
11010    if (this.initialized && this.restricted) {
11011      if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
11012        sliderValue = this.values[handleIdx-1];
11013      if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
11014        sliderValue = this.values[handleIdx+1];
11015    }
11016    sliderValue = this.getNearestValue(sliderValue);
11017    this.values[handleIdx] = sliderValue;
11018    this.value = this.values[0]; // assure backwards compat
11019
11020    this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
11021      this.translateToPx(sliderValue);
11022
11023    this.drawSpans();
11024    if (!this.dragging || !this.event) this.updateFinished();
11025  },
11026  setValueBy: function(delta, handleIdx) {
11027    this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
11028      handleIdx || this.activeHandleIdx || 0);
11029  },
11030  translateToPx: function(value) {
11031    return Math.round(
11032      ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
11033      (value - this.range.start)) + "px";
11034  },
11035  translateToValue: function(offset) {
11036    return ((offset/(this.trackLength-this.handleLength) *
11037      (this.range.end-this.range.start)) + this.range.start);
11038  },
11039  getRange: function(range) {
11040    var v = this.values.sortBy(Prototype.K);
11041    range = range || 0;
11042    return $R(v[range],v[range+1]);
11043  },
11044  minimumOffset: function(){
11045    return(this.isVertical() ? this.alignY : this.alignX);
11046  },
11047  maximumOffset: function(){
11048    return(this.isVertical() ?
11049      (this.track.offsetHeight != 0 ? this.track.offsetHeight :
11050        this.track.style.height.replace(/px$/,"")) - this.alignY :
11051      (this.track.offsetWidth != 0 ? this.track.offsetWidth :
11052        this.track.style.width.replace(/px$/,"")) - this.alignX);
11053  },
11054  isVertical:  function(){
11055    return (this.axis == 'vertical');
11056  },
11057  drawSpans: function() {
11058    var slider = this;
11059    if (this.spans)
11060      $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
11061    if (this.options.startSpan)
11062      this.setSpan(this.options.startSpan,
11063        $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
11064    if (this.options.endSpan)
11065      this.setSpan(this.options.endSpan,
11066        $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
11067  },
11068  setSpan: function(span, range) {
11069    if (this.isVertical()) {
11070      span.style.top = this.translateToPx(range.start);
11071      span.style.height = this.translateToPx(range.end - range.start + this.range.start);
11072    } else {
11073      span.style.left = this.translateToPx(range.start);
11074      span.style.width = this.translateToPx(range.end - range.start + this.range.start);
11075    }
11076  },
11077  updateStyles: function() {
11078    this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
11079    Element.addClassName(this.activeHandle, 'selected');
11080  },
11081  startDrag: function(event) {
11082    if (Event.isLeftClick(event)) {
11083      if (!this.disabled){
11084        this.active = true;
11085
11086        var handle = Event.element(event);
11087        var pointer  = [Event.pointerX(event), Event.pointerY(event)];
11088        var track = handle;
11089        if (track==this.track) {
11090          var offsets  = this.track.cumulativeOffset();
11091          this.event = event;
11092          this.setValue(this.translateToValue(
11093           (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
11094          ));
11095          var offsets  = this.activeHandle.cumulativeOffset();
11096          this.offsetX = (pointer[0] - offsets[0]);
11097          this.offsetY = (pointer[1] - offsets[1]);
11098        } else {
11099          // find the handle (prevents issues with Safari)
11100          while((this.handles.indexOf(handle) == -1) && handle.parentNode)
11101            handle = handle.parentNode;
11102
11103          if (this.handles.indexOf(handle)!=-1) {
11104            this.activeHandle    = handle;
11105            this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
11106            this.updateStyles();
11107
11108            var offsets  = this.activeHandle.cumulativeOffset();
11109            this.offsetX = (pointer[0] - offsets[0]);
11110            this.offsetY = (pointer[1] - offsets[1]);
11111          }
11112        }
11113      }
11114      Event.stop(event);
11115    }
11116  },
11117  update: function(event) {
11118   if (this.active) {
11119      if (!this.dragging) this.dragging = true;
11120      this.draw(event);
11121      if (Prototype.Browser.WebKit) window.scrollBy(0,0);
11122      Event.stop(event);
11123   }
11124  },
11125  draw: function(event) {
11126    var pointer = [Event.pointerX(event), Event.pointerY(event)];
11127    var offsets = this.track.cumulativeOffset();
11128    pointer[0] -= this.offsetX + offsets[0];
11129    pointer[1] -= this.offsetY + offsets[1];
11130    this.event = event;
11131    this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
11132    if (this.initialized && this.options.onSlide)
11133      this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
11134  },
11135  endDrag: function(event) {
11136    if (this.active && this.dragging) {
11137      this.finishDrag(event, true);
11138      Event.stop(event);
11139    }
11140    this.active = false;
11141    this.dragging = false;
11142  },
11143  finishDrag: function(event, success) {
11144    this.active = false;
11145    this.dragging = false;
11146    this.updateFinished();
11147  },
11148  updateFinished: function() {
11149    if (this.initialized && this.options.onChange)
11150      this.options.onChange(this.values.length>1 ? this.values : this.value, this);
11151    this.event = null;
11152  }
11153});// script.aculo.us sound.js v1.9.0, Thu Dec 23 16:54:48 -0500 2010
11154
11155// Copyright (c) 2005-2010 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
11156//
11157// Based on code created by Jules Gravinese (http://www.webveteran.com/)
11158//
11159// script.aculo.us is freely distributable under the terms of an MIT-style license.
11160// For details, see the script.aculo.us web site: http://script.aculo.us/
11161
11162Sound = {
11163  tracks: {},
11164  _enabled: true,
11165  template:
11166    new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
11167  enable: function(){
11168    Sound._enabled = true;
11169  },
11170  disable: function(){
11171    Sound._enabled = false;
11172  },
11173  play: function(url){
11174    if(!Sound._enabled) return;
11175    var options = Object.extend({
11176      track: 'global', url: url, replace: false
11177    }, arguments[1] || {});
11178
11179    if(options.replace && this.tracks[options.track]) {
11180      $R(0, this.tracks[options.track].id).each(function(id){
11181        var sound = $('sound_'+options.track+'_'+id);
11182        sound.Stop && sound.Stop();
11183        sound.remove();
11184      });
11185      this.tracks[options.track] = null;
11186    }
11187
11188    if(!this.tracks[options.track])
11189      this.tracks[options.track] = { id: 0 };
11190    else
11191      this.tracks[options.track].id++;
11192
11193    options.id = this.tracks[options.track].id;
11194    $$('body')[0].insert(
11195      Prototype.Browser.IE ? new Element('bgsound',{
11196        id: 'sound_'+options.track+'_'+options.id,
11197        src: options.url, loop: 1, autostart: true
11198      }) : Sound.template.evaluate(options));
11199  }
11200};
11201
11202if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
11203  if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
11204    Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>');
11205  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('Windows Media') != -1 }))
11206    Sound.template = new Template('<object id="sound_#{track}_#{id}" type="application/x-mplayer2" data="#{url}"></object>');
11207  else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('RealPlayer') != -1 }))
11208    Sound.template = new Template('<embed type="audio/x-pn-realaudio-plugin" style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>');
11209  else
11210    Sound.play = function(){};
11211} 

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.