library load times

Benchmark created on


Test runner

Ready to run.

Testing in
TestOps/sec
mootools
/*
---
MooTools: the javascript framework

web build:
 - http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7

packager build:
 - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff

/*
---

name: Core

description: The heart of MooTools.

license: MIT-style license.

copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/).

authors: The MooTools production team (http://mootools.net/developers/)

inspiration:
  - Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright (c) 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
  - Some functionality inspired by [Prototype.js](http://prototypejs.org) Copyright (c) 2005-2007 Sam Stephenson, [MIT License](http://opensource.org/licenses/mit-license.php)

provides: [Core, MooTools, Type, typeOf, instanceOf, Native]

...
*/

(function() {

 this.MooTools = {
  version: '1.3.1',
  build: 'af48c8d589f43f32212f9bb8ff68a127e6a3ba6c'
 };

 // typeOf, instanceOf
 var typeOf = this.typeOf = function(item) {
  if (item == null) return 'null';
  if (item.$family) return item.$family();

  if (item.nodeName) {
   if (item.nodeType == 1) return 'element';
   if (item.nodeType == 3) return (/\S/).test(item.nodeValue) ? 'textnode' : 'whitespace';
  } else if (typeof item.length == 'number') {
   if (item.callee) return 'arguments';
   if ('item' in item) return 'collection';
  }

  return typeof item;
 };

 var instanceOf = this.instanceOf = function(item, object) {
  if (item == null) return false;
  var constructor = item.$constructor || item.constructor;
  while (constructor) {
   if (constructor === object) return true;
   constructor = constructor.parent;
  }
  return item instanceof object;
 };

 // Function overloading
 var Function = this.Function;

 var enumerables = true;
 for (var i in {
  toString: 1
 }) enumerables = null;
 if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];

 Function.prototype.overloadSetter = function(usePlural) {
  var self = this;
  return function(a, b) {
   if (a == null) return this;
   if (usePlural || typeof a != 'string') {
    for (var k in a) self.call(this, k, a[k]);
    if (enumerables) for (var i = enumerables.length; i--;) {
     k = enumerables[i];
     if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
    }
   } else {
    self.call(this, a, b);
   }
   return this;
  };
 };

 Function.prototype.overloadGetter = function(usePlural) {
  var self = this;
  return function(a) {
   var args, result;
   if (usePlural || typeof a != 'string') args = a;
   else if (arguments.length > 1) args = arguments;
   if (args) {
    result = {};
    for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]);
   } else {
    result = self.call(this, a);
   }
   return result;
  };
 };

 Function.prototype.extend = function(key, value) {
  this[key] = value;
 }.overloadSetter();

 Function.prototype.implement = function(key, value) {
  this.prototype[key] = value;
 }.overloadSetter();

 // From
 var slice = Array.prototype.slice;

 Function.from = function(item) {
  return (typeOf(item) == 'function') ? item : function() {
   return item;
  };
 };

 Array.from = function(item) {
  if (item == null) return [];
  return (Type.isEnumerable(item) && typeof item != 'string') ? (typeOf(item) == 'array') ? item : slice.call(item) : [item];
 };

 Number.from = function(item) {
  var number = parseFloat(item);
  return isFinite(number) ? number : null;
 };

 String.from = function(item) {
  return item + '';
 };

 // hide, protect
 Function.implement({

  hide: function() {
   this.$hidden = true;
   return this;
  },

  protect: function() {
   this.$protected = true;
   return this;
  }

 });

 // Type
 var Type = this.Type = function(name, object) {
  if (name) {
   var lower = name.toLowerCase();
   var typeCheck = function(item) {
    return (typeOf(item) == lower);
   };

   Type['is' + name] = typeCheck;
   if (object != null) {
    object.prototype.$family = (function() {
     return lower;
    }).hide();
    //<1.2compat>
    object.type = typeCheck;
    //</1.2compat>
   }
  }

  if (object == null) return null;

  object.extend(this);
  object.$constructor = Type;
  object.prototype.$constructor = object;

  return object;
 };

 var toString = Object.prototype.toString;

 Type.isEnumerable = function(item) {
  return (item != null && typeof item.length == 'number' && toString.call(item) != '[object Function]');
 };

 var hooks = {};

 var hooksOf = function(object) {
  var type = typeOf(object.prototype);
  return hooks[type] || (hooks[type] = []);
 };

 var implement = function(name, method) {
  if (method && method.$hidden) return;

  var hooks = hooksOf(this);

  for (var i = 0; i < hooks.length; i++) {
   var hook = hooks[i];
   if (typeOf(hook) == 'type') implement.call(hook, name, method);
   else hook.call(this, name, method);
  }

  var previous = this.prototype[name];
  if (previous == null || !previous.$protected) this.prototype[name] = method;

  if (this[name] == null && typeOf(method) == 'function') extend.call(this, name, function(item) {
   return method.apply(item, slice.call(arguments, 1));
  });
 };

 var extend = function(name, method) {
  if (method && method.$hidden) return;
  var previous = this[name];
  if (previous == null || !previous.$protected) this[name] = method;
 };

 Type.implement({

  implement: implement.overloadSetter(),

  extend: extend.overloadSetter(),

  alias: function(name, existing) {
   implement.call(this, name, this.prototype[existing]);
  }.overloadSetter(),

  mirror: function(hook) {
   hooksOf(this).push(hook);
   return this;
  }

 });

 new Type('Type', Type);

 // Default Types
 var force = function(name, object, methods) {
  var isType = (object != Object),
      prototype = object.prototype;

  if (isType) object = new Type(name, object);

  for (var i = 0, l = methods.length; i < l; i++) {
   var key = methods[i],
       generic = object[key],
       proto = prototype[key];

   if (generic) generic.protect();

   if (isType && proto) {
    delete prototype[key];
    prototype[key] = proto.protect();
   }
  }

  if (isType) object.implement(prototype);

  return force;
 };

 force('String', String, ['charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase'])('Array', Array, ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight'])('Number', Number, ['toExponential', 'toFixed', 'toLocaleString', 'toPrecision'])('Function', Function, ['apply', 'call', 'bind'])('RegExp', RegExp, ['exec', 'test'])('Object', Object, ['create', 'defineProperty', 'defineProperties', 'keys', 'getPrototypeOf', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'freeze', 'isFrozen'])('Date', Date, ['now']);

 Object.extend = extend.overloadSetter();

 Date.extend('now', function() {
  return +(new Date);
 });

 new Type('Boolean', Boolean);

 // fixes NaN returning as Number
 Number.prototype.$family = function() {
  return isFinite(this) ? 'number' : 'null';
 }.hide();

 // Number.random
 Number.extend('random', function(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min);
 });

 // forEach, each
 var hasOwnProperty = Object.prototype.hasOwnProperty;
 Object.extend('forEach', function(object, fn, bind) {
  for (var key in object) {
   if (hasOwnProperty.call(object, key)) fn.call(bind, object[key], key, object);
  }
 });

 Object.each = Object.forEach;

 Array.implement({

  forEach: function(fn, bind) {
   for (var i = 0, l = this.length; i < l; i++) {
    if (i in this) fn.call(bind, this[i], i, this);
   }
  },

  each: function(fn, bind) {
   Array.forEach(this, fn, bind);
   return this;
  }

 });

 // Array & Object cloning, Object merging and appending
 var cloneOf = function(item) {
  switch (typeOf(item)) {
  case 'array':
   return item.clone();
  case 'object':
   return Object.clone(item);
  default:
   return item;
  }
 };

 Array.implement('clone', function() {
  var i = this.length,
      clone = new Array(i);
  while (i--) clone[i] = cloneOf(this[i]);
  return clone;
 });

 var mergeOne = function(source, key, current) {
  switch (typeOf(current)) {
  case 'object':
   if (typeOf(source[key]) == 'object') Object.merge(source[key], current);
   else source[key] = Object.clone(current);
   break;
  case 'array':
   source[key] = current.clone();
   break;
  default:
   source[key] = current;
  }
  return source;
 };

 Object.extend({

  merge: function(source, k, v) {
   if (typeOf(k) == 'string') return mergeOne(source, k, v);
   for (var i = 1, l = arguments.length; i < l; i++) {
    var object = arguments[i];
    for (var key in object) mergeOne(source, key, object[key]);
   }
   return source;
  },

  clone: function(object) {
   var clone = {};
   for (var key in object) clone[key] = cloneOf(object[key]);
   return clone;
  },

  append: function(original) {
   for (var i = 1, l = arguments.length; i < l; i++) {
    var extended = arguments[i] || {};
    for (var key in extended) original[key] = extended[key];
   }
   return original;
  }

 });

 // Object-less types
 ['Object', 'WhiteSpace', 'TextNode', 'Collection', 'Arguments'].each(function(name) {
  new Type(name);
 });

 // Unique ID
 var UID = Date.now();

 String.extend('uniqueID', function() {
  return (UID++).toString(36);
 });

 //<1.2compat>
 var Hash = this.Hash = new Type('Hash', function(object) {
  if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
  for (var key in object) this[key] = object[key];
  return this;
 });

 Hash.implement({

  forEach: function(fn, bind) {
   Object.forEach(this, fn, bind);
  },

  getClean: function() {
   var clean = {};
   for (var key in this) {
    if (this.hasOwnProperty(key)) clean[key] = this[key];
   }
   return clean;
  },

  getLength: function() {
   var length = 0;
   for (var key in this) {
    if (this.hasOwnProperty(key)) length++;
   }
   return length;
  }

 });

 Hash.alias('each', 'forEach');

 Object.type = Type.isObject;

 var Native = this.Native = function(properties) {
  return new Type(properties.name, properties.initialize);
 };

 Native.type = Type.type;

 Native.implement = function(objects, methods) {
  for (var i = 0; i < objects.length; i++) objects[i].implement(methods);
  return Native;
 };

 var arrayType = Array.type;
 Array.type = function(item) {
  return instanceOf(item, Array) || arrayType(item);
 };

 this.$A = function(item) {
  return Array.from(item).slice();
 };

 this.$arguments = function(i) {
  return function() {
   return arguments[i];
  };
 };

 this.$chk = function(obj) {
  return !!(obj || obj === 0);
 };

 this.$clear = function(timer) {
  clearTimeout(timer);
  clearInterval(timer);
  return null;
 };

 this.$defined = function(obj) {
  return (obj != null);
 };

 this.$each = function(iterable, fn, bind) {
  var type = typeOf(iterable);
  ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind);
 };

 this.$empty = function() {};

 this.$extend = function(original, extended) {
  return Object.append(original, extended);
 };

 this.$H = function(object) {
  return new Hash(object);
 };

 this.$merge = function() {
  var args = Array.slice(arguments);
  args.unshift({});
  return Object.merge.apply(null, args);
 };

 this.$lambda = Function.from;
 this.$mixin = Object.merge;
 this.$random = Number.random;
 this.$splat = Array.from;
 this.$time = Date.now;

 this.$type = function(object) {
  var type = typeOf(object);
  if (type == 'elements') return 'array';
  return (type == 'null') ? false : type;
 };

 this.$unlink = function(object) {
  switch (typeOf(object)) {
  case 'object':
   return Object.clone(object);
  case 'array':
   return Array.clone(object);
  case 'hash':
   return new Hash(object);
  default:
   return object;
  }
 };

 //</1.2compat>
}).call(this);


/*
---

name: Array

description: Contains Array Prototypes like each, contains, and erase.

license: MIT-style license.

requires: Type

provides: Array

...
*/

Array.implement({

 invoke: function(methodName) {
  var args = Array.slice(arguments, 1);
  return this.map(function(item) {
   return item[methodName].apply(item, args);
  });
 },

 every: function(fn, bind) {
  for (var i = 0, l = this.length; i < l; i++) {
   if ((i in this) && !fn.call(bind, this[i], i, this)) return false;
  }
  return true;
 },

 filter: function(fn, bind) {
  var results = [];
  for (var i = 0, l = this.length; i < l; i++) {
   if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]);
  }
  return results;
 },

 clean: function() {
  return this.filter(function(item) {
   return item != null;
  });
 },

 indexOf: function(item, from) {
  var len = this.length;
  for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++) {
   if (this[i] === item) return i;
  }
  return -1;
 },

 map: function(fn, bind) {
  var results = [];
  for (var i = 0, l = this.length; i < l; i++) {
   if (i in this) results[i] = fn.call(bind, this[i], i, this);
  }
  return results;
 },

 some: function(fn, bind) {
  for (var i = 0, l = this.length; i < l; i++) {
   if ((i in this) && fn.call(bind, this[i], i, this)) return true;
  }
  return false;
 },

 associate: function(keys) {
  var obj = {},
      length = Math.min(this.length, keys.length);
  for (var i = 0; i < length; i++) obj[keys[i]] = this[i];
  return obj;
 },

 link: function(object) {
  var result = {};
  for (var i = 0, l = this.length; i < l; i++) {
   for (var key in object) {
    if (object[key](this[i])) {
     result[key] = this[i];
     delete object[key];
     break;
    }
   }
  }
  return result;
 },

 contains: function(item, from) {
  return this.indexOf(item, from) != -1;
 },

 append: function(array) {
  this.push.apply(this, array);
  return this;
 },

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

 getRandom: function() {
  return (this.length) ? this[Number.random(0, this.length - 1)] : null;
 },

 include: function(item) {
  if (!this.contains(item)) this.push(item);
  return this;
 },

 combine: function(array) {
  for (var i = 0, l = array.length; i < l; i++) this.include(array[i]);
  return this;
 },

 erase: function(item) {
  for (var i = this.length; i--;) {
   if (this[i] === item) this.splice(i, 1);
  }
  return this;
 },

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

 flatten: function() {
  var array = [];
  for (var i = 0, l = this.length; i < l; i++) {
   var type = typeOf(this[i]);
   if (type == 'null') continue;
   array = array.concat((type == 'array' || type == 'collection' || type == 'arguments' || instanceOf(this[i], Array)) ? Array.flatten(this[i]) : this[i]);
  }
  return array;
 },

 pick: function() {
  for (var i = 0, l = this.length; i < l; i++) {
   if (this[i] != null) return this[i];
  }
  return null;
 },

 hexToRgb: function(array) {
  if (this.length != 3) return null;
  var rgb = this.map(function(value) {
   if (value.length == 1) value += value;
   return value.toInt(16);
  });
  return (array) ? rgb : 'rgb(' + rgb + ')';
 },

 rgbToHex: function(array) {
  if (this.length < 3) return null;
  if (this.length == 4 && this[3] == 0 && !array) return 'transparent';
  var hex = [];
  for (var i = 0; i < 3; i++) {
   var bit = (this[i] - 0).toString(16);
   hex.push((bit.length == 1) ? '0' + bit : bit);
  }
  return (array) ? hex : '#' + hex.join('');
 }

});

//<1.2compat>
Array.alias('extend', 'append');

var $pick = function() {
 return Array.from(arguments).pick();
};

//</1.2compat>
/*
---

name: String

description: Contains String Prototypes like camelCase, capitalize, test, and toInt.

license: MIT-style license.

requires: Type

provides: String

...
*/

String.implement({

 test: function(regex, params) {
  return ((typeOf(regex) == 'regexp') ? regex : new RegExp('' + regex, params)).test(this);
 },

 contains: function(string, separator) {
  return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1;
 },

 trim: function() {
  return this.replace(/^\s+|\s+$/g, '');
 },

 clean: function() {
  return this.replace(/\s+/g, ' ').trim();
 },

 camelCase: function() {
  return this.replace(/-\D/g, function(match) {
   return match.charAt(1).toUpperCase();
  });
 },

 hyphenate: function() {
  return this.replace(/[A-Z]/g, function(match) {
   return ('-' + match.charAt(0).toLowerCase());
  });
 },

 capitalize: function() {
  return this.replace(/\b[a-z]/g, function(match) {
   return match.toUpperCase();
  });
 },

 escapeRegExp: function() {
  return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
 },

 toInt: function(base) {
  return parseInt(this, base || 10);
 },

 toFloat: function() {
  return parseFloat(this);
 },

 hexToRgb: function(array) {
  var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);
  return (hex) ? hex.slice(1).hexToRgb(array) : null;
 },

 rgbToHex: function(array) {
  var rgb = this.match(/\d{1,3}/g);
  return (rgb) ? rgb.rgbToHex(array) : null;
 },

 substitute: function(object, regexp) {
  return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name) {
   if (match.charAt(0) == '\\') return match.slice(1);
   return (object[name] != null) ? object[name] : '';
  });
 }

});


/*
---

name: Number

description: Contains Number Prototypes like limit, round, times, and ceil.

license: MIT-style license.

requires: Type

provides: Number

...
*/

Number.implement({

 limit: function(min, max) {
  return Math.min(max, Math.max(min, this));
 },

 round: function(precision) {
  precision = Math.pow(10, precision || 0).toFixed(precision < 0 ? -precision : 0);
  return Math.round(this * precision) / precision;
 },

 times: function(fn, bind) {
  for (var i = 0; i < this; i++) fn.call(bind, i, this);
 },

 toFloat: function() {
  return parseFloat(this);
 },

 toInt: function(base) {
  return parseInt(this, base || 10);
 }

});

Number.alias('each', 'times');

(function(math) {
 var methods = {};
 math.each(function(name) {
  if (!Number[name]) methods[name] = function() {
   return Math[name].apply(null, [this].concat(Array.from(arguments)));
  };
 });
 Number.implement(methods);
})(['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'sin', 'sqrt', 'tan']);


/*
---

name: Function

description: Contains Function Prototypes like create, bind, pass, and delay.

license: MIT-style license.

requires: Type

provides: Function

...
*/

Function.extend({

 attempt: function() {
  for (var i = 0, l = arguments.length; i < l; i++) {
   try {
    return arguments[i]();
   } catch (e) {}
  }
  return null;
 }

});

Function.implement({

 attempt: function(args, bind) {
  try {
   return this.apply(bind, Array.from(args));
  } catch (e) {}

  return null;
 },

 bind: function(bind) {
  var self = this,
      args = (arguments.length > 1) ? Array.slice(arguments, 1) : null;

  return function() {
   if (!args && !arguments.length) return self.call(bind);
   if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments)));
   return self.apply(bind, args || arguments);
  };
 },

 pass: function(args, bind) {
  var self = this;
  if (args != null) args = Array.from(args);
  return function() {
   return self.apply(bind, args || arguments);
  };
 },

 delay: function(delay, bind, args) {
  return setTimeout(this.pass((args == null ? [] : args), bind), delay);
 },

 periodical: function(periodical, bind, args) {
  return setInterval(this.pass((args == null ? [] : args), bind), periodical);
 }

});

//<1.2compat>
delete Function.prototype.bind;

Function.implement({

 create: function(options) {
  var self = this;
  options = options || {};
  return function(event) {
   var args = options.arguments;
   args = (args != null) ? Array.from(args) : Array.slice(arguments, (options.event) ? 1 : 0);
   if (options.event) args = [event || window.event].extend(args);
   var returns = function() {
    return self.apply(options.bind || null, args);
   };
   if (options.delay) return setTimeout(returns, options.delay);
   if (options.periodical) return setInterval(returns, options.periodical);
   if (options.attempt) return Function.attempt(returns);
   return returns();
  };
 },

 bind: function(bind, args) {
  var self = this;
  if (args != null) args = Array.from(args);
  return function() {
   return self.apply(bind, args || arguments);
  };
 },

 bindWithEvent: function(bind, args) {
  var self = this;
  if (args != null) args = Array.from(args);
  return function(event) {
   return self.apply(bind, (args == null) ? arguments : [event].concat(args));
  };
 },

 run: function(args, bind) {
  return this.apply(bind, Array.from(args));
 }

});

var $try = Function.attempt;

//</1.2compat>
/*
---

name: Object

description: Object generic methods

license: MIT-style license.

requires: Type

provides: [Object, Hash]

...
*/

(function() {

 var hasOwnProperty = Object.prototype.hasOwnProperty;

 Object.extend({

  subset: function(object, keys) {
   var results = {};
   for (var i = 0, l = keys.length; i < l; i++) {
    var k = keys[i];
    results[k] = object[k];
   }
   return results;
  },

  map: function(object, fn, bind) {
   var results = {};
   for (var key in object) {
    if (hasOwnProperty.call(object, key)) results[key] = fn.call(bind, object[key], key, object);
   }
   return results;
  },

  filter: function(object, fn, bind) {
   var results = {};
   Object.each(object, function(value, key) {
    if (fn.call(bind, value, key, object)) results[key] = value;
   });
   return results;
  },

  every: function(object, fn, bind) {
   for (var key in object) {
    if (hasOwnProperty.call(object, key) && !fn.call(bind, object[key], key)) return false;
   }
   return true;
  },

  some: function(object, fn, bind) {
   for (var key in object) {
    if (hasOwnProperty.call(object, key) && fn.call(bind, object[key], key)) return true;
   }
   return false;
  },

  keys: function(object) {
   var keys = [];
   for (var key in object) {
    if (hasOwnProperty.call(object, key)) keys.push(key);
   }
   return keys;
  },

  values: function(object) {
   var values = [];
   for (var key in object) {
    if (hasOwnProperty.call(object, key)) values.push(object[key]);
   }
   return values;
  },

  getLength: function(object) {
   return Object.keys(object).length;
  },

  keyOf: function(object, value) {
   for (var key in object) {
    if (hasOwnProperty.call(object, key) && object[key] === value) return key;
   }
   return null;
  },

  contains: function(object, value) {
   return Object.keyOf(object, value) != null;
  },

  toQueryString: function(object, base) {
   var queryString = [];

   Object.each(object, function(value, key) {
    if (base) key = base + '[' + key + ']';
    var result;
    switch (typeOf(value)) {
    case 'object':
     result = Object.toQueryString(value, key);
     break;
    case 'array':
     var qs = {};
     value.each(function(val, i) {
      qs[i] = val;
     });
     result = Object.toQueryString(qs, key);
     break;
    default:
     result = key + '=' + encodeURIComponent(value);
    }
    if (value != null) queryString.push(result);
   });

   return queryString.join('&');
  }

 });

})();

//<1.2compat>
Hash.implement({

 has: Object.prototype.hasOwnProperty,

 keyOf: function(value) {
  return Object.keyOf(this, value);
 },

 hasValue: function(value) {
  return Object.contains(this, value);
 },

 extend: function(properties) {
  Hash.each(properties || {}, function(value, key) {
   Hash.set(this, key, value);
  }, this);
  return this;
 },

 combine: function(properties) {
  Hash.each(properties || {}, function(value, key) {
   Hash.include(this, key, value);
  }, this);
  return this;
 },

 erase: function(key) {
  if (this.hasOwnProperty(key)) delete this[key];
  return this;
 },

 get: function(key) {
  return (this.hasOwnProperty(key)) ? this[key] : null;
 },

 set: function(key, value) {
  if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
  return this;
 },

 empty: function() {
  Hash.each(this, function(value, key) {
   delete this[key];
  }, this);
  return this;
 },

 include: function(key, value) {
  if (this[key] == null) this[key] = value;
  return this;
 },

 map: function(fn, bind) {
  return new Hash(Object.map(this, fn, bind));
 },

 filter: function(fn, bind) {
  return new Hash(Object.filter(this, fn, bind));
 },

 every: function(fn, bind) {
  return Object.every(this, fn, bind);
 },

 some: function(fn, bind) {
  return Object.some(this, fn, bind);
 },

 getKeys: function() {
  return Object.keys(this);
 },

 getValues: function() {
  return Object.values(this);
 },

 toQueryString: function(base) {
  return Object.toQueryString(this, base);
 }

});

Hash.extend = Object.append;

Hash.alias({
 indexOf: 'keyOf',
 contains: 'hasValue'
});

//</1.2compat>
/*
---

name: Browser

description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.

license: MIT-style license.

requires: [Array, Function, Number, String]

provides: [Browser, Window, Document]

...
*/

(function() {

 var document = this.document;
 var window = document.window = this;

 var UID = 1;

 this.$uid = (window.ActiveXObject) ?
 function(item) {
  return (item.uid || (item.uid = [UID++]))[0];
 } : function(item) {
  return item.uid || (item.uid = UID++);
 };

 $uid(window);
 $uid(document);

 var ua = navigator.userAgent.toLowerCase(),
     platform = navigator.platform.toLowerCase(),
     UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
     mode = UA[1] == 'ie' && document.documentMode;

 var Browser = this.Browser = {

  extend: Function.prototype.extend,

  name: (UA[1] == 'version') ? UA[3] : UA[1],

  version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),

  Platform: {
   name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
  },

  Features: {
   xpath: !! (document.evaluate),
   air: !! (window.runtime),
   query: !! (document.querySelector),
   json: !! (window.JSON)
  },

  Plugins: {}

 };

 Browser[Browser.name] = true;
 Browser[Browser.name + parseInt(Browser.version, 10)] = true;
 Browser.Platform[Browser.Platform.name] = true;

 // Request
 Browser.Request = (function() {

  var XMLHTTP = function() {
   return new XMLHttpRequest();
  };

  var MSXML2 = function() {
   return new ActiveXObject('MSXML2.XMLHTTP');
  };

  var MSXML = function() {
   return new ActiveXObject('Microsoft.XMLHTTP');
  };

  return Function.attempt(function() {
   XMLHTTP();
   return XMLHTTP;
  }, function() {
   MSXML2();
   return MSXML2;
  }, function() {
   MSXML();
   return MSXML;
  });

 })();

 Browser.Features.xhr = !! (Browser.Request);

 // Flash detection
 var version = (Function.attempt(function() {
  return navigator.plugins['Shockwave Flash'].description;
 }, function() {
  return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
 }) || '0 r0').match(/\d+/g);

 Browser.Plugins.Flash = {
  version: Number(version[0] || '0.' + version[1]) || 0,
  build: Number(version[2]) || 0
 };

 // String scripts
 Browser.exec = function(text) {
  if (!text) return text;
  if (window.execScript) {
   window.execScript(text);
  } else {
   var script = document.createElement('script');
   script.setAttribute('type', 'text/javascript');
   script.text = text;
   document.head.appendChild(script);
   document.head.removeChild(script);
  }
  return text;
 };

 String.implement('stripScripts', function(exec) {
  var scripts = '';
  var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code) {
   scripts += code + '\n';
   return '';
  });
  if (exec === true) Browser.exec(scripts);
  else if (typeOf(exec) == 'function') exec(scripts, text);
  return text;
 });

 // Window, Document
 Browser.extend({
  Document: this.Document,
  Window: this.Window,
  Element: this.Element,
  Event: this.Event
 });

 this.Window = this.$constructor = new Type('Window', function() {});

 this.$family = Function.from('window').hide();

 Window.mirror(function(name, method) {
  window[name] = method;
 });

 this.Document = document.$constructor = new Type('Document', function() {});

 document.$family = Function.from('document').hide();

 Document.mirror(function(name, method) {
  document[name] = method;
 });

 document.html = document.documentElement;
 document.head = document.getElementsByTagName('head')[0];

 if (document.execCommand) try {
  document.execCommand("BackgroundImageCache", false, true);
 } catch (e) {}

 if (this.attachEvent && !this.addEventListener) {
  var unloadEvent = function() {
   this.detachEvent('onunload', unloadEvent);
   document.head = document.html = document.window = null;
  };
  this.attachEvent('onunload', unloadEvent);
 }

 // IE fails on collections and <select>.options (refers to <select>)
 var arrayFrom = Array.from;
 try {
  arrayFrom(document.html.childNodes);
 } catch (e) {
  Array.from = function(item) {
   if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array') {
    var i = item.length,
        array = new Array(i);
    while (i--) array[i] = item[i];
    return array;
   }
   return arrayFrom(item);
  };

  var prototype = Array.prototype,
      slice = prototype.slice;
  ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name) {
   var method = prototype[name];
   Array[name] = function(item) {
    return method.apply(Array.from(item), slice.call(arguments, 1));
   };
  });
 }

 //<1.2compat>
 if (Browser.Platform.ios) Browser.Platform.ipod = true;

 Browser.Engine = {};

 var setEngine = function(name, version) {
  Browser.Engine.name = name;
  Browser.Engine[name + version] = true;
  Browser.Engine.version = version;
 };

 if (Browser.ie) {
  Browser.Engine.trident = true;

  switch (Browser.version) {
  case 6:
   setEngine('trident', 4);
   break;
  case 7:
   setEngine('trident', 5);
   break;
  case 8:
   setEngine('trident', 6);
  }
 }

 if (Browser.firefox) {
  Browser.Engine.gecko = true;

  if (Browser.version >= 3) setEngine('gecko', 19);
  else setEngine('gecko', 18);
 }

 if (Browser.safari || Browser.chrome) {
  Browser.Engine.webkit = true;

  switch (Browser.version) {
  case 2:
   setEngine('webkit', 419);
   break;
  case 3:
   setEngine('webkit', 420);
   break;
  case 4:
   setEngine('webkit', 525);
  }
 }

 if (Browser.opera) {
  Browser.Engine.presto = true;

  if (Browser.version >= 9.6) setEngine('presto', 960);
  else if (Browser.version >= 9.5) setEngine('presto', 950);
  else setEngine('presto', 925);
 }

 if (Browser.name == 'unknown') {
  switch ((ua.match(/(?:webkit|khtml|gecko)/) || [])[0]) {
  case 'webkit':
  case 'khtml':
   Browser.Engine.webkit = true;
   break;
  case 'gecko':
   Browser.Engine.gecko = true;
  }
 }

 this.$exec = Browser.exec;

 //</1.2compat>
}).call(this);


/*
---

name: Event

description: Contains the Event Class, to make the event object cross-browser.

license: MIT-style license.

requires: [Window, Document, Array, Function, String, Object]

provides: Event

...
*/

var Event = new Type('Event', function(event, win) {
 if (!win) win = window;
 var doc = win.document;
 event = event || win.event;
 if (event.$extended) return event;
 this.$extended = true;
 var type = event.type,
     target = event.target || event.srcElement,
     page = {},
     client = {},
     related = null,
     rightClick, wheel, code, key;
 while (target && target.nodeType == 3) target = target.parentNode;

 if (type.indexOf('key') != -1) {
  code = event.which || event.keyCode;
  key = Object.keyOf(Event.Keys, code);
  if (type == 'keydown') {
   var fKey = code - 111;
   if (fKey > 0 && fKey < 13) key = 'f' + fKey;
  }
  if (!key) key = String.fromCharCode(code).toLowerCase();
 } else if ((/click|mouse|menu/i).test(type)) {
  doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body;
  page = {
   x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft,
   y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop
  };
  client = {
   x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX,
   y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY
  };
  if ((/DOMMouseScroll|mousewheel/).test(type)) {
   wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3;
  }
  rightClick = (event.which == 3) || (event.button == 2);
  if ((/over|out/).test(type)) {
   related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element'];
   var testRelated = function() {
    while (related && related.nodeType == 3) related = related.parentNode;
    return true;
   };
   var hasRelated = (Browser.firefox2) ? testRelated.attempt() : testRelated();
   related = (hasRelated) ? related : null;
  }
 } else if ((/gesture|touch/i).test(type)) {
  this.rotation = event.rotation;
  this.scale = event.scale;
  this.targetTouches = event.targetTouches;
  this.changedTouches = event.changedTouches;
  var touches = this.touches = event.touches;
  if (touches && touches[0]) {
   var touch = touches[0];
   page = {
    x: touch.pageX,
    y: touch.pageY
   };
   client = {
    x: touch.clientX,
    y: touch.clientY
   };
  }
 }

 return Object.append(this, {
  event: event,
  type: type,

  page: page,
  client: client,
  rightClick: rightClick,

  wheel: wheel,

  relatedTarget: document.id(related),
  target: document.id(target),

  code: code,
  key: key,

  shift: event.shiftKey,
  control: event.ctrlKey,
  alt: event.altKey,
  meta: event.metaKey
 });
});

Event.Keys = {
 'enter': 13,
 'up': 38,
 'down': 40,
 'left': 37,
 'right': 39,
 'esc': 27,
 'space': 32,
 'backspace': 8,
 'tab': 9,
 'delete': 46
};

//<1.2compat>
Event.Keys = new Hash(Event.Keys);

//</1.2compat>
Event.implement({

 stop: function() {
  return this.stopPropagation().preventDefault();
 },

 stopPropagation: function() {
  if (this.event.stopPropagation) this.event.stopPropagation();
  else this.event.cancelBubble = true;
  return this;
 },

 preventDefault: function() {
  if (this.event.preventDefault) this.event.preventDefault();
  else this.event.returnValue = false;
  return this;
 }

});


/*
---

name: Class

description: Contains the Class Function for easily creating, extending, and implementing reusable Classes.

license: MIT-style license.

requires: [Array, String, Function, Number]

provides: Class

...
*/

(function() {

 var Class = this.Class = new Type('Class', function(params) {
  if (instanceOf(params, Function)) params = {
   initialize: params
  };

  var newClass = function() {
   reset(this);
   if (newClass.$prototyping) return this;
   this.$caller = null;
   var value = (this.initialize) ? this.initialize.apply(this, arguments) : this;
   this.$caller = this.caller = null;
   return value;
  }.extend(this).implement(params);

  newClass.$constructor = Class;
  newClass.prototype.$constructor = newClass;
  newClass.prototype.parent = parent;

  return newClass;
 });

 var parent = function() {
  if (!this.$caller) throw new Error('The method "parent" cannot be called.');
  var name = this.$caller.$name,
      parent = this.$caller.$owner.parent,
      previous = (parent) ? parent.prototype[name] : null;
  if (!previous) throw new Error('The method "' + name + '" has no parent.');
  return previous.apply(this, arguments);
 };

 var reset = function(object) {
  for (var key in object) {
   var value = object[key];
   switch (typeOf(value)) {
   case 'object':
    var F = function() {};
    F.prototype = value;
    object[key] = reset(new F);
    break;
   case 'array':
    object[key] = value.clone();
    break;
   }
  }
  return object;
 };

 var wrap = function(self, key, method) {
  if (method.$origin) method = method.$origin;
  var wrapper = function() {
   if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.');
   var caller = this.caller,
       current = this.$caller;
   this.caller = current;
   this.$caller = wrapper;
   var result = method.apply(this, arguments);
   this.$caller = current;
   this.caller = caller;
   return result;
  }.extend({
   $owner: self,
   $origin: method,
   $name: key
  });
  return wrapper;
 };

 var implement = function(key, value, retain) {
  if (Class.Mutators.hasOwnProperty(key)) {
   value = Class.Mutators[key].call(this, value);
   if (value == null) return this;
  }

  if (typeOf(value) == 'function') {
   if (value.$hidden) return this;
   this.prototype[key] = (retain) ? value : wrap(this, key, value);
  } else {
   Object.merge(this.prototype, key, value);
  }

  return this;
 };

 var getInstance = function(klass) {
  klass.$prototyping = true;
  var proto = new klass;
  delete klass.$prototyping;
  return proto;
 };

 Class.implement('implement', implement.overloadSetter());

 Class.Mutators = {

  Extends: function(parent) {
   this.parent = parent;
   this.prototype = getInstance(parent);
  },

  Implements: function(items) {
   Array.from(items).each(function(item) {
    var instance = new item;
    for (var key in instance) implement.call(this, key, instance[key], true);
   }, this);
  }
 };

}).call(this);


/*
---

name: Class.Extras

description: Contains Utility Classes that can be implemented into your own Classes to ease the execution of many common tasks.

license: MIT-style license.

requires: Class

provides: [Class.Extras, Chain, Events, Options]

...
*/

(function() {

 this.Chain = new Class({

  $chain: [],

  chain: function() {
   this.$chain.append(Array.flatten(arguments));
   return this;
  },

  callChain: function() {
   return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
  },

  clearChain: function() {
   this.$chain.empty();
   return this;
  }

 });

 var removeOn = function(string) {
  return string.replace(/^on([A-Z])/, function(full, first) {
   return first.toLowerCase();
  });
 };

 this.Events = new Class({

  $events: {},

  addEvent: function(type, fn, internal) {
   type = removeOn(type);

   /*<1.2compat>*/
   if (fn == $empty) return this; /*</1.2compat>*/

   this.$events[type] = (this.$events[type] || []).include(fn);
   if (internal) fn.internal = true;
   return this;
  },

  addEvents: function(events) {
   for (var type in events) this.addEvent(type, events[type]);
   return this;
  },

  fireEvent: function(type, args, delay) {
   type = removeOn(type);
   var events = this.$events[type];
   if (!events) return this;
   args = Array.from(args);
   events.each(function(fn) {
    if (delay) fn.delay(delay, this, args);
    else fn.apply(this, args);
   }, this);
   return this;
  },

  removeEvent: function(type, fn) {
   type = removeOn(type);
   var events = this.$events[type];
   if (events && !fn.internal) {
    var index = events.indexOf(fn);
    if (index != -1) delete events[index];
   }
   return this;
  },

  removeEvents: function(events) {
   var type;
   if (typeOf(events) == 'object') {
    for (type in events) this.removeEvent(type, events[type]);
    return this;
   }
   if (events) events = removeOn(events);
   for (type in this.$events) {
    if (events && events != type) continue;
    var fns = this.$events[type];
    for (var i = fns.length; i--;) if (i in fns) {
     this.removeEvent(type, fns[i]);
    }
   }
   return this;
  }

 });

 this.Options = new Class({

  setOptions: function() {
   var options = this.options = Object.merge.apply(null, [{},
   this.options].append(arguments));
   if (this.addEvent) for (var option in options) {
    if (typeOf(options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
    this.addEvent(option, options[option]);
    delete options[option];
   }
   return this;
  }

 });

}).call(this);


/*
---
name: Slick.Parser
description: Standalone CSS3 Selector parser
provides: Slick.Parser
...
*/

;
(function() {

 var parsed, separatorIndex, combinatorIndex, reversed, cache = {},
     reverseCache = {},
     reUnescape = /\\/g;

 var parse = function(expression, isReversed) {
  if (expression == null) return null;
  if (expression.Slick === true) return expression;
  expression = ('' + expression).replace(/^\s+|\s+$/g, '');
  reversed = !! isReversed;
  var currentCache = (reversed) ? reverseCache : cache;
  if (currentCache[expression]) return currentCache[expression];
  parsed = {
   Slick: true,
   expressions: [],
   raw: expression,
   reverse: function() {
    return parse(this.raw, true);
   }
  };
  separatorIndex = -1;
  while (expression != (expression = expression.replace(regexp, parser)));
  parsed.length = parsed.expressions.length;
  return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed;
 };

 var reverseCombinator = function(combinator) {
  if (combinator === '!') return ' ';
  else if (combinator === ' ') return '!';
  else if ((/^!/).test(combinator)) return combinator.replace(/^!/, '');
  else return '!' + combinator;
 };

 var reverse = function(expression) {
  var expressions = expression.expressions;
  for (var i = 0; i < expressions.length; i++) {
   var exp = expressions[i];
   var last = {
    parts: [],
    tag: '*',
    combinator: reverseCombinator(exp[0].combinator)
   };

   for (var j = 0; j < exp.length; j++) {
    var cexp = exp[j];
    if (!cexp.reverseCombinator) cexp.reverseCombinator = ' ';
    cexp.combinator = cexp.reverseCombinator;
    delete cexp.reverseCombinator;
   }

   exp.reverse().push(last);
  }
  return expression;
 };

 var escapeRegExp = function(string) { // Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License
  return string.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, function(match) {
   return '\\' + match;
  });
 };

 var regexp = new RegExp(
/*
#!/usr/bin/env ruby
puts "\t\t" + DATA.read.gsub(/\(\?x\)|\s+#.*$|\s+|\\$|\\n/,'')
__END__
        "(?x)^(?:\
          \\s* ( , ) \\s*               # Separator          \n\
        | \\s* ( <combinator>+ ) \\s*   # Combinator         \n\
        |      ( \\s+ )                 # CombinatorChildren \n\
        |      ( <unicode>+ | \\* )     # Tag                \n\
        | \\#  ( <unicode>+       )     # ID                 \n\
        | \\.  ( <unicode>+       )     # ClassName          \n\
        |                               # Attribute          \n\
        \\[  \
                \\s* (<unicode1>+)  (?:  \
                        \\s* ([*^$!~|]?=)  (?:  \
                                \\s* (?:\
                                        ([\"']?)(.*?)\\9 \
                                )\
                        )  \
                )?  \\s*  \
        \\](?!\\]) \n\
        |   :+ ( <unicode>+ )(?:\
        \\( (?:\
                (?:([\"'])([^\\12]*)\\12)|((?:\\([^)]+\\)|[^()]*)+)\
        ) \\)\
        )?\
        )"
*/
 "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)".replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']').replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])').replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])'));

 function parser(
 rawMatch,

 separator, combinator, combinatorChildren,

 tagName, id, className,

 attributeKey, attributeOperator, attributeQuote, attributeValue,

 pseudoMarker, pseudoClass, pseudoQuote, pseudoClassQuotedValue, pseudoClassValue) {
  if (separator || separatorIndex === -1) {
   parsed.expressions[++separatorIndex] = [];
   combinatorIndex = -1;
   if (separator) return '';
  }

  if (combinator || combinatorChildren || combinatorIndex === -1) {
   combinator = combinator || ' ';
   var currentSeparator = parsed.expressions[separatorIndex];
   if (reversed && currentSeparator[combinatorIndex]) currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator);
   currentSeparator[++combinatorIndex] = {
    combinator: combinator,
    tag: '*'
   };
  }

  var currentParsed = parsed.expressions[separatorIndex][combinatorIndex];

  if (tagName) {
   currentParsed.tag = tagName.replace(reUnescape, '');

  } else if (id) {
   currentParsed.id = id.replace(reUnescape, '');

  } else if (className) {
   className = className.replace(reUnescape, '');

   if (!currentParsed.classList) currentParsed.classList = [];
   if (!currentParsed.classes) currentParsed.classes = [];
   currentParsed.classList.push(className);
   currentParsed.classes.push({
    value: className,
    regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)')
   });

  } else if (pseudoClass) {
   pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue;
   pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null;

   if (!currentParsed.pseudos) currentParsed.pseudos = [];
   currentParsed.pseudos.push({
    key: pseudoClass.replace(reUnescape, ''),
    value: pseudoClassValue,
    type: pseudoMarker.length == 1 ? 'class' : 'element'
   });

  } else if (attributeKey) {
   attributeKey = attributeKey.replace(reUnescape, '');
   attributeValue = (attributeValue || '').replace(reUnescape, '');

   var test, regexp;

   switch (attributeOperator) {
   case '^=':
    regexp = new RegExp('^' + escapeRegExp(attributeValue));
    break;
   case '$=':
    regexp = new RegExp(escapeRegExp(attributeValue) + '$');
    break;
   case '~=':
    regexp = new RegExp('(^|\\s)' + escapeRegExp(attributeValue) + '(\\s|$)');
    break;
   case '|=':
    regexp = new RegExp('^' + escapeRegExp(attributeValue) + '(-|$)');
    break;
   case '=':
    test = function(value) {
     return attributeValue == value;
    };
    break;
   case '*=':
    test = function(value) {
     return value && value.indexOf(attributeValue) > -1;
    };
    break;
   case '!=':
    test = function(value) {
     return attributeValue != value;
    };
    break;
   default:
    test = function(value) {
     return !!value;
    };
   }

   if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function() {
    return false;
   };

   if (!test) test = function(value) {
    return value && regexp.test(value);
   };

   if (!currentParsed.attributes) currentParsed.attributes = [];
   currentParsed.attributes.push({
    key: attributeKey,
    operator: attributeOperator,
    value: attributeValue,
    test: test
   });

  }

  return '';
 };

 // Slick NS
 var Slick = (this.Slick || {});

 Slick.parse = function(expression) {
  return parse(expression);
 };

 Slick.escapeRegExp = escapeRegExp;

 if (!this.Slick) this.Slick = Slick;

}).apply( /*<CommonJS>*/ (typeof exports != 'undefined') ? exports : /*</CommonJS>*/ this);


/*
---
name: Slick.Finder
description: The new, superfast css selector engine.
provides: Slick.Finder
requires: Slick.Parser
...
*/

;
(function() {

 var local = {},
     featuresCache = {},
     toString = Object.prototype.toString;

 // Feature / Bug detection
 local.isNativeCode = function(fn) {
  return (/\{\s*\[native code\]\s*\}/).test('' + fn);
 };

 local.isXML = function(document) {
  return ( !! document.xmlVersion) || ( !! document.xml) || (toString.call(document) == '[object XMLDocument]') || (document.nodeType == 9 && document.documentElement.nodeName != 'HTML');
 };

 local.setDocument = function(document) {

  // convert elements / window arguments to document. if document cannot be extrapolated, the function returns.
  var nodeType = document.nodeType;
  if (nodeType == 9); // document
  else if (nodeType) document = document.ownerDocument; // node
  else if (document.navigator) document = document.document; // window
  else return;

  // check if it's the old document
  if (this.document === document) return;
  this.document = document;

  // check if we have done feature detection on this document before
  var root = document.documentElement,
      rootUid = this.getUIDXML(root),
      features = featuresCache[rootUid],
      feature;

  if (features) {
   for (feature in features) {
    this[feature] = features[feature];
   }
   return;
  }

  features = featuresCache[rootUid] = {};

  features.root = root;
  features.isXMLDocument = this.isXML(document);

  features.brokenStarGEBTN = features.starSelectsClosedQSA = features.idGetsName = features.brokenMixedCaseQSA = features.brokenGEBCN = features.brokenCheckedQSA = features.brokenEmptyAttributeQSA = features.isHTMLDocument = features.nativeMatchesSelector = false;

  var starSelectsClosed, starSelectsComments, brokenSecondClassNameGEBCN, cachedGetElementsByClassName, brokenFormAttributeGetter;

  var selected, id = 'slick_uniqueid';
  var testNode = document.createElement('div');

  var testRoot = document.body || document.getElementsByTagName('body')[0] || root;
  testRoot.appendChild(testNode);

  // on non-HTML documents innerHTML and getElementsById doesnt work properly
  try {
   testNode.innerHTML = '<a id="' + id + '"></a>';
   features.isHTMLDocument = !! document.getElementById(id);
  } catch (e) {};

  if (features.isHTMLDocument) {

   testNode.style.display = 'none';

   // IE returns comment nodes for getElementsByTagName('*') for some documents
   testNode.appendChild(document.createComment(''));
   starSelectsComments = (testNode.getElementsByTagName('*').length > 1);

   // IE returns closed nodes (EG:"</foo>") for getElementsByTagName('*') for some documents
   try {
    testNode.innerHTML = 'foo</foo>';
    selected = testNode.getElementsByTagName('*');
    starSelectsClosed = (selected && !! selected.length && selected[0].nodeName.charAt(0) == '/');
   } catch (e) {};

   features.brokenStarGEBTN = starSelectsComments || starSelectsClosed;

   // IE returns elements with the name instead of just id for getElementsById for some documents
   try {
    testNode.innerHTML = '<a name="' + id + '"></a><b id="' + id + '"></b>';
    features.idGetsName = document.getElementById(id) === testNode.firstChild;
   } catch (e) {};

   if (testNode.getElementsByClassName) {

    // Safari 3.2 getElementsByClassName caches results
    try {
     testNode.innerHTML = '<a class="f"></a><a class="b"></a>';
     testNode.getElementsByClassName('b').length;
     testNode.firstChild.className = 'b';
     cachedGetElementsByClassName = (testNode.getElementsByClassName('b').length != 2);
    } catch (e) {};

    // Opera 9.6 getElementsByClassName doesnt detects the class if its not the first one
    try {
     testNode.innerHTML = '<a class="a"></a><a class="f b a"></a>';
     brokenSecondClassNameGEBCN = (testNode.getElementsByClassName('a').length != 2);
    } catch (e) {};

    features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN;
   }

   if (testNode.querySelectorAll) {
    // IE 8 returns closed nodes (EG:"</foo>") for querySelectorAll('*') for some documents
    try {
     testNode.innerHTML = 'foo</foo>';
     selected = testNode.querySelectorAll('*');
     features.starSelectsClosedQSA = (selected && !! selected.length && selected[0].nodeName.charAt(0) == '/');
    } catch (e) {};

    // Safari 3.2 querySelectorAll doesnt work with mixedcase on quirksmode
    try {
     testNode.innerHTML = '<a class="MiX"></a>';
     features.brokenMixedCaseQSA = !testNode.querySelectorAll('.MiX').length;
    } catch (e) {};

    // Webkit and Opera dont return selected options on querySelectorAll
    try {
     testNode.innerHTML = '<select><option selected="selected">a</option></select>';
     features.brokenCheckedQSA = (testNode.querySelectorAll(':checked').length == 0);
    } catch (e) {};

    // IE returns incorrect results for attr[*^$]="" selectors on querySelectorAll
    try {
     testNode.innerHTML = '<a class=""></a>';
     features.brokenEmptyAttributeQSA = (testNode.querySelectorAll('[class*=""]').length != 0);
    } catch (e) {};

   }

   // IE6-7, if a form has an input of id x, form.getAttribute(x) returns a reference to the input
   try {
    testNode.innerHTML = '<form action="s"><input id="action"/></form>';
    brokenFormAttributeGetter = (testNode.firstChild.getAttribute('action') != 's');
   } catch (e) {};

   // native matchesSelector function
   features.nativeMatchesSelector = root.matchesSelector || /*root.msMatchesSelector ||*/
   root.mozMatchesSelector || root.webkitMatchesSelector;
   if (features.nativeMatchesSelector) try {
    // if matchesSelector trows errors on incorrect sintaxes we can use it
    features.nativeMatchesSelector.call(root, ':slick');
    features.nativeMatchesSelector = null;
   } catch (e) {};

  }

  try {
   root.slick_expando = 1;
   delete root.slick_expando;
   features.getUID = this.getUIDHTML;
  } catch (e) {
   features.getUID = this.getUIDXML;
  }

  testRoot.removeChild(testNode);
  testNode = selected = testRoot = null;

  // getAttribute
  features.getAttribute = (features.isHTMLDocument && brokenFormAttributeGetter) ?
  function(node, name) {
   var method = this.attributeGetters[name];
   if (method) return method.call(node);
   var attributeNode = node.getAttributeNode(name);
   return (attributeNode) ? attributeNode.nodeValue : null;
  } : function(node, name) {
   var method = this.attributeGetters[name];
   return (method) ? method.call(node) : node.getAttribute(name);
  };

  // hasAttribute
  features.hasAttribute = (root && this.isNativeCode(root.hasAttribute)) ?
  function(node, attribute) {
   return node.hasAttribute(attribute);
  } : function(node, attribute) {
   node = node.getAttributeNode(attribute);
   return !!(node && (node.specified || node.nodeValue));
  };

  // contains
  // FIXME: Add specs: local.contains should be different for xml and html documents?
  features.contains = (root && this.isNativeCode(root.contains)) ?
  function(context, node) {
   return context.contains(node);
  } : (root && root.compareDocumentPosition) ?
  function(context, node) {
   return context === node || !! (context.compareDocumentPosition(node) & 16);
  } : function(context, node) {
   if (node) do {
    if (node === context) return true;
   } while ((node = node.parentNode));
   return false;
  };

  // document order sorting
  // credits to Sizzle (http://sizzlejs.com/)
  features.documentSorter = (root.compareDocumentPosition) ?
  function(a, b) {
   if (!a.compareDocumentPosition || !b.compareDocumentPosition) return 0;
   return a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
  } : ('sourceIndex' in root) ?
  function(a, b) {
   if (!a.sourceIndex || !b.sourceIndex) return 0;
   return a.sourceIndex - b.sourceIndex;
  } : (document.createRange) ?
  function(a, b) {
   if (!a.ownerDocument || !b.ownerDocument) return 0;
   var aRange = a.ownerDocument.createRange(),
       bRange = b.ownerDocument.createRange();
   aRange.setStart(a, 0);
   aRange.setEnd(a, 0);
   bRange.setStart(b, 0);
   bRange.setEnd(b, 0);
   return aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
  } : null;

  root = null;

  for (feature in features) {
   this[feature] = features[feature];
  }
 };

 // Main Method
 var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/,
     reEmptyAttribute = /\[.+[*$^]=(?:""|'')?\]/,
     qsaFailExpCache = {};

 local.search = function(context, expression, append, first) {

  var found = this.found = (first) ? null : (append || []);

  if (!context) return found;
  else if (context.navigator) context = context.document; // Convert the node from a window to a document
  else if (!context.nodeType) return found;

  // setup
  var parsed, i, uniques = this.uniques = {},
      hasOthers = !! (append && append.length),
      contextIsDocument = (context.nodeType == 9);

  if (this.document !== (contextIsDocument ? context : context.ownerDocument)) this.setDocument(context);

  // avoid duplicating items already in the append array
  if (hasOthers) for (i = found.length; i--;) uniques[this.getUID(found[i])] = true;

  // expression checks
  if (typeof expression == 'string') { // expression is a string
   /*<simple-selectors-override>*/
   var simpleSelector = expression.match(reSimpleSelector);
   simpleSelectors: if (simpleSelector) {

    var symbol = simpleSelector[1],
        name = simpleSelector[2],
        node, nodes;

    if (!symbol) {

     if (name == '*' && this.brokenStarGEBTN) break simpleSelectors;
     nodes = context.getElementsByTagName(name);
     if (first) return nodes[0] || null;
     for (i = 0; node = nodes[i++];) {
      if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
     }

    } else if (symbol == '#') {

     if (!this.isHTMLDocument || !contextIsDocument) break simpleSelectors;
     node = context.getElementById(name);
     if (!node) return found;
     if (this.idGetsName && node.getAttributeNode('id').nodeValue != name) break simpleSelectors;
     if (first) return node || null;
     if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);

    } else if (symbol == '.') {

     if (!this.isHTMLDocument || ((!context.getElementsByClassName || this.brokenGEBCN) && context.querySelectorAll)) break simpleSelectors;
     if (context.getElementsByClassName && !this.brokenGEBCN) {
      nodes = context.getElementsByClassName(name);
      if (first) return nodes[0] || null;
      for (i = 0; node = nodes[i++];) {
       if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
      }
     } else {
      var matchClass = new RegExp('(^|\\s)' + Slick.escapeRegExp(name) + '(\\s|$)');
      nodes = context.getElementsByTagName('*');
      for (i = 0; node = nodes[i++];) {
       className = node.className;
       if (!(className && matchClass.test(className))) continue;
       if (first) return node;
       if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
      }
     }

    }

    if (hasOthers) this.sort(found);
    return (first) ? null : found;

   } /*</simple-selectors-override>*/

   /*<query-selector-override>*/
   querySelector: if (context.querySelectorAll) {

    if (!this.isHTMLDocument || this.brokenMixedCaseQSA || qsaFailExpCache[expression] || (this.brokenCheckedQSA && expression.indexOf(':checked') > -1) || (this.brokenEmptyAttributeQSA && reEmptyAttribute.test(expression)) || Slick.disableQSA) break querySelector;

    var _expression = expression;
    if (!contextIsDocument) {
     // non-document rooted QSA
     // credits to Andrew Dupont
     var currentId = context.getAttribute('id'),
         slickid = 'slickid__';
     context.setAttribute('id', slickid);
     _expression = '#' + slickid + ' ' + _expression;
    }

    try {
     if (first) return context.querySelector(_expression) || null;
     else nodes = context.querySelectorAll(_expression);
    } catch (e) {
     qsaFailExpCache[expression] = 1;
     break querySelector;
    } finally {
     if (!contextIsDocument) {
      if (currentId) context.setAttribute('id', currentId);
      else context.removeAttribute('id');
     }
    }

    if (this.starSelectsClosedQSA) for (i = 0; node = nodes[i++];) {
     if (node.nodeName > '@' && !(hasOthers && uniques[this.getUID(node)])) found.push(node);
    } else for (i = 0; node = nodes[i++];) {
     if (!(hasOthers && uniques[this.getUID(node)])) found.push(node);
    }

    if (hasOthers) this.sort(found);
    return found;

   } /*</query-selector-override>*/

   parsed = this.Slick.parse(expression);
   if (!parsed.length) return found;
  } else if (expression == null) { // there is no expression
   return found;
  } else if (expression.Slick) { // expression is a parsed Slick object
   parsed = expression;
  } else if (this.contains(context.documentElement || context, expression)) { // expression is a node
   (found) ? found.push(expression) : found = expression;
   return found;
  } else { // other junk
   return found;
  }

  /*<pseudo-selectors>*/
  /*<nth-pseudo-selectors>*/

  // cache elements for the nth selectors
  this.posNTH = {};
  this.posNTHLast = {};
  this.posNTHType = {};
  this.posNTHTypeLast = {};

  /*</nth-pseudo-selectors>*/
  /*</pseudo-selectors>*/

  // if append is null and there is only a single selector with one expression use pushArray, else use pushUID
  this.push = (!hasOthers && (first || (parsed.length == 1 && parsed.expressions[0].length == 1))) ? this.pushArray : this.pushUID;

  if (found == null) found = [];

  // default engine
  var j, m, n;
  var combinator, tag, id, classList, classes, attributes, pseudos;
  var currentItems, currentExpression, currentBit, lastBit, expressions = parsed.expressions;

  search: for (i = 0;
  (currentExpression = expressions[i]); i++) for (j = 0;
  (currentBit = currentExpression[j]); j++) {

   combinator = 'combinator:' + currentBit.combinator;
   if (!this[combinator]) continue search;

   tag = (this.isXMLDocument) ? currentBit.tag : currentBit.tag.toUpperCase();
   id = currentBit.id;
   classList = currentBit.classList;
   classes = currentBit.classes;
   attributes = currentBit.attributes;
   pseudos = currentBit.pseudos;
   lastBit = (j === (currentExpression.length - 1));

   this.bitUniques = {};

   if (lastBit) {
    this.uniques = uniques;
    this.found = found;
   } else {
    this.uniques = {};
    this.found = [];
   }

   if (j === 0) {
    this[combinator](context, tag, id, classes, attributes, pseudos, classList);
    if (first && lastBit && found.length) break search;
   } else {
    if (first && lastBit) for (m = 0, n = currentItems.length; m < n; m++) {
     this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
     if (found.length) break search;
    } else for (m = 0, n = currentItems.length; m < n; m++) this[combinator](currentItems[m], tag, id, classes, attributes, pseudos, classList);
   }

   currentItems = this.found;
  }

  // should sort if there are nodes in append and if you pass multiple expressions.
  if (hasOthers || (parsed.expressions.length > 1)) this.sort(found);

  return (first) ? (found[0] || null) : found;
 };

 // Utils
 local.uidx = 1;
 local.uidk = 'slick-uniqueid';

 local.getUIDXML = function(node) {
  var uid = node.getAttribute(this.uidk);
  if (!uid) {
   uid = this.uidx++;
   node.se
ready
jQuery
/*!
 * jQuery JavaScript Library v1.5.2
 * http://jquery.com/
 *
 * Copyright 2011, John Resig
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * Includes Sizzle.js
 * http://sizzlejs.com/
 * Copyright 2011, The Dojo Foundation
 * Released under the MIT, BSD, and GPL Licenses.
 *
 * Date: Thu Mar 31 15:28:23 2011 -0400
 */
(function(window, undefined) {

 // Use the correct document accordingly with window argument (sandbox)
 var document = window.document;
 var jQuery = (function() {

  // Define a local copy of jQuery
  var jQuery = function(selector, context) {
   // The jQuery object is actually just the init constructor 'enhanced'
   return new jQuery.fn.init(selector, context, rootjQuery);
  },
      
      
      
      
      // Map over jQuery in case of overwrite
      _jQuery = window.jQuery,
      
      
      
      
      // Map over the $ in case of overwrite
      _$ = window.$,
      
      
      
      
      // A central reference to the root jQuery(document)
      rootjQuery,
      
      
      
      // A simple way to check for HTML strings or ID strings
       // (both of which we optimize for)
      quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,
      
      
      
      
      // Check if a string has a non-whitespace character in it
      rnotwhite = /\S/,
      
      
      
      
      // Used for trimming whitespace
      trimLeft = /^\s+/,
      trimRight = /\s+$/,
      
      
      
      
      // Check for digits
      rdigit = /\d/,
      
      
      
      
      // Match a standalone tag
      rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
      
      
      
      
      // JSON RegExp
      rvalidchars = /^[\],:{}\s]*$/,
      rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
      rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
      rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
      
      
      
      
      // Useragent RegExp
      rwebkit = /(webkit)[ \/]([\w.]+)/,
      ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
      rmsie = /(msie) ([\w.]+)/,
      rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
      
      
      
      
      // Keep a UserAgent string for use with jQuery.browser
      userAgent = navigator.userAgent,
      
      
      
      
      // For matching the engine and version of the browser
      browserMatch,
      
      
      
      // The deferred used on DOM ready
      readyList,
      
      
      
      // The ready event handler
      DOMContentLoaded,
      
      
      
      // Save a reference to some core methods
      toString = Object.prototype.toString,
      hasOwn = Object.prototype.hasOwnProperty,
      push = Array.prototype.push,
      slice = Array.prototype.slice,
      trim = String.prototype.trim,
      indexOf = Array.prototype.indexOf,
      
      
      
      
      // [[Class]] -> type pairs
      class2type = {};

  jQuery.fn = jQuery.prototype = {
   constructor: jQuery,
   init: function(selector, context, rootjQuery) {
    var match, elem, ret, doc;

    // Handle $(""), $(null), or $(undefined)
    if (!selector) {
     return this;
    }

    // Handle $(DOMElement)
    if (selector.nodeType) {
     this.context = this[0] = selector;
     this.length = 1;
     return this;
    }

    // The body element only exists once, optimize finding it
    if (selector === "body" && !context && document.body) {
     this.context = document;
     this[0] = document.body;
     this.selector = "body";
     this.length = 1;
     return this;
    }

    // Handle HTML strings
    if (typeof selector === "string") {
     // Are we dealing with HTML string or an ID?
     match = quickExpr.exec(selector);

     // Verify a match, and that no context was specified for #id
     if (match && (match[1] || !context)) {

      // HANDLE: $(html) -> $(array)
      if (match[1]) {
       context = context instanceof jQuery ? context[0] : context;
       doc = (context ? context.ownerDocument || context : document);

       // If a single string is passed in and it's a single tag
       // just do a createElement and skip the rest
       ret = rsingleTag.exec(selector);

       if (ret) {
        if (jQuery.isPlainObject(context)) {
         selector = [document.createElement(ret[1])];
         jQuery.fn.attr.call(selector, context, true);

        } else {
         selector = [doc.createElement(ret[1])];
        }

       } else {
        ret = jQuery.buildFragment([match[1]], [doc]);
        selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;
       }

       return jQuery.merge(this, selector);

       // HANDLE: $("#id")
      } else {
       elem = document.getElementById(match[2]);

       // Check parentNode to catch when Blackberry 4.6 returns
       // nodes that are no longer in the document #6963
       if (elem && elem.parentNode) {
        // Handle the case where IE and Opera return items
        // by name instead of ID
        if (elem.id !== match[2]) {
         return rootjQuery.find(selector);
        }

        // Otherwise, we inject the element directly into the jQuery object
        this.length = 1;
        this[0] = elem;
       }

       this.context = document;
       this.selector = selector;
       return this;
      }

      // HANDLE: $(expr, $(...))
     } else if (!context || context.jquery) {
      return (context || rootjQuery).find(selector);

      // HANDLE: $(expr, context)
      // (which is just equivalent to: $(context).find(expr)
     } else {
      return this.constructor(context).find(selector);
     }

     // HANDLE: $(function)
     // Shortcut for document ready
    } else if (jQuery.isFunction(selector)) {
     return rootjQuery.ready(selector);
    }

    if (selector.selector !== undefined) {
     this.selector = selector.selector;
     this.context = selector.context;
    }

    return jQuery.makeArray(selector, this);
   },

   // Start with an empty selector
   selector: "",

   // The current version of jQuery being used
   jquery: "1.5.2",

   // The default length of a jQuery object is 0
   length: 0,

   // The number of elements contained in the matched element set
   size: function() {
    return this.length;
   },

   toArray: function() {
    return slice.call(this, 0);
   },

   // Get the Nth element in the matched element set OR
   // Get the whole matched element set as a clean array
   get: function(num) {
    return num == null ?

    // Return a 'clean' array
    this.toArray() :

    // Return just the object
    (num < 0 ? this[this.length + num] : this[num]);
   },

   // Take an array of elements and push it onto the stack
   // (returning the new matched element set)
   pushStack: function(elems, name, selector) {
    // Build a new jQuery matched element set
    var ret = this.constructor();

    if (jQuery.isArray(elems)) {
     push.apply(ret, elems);

    } else {
     jQuery.merge(ret, elems);
    }

    // Add the old object onto the stack (as a reference)
    ret.prevObject = this;

    ret.context = this.context;

    if (name === "find") {
     ret.selector = this.selector + (this.selector ? " " : "") + selector;
    } else if (name) {
     ret.selector = this.selector + "." + name + "(" + selector + ")";
    }

    // Return the newly-formed element set
    return ret;
   },

   // Execute a callback for every element in the matched set.
   // (You can seed the arguments with an array of args, but this is
   // only used internally.)
   each: function(callback, args) {
    return jQuery.each(this, callback, args);
   },

   ready: function(fn) {
    // Attach the listeners
    jQuery.bindReady();

    // Add the callback
    readyList.done(fn);

    return this;
   },

   eq: function(i) {
    return i === -1 ? this.slice(i) : this.slice(i, +i + 1);
   },

   first: function() {
    return this.eq(0);
   },

   last: function() {
    return this.eq(-1);
   },

   slice: function() {
    return this.pushStack(slice.apply(this, arguments), "slice", slice.call(arguments).join(","));
   },

   map: function(callback) {
    return this.pushStack(jQuery.map(this, function(elem, i) {
     return callback.call(elem, i, elem);
    }));
   },

   end: function() {
    return this.prevObject || this.constructor(null);
   },

   // For internal use only.
   // Behaves like an Array's method, not like a jQuery method.
   push: push,
   sort: [].sort,
   splice: [].splice
  };

  // Give the init function the jQuery prototype for later instantiation
  jQuery.fn.init.prototype = jQuery.fn;

  jQuery.extend = jQuery.fn.extend = function() {
   var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},
       i = 1,
       length = arguments.length,
       deep = false;

   // Handle a deep copy situation
   if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
   }

   // Handle case when target is a string or something (possible in deep copy)
   if (typeof target !== "object" && !jQuery.isFunction(target)) {
    target = {};
   }

   // extend jQuery itself if only one argument is passed
   if (length === i) {
    target = this;
    --i;
   }

   for (; i < length; i++) {
    // Only deal with non-null/undefined values
    if ((options = arguments[i]) != null) {
     // Extend the base object
     for (name in options) {
      src = target[name];
      copy = options[name];

      // Prevent never-ending loop
      if (target === copy) {
       continue;
      }

      // Recurse if we're merging plain objects or arrays
      if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
       if (copyIsArray) {
        copyIsArray = false;
        clone = src && jQuery.isArray(src) ? src : [];

       } else {
        clone = src && jQuery.isPlainObject(src) ? src : {};
       }

       // Never move original objects, clone them
       target[name] = jQuery.extend(deep, clone, copy);

       // Don't bring in undefined values
      } else if (copy !== undefined) {
       target[name] = copy;
      }
     }
    }
   }

   // Return the modified object
   return target;
  };

  jQuery.extend({
   noConflict: function(deep) {
    window.$ = _$;

    if (deep) {
     window.jQuery = _jQuery;
    }

    return jQuery;
   },

   // Is the DOM ready to be used? Set to true once it occurs.
   isReady: false,

   // A counter to track how many items to wait for before
   // the ready event fires. See #6781
   readyWait: 1,

   // Handle when the DOM is ready
   ready: function(wait) {
    // A third-party is pushing the ready event forwards
    if (wait === true) {
     jQuery.readyWait--;
    }

    // Make sure that the DOM is not already loaded
    if (!jQuery.readyWait || (wait !== true && !jQuery.isReady)) {
     // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
     if (!document.body) {
      return setTimeout(jQuery.ready, 1);
     }

     // Remember that the DOM is ready
     jQuery.isReady = true;

     // If a normal DOM Ready event fired, decrement, and wait if need be
     if (wait !== true && --jQuery.readyWait > 0) {
      return;
     }

     // If there are functions bound, to execute
     readyList.resolveWith(document, [jQuery]);

     // Trigger any bound ready events
     if (jQuery.fn.trigger) {
      jQuery(document).trigger("ready").unbind("ready");
     }
    }
   },

   bindReady: function() {
    if (readyList) {
     return;
    }

    readyList = jQuery._Deferred();

    // Catch cases where $(document).ready() is called after the
    // browser event has already occurred.
    if (document.readyState === "complete") {
     // Handle it asynchronously to allow scripts the opportunity to delay ready
     return setTimeout(jQuery.ready, 1);
    }

    // Mozilla, Opera and webkit nightlies currently support this event
    if (document.addEventListener) {
     // Use the handy event callback
     document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);

     // A fallback to window.onload, that will always work
     window.addEventListener("load", jQuery.ready, false);

     // If IE event model is used
    } else if (document.attachEvent) {
     // ensure firing before onload,
     // maybe late but safe also for iframes
     document.attachEvent("onreadystatechange", DOMContentLoaded);

     // A fallback to window.onload, that will always work
     window.attachEvent("onload", jQuery.ready);

     // If IE and not a frame
     // continually check to see if the document is ready
     var toplevel = false;

     try {
      toplevel = window.frameElement == null;
     } catch (e) {}

     if (document.documentElement.doScroll && toplevel) {
      doScrollCheck();
     }
    }
   },

   // See test/unit/core.js for details concerning isFunction.
   // Since version 1.3, DOM methods and functions like alert
   // aren't supported. They return false on IE (#2968).
   isFunction: function(obj) {
    return jQuery.type(obj) === "function";
   },

   isArray: Array.isArray ||
   function(obj) {
    return jQuery.type(obj) === "array";
   },

   // A crude way of determining if an object is a window
   isWindow: function(obj) {
    return obj && typeof obj === "object" && "setInterval" in obj;
   },

   isNaN: function(obj) {
    return obj == null || !rdigit.test(obj) || isNaN(obj);
   },

   type: function(obj) {
    return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
   },

   isPlainObject: function(obj) {
    // Must be an Object.
    // Because of IE, we also have to check the presence of the constructor property.
    // Make sure that DOM nodes and window objects don't pass through, as well
    if (!obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow(obj)) {
     return false;
    }

    // Not own constructor property must be Object
    if (obj.constructor && !hasOwn.call(obj, "constructor") && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
     return false;
    }

    // Own properties are enumerated firstly, so to speed up,
    // if last one is own, then all properties are own.
    var key;
    for (key in obj) {}

    return key === undefined || hasOwn.call(obj, key);
   },

   isEmptyObject: function(obj) {
    for (var name in obj) {
     return false;
    }
    return true;
   },

   error: function(msg) {
    throw msg;
   },

   parseJSON: function(data) {
    if (typeof data !== "string" || !data) {
     return null;
    }

    // Make sure leading/trailing whitespace is removed (IE can't handle it)
    data = jQuery.trim(data);

    // Make sure the incoming data is actual JSON
    // Logic borrowed from http://json.org/json2.js
    if (rvalidchars.test(data.replace(rvalidescape, "@").replace(rvalidtokens, "]").replace(rvalidbraces, ""))) {

     // Try to use the native JSON parser first
     return window.JSON && window.JSON.parse ? window.JSON.parse(data) : (new Function("return " + data))();

    } else {
     jQuery.error("Invalid JSON: " + data);
    }
   },

   // Cross-browser xml parsing
   // (xml & tmp used internally)
   parseXML: function(data, xml, tmp) {

    if (window.DOMParser) { // Standard
     tmp = new DOMParser();
     xml = tmp.parseFromString(data, "text/xml");
    } else { // IE
     xml = new ActiveXObject("Microsoft.XMLDOM");
     xml.async = "false";
     xml.loadXML(data);
    }

    tmp = xml.documentElement;

    if (!tmp || !tmp.nodeName || tmp.nodeName === "parsererror") {
     jQuery.error("Invalid XML: " + data);
    }

    return xml;
   },

   noop: function() {},

   // Evalulates a script in a global context
   globalEval: function(data) {
    if (data && rnotwhite.test(data)) {
     // Inspired by code by Andrea Giammarchi
     // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
     var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement,
         script = document.createElement("script");

     if (jQuery.support.scriptEval()) {
      script.appendChild(document.createTextNode(data));
     } else {
      script.text = data;
     }

     // Use insertBefore instead of appendChild to circumvent an IE6 bug.
     // This arises when a base node is used (#2709).
     head.insertBefore(script, head.firstChild);
     head.removeChild(script);
    }
   },

   nodeName: function(elem, name) {
    return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
   },

   // args is for internal usage only
   each: function(object, callback, args) {
    var name, i = 0,
        length = object.length,
        isObj = length === undefined || jQuery.isFunction(object);

    if (args) {
     if (isObj) {
      for (name in object) {
       if (callback.apply(object[name], args) === false) {
        break;
       }
      }
     } else {
      for (; i < length;) {
       if (callback.apply(object[i++], args) === false) {
        break;
       }
      }
     }

     // A special, fast, case for the most common use of each
    } else {
     if (isObj) {
      for (name in object) {
       if (callback.call(object[name], name, object[name]) === false) {
        break;
       }
      }
     } else {
      for (var value = object[0];
      i < length && callback.call(value, i, value) !== false; value = object[++i]) {}
     }
    }

    return object;
   },

   // Use native String.trim function wherever possible
   trim: trim ?
   function(text) {
    return text == null ? "" : trim.call(text);
   } :

   // Otherwise use our own trimming functionality


   function(text) {
    return text == null ? "" : text.toString().replace(trimLeft, "").replace(trimRight, "");
   },

   // results is for internal usage only
   makeArray: function(array, results) {
    var ret = results || [];

    if (array != null) {
     // The window, strings (and functions) also have 'length'
     // The extra typeof function check is to prevent crashes
     // in Safari 2 (See: #3039)
     // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
     var type = jQuery.type(array);

     if (array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow(array)) {
      push.call(ret, array);
     } else {
      jQuery.merge(ret, array);
     }
    }

    return ret;
   },

   inArray: function(elem, array) {
    if (array.indexOf) {
     return array.indexOf(elem);
    }

    for (var i = 0, length = array.length; i < length; i++) {
     if (array[i] === elem) {
      return i;
     }
    }

    return -1;
   },

   merge: function(first, second) {
    var i = first.length,
        j = 0;

    if (typeof second.length === "number") {
     for (var l = second.length; j < l; j++) {
      first[i++] = second[j];
     }

    } else {
     while (second[j] !== undefined) {
      first[i++] = second[j++];
     }
    }

    first.length = i;

    return first;
   },

   grep: function(elems, callback, inv) {
    var ret = [],
        retVal;
    inv = !! inv;

    // Go through the array, only saving the items
    // that pass the validator function
    for (var i = 0, length = elems.length; i < length; i++) {
     retVal = !! callback(elems[i], i);
     if (inv !== retVal) {
      ret.push(elems[i]);
     }
    }

    return ret;
   },

   // arg is for internal usage only
   map: function(elems, callback, arg) {
    var ret = [],
        value;

    // Go through the array, translating each of the items to their
    // new value (or values).
    for (var i = 0, length = elems.length; i < length; i++) {
     value = callback(elems[i], i, arg);

     if (value != null) {
      ret[ret.length] = value;
     }
    }

    // Flatten any nested arrays
    return ret.concat.apply([], ret);
   },

   // A global GUID counter for objects
   guid: 1,

   proxy: function(fn, proxy, thisObject) {
    if (arguments.length === 2) {
     if (typeof proxy === "string") {
      thisObject = fn;
      fn = thisObject[proxy];
      proxy = undefined;

     } else if (proxy && !jQuery.isFunction(proxy)) {
      thisObject = proxy;
      proxy = undefined;
     }
    }

    if (!proxy && fn) {
     proxy = function() {
      return fn.apply(thisObject || this, arguments);
     };
    }

    // Set the guid of unique handler to the same of original handler, so it can be removed
    if (fn) {
     proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
    }

    // So proxy can be declared as an argument
    return proxy;
   },

   // Mutifunctional method to get and set values to a collection
   // The value/s can be optionally by executed if its a function
   access: function(elems, key, value, exec, fn, pass) {
    var length = elems.length;

    // Setting many attributes
    if (typeof key === "object") {
     for (var k in key) {
      jQuery.access(elems, k, key[k], exec, fn, value);
     }
     return elems;
    }

    // Setting one attribute
    if (value !== undefined) {
     // Optionally, function values get executed if exec is true
     exec = !pass && exec && jQuery.isFunction(value);

     for (var i = 0; i < length; i++) {
      fn(elems[i], key, exec ? value.call(elems[i], i, fn(elems[i], key)) : value, pass);
     }

     return elems;
    }

    // Getting an attribute
    return length ? fn(elems[0], key) : undefined;
   },

   now: function() {
    return (new Date()).getTime();
   },

   // Use of jQuery.browser is frowned upon.
   // More details: http://docs.jquery.com/Utilities/jQuery.browser
   uaMatch: function(ua) {
    ua = ua.toLowerCase();

    var match = rwebkit.exec(ua) || ropera.exec(ua) || rmsie.exec(ua) || ua.indexOf("compatible") < 0 && rmozilla.exec(ua) || [];

    return {
     browser: match[1] || "",
     version: match[2] || "0"
    };
   },

   sub: function() {
    function jQuerySubclass(selector, context) {
     return new jQuerySubclass.fn.init(selector, context);
    }
    jQuery.extend(true, jQuerySubclass, this);
    jQuerySubclass.superclass = this;
    jQuerySubclass.fn = jQuerySubclass.prototype = this();
    jQuerySubclass.fn.constructor = jQuerySubclass;
    jQuerySubclass.subclass = this.subclass;
    jQuerySubclass.fn.init = function init(selector, context) {
     if (context && context instanceof jQuery && !(context instanceof jQuerySubclass)) {
      context = jQuerySubclass(context);
     }

     return jQuery.fn.init.call(this, selector, context, rootjQuerySubclass);
    };
    jQuerySubclass.fn.init.prototype = jQuerySubclass.fn;
    var rootjQuerySubclass = jQuerySubclass(document);
    return jQuerySubclass;
   },

   browser: {}
  });

  // Populate the class2type map
  jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
   class2type["[object " + name + "]"] = name.toLowerCase();
  });

  browserMatch = jQuery.uaMatch(userAgent);
  if (browserMatch.browser) {
   jQuery.browser[browserMatch.browser] = true;
   jQuery.browser.version = browserMatch.version;
  }

  // Deprecated, use jQuery.browser.webkit instead
  if (jQuery.browser.webkit) {
   jQuery.browser.safari = true;
  }

  if (indexOf) {
   jQuery.inArray = function(elem, array) {
    return indexOf.call(array, elem);
   };
  }

  // IE doesn't match non-breaking spaces with \s
  if (rnotwhite.test("\xA0")) {
   trimLeft = /^[\s\xA0]+/;
   trimRight = /[\s\xA0]+$/;
  }

  // All jQuery objects should point back to these
  rootjQuery = jQuery(document);

  // Cleanup functions for the document ready method
  if (document.addEventListener) {
   DOMContentLoaded = function() {
    document.removeEventListener("DOMContentLoaded", DOMContentLoaded, false);
    jQuery.ready();
   };

  } else if (document.attachEvent) {
   DOMContentLoaded = function() {
    // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
    if (document.readyState === "complete") {
     document.detachEvent("onreadystatechange", DOMContentLoaded);
     jQuery.ready();
    }
   };
  }

  // The DOM ready check for Internet Explorer


  function doScrollCheck() {
   if (jQuery.isReady) {
    return;
   }

   try {
    // If IE is used, use the trick by Diego Perini
    // http://javascript.nwbox.com/IEContentLoaded/
    document.documentElement.doScroll("left");
   } catch (e) {
    setTimeout(doScrollCheck, 1);
    return;
   }

   // and execute any waiting functions
   jQuery.ready();
  }

  // Expose jQuery to the global object
  return jQuery;

 })();


 var // Promise methods
 promiseMethods = "then done fail isResolved isRejected promise".split(" "),
     
     
     // Static reference to slice
     sliceDeferred = [].slice;

 jQuery.extend({
  // Create a simple deferred (one callbacks list)
  _Deferred: function() {
   var // callbacks list
   callbacks = [],
       
       
       // stored [ context , args ]
       fired,
       
       // to avoid firing when already doing so
       firing,
       
       // flag to know if the deferred has been cancelled
       cancelled,
       
       // the deferred itself
       deferred = {

     // done( f1, f2, ...)
     done: function() {
      if (!cancelled) {
       var args = arguments,
           i, length, elem, type, _fired;
       if (fired) {
        _fired = fired;
        fired = 0;
       }
       for (i = 0, length = args.length; i < length; i++) {
        elem = args[i];
        type = jQuery.type(elem);
        if (type === "array") {
         deferred.done.apply(deferred, elem);
        } else if (type === "function") {
         callbacks.push(elem);
        }
       }
       if (_fired) {
        deferred.resolveWith(_fired[0], _fired[1]);
       }
      }
      return this;
     },

     // resolve with given context and args
     resolveWith: function(context, args) {
      if (!cancelled && !fired && !firing) {
       // make sure args are available (#8421)
       args = args || [];
       firing = 1;
       try {
        while (callbacks[0]) {
         callbacks.shift().apply(context, args);
        }
       }
       finally {
        fired = [context, args];
        firing = 0;
       }
      }
      return this;
     },

     // resolve with this as context and given arguments
     resolve: function() {
      deferred.resolveWith(this, arguments);
      return this;
     },

     // Has this deferred been resolved?
     isResolved: function() {
      return !!(firing || fired);
     },

     // Cancel
     cancel: function() {
      cancelled = 1;
      callbacks = [];
      return this;
     }
       };

   return deferred;
  },

  // Full fledged deferred (two callbacks list)
  Deferred: function(func) {
   var deferred = jQuery._Deferred(),
       failDeferred = jQuery._Deferred(),
       promise;
   // Add errorDeferred methods, then and promise
   jQuery.extend(deferred, {
    then: function(doneCallbacks, failCallbacks) {
     deferred.done(doneCallbacks).fail(failCallbacks);
     return this;
    },
    fail: failDeferred.done,
    rejectWith: failDeferred.resolveWith,
    reject: failDeferred.resolve,
    isRejected: failDeferred.isResolved,
    // Get a promise for this deferred
    // If obj is provided, the promise aspect is added to the object
    promise: function(obj) {
     if (obj == null) {
      if (promise) {
       return promise;
      }
      promise = obj = {};
     }
     var i = promiseMethods.length;
     while (i--) {
      obj[promiseMethods[i]] = deferred[promiseMethods[i]];
     }
     return obj;
    }
   });
   // Make sure only one callback list will be used
   deferred.done(failDeferred.cancel).fail(deferred.cancel);
   // Unexpose cancel
   delete deferred.cancel;
   // Call given func if any
   if (func) {
    func.call(deferred, deferred);
   }
   return deferred;
  },

  // Deferred helper
  when: function(firstParam) {
   var args = arguments,
       i = 0,
       length = args.length,
       count = length,
       deferred = length <= 1 && firstParam && jQuery.isFunction(firstParam.promise) ? firstParam : jQuery.Deferred();

   function resolveFunc(i) {
    return function(value) {
     args[i] = arguments.length > 1 ? sliceDeferred.call(arguments, 0) : value;
     if (!(--count)) {
      // Strange bug in FF4:
      // Values changed onto the arguments object sometimes end up as undefined values
      // outside the $.when method. Cloning the object into a fresh array solves the issue
      deferred.resolveWith(deferred, sliceDeferred.call(args, 0));
     }
    };
   }
   if (length > 1) {
    for (; i < length; i++) {
     if (args[i] && jQuery.isFunction(args[i].promise)) {
      args[i].promise().then(resolveFunc(i), deferred.reject);
     } else {
      --count;
     }
    }
    if (!count) {
     deferred.resolveWith(deferred, args);
    }
   } else if (deferred !== firstParam) {
    deferred.resolveWith(deferred, length ? [firstParam] : []);
   }
   return deferred.promise();
  }
 });




 (function() {

  jQuery.support = {};

  var div = document.createElement("div");

  div.style.display = "none";
  div.innerHTML = "   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";

  var all = div.getElementsByTagName("*"),
      a = div.getElementsByTagName("a")[0],
      select = document.createElement("select"),
      opt = select.appendChild(document.createElement("option")),
      input = div.getElementsByTagName("input")[0];

  // Can't get basic test support
  if (!all || !all.length || !a) {
   return;
  }

  jQuery.support = {
   // IE strips leading whitespace when .innerHTML is used
   leadingWhitespace: div.firstChild.nodeType === 3,

   // Make sure that tbody elements aren't automatically inserted
   // IE will insert them into empty tables
   tbody: !div.getElementsByTagName("tbody").length,

   // Make sure that link elements get serialized correctly by innerHTML
   // This requires a wrapper element in IE
   htmlSerialize: !! div.getElementsByTagName("link").length,

   // Get the style information from getAttribute
   // (IE uses .cssText insted)
   style: /red/.test(a.getAttribute("style")),

   // Make sure that URLs aren't manipulated
   // (IE normalizes it by default)
   hrefNormalized: a.getAttribute("href") === "/a",

   // Make sure that element opacity exists
   // (IE uses filter instead)
   // Use a regex to work around a WebKit issue. See #5145
   opacity: /^0.55$/.test(a.style.opacity),

   // Verify style float existence
   // (IE uses styleFloat instead of cssFloat)
   cssFloat: !! a.style.cssFloat,

   // Make sure that if no value is specified for a checkbox
   // that it defaults to "on".
   // (WebKit defaults to "" instead)
   checkOn: input.value === "on",

   // Make sure that a selected-by-default option has a working selected property.
   // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
   optSelected: opt.selected,

   // Will be defined later
   deleteExpando: true,
   optDisabled: false,
   checkClone: false,
   noCloneEvent: true,
   noCloneChecked: true,
   boxModel: null,
   inlineBlockNeedsLayout: false,
   shrinkWrapBlocks: false,
   reliableHiddenOffsets: true,
   reliableMarginRight: true
  };

  input.checked = true;
  jQuery.support.noCloneChecked = input.cloneNode(true).checked;

  // Make sure that the options inside disabled selects aren't marked as disabled
  // (WebKit marks them as diabled)
  select.disabled = true;
  jQuery.support.optDisabled = !opt.disabled;

  var _scriptEval = null;
  jQuery.support.scriptEval = function() {
   if (_scriptEval === null) {
    var root = document.documentElement,
        script = document.createElement("script"),
        id = "script" + jQuery.now();

    // Make sure that the execution of code works by injecting a script
    // tag with appendChild/createTextNode
    // (IE doesn't support this, fails, and uses .text instead)
    try {
     script.appendChild(document.createTextNode("window." + id + "=1;"));
    } catch (e) {}

    root.insertBefore(script, root.firstChild);

    if (window[id]) {
     _scriptEval = true;
     delete window[id];
    } else {
     _scriptEval = false;
    }

    root.removeChild(script);
   }

   return _scriptEval;
  };

  // Test to see if it's possible to delete an expando from an element
  // Fails in Internet Explorer
  try {
   delete div.test;

  } catch (e) {
   jQuery.support.deleteExpando = false;
  }

  if (!div.addEventListener && div.attachEvent && div.fireEvent) {
   div.attachEvent("onclick", function click() {
    // Cloning a node shouldn't copy over any
    // bound event handlers (IE does this)
    jQuery.support.noCloneEvent = false;
    div.detachEvent("onclick", click);
   });
   div.cloneNode(true).fireEvent("onclick");
  }

  div = document.createElement("div");
  div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";

  var fragment = document.createDocumentFragment();
  fragment.appendChild(div.firstChild);

  // WebKit doesn't clone checked state correctly in fragments
  jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;

  // Figure out if the W3C box model works as expected
  // document.body must exist before we can do this
  jQuery(function() {
   var div = document.createElement("div"),
       body = document.getElementsByTagName("body")[0];

   // Frameset documents with no body should not run this code
   if (!body) {
    return;
   }

   div.style.width = div.style.paddingLeft = "1px";
   body.appendChild(div);
   jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;

   if ("zoom" in div.style) {
    // Check if natively block-level elements act like inline-block
    // elements when setting their display to 'inline' and giving
    // them layout
    // (IE < 8 does this)
    div.style.display = "inline";
    div.style.zoom = 1;
    jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2;

    // Check if elements with layout shrink-wrap their children
    // (IE 6 does this)
    div.style.display = "";
    div.innerHTML = "<div style='width:4px;'></div>";
    jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2;
   }

   div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
   var tds = div.getElementsByTagName("td");

   // Check if table cells still have offsetWidth/Height when they are set
   // to display:none and there are still other visible table cells in a
   // table row; if so, offsetWidth/Height are not reliable for use when
   // determining if an element has been hidden directly using
   // display:none (it is still safe to use offsets if a parent element is
   // hidden; don safety goggles and see bug #4512 for more information).
   // (only IE 8 fails this test)
   jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0;

   tds[0].style.display = "";
   tds[1].style.display = "none";

   // Check if empty table cells still have offsetWidth/Height
   // (IE < 8 fail this test)
   jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0;
   div.innerHTML = "";

   // Check if div with explicit width and no margin-right incorrectly
   // gets computed margin-right based on width of container. For more
   // info see bug #3333
   // Fails in WebKit before Feb 2011 nightlies
   // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
   if (document.defaultView && document.defaultView.getComputedStyle) {
    div.style.width = "1px";
    div.style.marginRight = "0";
    jQuery.support.reliableMarginRight = (parseInt(document.defaultView.getComputedStyle(div, null).marginRight, 10) || 0) === 0;
   }

   body.removeChild(div).style.display = "none";
   div = tds = null;
  });

  // Technique from Juriy Zaytsev
  // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
  var eventSupported = function(eventName) {
   var el = document.createElement("div");
   eventName = "on" + eventName;

   // We only care about the case where non-standard event systems
   // are used, namely in IE. Short-circuiting here helps us to
   // avoid an eval call (in setAttribute) which can cause CSP
   // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
   if (!el.attachEvent) {
    return true;
   }

   var isSupported = (eventName in el);
   if (!isSupported) {
    el.setAttribute(eventName, "return;");
    isSupported = typeof el[eventName] === "function";
   }
   return isSupported;
  };

  jQuery.support.submitBubbles = eventSupported("submit");
  jQuery.support.changeBubbles = eventSupported("change");

  // release memory in IE
  div = all = a = null;
 })();



 var rbrace = /^(?:\{.*\}|\[.*\])$/;

 jQuery.extend({
  cache: {},

  // Please use with caution
  uuid: 0,

  // Unique for each copy of jQuery on the page
  // Non-digits removed to match rinlinejQuery
  expando: "jQuery" + (jQuery.fn.jquery + Math.random()).replace(/\D/g, ""),

  // The following elements throw uncatchable exceptions if you
  // attempt to add expando properties to them.
  noData: {
   "embed": true,
   // Ban all objects except for Flash (which handle expandos)
   "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
   "applet": true
  },

  hasData: function(elem) {
   elem = elem.nodeType ? jQuery.cache[elem[jQuery.expando]] : elem[jQuery.expando];

   return !!elem && !isEmptyDataObject(elem);
  },

  data: function(elem, name, data, pvt /* Internal Use Only */ ) {
   if (!jQuery.acceptData(elem)) {
    return;
   }

   var internalKey = jQuery.expando,
       getByName = typeof name === "string",
       thisCache,
       
       
       
       // We have to handle DOM nodes and JS objects differently because IE6-7
        // can't GC object references properly across the DOM-JS boundary
       isNode = elem.nodeType,
       
       
       
       
       // Only DOM nodes need the global jQuery cache; JS object data is
        // attached directly to the object so GC can occur automatically
       cache = isNode ? jQuery.cache : elem,
       
       
       
       
       // Only defining an ID for JS objects if its cache already exists allows
        // the code to shortcut on the same path as a DOM node with no cache
       id = isNode ? elem[jQuery.expando] : elem[jQuery.expando] && jQuery.expando;

   // Avoid doing any more work than we need to when trying to get data on an
   // object that has no data at all
   if ((!id || (pvt && id && !cache[id][internalKey])) && getByName && data === undefined) {
    return;
   }

   if (!id) {
    // Only DOM nodes need a new unique ID for each element since their data
    // ends up in the global cache
    if (isNode) {
     elem[jQuery.expando] = id = ++jQuery.uuid;
    } else {
     id = jQuery.expando;
    }
   }

   if (!cache[id]) {
    cache[id] = {};

    // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
    // metadata on plain JS objects when the object is serialized using
    // JSON.stringify
    if (!isNode) {
     cache[id].toJSON = jQuery.noop;
    }
   }

   // An object can be passed to jQuery.data instead of a key/value pair; this gets
   // shallow copied over onto the existing cache
   if (typeof name === "object" || typeof name === "function") {
    if (pvt) {
     cache[id][internalKey] = jQuery.extend(cache[id][internalKey], name);
    } else {
     cache[id] = jQuery.extend(cache[id], name);
    }
   }

   thisCache = cache[id];

   // Internal jQuery data is stored in a separate object inside the object's data
   // cache in order to avoid key collisions between internal data and user-defined
   // data
   if (pvt) {
    if (!thisCache[internalKey]) {
     thisCache[internalKey] = {};
    }

    thisCache = thisCache[internalKey];
   }

   if (data !== undefined) {
    thisCache[name] = data;
   }

   // TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
   // not attempt to inspect the internal events object using jQuery.data, as this
   // internal data object is undocumented and subject to change.
   if (name === "events" && !thisCache[name]) {
    return thisCache[internalKey] && thisCache[internalKey].events;
   }

   return getByName ? thisCache[name] : thisCache;
  },

  removeData: function(elem, name, pvt /* Internal Use Only */ ) {
   if (!jQuery.acceptData(elem)) {
    return;
   }

   var internalKey = jQuery.expando,
       isNode = elem.nodeType,
       
       
       
       
       // See jQuery.data for more information
       cache = isNode ? jQuery.cache : elem,
       
       
       
       
       // See jQuery.data for more information
       id = isNode ? elem[jQuery.expando] : jQuery.expando;

   // If there is already no cache entry for this object, there is no
   // purpose in continuing
   if (!cache[id]) {
    return;
   }

   if (name) {
    var thisCache = pvt ? cache[id][internalKey] : cache[id];

    if (thisCache) {
     delete thisCache[name];

     // If there is no data left in the cache, we want to continue
     // and let the cache object itself get destroyed
     if (!isEmptyDataObject(thisCache)) {
      return;
     }
    }
   }

   // See jQuery.data for more information
   if (pvt) {
    delete cache[id][internalKey];

    // Don't destroy the parent cache unless the internal data object
    // had been the only thing left in it
    if (!isEmptyDataObject(cache[id])) {
     return;
    }
   }

   var internalCache = cache[id][internalKey];

   // Browsers that fail expando deletion also refuse to delete expandos on
   // the window, but it will allow it on all other JS objects; other browsers
   // don't care
   if (jQuery.support.deleteExpando || cache != window) {
    delete cache[id];
   } else {
    cache[id] = null;
   }

   // We destroyed the entire user cache at once because it's faster than
   // iterating through each key, but we need to continue to persist internal
   // data if it existed
   if (internalCache) {
    cache[id] = {};
    // TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery
    // metadata on plain JS objects when the object is serialized using
    // JSON.stringify
    if (!isNode) {
     cache[id].toJSON = jQuery.noop;
    }

    cache[id][internalKey] = internalCache;

    // Otherwise, we need to eliminate the expando on the node to avoid
    // false lookups in the cache for entries that no longer exist
   } else if (isNode) {
    // IE does not allow us to delete expando properties from nodes,
    // nor does it have a removeAttribute function on Document nodes;
    // we must handle all of these cases
    if (jQuery.support.deleteExpando) {
     delete elem[jQuery.expando];
    } else if (elem.removeAttribute) {
     elem.removeAttribute(jQuery.expando);
    } else {
     elem[jQuery.expando] = null;
    }
   }
  },

  // For internal use only.
  _data: function(elem, name, data) {
   return jQuery.data(elem, name, data, true);
  },

  // A method for determining if a DOM node can handle the data expando
  acceptData: function(elem) {
   if (elem.nodeName) {
    var match = jQuery.noData[elem.nodeName.toLowerCase()];

    if (match) {
     return !(match === true || elem.getAttribute("classid") !== match);
    }
   }

   return true;
  }
 });

 jQuery.fn.extend({
  data: function(key, value) {
   var data = null;

   if (typeof key === "undefined") {
    if (this.length) {
     data = jQuery.data(this[0]);

     if (this[0].nodeType === 1) {
      var attr = this[0].attributes,
          name;
      for (var i = 0, l = attr.length; i < l; i++) {
       name = attr[i].name;

       if (name.indexOf("data-") === 0) {
        name = name.substr(5);
        dataAttr(this[0], name, data[name]);
       }
      }
     }
    }

    return data;

   } else if (typeof key === "object") {
    return this.each(function() {
     jQuery.data(this, key);
    });
   }

   var parts = key.split(".");
   parts[1] = parts[1] ? "." + parts[1] : "";

   if (value === undefined) {
    data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

    // Try to fetch any internally stored data first
    if (data === undefined && this.length) {
     data = jQuery.data(this[0], key);
     data = dataAttr(this[0], key, data);
    }

    return data === undefined && parts[1] ? this.data(parts[0]) : data;

   } else {
    return this.each(function() {
     var $this = jQuery(this),
         args = [parts[0], value];

     $this.triggerHandler("setData" + parts[1] + "!", args);
     jQuery.data(this, key, value);
     $this.triggerHandler("changeData" + parts[1] + "!", args);
    });
   }
  },

  removeData: function(key) {
   return this.each(function() {
    jQuery.removeData(this, key);
   });
  }
 });

 function dataAttr(elem, key, data) {
  // If nothing was found internally, try to fetch any
  // data from the HTML5 data-* attribute
  if (data === undefined && elem.nodeType === 1) {
   data = elem.getAttribute("data-" + key);

   if (typeof data === "string") {
    try {
     data = data === "true" ? true : data === "false" ? false : data === "null" ? null : !jQuery.isNaN(data) ? parseFloat(data) : rbrace.test(data) ? jQuery.parseJSON(data) : data;
    } catch (e) {}

    // Make sure we set the data so it isn't changed later
    jQuery.data(elem, key, data);

   } else {
    data = undefined;
   }
  }

  return data;
 }

 // TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON
 // property to be considered empty objects; this property always exists in
 // order to make sure JSON.stringify does not expose internal metadata


 function isEmptyDataObject(obj) {
  for (var name in obj) {
   if (name !== "toJSON") {
    return false;
   }
  }

  return true;
 }




 jQuery.extend({
  queue: function(elem, type, data) {
   if (!elem) {
    return;
   }

   type = (type || "fx") + "queue";
   var q = jQuery._data(elem, type);

   // Speed up dequeue by getting out quickly if this is just a lookup
   if (!data) {
    return q || [];
   }

   if (!q || jQuery.isArray(data)) {
    q = jQuery._data(elem, type, jQuery.makeArray(data));

   } else {
    q.push(data);
   }

   return q;
  },

  dequeue: function(elem, type) {
   type = type || "fx";

   var queue = jQuery.queue(elem, type),
       fn = queue.shift();

   // If the fx queue is dequeued, always remove the progress sentinel
   if (fn === "inprogress") {
    fn = queue.shift();
   }

   if (fn) {
    // Add a progress sentinel to prevent the fx queue from being
    // automatically dequeued
    if (type === "fx") {
     queue.unshift("inprogress");
    }

    fn.call(elem, function() {
     jQuery.dequeue(elem, type);
    });
   }

   if (!queue.length) {
    jQuery.removeData(elem, type + "queue", true);
   }
  }
 });

 jQuery.fn.extend({
  queue: function(type, data) {
   if (typeof type !== "string") {
    data = type;
    type = "fx";
   }

   if (data === undefined) {
    return jQuery.queue(this[0], type);
   }
   return this.each(function(i) {
    var queue = jQuery.queue(this, type, data);

    if (type === "fx" && queue[0] !== "inprogress") {
     jQuery.dequeue(this, type);
    }
   });
  },
  dequeue: function(type) {
   return this.each(function() {
    jQuery.dequeue(this, type);
   });
  },

  // Based off of the plugin by Clint Helfers, with permission.
  // http://blindsignals.com/index.php/2009/07/jquery-delay/
  delay: function(time, type) {
   time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
   type = type || "fx";

   return this.queue(type, function() {
    var elem = this;
    setTimeout(function() {
     jQuery.dequeue(elem, type);
    }, time);
   });
  },

  clearQueue: function(type) {
   return this.queue(type || "fx", []);
  }
 });




 var rclass = /[\n\t\r]/g,
     rspaces = /\s+/,
     rreturn = /\r/g,
     rspecialurl = /^(?:href|src|style)$/,
     rtype = /^(?:button|input)$/i,
     rfocusable = /^(?:button|input|object|select|textarea)$/i,
     rclickable = /^a(?:rea)?$/i,
     rradiocheck = /^(?:radio|checkbox)$/i;

 jQuery.props = {
  "for": "htmlFor",
  "class": "className",
  readonly: "readOnly",
  maxlength: "maxLength",
  cellspacing: "cellSpacing",
  rowspan: "rowSpan",
  colspan: "colSpan",
  tabindex: "tabIndex",
  usemap: "useMap",
  frameborder: "frameBorder"
 };

 jQuery.fn.extend({
  attr: function(name, value) {
   return jQuery.access(this, name, value, true, jQuery.attr);
  },

  removeAttr: function(name, fn) {
   return this.each(function() {
    jQuery.attr(this, name, "");
    if (this.nodeType === 1) {
     this.removeAttribute(name);
    }
   });
  },

  addClass: function(value) {
   if (jQuery.isFunction(value)) {
    return this.each(function(i) {
     var self = jQuery(this);
     self.addClass(value.call(this, i, self.attr("class")));
    });
   }

   if (value && typeof value === "string") {
    var classNames = (value || "").split(rspaces);

    for (var i = 0, l = this.length; i < l; i++) {
     var elem = this[i];

     if (elem.nodeType === 1) {
      if (!elem.className) {
       elem.className = value;

      } else {
       var className = " " + elem.className + " ",
           setClass = elem.className;

       for (var c = 0, cl = classNames.length; c < cl; c++) {
        if (className.indexOf(" " + classNames[c] + " ") < 0) {
         setClass += " " + classNames[c];
        }
       }
       elem.className = jQuery.trim(setClass);
      }
     }
    }
   }

   return this;
  },

  removeClass: function(value) {
   if (jQuery.isFunction(value)) {
    return this.each(function(i) {
     var self = jQuery(this);
     self.removeClass(value.call(this, i, self.attr("class")));
    });
   }

   if ((value && typeof value === "string") || value === undefined) {
    var classNames = (value || "").split(rspaces);

    for (var i = 0, l = this.length; i < l; i++) {
     var elem = this[i];

     if (elem.nodeType === 1 && elem.className) {
      if (value) {
       var className = (" " + elem.className + " ").replace(rclass, " ");
       for (var c = 0, cl = classNames.length; c < cl; c++) {
        className = className.replace(" " + classNames[c] + " ", " ");
       }
       elem.className = jQuery.trim(className);

      } else {
       elem.className = "";
      }
     }
    }
   }

   return this;
  },

  toggleClass: function(value, stateVal) {
   var type = typeof value,
       isBool = typeof stateVal === "boolean";

   if (jQuery.isFunction(value)) {
    return this.each(function(i) {
     var self = jQuery(this);
     self.toggleClass(value.call(this, i, self.attr("class"), stateVal), stateVal);
    });
   }

   return this.each(function() {
    if (type === "string") {
     // toggle individual class names
     var className, i = 0,
         self = jQuery(this),
         state = stateVal,
         classNames = value.split(rspaces);

     while ((className = classNames[i++])) {
      // check each className given, space seperated list
      state = isBool ? state : !self.hasClass(className);
      self[state ? "addClass" : "removeClass"](className);
     }

    } else if (type === "undefined" || type === "boolean") {
     if (this.className) {
      // store className if set
      jQuery._data(this, "__className__", this.className);
     }

     // toggle whole className
     this.className = this.className || value === false ? "" : jQuery._data(this, "__className__") || "";
    }
   });
  },

  hasClass: function(selector) {
   var className = " " + selector + " ";
   for (var i = 0, l = this.length; i < l; i++) {
    if ((" " + this[i].className + " ").replace(rclass, " ").indexOf(className) > -1) {
     return true;
    }
   }

   return false;
  },

  val: function(value) {
   if (!arguments.length) {
    var elem = this[0];

    if (elem) {
     if (jQuery.nodeName(elem, "option")) {
      // attributes.value is undefined in Blackberry 4.7 but
      // uses .value. See #6932
      var val = elem.attributes.value;
      return !val || val.specified ? elem.value : elem.text;
     }

     // We need to handle select boxes special
     if (jQuery.nodeName(elem, "select")) {
      var index = elem.selectedIndex,
          values = [],
          options = elem.options,
          one = elem.type === "select-one";

      // Nothing was selected
      if (index < 0) {
       return null;
      }

      // Loop through all the selected options
      for (var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++) {
       var option = options[i];

       // Don't return options that are disabled or in a disabled optgroup
       if (option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && (!option.parentNode.disabled || !jQuery.nodeName(option.parentNode, "optgroup"))) {

        // Get the specific value for the option
        value = jQuery(option).val();

        // We don't need an array for one selects
        if (one) {
         return value;
        }

        // Multi-Selects return an array
        values.push(value);
       }
      }

      // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
      if (one && !values.length && options.length) {
       return jQuery(options[index]).val();
      }

      return values;
     }

     // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
     if (rradiocheck.test(elem.type) && !jQuery.support.checkOn) {
      return elem.getAttribute("value") === null ? "on" : elem.value;
     }

     // Everything else, we just grab the value
     return (elem.value || "").replace(rreturn, "");

    }

    return undefined;
   }

   var isFunction = jQuery.isFunction(value);

   return this.each(function(i) {
    var self = jQuery(this),
        val = value;

    if (this.nodeType !== 1) {
     return;
    }

    if (isFunction) {
     val = value.call(this, i, self.val());
    }

    // Treat null/undefined as ""; convert numbers to string
    if (val == null) {
     val = "";
    } else if (typeof val === "number") {
     val += "";
    } else if (jQuery.isArray(val)) {
     val = jQuery.map(val, function(value) {
      return value == null ? "" : value + "";
     });
    }

    if (jQuery.isArray(val) && rradiocheck.test(this.type)) {
     this.checked = jQuery.inArray(self.val(), val) >= 0;

    } else if (jQuery.nodeName(this, "select")) {
     var values = jQuery.makeArray(val);

     jQuery("option", this).each(function() {
      this.selected = jQuery.inArray(jQuery(this).val(), values) >= 0;
     });

     if (!values.length) {
      this.selectedIndex = -1;
     }

    } else {
     this.value = val;
    }
   });
  }
 });

 jQuery.extend({
  attrFn: {
   val: true,
   css: true,
   html: true,
   text: true,
   data: true,
   width: true,
   height: true,
   offset: true
  },

  attr: function(elem, name, value, pass) {
   // don't get/set attributes on text, comment and attribute nodes
   if (!elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2) {
    return undefined;
   }

   if (pass && name in jQuery.attrFn) {
    return jQuery(elem)[name](value);
   }

   var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc(elem),
       
       
       // Whether we are setting (or getting)
       set = value !== undefined;

   // Try to normalize/fix the name
   name = notxml && jQuery.props[name] || name;

   // Only do all the following if this is a node (faster for style)
   if (elem.nodeType === 1) {
    // These attributes require special treatment
    var special = rspecialurl.test(name);

    // Safari mis-reports the default selected property of an option
    // Accessing the parent's selectedIndex property fixes it
    if (name === "selected" && !jQuery.support.optSelected) {
     var parent = elem.parentNode;
     if (parent) {
      parent.selectedIndex;

      // Make sure that it also works with optgroups, see #5701
      if (parent.parentNode) {
       parent.parentNode.selectedIndex;
      }
     }
    }

    // If applicable, access the attribute via the DOM 0 way
    // 'in' checks fail in Blackberry 4.7 #6931
    if ((name in elem || elem[name] !== undefined) && notxml && !special) {
     if (set) {
      // We can't allow the type property to be changed (since it causes problems in IE)
      if (name === "type" && rtype.test(elem.nodeName) && elem.parentNode) {
       jQuery.error("type property can't be changed");
      }

      if (value === null) {
       if (elem.nodeType === 1) {
        elem.removeAttribute(name);
       }

      } else {
       elem[name] = value;
      }
     }

     // browsers index elements by id/name on forms, give priority to attributes.
     if (jQuery.nodeName(elem, "form") && elem.getAttributeNode(name)) {
      return elem.getAttributeNode(name).nodeValue;
     }

     // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
     // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
     if (name === "tabIndex") {
      var attributeNode = elem.getAttributeNode("tabIndex");

      return attributeNode && attributeNode.specified ? attributeNode.value : rfocusable.test(elem.nodeName) || rclickable.test(elem.nodeName) && elem.href ? 0 : undefined;
     }

     return elem[name];
    }

    if (!jQuery.support.style && notxml && name === "style") {
     if (set) {
      elem.style.cssText = "" + value;
     }

     return elem.style.cssText;
    }

    if (set) {
     // convert the value to a string (all browsers do this but IE) see #1070
     elem.setAttribute(name, "" + value);
    }

    // Ensure that missing attributes return undefined
    // Blackberry 4.7 returns "" from getAttribute #6938
    if (!elem.attributes[name] && (elem.hasAttribute && !elem.hasAttribute(name))) {
     return undefined;
    }

    var attr = !jQuery.support.hrefNormalized && notxml && special ?
    // Some attributes require a special call on IE
    elem.getAttribute(name, 2) : elem.getAttribute(name);

    // Non-existent attributes return null, we normalize to undefined
    return attr === null ? undefined : attr;
   }
   // Handle everything which isn't a DOM element node
   if (set) {
    elem[name] = value;
   }
   return elem[name];
  }
 });




 var rnamespaces = /\.(.*)$/,
     rformElems = /^(?:textarea|input|select)$/i,
     rperiod = /\./g,
     rspace = / /g,
     rescape = /[^\w\s.|`]/g,
     fcleanup = function(nm) {
   return nm.replace(rescape, "\\$&");
     };

/*
 * A number of helper functions used for managing events.
 * Many of the ideas behind this code originated from
 * Dean Edwards' addEvent library.
 */
 jQuery.event = {

  // Bind an event to an element
  // Original by Dean Edwards
  add: function(elem, types, handler, data) {
   if (elem.nodeType === 3 || elem.nodeType === 8) {
    return;
   }

   // TODO :: Use a try/catch until it's safe to pull this out (likely 1.6)
   // Minor release fix for bug #8018
   try {
    // For whatever reason, IE has trouble passing the window object
    // around, causing it to be cloned in the process
    if (jQuery.isWindow(elem) && (elem !== window && !elem.frameElement)) {
     elem = window;
    }
   }
   catch (e) {}

   if (handler === false) {
    handler = returnFalse;
   } else if (!handler) {
    // Fixes bug #7229. Fix recommended by jdalton
    return;
   }

   var handleObjIn, handleObj;

   if (handler.handler) {
    handleObjIn = handler;
    handler = handleObjIn.handler;
   }

   // Make sure that the function being executed has a unique ID
   if (!handler.guid) {
    handler.guid = jQuery.guid++;
   }

   // Init the element's event structure
   var elemData = jQuery._data(elem);

   // If no elemData is found then we must be trying to bind to one of the
   // banned noData elements
   if (!elemData) {
    return;
   }

   var events = elemData.events,
       eventHandle = elemData.handle;

   if (!events) {
    elemData.events = events = {};
   }

   if (!eventHandle) {
    elemData.handle = eventHandle = function(e) {
     // Handle the second event of a trigger and when
     // an event is called after a page has unloaded
     return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? jQuery.event.handle.apply(eventHandle.elem, arguments) : undefined;
    };
   }

   // Add elem as a property of the handle function
   // This is to prevent a memory leak with non-native events in IE.
   eventHandle.elem = elem;

   // Handle multiple events separated by a space
   // jQuery(...).bind("mouseover mouseout", fn);
   types = types.split(" ");

   var type, i = 0,
       namespaces;

   while ((type = types[i++])) {
    handleObj = handleObjIn ? jQuery.extend({}, handleObjIn) : {
     handler: handler,
     data: data
    };

    // Namespaced event handlers
    if (type.indexOf(".") > -1) {
     namespaces = type.split(".");
     type = namespaces.shift();
     handleObj.namespace = namespaces.slice(0).sort().join(".");

    } else {
     namespaces = [];
     handleObj.namespace = "";
    }

    handleObj.type = type;
    if (!handleObj.guid) {
     handleObj.guid = handler.guid;
    }

    // Get the current list of functions bound to this event
    var handlers = events[type],
        special = jQuery.event.special[type] || {};

    // Init the event handler queue
    if (!handlers) {
     handlers = events[type] = [];

     // Check for a special event handler
     // Only use addEventListener/attachEvent if the special
     // events handler returns false
     if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) {
      // Bind the global event handler to the element
      if (elem.addEventListener) {
       elem.addEventListener(type, eventHandle, false);

      } else if (elem.attachEvent) {
       elem.attachEvent("on" + type, eventHandle);
      }
     }
    }

    if (special.add) {
     special.add.call(elem, handleObj);

     if (!handleObj.handler.guid) {
      handleObj.handler.guid = handler.guid;
     }
    }

    // Add the function to the element's handler list
    handlers.push(handleObj);

    // Keep track of which events have been used, for global triggering
    jQuery.event.global[type] = true;
   }

   // Nullify elem to prevent memory leaks in IE
   elem = null;
  },

  global: {},

  // Detach an event or set of events from an element
  remove: function(elem, types, handler, pos) {
   // don't do events on text and comment nodes
   if (elem.nodeType === 3 || elem.nodeType === 8) {
    return;
   }

   if (handler === false) {
    handler = returnFalse;
   }

   var ret, type, fn, j, i = 0,
       all, namespaces, namespace, special, eventType, handleObj, origType, elemData = jQuery.hasData(elem) && jQuery._data(elem),
       events = elemData && elemData.events;

   if (!elemData || !events) {
    return;
   }

   // types is actually an event object here
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.