1    /* Prototype JavaScript framework, version 1.5.1
2     * (c) 2005-2007 Sam Stephenson
3     *
4     * Prototype is freely distributable under the terms of an MIT-style license.
5     * For details, see the Prototype web site: http://www.prototypejs.org/
6     *
7    /*--------------------------------------------------------------------------*/
8    
9    var Prototype = {
10     Version: '1.5.1',
11   
12     Browser: {
13       IE:     !!(window.attachEvent && !window.opera),
14       Opera:  !!window.opera,
15       WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
16       Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
17     },
18   
19     BrowserFeatures: {
20       XPath: !!document.evaluate,
21       ElementExtensions: !!window.HTMLElement,
22       SpecificElementExtensions:
23         (document.createElement('div').__proto__ !==
24          document.createElement('form').__proto__)
25     },
26   
27     ScriptFragment: '<script[^>]*>([\u0001-\uFFFF]*?)</script>',
28     JSONFilter: /^\/\*-secure-\s*(.*)\s*\*\/\s*$/,
29   
30     emptyFunction: function() { },
31     K: function(x) { return x }
32   }
33   
34   var Class = {
35     create: function() {
36       returnfunction() {
37         this.initialize.apply(this, arguments);
38       }
39     }
40   }
41   
42   var Abstract = newObject();
43   
44   Object.extend = function(destination, source) {
45     for (var property in source) {
46       destination[property] = source[property];
47     }
48     return destination;
49   }
50   
51   Object.extend(Object, {
52     inspect: function(object) {
53       try {
54         if (object === undefined) return'undefined';
55         if (object === null) return'null';
56         return object.inspect ? object.inspect() : object.toString();
57       } catch (e) {
58         if (e instanceof RangeError) return'...';
59         throw e;
60       }
61     },
62   
63     toJSON: function(object) {
64       var type = typeof object;
65       switch(type) {
66         case'undefined':
67         case'function':
68         case'unknown': return;
69         case'boolean': return object.toString();
70       }
71       if (object === null) return'null';
72       if (object.toJSON) return object.toJSON();
73       if (object.ownerDocument === document) return;
74       var results = [];
75       for (var property in object) {
76         varvalue = Object.toJSON(object[property]);
77         if (value !== undefined)
78           results.push(property.toJSON() + ': ' + value);
79       }
80       return'{' + results.join(', ') + '}';
81     },
82   
83     keys: function(object) {
84       var keys = [];
85       for (var property in object)
86         keys.push(property);
87       return keys;
88     },
89   
90     values: function(object) {
91       var values = [];
92       for (var property in object)
93         values.push(object[property]);
94       return values;
95     },
96   
97     clone: function(object) {
98       returnObject.extend({}, object);
99     }
100  });
101  
102  Function.prototype.bind = function() {
103    var __method = this, args = $A(arguments), object = args.shift();
104    returnfunction() {
105      return __method.apply(object, args.concat($A(arguments)));
106    }
107  }
108  
109  Function.prototype.bindAsEventListener = function(object) {
110    var __method = this, args = $A(arguments), object = args.shift();
111    returnfunction(event) {
112      return __method.apply(object, [event || window.event].concat(args));
113    }
114  }
115  
116  Object.extend(Number.prototype, {
117    toColorPart: function() {
118      returnthis.toPaddedString(2, 16);
119    },
120  
121    succ: function() {
122      returnthis + 1;
123    },
124  
125    times: function(iterator) {
126      $R(0, this, true).each(iterator);
127      returnthis;
128    },
129  
130    toPaddedString: function(length, radix) {
131      var string = this.toString(radix || 10);
132      return'0'.times(length - string.length) + string;
133    },
134  
135    toJSON: function() {
136      returnisFinite(this) ? this.toString() : 'null';
137    }
138  });
139  
140  Date.prototype.toJSON = function() {
141    return'"' + this.getFullYear() + '-' +
142      (this.getMonth() + 1).toPaddedString(2) + '-' +
143      this.getDate().toPaddedString(2) + 'T' +
144      this.getHours().toPaddedString(2) + ':' +
145      this.getMinutes().toPaddedString(2) + ':' +
146      this.getSeconds().toPaddedString(2) + '"';
147  };
148  
149  var Try = {
150    these: function() {
151      var returnValue;
152  
153      for (var i = 0, length = arguments.length; i < length; i++) {
154        var lambda = arguments[i];
155        try {
156          returnValue = lambda();
157          break;
158        } catch (e) {}
159      }
160  
161      return returnValue;
162    }
163  }
164  
165  /*--------------------------------------------------------------------------*/
166  
167  var PeriodicalExecuter = Class.create();
168  PeriodicalExecuter.prototype = {
169    initialize: function(callback, frequency) {
170      this.callback = callback;
171      this.frequency = frequency;
172      this.currentlyExecuting = false;
173  
174      this.registerCallback();
175    },
176  
177    registerCallback: function() {
178      this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
179    },
180  
181    stop: function() {
182      if (!this.timer) return;
183      clearInterval(this.timer);
184      this.timer = null;
185    },
186  
187    onTimerEvent: function() {
188      if (!this.currentlyExecuting) {
189        try {
190          this.currentlyExecuting = true;
191          this.callback(this);
192        } finally {
193          this.currentlyExecuting = false;
194        }
195      }
196    }
197  }
198  Object.extend(String, {
199    interpret: function(value) {
200      returnvalue == null ? '' : String(value);
201    },
202    specialChar: {
203      '\b': '\\b',
204      '\t': '\\t',
205      '\n': '\\n',
206      '\f': '\\f',
207      '\r': '\\r',
208      '\\': '\\\\'
209    }
210  });
211  
212  Object.extend(String.prototype, {
213    gsub: function(pattern, replacement) {
214      var result = '', source = this, match;
215      replacement = arguments.callee.prepareReplacement(replacement);
216  
217      while (source.length > 0) {
218        if (match = source.match(pattern)) {
219          result += source.slice(0, match.index);
220          result += String.interpret(replacement(match));
221          source  = source.slice(match.index + match[0].length);
222        } else {
223          result += source, source = '';
224        }
225      }
226      return result;
227    },
228  
229    sub: function(pattern, replacement, count) {
230      replacement = this.gsub.prepareReplacement(replacement);
231      count = count === undefined ? 1 : count;
232  
233      returnthis.gsub(pattern, function(match) {
234        if (--count < 0) return match[0];
235        return replacement(match);
236      });
237    },
238  
239    scan: function(pattern, iterator) {
240      this.gsub(pattern, iterator);
241      returnthis;
242    },
243  
244    truncate: function(length, truncation) {
245      length = length || 30;
246      truncation = truncation === undefined ? '...' : truncation;
247      returnthis.length > length ?
248        this.slice(0, length - truncation.length) + truncation : this;
249    },
250  
251    strip: function() {
252      returnthis.replace(/^\s+/, '').replace(/\s+$/, '');
253    },
254  
255    stripTags: function() {
256      returnthis.replace(/<\/?[^>]+>/gi, '');
257    },
258  
259    stripScripts: function() {
260      returnthis.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
261    },
262  
263    extractScripts: function() {
264      var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
265      var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
266      return (this.match(matchAll) || []).map(function(scriptTag) {
267        return (scriptTag.match(matchOne) || ['', ''])[1];
268      });
269    },
270  
271    evalScripts: function() {
272      returnthis.extractScripts().map(function(script) { returneval(script) });
273    },
274  
275    escapeHTML: function() {
276      var self = arguments.callee;
277      self.text.data = this;
278      return self.div.innerHTML;
279    },
280  
281    unescapeHTML: function() {
282      var div = document.createElement('div');
283      div.innerHTML = this.stripTags();
284      return div.childNodes[0] ? (div.childNodes.length > 1 ?
285        $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
286        div.childNodes[0].nodeValue) : '';
287    },
288  
289    toQueryParams: function(separator) {
290      var match = this.strip().match(/([^?#]*)(#.*)?$/);
291      if (!match) return {};
292  
293      return match[1].split(separator || '&').inject({}, function(hash, pair) {
294        if ((pair = pair.split('='))[0]) {
295          var key = decodeURIComponent(pair.shift());
296          varvalue = pair.length > 1 ? pair.join('=') : pair[0];
297          if (value != undefined) value = decodeURIComponent(value);
298  
299          if (key in hash) {
300            if (hash[key].constructor != Array) hash[key] = [hash[key]];
301            hash[key].push(value);
302          }
303          else hash[key] = value;
304        }
305        return hash;
306      });
307    },
308  
309    toArray: function() {
310      returnthis.split('');
311    },
312  
313    succ: function() {
314      returnthis.slice(0, this.length - 1) +
315        String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
316    },
317  
318    times: function(count) {
319      var result = '';
320      for (var i = 0; i < count; i++) result += this;
321      return result;
322    },
323  
324    camelize: function() {
325      var parts = this.split('-'), len = parts.length;
326      if (len == 1) return parts[0];
327  
328      var camelized = this.charAt(0) == '-'
329        ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
330        : parts[0];
331  
332      for (var i = 1; i < len; i++)
333        camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
334  
335      return camelized;
336    },
337  
338    capitalize: function() {
339      returnthis.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
340    },
341  
342    underscore: function() {
343      returnthis.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
344    },
345  
346    dasherize: function() {
347      returnthis.gsub(/_/,'-');
348    },
349  
350    inspect: function(useDoubleQuotes) {
351      var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
352        var character = String.specialChar[match[0]];
353        return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
354      });
355      if (useDoubleQuotes) return'"' + escapedString.replace(/"/g, '\\"') + '"';
356      return"'" + escapedString.replace(/'/g, '\\\'') + "'";
357    },
358  
359    toJSON: function() {
360      returnthis.inspect(true);
361    },
362  
363    unfilterJSON: function(filter) {
364      returnthis.sub(filter || Prototype.JSONFilter, '#{1}');
365    },
366  
367    evalJSON: function(sanitize) {
368      var json = this.unfilterJSON();
369      try {
370        if (!sanitize || (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(json)))
371          returneval('(' + json + ')');
372      } catch (e) { }
373      thrownew SyntaxError('Badly formed JSON string: ' + this.inspect());
374    },
375  
376    include: function(pattern) {
377      returnthis.indexOf(pattern) > -1;
378    },
379  
380    startsWith: function(pattern) {
381      returnthis.indexOf(pattern) === 0;
382    },
383  
384    endsWith: function(pattern) {
385      var d = this.length - pattern.length;
386      return d >= 0 && this.lastIndexOf(pattern) === d;
387    },
388  
389    empty: function() {
390      returnthis == '';
391    },
392  
393    blank: function() {
394      return/^\s*$/.test(this);
395    }
396  });
397  
398  if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
399    escapeHTML: function() {
400      returnthis.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
401    },
402    unescapeHTML: function() {
403      returnthis.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
404    }
405  });
406  
407  String.prototype.gsub.prepareReplacement = function(replacement) {
408    if (typeof replacement == 'function') return replacement;
409    var template = new Template(replacement);
410    returnfunction(match) { return template.evaluate(match) };
411  }
412  
413  String.prototype.parseQuery = String.prototype.toQueryParams;
414  
415  Object.extend(String.prototype.escapeHTML, {
416    div:  document.createElement('div'),
417    text: document.createTextNode('')
418  });
419  
420  with (String.prototype.escapeHTML) div.appendChild(text);
421  
422  var Template = Class.create();
423  Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
424  Template.prototype = {
425    initialize: function(template, pattern) {
426      this.template = template.toString();
427      this.pattern  = pattern || Template.Pattern;
428    },
429  
430    evaluate: function(object) {
431      returnthis.template.gsub(this.pattern, function(match) {
432        var before = match[1];
433        if (before == '\\') return match[2];
434        return before + String.interpret(object[match[3]]);
435      });
436    }
437  }
438  
439  var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead');
440  
441  var Enumerable = {
442    each: function(iterator) {
443      var index = 0;
444      try {
445        this._each(function(value) {
446          iterator(value, index++);
447        });
448      } catch (e) {
449        if (e != $break) throw e;
450      }
451      returnthis;
452    },
453  
454    eachSlice: function(number, iterator) {
455      var index = -number, slices = [], array = this.toArray();
456      while ((index += number) < array.length)
457        slices.push(array.slice(index, index+number));
458      return slices.map(iterator);
459    },
460  
461    all: function(iterator) {
462      var result = true;
463      this.each(function(value, index) {
464        result = result && !!(iterator || Prototype.K)(value, index);
465        if (!result) throw $break;
466      });
467      return result;
468    },
469  
470    any: function(iterator) {
471      var result = false;
472      this.each(function(value, index) {
473        if (result = !!(iterator || Prototype.K)(value, index))
474          throw $break;
475      });
476      return result;
477    },
478  
479    collect: function(iterator) {
480      var results = [];
481      this.each(function(value, index) {
482        results.push((iterator || Prototype.K)(value, index));
483      });
484      return results;
485    },
486  
487    detect: function(iterator) {
488      var result;
489      this.each(function(value, index) {
490        if (iterator(value, index)) {
491          result = value;
492          throw $break;
493        }
494      });
495      return result;
496    },
497  
498    findAll: function(iterator) {
499      var results = [];
500      this.each(function(value, index) {
501        if (iterator(value, index))
502          results.push(value);
503      });
504      return results;
505    },
506  
507    grep: function(pattern, iterator) {
508      var results = [];
509      this.each(function(value, index) {
510        var stringValue = value.toString();
511        if (stringValue.match(pattern))
512          results.push((iterator || Prototype.K)(value, index));
513      })
514      return results;
515    },
516  
517    include: function(object) {
518      var found = false;
519      this.each(function(value) {
520        if (value == object) {
521          found = true;
522          throw $break;
523        }
524      });
525      return found;
526    },
527  
528    inGroupsOf: function(number, fillWith) {
529      fillWith = fillWith === undefined ? null : fillWith;
530      returnthis.eachSlice(number, function(slice) {
531        while(slice.length < number) slice.push(fillWith);
532        return slice;
533      });
534    },
535  
536    inject: function(memo, iterator) {
537      this.each(function(value, index) {
538        memo = iterator(memo, value, index);
539      });
540      return memo;
541    },
542  
543    invoke: function(method) {
544      var args = $A(arguments).slice(1);
545      returnthis.map(function(value) {
546        returnvalue[method].apply(value, args);
547      });
548    },
549  
550    max: function(iterator) {
551      var result;
552      this.each(function(value, index) {
553        value = (iterator || Prototype.K)(value, index);
554        if (result == undefined || value >= result)
555          result = value;
556      });
557      return result;
558    },
559  
560    min: function(iterator) {
561      var result;
562      this.each(function(value, index) {
563        value = (iterator || Prototype.K)(value, index);
564        if (result == undefined || value < result)
565          result = value;
566      });
567      return result;
568    },
569  
570    partition: function(iterator) {
571      var trues = [], falses = [];
572      this.each(function(value, index) {
573        ((iterator || Prototype.K)(value, index) ?
574          trues : falses).push(value);
575      });
576      return [trues, falses];
577    },
578  
579    pluck: function(property) {
580      var results = [];
581      this.each(function(value, index) {
582        results.push(value[property]);
583      });
584      return results;
585    },
586  
587    reject: function(iterator) {
588      var results = [];
589      this.each(function(value, index) {
590        if (!iterator(value, index))
591          results.push(value);
592      });
593      return results;
594    },
595  
596    sortBy: function(iterator) {
597      returnthis.map(function(value, index) {
598        return {value: value, criteria: iterator(value, index)};
599      }).sort(function(left, right) {
600        var a = left.criteria, b = right.criteria;
601        return a < b ? -1 : a > b ? 1 : 0;
602      }).pluck('value');
603    },
604  
605    toArray: function() {
606      returnthis.map();
607    },
608  
609    zip: function() {
610      var iterator = Prototype.K, args = $A(arguments);
611      if (typeof args.last() == 'function')
612        iterator = args.pop();
613  
614      var collections = [this].concat(args).map($A);
615      returnthis.map(function(value, index) {
616        return iterator(collections.pluck(index));
617      });
618    },
619  
620    size: function() {
621      returnthis.toArray().length;
622    },
623  
624    inspect: function() {
625      return'#<Enumerable:' + this.toArray().inspect() + '>';
626    }
627  }
628  
629  Object.extend(Enumerable, {
630    map:     Enumerable.collect,
631    find:    Enumerable.detect,
632    select:  Enumerable.findAll,
633    member:  Enumerable.include,
634    entries: Enumerable.toArray
635  });
636  var $A = Array.from = function(iterable) {
637    if (!iterable) return [];
638    if (iterable.toArray) {
639      return iterable.toArray();
640    } else {
641      var results = [];
642      for (var i = 0, length = iterable.length; i < length; i++)
643        results.push(iterable[i]);
644      return results;
645    }
646  }
647  
648  if (Prototype.Browser.WebKit) {
649    $A = Array.from = function(iterable) {
650      if (!iterable) return [];
651      if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
652        iterable.toArray) {
653        return iterable.toArray();
654      } else {
655        var results = [];
656        for (var i = 0, length = iterable.length; i < length; i++)
657          results.push(iterable[i]);
658        return results;
659      }
660    }
661  }
662  
663  Object.extend(Array.prototype, Enumerable);
664  
665  if (!Array.prototype._reverse)
666    Array.prototype._reverse = Array.prototype.reverse;
667  
668  Object.extend(Array.prototype, {
669    _each: function(iterator) {
670      for (var i = 0, length = this.length; i < length; i++)
671        iterator(this[i]);
672    },
673  
674    clear: function() {
675      this.length = 0;
676      returnthis;
677    },
678  
679    first: function() {
680      returnthis[0];
681    },
682  
683    last: function() {
684      returnthis[this.length - 1];
685    },
686  
687    compact: function() {
688      returnthis.select(function(value) {
689        returnvalue != null;
690      });
691    },
692  
693    flatten: function() {
694      returnthis.inject([], function(array, value) {
695        returnarray.concat(value && value.constructor == Array ?
696          value.flatten() : [value]);
697      });
698    },
699  
700    without: function() {
701      var values = $A(arguments);
702      returnthis.select(function(value) {
703        return !values.include(value);
704      });
705    },
706  
707    indexOf: function(object) {
708      for (var i = 0, length = this.length; i < length; i++)
709        if (this[i] == object) return i;
710      return -1;
711    },
712  
713    reverse: function(inline) {
714      return (inline !== false ? this : this.toArray())._reverse();
715    },
716  
717    reduce: function() {
718      returnthis.length > 1 ? this : this[0];
719    },
720  
721    uniq: function(sorted) {
722      returnthis.inject([], function(array, value, index) {
723        if (0 == index || (sorted ? array.last() != value : !array.include(value)))
724          array.push(value);
725        returnarray;
726      });
727    },
728  
729    clone: function() {
730      return [].concat(this);
731    },
732  
733    size: function() {
734      returnthis.length;
735    },
736  
737    inspect: function() {
738      return'[' + this.map(Object.inspect).join(', ') + ']';
739    },
740  
741    toJSON: function() {
742      var results = [];
743      this.each(function(object) {
744        varvalue = Object.toJSON(object);
745        if (value !== undefined) results.push(value);
746      });
747      return'[' + results.join(', ') + ']';
748    }
749  });
750  
751  Array.prototype.toArray = Array.prototype.clone;
752  
753  function $w(string) {
754    string = string.strip();
755    return string ? string.split(/\s+/) : [];
756  }
757  
758  if (Prototype.Browser.Opera){
759    Array.prototype.concat = function() {
760      vararray = [];
761      for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
762      for (var i = 0, length = arguments.length; i < length; i++) {
763        if (arguments[i].constructor == Array) {
764          for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
765            array.push(arguments[i][j]);
766        } else {
767          array.push(arguments[i]);
768        }
769      }
770      returnarray;
771    }
772  }
773  var Hash = function(object) {
774    if (object instanceof Hash) this.merge(object);
775    elseObject.extend(this, object || {});
776  };
777  
778  Object.extend(Hash, {
779    toQueryString: function(obj) {
780      var parts = [];
781      parts.add = arguments.callee.addPair;
782  
783      this.prototype._each.call(obj, function(pair) {
784        if (!pair.key) return;
785        varvalue = pair.value;
786  
787        if (value && typeofvalue == 'object') {
788          if (value.constructor == Array) value.each(function(value) {
789            parts.add(pair.key, value);
790          });
791          return;
792        }
793        parts.add(pair.key, value);
794      });
795  
796      return parts.join('&');
797    },
798  
799    toJSON: function(object) {
800      var results = [];
801      this.prototype._each.call(object, function(pair) {
802        varvalue = Object.toJSON(pair.value);
803        if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value);
804      });
805      return'{' + results.join(', ') + '}';
806    }
807  });
808  
809  Hash.toQueryString.addPair = function(key, value, prefix) {
810    key = encodeURIComponent(key);
811    if (value === undefined) this.push(key);
812    elsethis.push(key + '=' + (value == null ? '' : encodeURIComponent(value)));
813  }
814  
815  Object.extend(Hash.prototype, Enumerable);
816  Object.extend(Hash.prototype, {
817    _each: function(iterator) {
818      for (var key in this) {
819        varvalue = this[key];
820        if (value && value == Hash.prototype[key]) continue;
821  
822        var pair = [key, value];
823        pair.key = key;
824        pair.value = value;
825        iterator(pair);
826      }
827    },
828  
829    keys: function() {
830      returnthis.pluck('key');
831    },
832  
833    values: function() {
834      returnthis.pluck('value');
835    },
836  
837    merge: function(hash) {
838      return $H(hash).inject(this, function(mergedHash, pair) {
839        mergedHash[pair.key] = pair.value;
840        return mergedHash;
841      });
842    },
843  
844    remove: function() {
845      var result;
846      for(var i = 0, length = arguments.length; i < length; i++) {
847        varvalue = this[arguments[i]];
848        if (value !== undefined){
849          if (result === undefined) result = value;
850          else {
851            if (result.constructor != Array) result = [result];
852            result.push(value)
853          }
854    &ens