原创  prototype1.6源码 收藏




/*  Prototype JavaScript framework, version 1.6.0.2
 *  (c) 2005-2008 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------
*/

var Prototype = {
  Version: 
'1.6.0.2',

  Browser: 
{
    IE:     
!!(window.attachEvent && !window.opera),
    Opera:  
!!window.opera,
    WebKit: navigator.userAgent.indexOf(
'AppleWebKit/'> -1,
    Gecko:  navigator.userAgent.indexOf(
'Gecko'> -1 && navigator.userAgent.indexOf('KHTML'== -1,
    MobileSafari: 
!!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  }
,

  BrowserFeatures: 
{
    XPath: 
!!document.evaluate,
    ElementExtensions: 
!!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement(
'div').__proto__ &&
      document.createElement(
'div').__proto__ !==
        document.createElement(
'form').__proto__
  }
,

  ScriptFragment: 
'<script[^>]*>([\S\s]*?)</script>',
  JSONFilter: 
/^/*-secure-([sS]*)*/s*$/,

  emptyFunction: 
function() { },
  K: 
function(x) return x }
}
;

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions 
= false;
/* Based on Alex Arnell's inheritance implementation. */
var Class = {
  create: 
function() {
    
var parent = null, properties = $A(arguments);
    
if (Object.isFunction(properties[0]))
      parent 
= properties.shift();

    
function klass() {
      
this.initialize.apply(this, arguments);
    }


    Object.extend(klass, Class.Methods);
    klass.superclass 
= parent;
    klass.subclasses 
= [];

    
if (parent) {
      
var subclass = function() { };
      subclass.prototype 
= parent.prototype;
      klass.prototype 
= new subclass;
      parent.subclasses.push(klass);
    }


    
for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);

    
if (!klass.prototype.initialize)
      klass.prototype.initialize 
= Prototype.emptyFunction;

    klass.prototype.constructor 
= klass;

    
return klass;
  }

}
;

Class.Methods 
= {
  addMethods: 
function(source) {
    
var ancestor   = this.superclass && this.superclass.prototype;
    
var properties = Object.keys(source);

    
if (!Object.keys({ toString: true }).length)
      properties.push(
"toString""valueOf");

    
for (var i = 0, length = properties.length; i < length; i++{
      
var property = properties[i], value = source[property];
      
if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() 
== "$super"{
        
var method = value, value = Object.extend((function(m) {
          
return function() return ancestor[m].apply(this, arguments) };
        }
)(property).wrap(method), {
          valueOf:  
function() return method },
          toString: 
function() return method.toString() }
        }
);
      }

      
this.prototype[property] = value;
    }


    
return this;
  }

}
;
var Abstract = { };

Object.extend 
= function(destination, source) {
  
for (var property in source)
    destination[property] 
= source[property];
  
return destination;
}
;

Object.extend(Object, 
{
  inspect: 
function(object) {
    
try {
      
if (Object.isUndefined(object)) return 'undefined';
      
if (object === nullreturn 'null';
      
return object.inspect ? object.inspect() : String(object);
    }
 catch (e) {
      
if (e instanceof RangeError) return '...';
      
throw e;
    }

  }
,

  toJSON: 
function(object) {
    
var type = typeof object;
    
switch (type) {
      
case 'undefined':
      
case 'function':
      
case 'unknown'return;
      
case 'boolean'return object.toString();
    }


    
if (object === nullreturn 'null';
    
if (object.toJSON) return object.toJSON();
    
if (Object.isElement(object)) return;

    
var results = [];
    
for (var property in object) {
      
var value = Object.toJSON(object[property]);
      
if (!Object.isUndefined(value))
        results.push(property.toJSON() 
+ '' + value);
    }


    
return '{' + results.join(''+ '}';
  }
,

  toQueryString: 
function(object) {
    
return $H(object).toQueryString();
  }
,

  toHTML: 
function(object) {
    
return object && object.toHTML ? object.toHTML() : String.interpret(object);
  }
,

  keys: 
function(object) {
    
var keys = [];
    
for (var property in object)
      keys.push(property);
    
return keys;
  }
,

  values: 
function(object) {
    
var values = [];
    
for (var property in object)
      values.push(object[property]);
    
return values;
  }
,

  clone: 
function(object) {
    
return Object.extend({ }, object);
  }
,

  isElement: 
function(object) {
    
return object && object.nodeType == 1;
  }
,

  isArray: 
function(object) {
    
return object != null && typeof object == "object" &&
      
'splice' in object && 'join' in object;
  }
,

  isHash: 
function(object) {
    
return object instanceof Hash;
  }
,

  isFunction: 
function(object) {
    
return typeof object == "function";
  }
,

  isString: 
function(object) {
    
return typeof object == "string";
  }
,

  isNumber: 
function(object) {
    
return typeof object == "number";
  }
,

  isUndefined: 
function(object) {
    
return typeof object == "undefined";
  }

}
);

Object.extend(Function.prototype, 
{
  argumentNames: 
function() {
    
var names = this.toString().match(/^[s(]*function[^(]*((.*?))/)[1].split(",").invoke("strip");
    
return names.length == 1 && !names[0? [] : names;
  }
,

  bind: 
function() {
    
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    
var __method = this, args = $A(arguments), object = args.shift();
    
return function() {
      
return __method.apply(object, args.concat($A(arguments)));
    }

  }
,

  bindAsEventListener: 
function() {
    
var __method = this, args = $A(arguments), object = args.shift();
    
return function(event) {
      
return __method.apply(object, [event || window.event].concat(args));
    }

  }
,

  curry: 
function() {
    
if (!arguments.length) return this;
    
var __method = this, args = $A(arguments);
    
return function() {
      
return __method.apply(this, args.concat($A(arguments)));
    }

  }
,

  delay: 
function() {
    
var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
    
return window.setTimeout(function() {
      
return __method.apply(__method, args);
    }
, timeout);
  }
,

  wrap: 
function(wrapper) {
    
var __method = this;
    
return function() {
      
return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }

  }
,

  methodize: 
function() {
    
if (this._methodized) return this._methodized;
    
var __method = this;
    
return this._methodized = function() {
      
return __method.apply(null, [this].concat($A(arguments)));
    }
;
  }

}
);

Function.prototype.defer 
= Function.prototype.delay.curry(0.01);

Date.prototype.toJSON 
= function() {
  
return '"' + this.getUTCFullYear() + '-' +
    (
this.getUTCMonth() + 1).toPaddedString(2+ '-' +
    
this.getUTCDate().toPaddedString(2+ 'T' +
    
this.getUTCHours().toPaddedString(2+ ':' +
    
this.getUTCMinutes().toPaddedString(2+ ':' +
    
this.getUTCSeconds().toPaddedString(2+ 'Z"';
}
;

var Try = {
  these: 
function() {
    
var returnValue;

    
for (var i = 0, length = arguments.length; i < length; i++{
      
var lambda = arguments[i];
      
try {
        returnValue 
= lambda();
        
break;
      }
 catch (e) { }
    }


    
return returnValue;
  }

}
;

RegExp.prototype.match 
= RegExp.prototype.test;

RegExp.escape 
= function(str) {
  
return String(str).replace(/([.*+?^=!:${}()|[]/\])/g, '\$1');
}
;
/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create({
  initialize: 
function(callback, frequency) {
    
this.callback = callback;
    
this.frequency = frequency;
    
this.currentlyExecuting = false;

    
this.registerCallback();
  }
,

  registerCallback: 
function() {
    
this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  }
,

  execute: 
function() {
    
this.callback(this);
  }
,

  stop: 
function() {
    
if (!this.timer) return;
    clearInterval(
this.timer);
    
this.timer = null;
  }
,

  onTimerEvent: 
function() {
    
if (!this.currentlyExecuting) {
      
try {
        
this.currentl yExecuting = true;
        
this.execute();
      }
 finally {
        
this.currentlyExecuting = false;
      }

    }

  }

}
);
Object.extend(String.prototype, {
  gsub: 
function(pattern, replacement) {
    
var result = '', source = this, match;
    replacement 
= arguments.callee.prepareReplacement(replacement);

    
while (source.length > 0{
      
if (match = source.match(pattern)) {
        result 
+= source.slice(0, match.index);
        result 
+= String.interpret(replacement(match));
        source  
= source.slice(match.index + match[0].length);
      }
 else {
        result 
+= source, source = '';
      }

    }

    
return result;
  }
,

  sub: 
function(pattern, replacement, count) {
    replacement 
= this.gsub.prepareReplacement(replacement);
    count 
= Object.isUndefined(count) ? 1 : count;

    
return this.gsub(pattern, function(match) {
      
if (--count < 0return match[0];
      
return replacement(match);
    }
);
  }
,

  scan: 
function(pattern, iterator) {
    
this.gsub(pattern, iterator);
    
return String(this);
  }
,

  truncate: 
function(length, truncation) {
    length 
= length || 30;
    truncation 
= Object.isUndefined(truncation) ? '...' : truncation;
    
return this.length > length ?
      
this.slice(0, length - truncation.length) + truncation : String(this);
  }
,

  strip: 
function() {
    
return this.replace(/^s+/'').replace(/s+$/'');
  }
,

  stripTags: 
function() {
    
return this.replace(/</?[^>]+>/gi, '');
  }
,

  stripScripts: 
function() {
    
return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  }
,

  extractScripts: 
function() {
    
var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    
var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    
return (this.match(matchAll) || []).map(function(scriptTag) {
      
return (scriptTag.match(matchOne) || [''''])[1];
    }
);
  }
,

  evalScripts: 
function() {
    
return this.extractScripts().map(function(script) return eval(script) });
  }
,

  escapeHTML: 
function() {
    
var self = arguments.callee;
    self.text.data 
= this;
    
return self.div.innerHTML;
  }
,

  unescapeHTML: 
function() {
    
var div = new Element('div');
    div.innerHTML 
= this.stripTags();
    
return div.childNodes[0? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject(
''function(memo, node) return memo+node.nodeValue }) :
      div.childNodes[
0].nodeValue) : '';
  }
,

  toQueryParams: 
function(separator) {
    
var match = this.strip().match(/([^?#]*)(#.*)?$/);
    
if (!match) return { };

    
return match[1].split(separator || '&').inject({ }function(hash, pair) {
      
if ((pair = pair.split('='))[0]) {
        
var key = decodeURIComponent(pair.shift());
        
var value = pair.length > 1 ? pair.join('=') : pair[0];
        
if (value != undefined) value = decodeURIComponent(value);

        
if (key in hash) {
          
if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }

        
else hash[key] = value;
      }

      
return hash;
    }
);
  }
,

  toArray: 
function() {
    
return this.split('');
  }
,

  succ: 
function() {
    
return this.slice(0this.length - 1+
      String.fromCharCode(
this.charCodeAt(this.length - 1+ 1);
  }
,

  times: 
function(count) {
    
return count < 1 ? '' : new Array(count + 1).join(this);
  }
,

  camelize: 
function() {
    
var parts = this.split('-'), len = parts.length;
    
if (len == 1return parts[0];

    
var camelized = this.charAt(0== '-'
      
? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[
0];

    
for (var i = 1; i < len; i++)
      camelized 
+= parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    
return camelized;
  }
,

  capitalize: 
function() {
    
return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  }
,

  underscore: 
function() {
    
return this.gsub(/::/'/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-zd])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  }
,

  dasherize: 
function() {
    
return this.gsub(/_/,'-');
  }
,

  inspect: 
function(useDoubleQuotes) {
    
var escapedString = this.gsub(/[x00-x1f\]/function(match) {
      
var character = String.specialChar[match[0]];
      
return character ? character : '\u00' + match[0].charCodeAt().toPaddedString(216);
    }
);
    
if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\"') + '"';
    return 
"'" + escapedString.replace(/'/g, '\''+ "'";
  }
,

  toJSON: 
function() {
    
return this.inspect(true);
  }
,

  unfilterJSON: 
function(filter) {
    
return this.sub(filter || Prototype.JSONFilter, '#{1}');
  }
,

  isJSON: 
function() {
    
var str = this;
    
if (str.blank()) return false;
    str 
= this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    
return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  }
,

  evalJSON: 
function(sanitize) {
    
var json = this.unfilterJSON();
    
try {
      
if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    }
 catch (e) { }
    
throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  }
,

  include: 
function(pattern) {
    
return this.indexOf(pattern) > -1;
  }
,

  startsWith: 
function(pattern) {
    
return this.indexOf(pattern) === 0;
  }
,

  endsWith: 
function(pattern) {
    
var d = this.length - pattern.length;
    
return d >= 0 && this.lastIndexOf(pattern) === d;
  }
,

  empty: 
function() {
    
return this == '';
  }
,

  blank: 
function() {
    
return /^s*$/.test(this);
  }
,

  interpolate: 
function(object, pattern) {
    
return new Template(this, pattern).evaluate(object);
  }

}
);
var $break = { };

var Enumerable = {
  each: 
function(iterator, context) {
    
var index = 0;
    iterator 
= iterator.bind(context);
    
try {
      
this._each(function(value) {
        iterator(value, index
++);
      }
);
    }
 catch (e) {
      
if (e != $breakthrow e;
    }

    
return this;
  }
,

  eachSlice: 
function(number, iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var index = -number, slices = [], array = this.toArray();
    
while ((index += number) < array.length)
      slices.push(array.slice(index, index
+number));
    
return slices.collect(iterator, context);
  }
,

  all: 
function(iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var result = true;
    
this.each(function(value, index) {
      result 
= result && !!iterator(value, index);
      
if (!result) throw $break;
    }
);
    
return result;
  }
,

  any: 
function(iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var result = false;
    
this.each(function(value, index) {
      
if (result = !!iterator(value, index))
        
throw $break;
    }
);
    
return result;
  }
,

  collect: 
function(iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var results = [];
    
this.each(function(value, index) {
      results.push(iterator(value, index));
    }
);
    
return results;
  }
,

  detect: 
function(iterator, context) {
    iterator 
= iterator.bind(context);
    
var result;
    
this.each(function(value, index) {
      
if (iterator(value, index)) {
        result 
= value;
        
throw $break;
      }

    }
);
    
return result;
  }
,

  findAll: 
function(iterator, context) {
    iterator 
= iterator.bind(context);
    
var results = [];
    
this.each(function(value, index) {
      
if (iterator(value, index))
        results.push(value);
    }
);
    
return results;
  }
,

  grep: 
function(filter, iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var results = [];

    
if (Object.isString(filter))
      filter 
= new RegExp(filter);

    
this.each(function(value, index) {
      
if (filter.match(value))
        results.push(iterator(value, index));
    }
);
    
return results;
  }
,

  include: 
function(object) {
    
if (Object.isFunction(this.indexOf))
      
if (this.indexOf(object) != -1return true;

    
var found = false;
    
this.each(function(value) {
      
if (value == object) {
        found 
= true;
        
throw $break;
      }

    }
);
    
return found;
  }
,

  inGroupsOf: 
function(number, fillWith) {
    fillWith 
= Object.isUndefined(fillWith) ? null : fillWith;
    
return this.eachSlice(number, function(slice) {
      
while(slice.length < number) slice.push(fillWith);
      
return slice;
    }
);
  }
,

  inject: 
function(memo, iterator, context) {
    iterator 
= iterator.bind(context);
    
this.each(function(value, index) {
      memo 
= iterator(memo, value, index);
    }
);
    
return memo;
  }
,

  invoke: 
function(method) {
    
var args = $A(arguments).slice(1);
    
return this.map(function(value) {
      
return value[method].apply(value, args);
    }
);
  }
,

  max: 
function(iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var result;
    
this.each(function(value, index) {
      value 
= iterator(value, index);
      
if (result == null || value >= result)
        result 
= value;
    }
);
    
return result;
  }
,

  min: 
function(iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var result;
    
this.each(function(value, index) {
      value 
= iterator(value, index);
      
if (result == null || value < result)
        result 
= value;
    }
);
    
return result;
  }
,

  partition: 
function(iterator, context) {
    iterator 
= iterator ? iterator.bind(context) : Prototype.K;
    
var trues = [], falses = [];
    
this.each(function(value, index) {
      (iterator(value, index) 
?
        trues : falses).push(value);
    }
);
    
return [trues, falses];
  }
,

  pluck: 
function(property) {
    
var results = [];
    
this.each(function(value) {
      results.push(value[property]);
    }
);
    
return results;
  }
,

  reject: 
function(iterator, context) {
    iterator 
= iterator.bind(context);
    
var results = [];
    
this.each(function(value, index) {
      
if (!iterator(value, index))
        results.push(value);
    }
);
    
return results;
  }
,

  sortBy: 
function(iterator, context) {
    iterator 
= iterator.bind(context);
    
return this.map(function(value, index) {
      
return {value: value, criteria: iterator(value, index)};
    }
).sort(function(left, right) {
      
var a = left.criteria, b = right.criteria;
      
return a < b ? -1 : a > b ? 1 : 0;
    }
).pluck('value');
  }
,

  toArray: 
function() {
    
return this.map();
  }
,

  zip: 
function() {
    
var iterator = Prototype.K, args = $A(arguments);
    
if (Object.isFunction(args.last()))
      iterator 
= args.pop();

    
var collections = [this].concat(args).map($A);
    
return this.map(function(value, index) {
      
return iterator(collections.pluck(index));
    }
);
  }
,

  size: 
function() {
    
return this.toArray().length;
  }
,

  inspect: 
function() {
    
return '#<Enumerable:' + this.toArray().inspect() + '>';
  }

}
;

Object.extend(Enumerable, 
{
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  filter:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray,
  every:   Enumerable.all,
  some:    Enumerable.any
}
);
function $A(iterable) {
  
if (!iterable) return [];
  
if (iterable.toArray) return iterable.toArray();
  
var length = iterable.length || 0, results = new Array(length);
  
while (length--) results[length] = iterable[length];
  
return results;
}


if (Prototype.Browser.WebKit) {
  $A 
= function(iterable) {
    
if (!iterable) return [];
    
if (!(Object.isFunction(iterable) && iterable == '[object NodeList]'&&
        iterable.toArray) 
return iterable.toArray();
    
var length = iterable.length || 0, results = new Array(length);
    
while (length--) results[length] = iterable[length];
    
return results;
  }
;
}


Array.from 
= $A;

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
Object.extend(Array.prototype, {
  _each: 
function(iterator) {
    
for (var i = 0, length = this.length; i < length; i++)
      iterator(
this[i]);
  }
,

  clear: 
function() {
    
this.length = 0;
    
return this;
  }
,

  first: 
function() {
    
return this[0];
  }
,

  last: 
function() {
    
return this[this.length - 1];
  }
,

  compact: 
function() {
    
return this.select(function(value) {
      
return value != null;
    }
);
  }
,

  flatten: 
function() {
    
return this.inject([], function(array, value) {
      
return array.concat(Object.isArray(value) ?
        value.flatten() : [value]);
    }
);
  }
,

  without: 
function() {
    
var values = $A(arguments);
    
return this.select(function(value) {
      
return !values.include(value);
    }
);
  }
,

  reverse: 
function(inline) {
    
return (inline !== false ? this : this.toArray())._reverse();
  }
,

  reduce: 
function() {
    
return this.length > 1 ? this : this[0];
  }
,

  uniq: 
function(sorted) {
    
return this.inject([], function(array, value, index) {
      
if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      
return array;
    }
);
  }
,

  intersect: 
function(array) {
    
return this.uniq().findAll(function(item) {
      
return array.detect(function(value) return item === value });
    }
);
  }
,

  clone: 
function() {
    
return [].concat(this);
  }
,

  size: 
function() {
    
return this.length;
  }
,

  inspect: 
function() {
    
return '[' + this.map(Object.inspect).join(''+ ']';
  }
,

  toJSON: 
function() {
    
var results = [];
    
this.each(function(object) {
      
var value = Object.toJSON(object);
      
if (!Object.isUndefined(value)) results.push(value);
    }
);
    
return '[' + results.join(''+ ']';
  }

}
);

// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
  Array.prototype._each 
= Array.prototype.forEach;

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
  i 
|| (i = 0);
  
var length = this.length;
  
if (i < 0) i = length + i;
  
for (; i < length; i++)
    
if (this[i] === item) return i;
  
return -1;
}
;

if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
  i 
= isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
  
var n = this.slice(0, i).reverse().indexOf(item);
  
return (n < 0? n : i - n - 1;
}
;

Array.prototype.toArray 
= Array.prototype.clone;

function $w(string) {
  
if (!Object.isString(string)) return [];
  string 
= string.strip();
  
return string ? string.split(/s+/) : [];
}


if (Prototype.Browser.Opera){
  Array.prototype.concat 
= function() {
    
var array = [];
    
for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    
for (var i = 0, length = arguments.length; i < length; i++{
      
if (Object.isArray(arguments[i])) {
        
for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      }
 else {
        array.push(arguments[i]);
      }

    }

    
return array;
  }
;
}

Object.extend(Number.prototype, {
  toColorPart: 
function() {
    
return this.toPaddedString(216);
  }
,

  succ: 
function() {
    
return this + 1;
  }
,

  times: 
function(iterator) {
    $R(
0thistrue).each(iterator);
    
return this;
  }
,

  toPaddedString: 
function(length, radix) {
    
var string = this.toString(radix || 10);
    
return '0'.times(length - string.length) + string;
  }
,

  toJSON: 
function() {
    
return isFinite(this? this.toString() : 'null';
  }

}
);

$w(
'abs round ceil floor').each(function(method){
  Number.prototype[method] 
= Math[method].methodize();
}
);
function $H(object) {
  
return new Hash(object);
}
;

var Hash = Class.create(Enumerable, (function() {

  
function toQueryPair(key, value) {
    
if (Object.isUndefined(value)) return key;
    
return key + '=' + encodeURIComponent(String.interpret(value));
  }


  
return {
    initialize: 
function(object) {
      
this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    }
,

    _each: 
function(iterator) {
      
for (var key in this._object) {
        
var value = this._object[key], pair = [key, value];
        pair.key 
= key;
        pair.value 
= value;
        iterator(pair);
      }

    }
,

    set: 
function(key, value) {
      
return this._object[key] = value;
    }
,

    get: 
function(key) {
      
return this._object[key];
    }
,

    unset: 
function(key) {
      
var value = this._object[key];
      
delete this._object[key];
      
return value;
    }
,

    toObject: 
function() {
      
return Object.clone(this._object);
    }
,

    keys: 
function() {
      
return this.pluck('key');
    }
,

    values: 
function() {
      
return this.pluck('value');
    }
,

    index: 
function(value) {
      
var match = this.detect(function(pair) {
        
return pair.value === value;
      }
);
      
return match && match.key;
    }
,

    merge: 
function(object) {
      
return this.clone().update(object);
    }
,

    update: 
function(object) {
      
return new Hash(object).inject(thisfunction(result, pair) {
        result.set(pair.key, pair.value);
        
return result;
      }
);
    }
,

    toQueryString: 
function() {
      
return this.map(function(pair) {
        
var key = encodeURIComponent(pair.key), values = pair.value;

        
if (values && typeof values == 'object'{
          
if (Object.isArray(values))
            
return values.map(toQueryPair.curry(key)).join('&');
        }

        
return toQueryPair(key, values);
      }
).join('&');
    }
,

    inspect: 
function() {
      
return '#<Hash:{' + this.map(function(pair) {
        
return pair.map(Object.inspect).join('');
      }
).join(''+ '}>';
    }
,

    toJSON: 
function() {
      
return Object.toJSON(this.toObject());
    }
,

    clone: 
function() {
      
return new Hash(this);
    }

  }

}
)());

Hash.prototype.toTemplateReplacements 
= Hash.prototype.toObject;
Hash.from 
= $H;
var ObjectRange = Class.create(Enumerable, {
  initialize: 
function(start, end, exclusive) {
    
this.start = start;
    
this.end = end;
    
this.exclusive = exclusive;
  }
,

  _each: 
function(iterator) {
    
var value = this.start;
    
while (this.include(value)) {
      iterator(value);
      value 
= value.succ();
    }

  }
,

  include: 
function(value) {
    
if (value < this.start)
      
return false;
    
if (this.exclusive)
      
return value < this.end;
    
return value <= this.end;
  }

}
);
var $R = function(start, end, exclusive) {
  
return new ObjectRange(start, end, exclusive);
}
;

var Ajax = {
  getTransport: 
function() {
    
return Try.these(
      
function() {return new XMLHttpRequest()},
      
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      
function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) 
|| false;
  }
,

  activeRequestCount: 
0
}
;

Ajax.Responders 
= {
  responders: [],

  _each: 
function(iterator) {
    
this.responders._each(iterator);
  }
,

  register: 
function(responder) {
    
if (!this.include(responder))
      
this.responders.push(responder);
  }
,

  unregister: 
function(responder) {
    
this.responders = this.responders.without(responder);
  }
,

  dispatch: 
function(callback, request, transport, json) {
    
this.each(function(responder) {
      
if (Object.isFunction(responder[callback])) {
        
try {
          responder[callback].apply(responder, [request, transport, json]);
        }
 catch (e) { }
      }

    }
);
  }

}
;

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register(
{
  onCreate:   
function() { Ajax.activeRequestCount++ },
  onComplete: 
function() { Ajax.activeRequestCount-- }
}
);

Ajax.Base 
= Class.create({
  initialize: 
function(options) {
    
this.options = {
      method:       
'post',
      asynchronous: 
true,
      contentType:  
'application/x-www-form-urlencoded',
      encoding:     
'UTF-8',
      parameters:   
'',
      evalJSON:     
true,
      evalJS:       
true
    }
;
    Object.extend(
this.options, options || { });

    
this.options.method = this.options.method.toLowerCase();

    
if (Object.isString(this.options.parameters))
      
this.options.parameters = this.options.parameters.toQueryParams();
    
else if (Object.isHash(this.options.parameters))
      
this.options.parameters = this.options.parameters.toObject();
  }

}
);

Ajax.Request 
= Class.create(Ajax.Base, {
  _complete: 
false,

  initialize: 
function($super, url, options) {
    $super(options);
    
this.transport = Ajax.getTransport();
    
this.request(url);
  }
,

  request: 
function(url) {
    
this.url = url;
    
this.method = this.options.method;
    
var params = Object.clone(this.options.parameters);

    
if (!['get''post'].include(this.method)) {
      
// simulate other verbs over post
      params['_method'= this.method;
      
this.method = 'post';
    }


    
this.parameters = params;

    
if (params = Object.toQueryString(params)) {
      
// when GET, append parameters to URL
      if (this.method == 'get')
        
this.url += (this.url.include('?'? '&' : '?'+ params;
      
else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params 
+= '&_=';
    }


    
try {
      
var response = new Ajax.Response(this);
      
if (this.options.onCreate) this.options.onCreate(response);
      Ajax.Responders.dispatch(
'onCreate'this, response);

      
this.transport.open(this.method.toUpperCase(), this.url,
        
this.options.asynchronous);

      
if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

      
this.transport.onreadystatechange = this.onStateChange.bind(this);
      
this.setRequestHeaders();

      
this.body = this.method == 'post' ? (this.options.postBody || params) : null;
      
this.transport.send(this.body);

      
/* Force Firefox to handle ready state 4 for synchronous requests */
      
if (!this.options.asynchronous && this.transport.overrideMimeType)
        
this.onStateChange();

    }

    
catch (e) {
      
this.dispatchException(e);
    }

  }
,

  onStateChange: 
function() {
    
var readyState = this.transport.readyState;
    
if (readyState > 1 && !((readyState == 4&& this._complete))
      
this.respondToReadyState(this.transport.readyState);
  }
,

  setRequestHeaders: 
function() {
    
var headers = {
      
'X-Requested-With''XMLHttpRequest',
      
'X-Prototype-Version': Prototype.Version,
      
'Accept''text/javascript, text/html, application/xml, text/xml, */*'
    }
;

    
if (this.method == 'post'{
      headers[
'Content-type'= this.options.contentType +
        (
this.options.encoding ? '; charset=' + this.options.encoding : '');

      
/* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       
*/

      
if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(
/Gecko/(d{4})/|| [0,2005])[1< 2005)
            headers[
'Connection'= 'close';
    }


    
// user-defined headers
    if (typeof this.options.requestHeaders == 'object'{
      
var extras = this.options.requestHeaders;

      
if (Object.isFunction(extras.push))
        
for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] 
= extras[i+1];
      
else
        $H(extras).each(
function(pair) { headers[pair.key] = pair.value });
    }


    
for (var name in headers)
      
this.transport.setRequestHeader(name, headers[name]);
  }
,

  success: 
function() {
    
var status = this.getStatus();
    
return !status || (status >= 200 && status < 300);
  }
,

  getStatus: 
function() {
    
try {
      
return this.transport.status || 0;
    }
 catch (e) return 0 }
  }
,

  respondToReadyState: 
function(readyState) {
    
var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);

    
if (state == 'Complete'{
      
try {
        
this._complete = true;
        (
this.options['on' + response.status]
         
|| this.options['on' + (this.success() ? 'Success' : 'Failure')]
         
|| Prototype.emptyFunction)(response, response.headerJSON);
      }
 catch (e) {
        
this.dispatchException(e);
      }


      
var contentType = response.getHeader('Content-type');
      
if (this.options.evalJS == 'force'
          
|| (this.options.evalJS && this.isSameOrigin() && contentType
          
&& contentType.match(/^s*(text|application)/(x-)?(java|ecma)script(;.*)?s*$/i)))
        
this.evalResponse();
    }


    
try {
      (
this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
      Ajax.Responders.dispatch(
'on' + state, this, response, response.headerJSON);
    }
 catch (e) {
      
this.dispatchException(e);
    }


    
if (state == 'Complete'{
      
// avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }

  }
,

  isSameOrigin: 
function() {
    
var m = this.url.match(/^s*https?://[^/]*/);
    
return !|| (m[0== '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port 
? ':' + location.port : ''
    }
));
  }
,

  getHeader: 
function(name) {
    
try {
      
return this.transport.getResponseHeader(name) || null;
    }
 catch (e) return null }
  }
,

  evalResponse: 
function() {
    
try {
      
return eval((this.transport.responseText || '').unfilterJSON());
    }
 catch (e) {
      
this.dispatchException(e);
    }

  }
,

  dispatchException: 
function(exception) {
    (
this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch(
'onException'this, exception);
  }

}
);

Ajax.Request.Events 
=
  [
'Uninitialized''Loading''Loaded''Interactive''Complete'];

Ajax.Response 
= Class.create({
  initialize: 
function(request){
    
this.request = request;
    
var transport  = this.transport  = request.transport,
        readyState 
= this.readyState = transport.readyState;

    
if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4{
      
this.status       = this.getStatus();
      
this.statusText   = this.getStatusText();
      
this.responseText = String.interpret(transport.responseText);
      
this.headerJSON   = this._getHeaderJSON();
    }


    
if(readyState == 4{
      
var xml = transport.responseXML;
      
this.responseXML  = Object.isUndefined(xml) ? null : xml;
      
this.responseJSON = this._getResponseJSON();
    }

  }
,

  status:      
0,
  statusText: 
'',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: 
function() {
    
try {
      
return this.transport.statusText || '';
    }
 catch (e) return '' }
  }
,

  getHeader: Ajax.Request.prototype.getHeader,

  getAllHeaders: 
function() {
    
try {
      
return this.getAllResponseHeaders();
    }
 catch (e) return null }
  }
,

  getResponseHeader: 
function(name) {
    
return this.transport.getResponseHeader(name);
  }
,

  getAllResponseHeaders: 
function() {
    
return this.transport.getAllResponseHeaders();
  }
,

  _getHeaderJSON: 
function() {
    
var json = this.getHeader('X-JSON');
    
if (!json) return null;
    json 
= decodeURIComponent(escape(json));
    
try {
      
return json.evalJSON(this.request.options.sanitizeJSON ||
        
!this.request.isSameOrigin());
    }
 catch (e) {
      
this.request.dispatchException(e);
    }

  }
,

  _getResponseJSON: 
function() {
    
var options = this.request.options;
    
if (!options.evalJSON || (options.evalJSON != 'force' &&
      
!(this.getHeader('Content-type'|| '').include('application/json')) ||
        
this.responseText.blank())
          
return null;
    
try {
      
return this.responseText.evalJSON(options.sanitizeJSON ||
        
!this.request.isSameOrigin());
    }
 catch (e) {
      
this.request.dispatchException(e);
    }

  }

}
);

Ajax.Updater 
= Class.create(Ajax.Request, {
  initialize: 
function($super, container, url, options) {
    
this.container = {
      success: (container.success 
|| container),
      failure: (container.failure 
|| (container.success ? null : container))
    }
;

    options 
= Object.clone(options);
    
var onComplete = options.onComplete;
    options.onComplete 
= (function(response, json) {
      
this.updateContent(response.responseText);
      
if (Object.isFunction(onComplete)) onComplete(response, json);
    }
).bind(this);

    $super(url, options);
  }
,

  updateContent: 
function(responseText) {
    
var receiver = this.container[this.success() ? 'success' : 'failure'],
        options 
= this.options;

    
if (!options.evalScripts) responseText = responseText.stripScripts();

    
if (receiver = $(receiver)) {
      
if (options.insertion) {
        
if (Object.isString(options.insertion)) {
          
var insertion = { }; insertion[options.insertion] = responseText;
          receiver.insert(insertion);
        }

        
else options.insertion(receiver, responseText);
      }

      
else receiver.update(responseText);
    }

  }

}
);

Ajax.PeriodicalUpdater 
= Class.create(Ajax.Base, {
  initialize: 
function($super, container, url, options) {
    $super(options);
    
this.onComplete = this.options.onComplete;

    
this.frequency = (this.options.frequency || 2);
    
this.decay = (this.options.decay || 1);

    
this.updater = { };
    
this.container = container;
    
this.url = url;

    
this.start();
  }
,

  start: 
function() {
    
this.options.onComplete = this.updateComplete.bind(this);
    
this.onTimerEvent();
  }
,

  stop: 
function() {
    
this.updater.options.onComplete = undefined;
    clearTimeout(
this.timer);
    (
this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  }
,

  updateComplete: 
function(response) {
    
if (this.options.decay) {
      
this.decay = (response.responseText == this.lastText ?
        
this.decay * this.options.decay : 1);

      
this.lastText = response.responseText;
    }

    
this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  }
,

  onTimerEvent: 
function() {
    
this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }

}
);
function $(element) {
  
if (arguments.length > 1{
    
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    
return elements;
  }

  
if (Object.isString(element))
    element 
= document.getElementById(element);
  
return Element.extend(element);
}


if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath 
= function(expression, parentElement) {
    
var results = [];
    
var query = document.evaluate(expression, $(parentElement) || document,
      
null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    
for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(Element.extend(query.snapshotItem(i)));
    
return results;
  }
;
}


/*--------------------------------------------------------------------------*/

if (!window.Node) var Node = { };

if (!Node.ELEMENT_NODE) {
  
// DOM level 2 ECMAScript Language Binding
  Object.extend(Node, {
    ELEMENT_NODE: 
1,
    ATTRIBUTE_NODE: 
2,
    TEXT_NODE: 
3,
    CDATA_SECTION_NODE: 
4,
    ENTITY_REFERENCE_NODE: 
5,
    ENTITY_NODE: 
6,
    PROCESSING_INSTRUCTION_NODE: 
7,
    COMMENT_NODE: 
8,
    DOCUMENT_NODE: 
9,
    DOCUMENT_TYPE_NODE: 
10,
    DOCUMENT_FRAGMENT_NODE: 
11,
    NOTATION_NODE: 
12
  }
);
}


(
function() {
  
var element = this.Element;
  
this.Element = function(tagName, attributes) {
    attributes 
= attributes || { };
    tagName 
= tagName.toLowerCase();
    
var cache = Element.cache;
    
if (Prototype.Browser.IE && attributes.name) {
      tagName 
= '<' + tagName + ' name="' + attributes.name + '">';
      
delete attributes.name;
      
return Element.writeAttribute(document.createElement(tagName), attributes);
    }

    
if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    
return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  }
;
  Object.extend(
this.Element, element || { });
}
).call(window);

Element.cache 
= { };

Element.Methods 
= {
  visible: 
function(element) {
    
return $(element).style.display != 'none';
  }
,

  toggle: 
function(element) {
    element 
= $(element);
    Element[Element.visible(element) 
? 'hide' : 'show'](element);
    
return element;
  }
,

  hide: 
function(element) {
    $(element).style.display 
= 'none';
    
return element;
  }
,

  show: 
function(element) {
    $(element).style.display 
= '';
    
return element;
  }
,

  remove: 
function(element) {
    element 
= $(element);
    element.parentNode.removeChild(element);
    
return element;
  }
,

  update: 
function(element, content) {
    element 
= $(element);
    
if (content && content.toElement) content = content.toElement();
    
if (Object.isElement(content)) return element.update().insert(content);
    content 
= Object.toHTML(content);
    element.innerHTML 
= content.stripScripts();
    content.evalScripts.bind(content).defer();
    
return element;
  }
,

  replace: 
function(element, content) {
    element 
= $(element);
    
if (content && content.toElement) content = content.toElement();
    
else if (!Object.isElement(content)) {
      content 
= Object.toHTML(content);
      
var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content 
= range.createContextualFragment(content.stripScripts());
    }

    element.parentNode.replaceChild(content, element);
    
return element;
  }
,

  insert: 
function(element, insertions) {
    element 
= $(element);

    
if (Object.isString(insertions) || Object.isNumber(insertions) ||
        Object.isElement(insertions) 
|| (insertions && (insertions.toElement || insertions.toHTML)))
          insertions 
= {bottom:insertions};

    
var content, insert, tagName, childNodes;

    
for (var position in insertions) {
      content  
= insertions[position];
      position 
= position.toLowerCase();
      insert 
= Element._insertionTranslations[position];

      
if (content && content.toElement) content = content.toElement();
      
if (Object.isElement(content)) {
        insert(element, content);
        
continue;
      }


      content 
= Object.toHTML(content);

      tagName 
= ((position == 'before' || position == 'after')
        
? element.parentNode : element).tagName.toUpperCase();

      childNodes 
= Element._getContentFromAnonymousElement(tagName, content.stripScripts());

      
if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }


    
return element;
  }
,

  wrap: 
function(element, wrapper, attributes) {
    element 
= $(element);
    
if (Object.isElement(wrapper))
      $(wrapper).writeAttribute(attributes 
|| { });
    
else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
    
else wrapper = new Element('div', wrapper);
    
if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    
return wrapper;
  }
,

  inspect: 
function(element) {
    element 
= $(element);
    
var result = '<' + element.tagName.toLowerCase();
    $H(
{'id''id''className''class'}).each(function(pair) {
      
var property = pair.first(), attribute = pair.last();
      
var value = (element[property] || '').toString();
      
if (value) result += ' ' + attribute + '=' + value.inspect(true);
    }
);
    
return result + '>';
  }
,

  recursivelyCollect: 
function(element, property) {
    element 
= $(element);
    
var elements = [];
    
while (element = element[property])
      
if (element.nodeType == 1)
        elements.push(Element.extend(element));
    
return elements;
  }
,

  ancestors: 
function(element) {
    
return $(element).recursivelyCollect('parentNode');
  }
,

  descendants: 
function(element) {
    
return $(element).select("*");
  }
,

  firstDescendant: 
function(element) {
    element 
= $(element).firstChild;
    
while (element && element.nodeType != 1) element = element.nextSibling;
    
return $(element);
  }
,

  immediateDescendants: 
function(element) {
    
if (!(element = $(element).firstChild)) return [];
    
while (element && element.nodeType != 1) element = element.nextSibling;
    
if (element) return [element].concat($(element).nextSiblings());
    
return [];
  }
,

  previousSiblings: 
function(element) {
    
return $(element).recursivelyCollect('previousSibling');
  }
,

  nextSiblings: 
function(element) {
    
return $(element).recursivelyCollect('nextSibling');
  }
,

  siblings: 
function(element) {
    element 
= $(element);
    
return element.previousSiblings().reverse().concat(element.nextSiblings());
  }
,

  match: 
function(element, selector) {
    
if (Object.isString(selector))
      selector 
= new Selector(selector);
    
return selector.match($(element));
  }
,

  up: 
function(element, expression, index) {
    element 
= $(element);
    
if (arguments.length == 1return $(element.parentNode);
    
var ancestors = element.ancestors();
    
return Object.isNumber(expression) ? ancestors[expression] :
      Selector.findElement(ancestors, expression, index);
  }
,

  down: 
function(element, expression, index) {
    element 
= $(element);
    
if (arguments.length == 1return element.firstDescendant();
    
return Object.isNumber(expression) ? element.descendants()[expression] :
      element.select(expression)[index 
|| 0];
  }
,

  previous: 
function(element, expression, index) {
    element 
= $(element);
    
if (arguments.length == 1return $(Selector.handlers.previousElementSibling(element));
    
var previousSiblings = element.previousSiblings();
    
return Object.isNumber(expression) ? previousSiblings[expression] :
      Selector.findElement(previousSiblings, expression, index);
  }
,

  next: 
function(element, expression, index) {
    element 
= $(element);
    
if (arguments.length == 1return $(Selector.handlers.nextElementSibling(element));
    
var nextSiblings = element.nextSiblings();
    
return Object.isNumber(expression) ? nextSiblings[expression] :
      Selector.findElement(nextSiblings, expression, index);
  }
,

  select: 
function() {
    
var args = $A(arguments), element = $(args.shift());
    
return Selector.findChildElements(element, args);
  }
,

  adjacent: 
function() {
    
var args = $A(arguments), element = $(args.shift());
    
return Selector.findChildElements(element.parentNode, args).without(element);
  }
,

  identify: 
function(element) {
    element 
= $(element);
    
var id = element.readAttribute('id'), self = arguments.callee;
    
if (id) return id;
    
do { id = 'anonymous_element_' + self.counter++ } while ($(id));
    element.writeAttribute(
'id', id);
    
return id;
  }
,

  readAttribute: 
function(element, name) {
    element 
= $(element);
    
if (Prototype.Browser.IE) {
      
var t = Element._attributeTranslations.read;
      
if (t.values[name]) return t.values[name](element, name);
      
if (t.names[name]) name = t.names[name];
      
if (name.include(':')) {
        
return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }

    }

    
return element.getAttribute(name);
  }
,

  writeAttribute: 
function(element, name, value) {
    element 
= $(element);
    
var attributes = { }, t = Element._attributeTranslations.write;

    
if (typeof name == 'object') attributes = name;
    
else attributes[name] = Object.isUndefined(value) ? true : value;

    
for (var attr in attributes) {
      name 
= t.names[attr] || attr;
      value 
= attributes[attr];
      
if (t.values[attr]) name = t.values[attr](element, value);
      
if (value === false || value === null)
        element.removeAttribute(name);
      
else if (value === true)
        element.setAttribute(name, name);
      
else element.setAttribute(name, value);
    }

    
return element;
  }
,

  getHeight: 
function(element) {
    
return $(element).getDimensions().height;
  }
,

  getWidth: 
function(element) {
    
return $(element).getDimensions().width;
  }
,

  classNames: 
function(element) {
    
return new Element.ClassNames(element);
  }
,

  hasClassName: 
function(element, className) {
    
if (!(element = $(element))) return;
    
var elementClassName = element.className;
    
return (elementClassName.length > 0 && (elementClassName == className ||
      
new RegExp("(^|\s)" + className + "(\s|$)").test(elementClassName)));
  }
,

  addClassName: 
function(element, className) {
    
if (!(element = $(element))) return;
    
if (!element.hasClassName(className))
      element.className 
+= (element.className ? ' ' : ''+ className;
    
return element;
  }
,

  removeClassName: 
function(element, className) {
    
if (!(element = $(element))) return;
    element.className 
= element.className.replace(
      
new RegExp("(^|\s+)" + className + "(\s+|$)"), ' ').strip();
    
return element;
  }
,

  toggleClassName: 
function(element, className) {
    
if (!(element = $(element))) return;
    
return element[element.hasClassName(className) ?
      
'removeClassName' : 'addClassName'](className);
  }
,

  
// removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element 
= $(element);
    
var node = element.firstChild;
    
while (node) {
      
var nextNode = node.nextSibling;
      
if (node.nodeType == 3 && !/S/.test(node.nodeValue))
        element.removeChild(node);
      node 
= nextNode;
    }

    
return element;
  }
,

  empty: 
function(element) {
    
return $(element).innerHTML.blank();
  }
,

  descendantOf: 
function(element, ancestor) {
    element 
= $(element), ancestor = $(ancestor);
    
var originalAncestor = ancestor;

    
if (element.compareDocumentPosition)
      
return (element.compareDocumentPosition(ancestor) & 8=== 8;

    
if (element.sourceIndex && !Prototype.Browser.Opera) {
      
var e = element.sourceIndex, a = ancestor.sourceIndex,
       nextAncestor 
= ancestor.nextSibling;
      
if (!nextAncestor) {
        
do { ancestor = ancestor.parentNode; }
        
while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
      }

      
if (nextAncestor && nextAncestor.sourceIndex)
       
return (e > a && e < nextAncestor.sourceIndex);
    }


    
while (element = element.parentNode)
      
if (element == originalAncestor) return true;
    
return false;
  }
,

  scrollTo: 
function(element) {
    element 
= $(element);
    
var pos = element.cumulativeOffset();
    window.scrollTo(pos[
0], pos[1]);
    
return element;
  }
,

  getStyle: 
function(element, style) {
    element 
= $(element);
    style 
= style == 'float' ? 'cssFloat' : style.camelize();
    
var value = element.style[style];
    
if (!value) {
      
var css = document.defaultView.getComputedStyle(element, null);
      value 
= css ? css[style] : null;
    }

    
if (style == 'opacity'return value ? parseFloat(value) : 1.0;
    
return value == 'auto' ? null : value;
  }
,

  getOpacity: 
function(element) {
    
return $(element).getStyle('opacity');
  }
,

  setStyle: 
function(element, styles) {
    element 
= $(element);
    
var elementStyle = element.style, match;
    
if (Object.isString(styles)) {
      element.style.cssText 
+= ';' + styles;
      
return styles.include('opacity'?
        element.setOpacity(styles.match(
/opacity:s*(d?.?d*)/)[1]) : element;
    }

    
for (var property in styles)
      
if (property == 'opacity') element.setOpacity(styles[property]);
      
else
        elementStyle[(property 
== 'float' || property == 'cssFloat'?
          (Object.isUndefined(elementStyle.styleFloat) 
? 'cssFloat' : 'styleFloat') :
            property] 
= styles[property];

    
return element;
  }
,

  setOpacity: 
function(element, value) {
    element 
= $(element);
    element.style.opacity 
= (value == 1 || value === ''? '' :
      (value 
< 0.00001? 0 : value;
    
return element;
  }
,

  getDimensions: 
function(element) {
    element 
= $(element);
    
var display = $(element).getStyle('display');
    
if (display != 'none' && display != null// Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    
// All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    
var originalVisibility = els.visibility;
    
var originalPosition = els.position;
    
var originalDisplay = els.display;
    els.visibility 
= 'hidden';
    els.position 
= 'absolute';
    els.display 
= 'block';
    
var originalWidth = element.clientWidth;
    
var originalHeight = element.clientHeight;
    els.display 
= originalDisplay;
    els.position 
= originalPosition;
    els.visibility 
= originalVisibility;
    
return {width: originalWidth, height: originalHeight};
  }
,

  makePositioned: 
function(element) {
    element 
= $(element);
    
var pos = Element.getStyle(element, 'position');
    
if (pos == 'static' || !pos) {
      element._madePositioned 
= true;
      element.style.position 
= 'relative';
      
// Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top 
= 0;
        element.style.left 
= 0;
      }

    }

    
return element;
  }
,

  undoPositioned: 
function(element) {
    element 
= $(element);
    
if (element._madePositioned) {
      element._madePositioned 
= undefined;
      element.style.position 
=
        element.style.top 
=
        element.style.left 
=
        element.style.bottom 
=
        element.style.right 
= '';
    }

    
return element;
  }
,

  makeClipping: 
function(element) {
    element 
= $(element);
    
if (element._overflow) return element;
    element._overflow 
= Element.getStyle(element, 'overflow'|| 'auto';
    
if (element._overflow !== 'hidden')
      element.style.overflow 
= 'hidden';
    
return element;
  }
,

  undoClipping: 
function(element) {
    element 
= $(element);
    
if (!element._overflow) return element;
    element.style.overflow 
= element._overflow == 'auto' ? '' : element._overflow;
    element._overflow 
= null;
    
return element;
  }
,

  cumulativeOffset: 
function(element) {
    
var valueT = 0, valueL = 0;
    
do {
      valueT 
+= element.offsetTop  || 0;
      valueL 
+= element.offsetLeft || 0;
      element 
= element.offsetParent;
    }
 while (element);
    
return Element._returnOffset(valueL, valueT);
  }
,

  positionedOffset: 
function(element) {
    
var valueT = 0, valueL = 0;
    
do {
      valueT 
+= element.offsetTop  || 0;
      valueL 
+= element.offsetLeft || 0;
      element 
= element.offsetParent;
      
if (element) {
        
if (element.tagName == 'BODY'break;
        
var p = Element.getStyle(element, 'position');
        
if (p !== 'static'break;
      }

    }
 while (element);
    
return Element._returnOffset(valueL, valueT);
  }
,

  absolutize: 
function(element) {
    element 
= $(element);
    
if (element.getStyle('position'== 'absolute'return;
    
// Position.prepare(); // To be done manually by Scripty when it needs it.

    
var offsets = element.positionedOffset();
    
var top     = offsets[1];
    
var left    = offsets[0];
    
var width   = element.clientWidth;
    
var height  = element.clientHeight;

    element._originalLeft   
= left - parseFloat(element.style.left  || 0);
    element._originalTop    
= top  - parseFloat(element.style.top || 0);
    element._originalWidth  
= element.style.width;
    element._originalHeight 
= element.style.height;

    element.style.position 
= 'absolute';
    element.style.top    
= top + 'px';
    element.style.left   
= left + 'px';
    element.style.width  
= width + 'px';
    element.style.height 
= height + 'px';
    
return element;
  }
,

  relativize: 
function(element) {
    element 
= $(element);
    
if (element.getStyle('position'== 'relative'return;
    
// Position.prepare(); // To be done manually by Scripty when it needs it.

    element.style.position 
= 'relative';
    
var top  = parseFloat(element.style.top  || 0- (element._originalTop || 0);
    
var left = parseFloat(element.style.left || 0- (element._originalLeft || 0);

    element.style.top    
= top + 'px';
    element.style.left   
= left + 'px';
    element.style.height 
= element._originalHeight;
    element.style.width  
= element._originalWidth;
    
return element;
  }
,

  cumulativeScrollOffset: 
function(element) {
    
var valueT = 0, valueL = 0;
    
do {
      valueT 
+= element.scrollTop  || 0;
      valueL 
+= element.scrollLeft || 0;
      element 
= element.parentNode;
    }
 while (element);
    
return Element._returnOffset(valueL, valueT);
  }
,

  getOffsetParent: 
function(element) {
    
if (element.offsetParent) return $(element.offsetParent);
    
if (element == document.body) return $(element);

    
while ((element = element.parentNode) && element != document.body)
      
if (Element.getStyle(element, 'position'!= 'st