.

Prototype Deconstructed

Navigate the physical Prototype code to see what, where, and how the magic happens

Home

The Deconstructed series is designed to visually and interactively deconstruct the internal code of JavaScript libraries, including jQuery, Prototype and MooTools.

It breaks the physical JavaScript into visual blocks that you can easiliy navigate. Each block opens to reveal its internal code. Clickable hyperlinks allow you to follow program flow.

Instructions: click element headers to view specific code. Click group headers to preview all nested descriptions. CTRL+click group headers to view all nested code. Click function names to view official API docs.

Key

Section A logical API group
Extend A native class extended
Class A new Class object
Object A top-level class-like Object
Function A function or method
Properties Variables or properties
Code Executing code

Theme

Object : Prototype

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

Section : Core functionality

Object : Prototype

The Prototype namespace provides fundamental information about the Prototype library you’re using, as well as a central repository for default iterators or functions

var Prototype = {

Properties : Version

	Version: '1.6.1',

Properties : Browser

	Browser: (function(){
		var ua = navigator.userAgent;
		var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]';
		return {
			IE:             !!window.attachEvent && !isOpera,
			Opera:          isOpera,
			WebKit:         ua.indexOf('AppleWebKit/') > -1,
			Gecko:          ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1,
			MobileSafari:   /Apple.*Mobile.*Safari/.test(ua)
		}
	})(),

Properties : BrowserFeatures

	BrowserFeatures: {
		XPath: !!document.evaluate,
		SelectorsAPI: !!document.querySelector,
		ElementExtensions: (function() {
			var constructor = window.Element || window.HTMLElement;
			return !!(constructor && constructor.prototype);
		})(),
		SpecificElementExtensions: (function() {
			if (typeof window.HTMLDivElement !== 'undefined')
				return true;

			var div = document.createElement('div');
			var form = document.createElement('form');
			var isSupported = false;

			if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
				isSupported = true;
			}

			div = form = null;

			return isSupported;
		})()
	},

Properties : ScriptFragment

	ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',

Properties : JSONFilter

	JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,

Function : emptyFunction

The emptyFunction does nothing… and returns nothing!

	emptyFunction: function() { },

Function : K

K is Prototype’s very own identity function, i.e. it returns its argument untouched.

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

Code : Mobile Safari-specific code

if (Prototype.Browser.MobileSafari)
	Prototype.BrowserFeatures.SpecificElementExtensions = false;

Properties : Abstract

var Abstract = { };

Function : Try.these

Accepts an arbitrary number of functions and returns the result of the first one that doesn't throw an error

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

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

		return returnValue;
	}};

Object : Class

Prototype’s object for class-based OOP

/* Based on Alex Arnell's inheritance implementation. */

var Class = (function() {

Function : subclass

Internal function

	function subclass() {};

Function : create

Creates a class

	function create() {
		var parent = null, properties = $A(arguments);
		if (Object.isFunction(properties[0]))
			parent = properties.shift();

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

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

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

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

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

		klass.prototype.constructor = klass;
		return klass;
	}

Function : addMethods

Adds methods to an existing class

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

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

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

				value.valueOf = method.valueOf.bind(method);
				value.toString = method.toString.bind(method);
			}
			this.prototype[property] = value;
		}

		return this;
	}

Code : Assign methods

	return {
		create: create,
		Methods: {
			addMethods: addMethods
		}
	};
})();

Extend : Object

Object is used by Prototype as a namespace; that is, it just keeps a few new methods together, which are intended for namespaced access

(function() {

Function : _toString

Description

	var _toString = Object.prototype.toString;

Function : extend

Copies all properties from the source to the destination object. Used by Prototype to simulate inheritance (rather statically) by copying to prototypes

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

Function : inspect

Returns the debug-oriented string representation of the object

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

Function : toJSON

Returns a JSON string

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

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

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

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

Function : toQueryString

Turns an object into its URL-encoded query string representation

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

Function : toHTML

Returns the return value of obj’s toHTML method if it exists, else runs obj through String.interpret

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

Function : keys

reats any object as a Hash and fetches the list of its property names

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

Function : values

Treats any object as a Hash and fetches the list of its property values

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

Function : clone

Clones the passed object using shallow copy (copies all the original’s properties to the result)

	function clone(object) {
		return extend({ }, object);
	}

Function : isElement

Returns true if obj is a DOM node of type 1, false otherwise

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

Function : isArray

Returns true if obj is an array, false otherwise

	function isArray(object) {
		return _toString.call(object) == "[object Array]";
	}

Function : isHash

Returns true if obj is an instance of the Hash class, false otherwise

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

Function : isFunction

Returns true if obj is of type function, false otherwise

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

Function : isString

Returns true if obj is of type string, false otherwise

	function isString(object) {
		return _toString.call(object) == "[object String]";
	}

Function : isNumber

Returns true if obj is of type number, false otherwise

	function isNumber(object) {
		return _toString.call(object) == "[object Number]";
	}

Function : isUndefined

Returns true if obj is of type undefined, false otherwise

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

Code : Assign methods

	extend(Object, {
		extend:        extend,
		inspect:       inspect,
		toJSON:        toJSON,
		toQueryString: toQueryString,
		toHTML:        toHTML,
		keys:          keys,
		values:        values,
		clone:         clone,
		isElement:     isElement,
		isArray:       isArray,
		isHash:        isHash,
		isFunction:    isFunction,
		isString:      isString,
		isNumber:      isNumber,
		isUndefined:   isUndefined
	});
})();

Extend : Function

Prototype's extended Function object

Object.extend(Function.prototype, (function() {

Properties : slice

	var slice = Array.prototype.slice;

Function : update

Internal function

	function update(array, args) {
		var arrayLength = array.length, length = args.length;
		while (length--) array[arrayLength + length] = args[length];
		return array;
	}

Function : merge

Internal function

	function merge(array, args) {
		array = slice.call(array, 0);
		return update(array, args);
	}

Function : argumentNames

Reads the argument names as defined in the function definition and returns the values as an array of strings, or an empty array if the function is defined without parameters

	function argumentNames() {
		var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
			.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
			.replace(/\s+/g, '').split(',');
		return names.length == 1 && !names[0] ? [] : names;
	}

Function : bind

Wraps the function in another, locking its execution scope to an object specified by thisObj

	function bind(context) {
		if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
		var __method = this, args = slice.call(arguments, 1);
		return function() {
			var a = merge(args, arguments);
			return __method.apply(context, a);
		}
	}

Function : bindAsEventListener

An event-specific variant of bind which makes sure the function will recieve the current event object as the first argument when executing

	function bindAsEventListener(context) {
		var __method = this, args = slice.call(arguments, 1);
		return function(event) {
			var a = update([event || window.event], args);
			return __method.apply(context, a);
		}
	}

Function : curry

artially applies the function, returning a function with one or more arguments already “filled in.”

	function curry() {
		if (!arguments.length) return this;
		var __method = this, args = slice.call(arguments, 0);
		return function() {
			var a = merge(args, arguments);
			return __method.apply(this, a);
		}
	}

Function : delay

chedules the function to run after the specified amount of time, passing any arguments given

	function delay(timeout) {
		var __method = this, args = slice.call(arguments, 1);
		timeout = timeout * 1000
		return window.setTimeout(function() {
			return __method.apply(__method, args);
		}, timeout);
	}

Function : defer

Schedules the function to run as soon as the interpreter is idle

	function defer() {
		var args = update([0.01], arguments);
		return this.delay.apply(this, args);
	}

Function : wrap

Returns a function “wrapped” around the original function

	function wrap(wrapper) {
		var __method = this;
		return function() {
			var a = update([__method.bind(this)], arguments);
			return wrapper.apply(this, a);
		}
	}

Function : methodize

Takes a function and wraps it in another function that, at call time, pushes this to the original function as the first argument

	function methodize() {
		if (this._methodized) return this._methodized;
		var __method = this;
		return this._methodized = function() {
			var a = update([this], arguments);
			return __method.apply(null, a);
		};
	}

Code : Assign methods

	return {
		argumentNames:       argumentNames,
		bind:                bind,
		bindAsEventListener: bindAsEventListener,
		curry:               curry,
		delay:               delay,
		defer:               defer,
		wrap:                wrap,
		methodize:           methodize
	}
})());

Properties : Date.prototype.toJSON

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

Properties : RegExp.prototype.match

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

Properties : RegExp.escape

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

Class : PeriodicalExecuter

This is a simple facility for periodical execution of a function. This essentially encapsulates the native clearInterval/setInterval mechanism found in native Window objects


var PeriodicalExecuter = Class.create({

Function : initialize

Constructor function

	initialize: function(callback, frequency) {
		this.callback = callback;
		this.frequency = frequency;
		this.currentlyExecuting = false;

		this.registerCallback();
	},

Function : registerCallback

Internal function

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

Function : execute

Internal function

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

Function : stop

Stops the periodical executer (there will be no further triggers)

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

Function : onTimerEvent

Internal function

	onTimerEvent: function() {
		if (!this.currentlyExecuting) {
			try {
				this.currentlyExecuting = true;
				this.execute();
				this.currentlyExecuting = false;
			} catch(e) {
				this.currentlyExecuting = false;
				throw e;
			}
		}
	}
});

Extend : String

Prototype enhances the String object with a series of useful methods for String.prototype ranging from the trivial to the complex

Object.extend(String, {
	interpret: function(value) {
		return value == null ? '' : String(value);
	},
	specialChar: {
		'\b': '\\b',
		'\t': '\\t',
		'\n': '\\n',
		'\f': '\\f',
		'\r': '\\r',
		'\\': '\\\\'
	}
});

Extend : String.prototype

Prototype enhances the String object with a series of useful methods for String.prototype ranging from the trivial to the complex

Object.extend(String.prototype, (function() {

Function : prepareReplacement

Internal function

	function prepareReplacement(replacement) {
		if (Object.isFunction(replacement)) return replacement;
		var template = new Template(replacement);
		return function(match) { return template.evaluate(match) };
	}

Function : gsub

Returns the string with every occurence of a given pattern replaced by either a regular string, the returned value of a function or a Template string

	function gsub(pattern, replacement) {
		var result = '', source = this, match;
		replacement = prepareReplacement(replacement);

		if (Object.isString(pattern))
			pattern = RegExp.escape(pattern);

		if (!(pattern.length || pattern.source)) {
			replacement = replacement('');
			return replacement + source.split('').join(replacement) + replacement;
		}

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

Function : sub

Returns a string with the first count occurrences of pattern replaced by either a regular string, the returned value of a function or a Template string

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

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

Function : scan

Allows iterating over every occurrence of the given pattern (which can be a string or a regular expression). Returns the original string

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

Function : truncate

Truncates a string to the given length and appends a suffix to it (indicating that it is only an excerpt)

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

Function : strip

Strips all leading and trailing whitespace from a string

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

Function : stripTags

Strips a string of any HTML tag

	function stripTags() {
		return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, '');
	}

Function : stripScripts

Strips a string of anything that looks like an HTML script block

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

Function : extractScripts

Exctracts the content of any script block present in the string and returns them as an array of strings

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

Function : evalScripts

Evaluates the content of any script block present in the string. Returns an array containing the value returned by each script

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

Function : escapeHTML

Converts HTML special characters to their entity equivalents

	function escapeHTML() {
		return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
	}

Function : unescapeHTML

Strips tags and converts the entity forms of special HTML characters to their normal form

	function unescapeHTML() {
		return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&');
	}

Function : toQueryParams

Parses a URI-like query string and returns an object composed of parameter/value pairs

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

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

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

Function : toArray

Splits the string character-by-character and returns an array with the result

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

Function : succ

Used internally by ObjectRange. Converts the last character of the string to the following character in the Unicode alphabet

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

Function : times

Concatenates the string count times

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

Function : camelize

Converts a string separated by dashes into a camelCase equivalent. For instance, 'foo-bar' would be converted to 'fooBar'

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

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

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

		return camelized;
	}

Function : capitalize

Capitalizes the first letter of a string and downcases all the others

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

Function : underscore

Converts a camelized string into a series of words separated by an underscore ("_")

	function underscore() {
		return this.replace(/::/g, '/')
 							.replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
 							.replace(/([a-z\d])([A-Z])/g, '$1_$2')
 							.replace(/-/g, '_')
 							.toLowerCase();
	}

Function : dasherize

Replaces every instance of the underscore character ("_") by a dash ("-")

	function dasherize() {
		return this.replace(/_/g, '-');
	}

Function : inspect

Returns a debug-oriented version of the string (i.e. wrapped in single or double quotes, with backslashes and quotes escaped)

	function inspect(useDoubleQuotes) {
		var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) {
			if (character in String.specialChar) {
				return String.specialChar[character];
			}
			return '\\u00' + character.charCodeAt().toPaddedString(2, 16);
		});
		if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
		return "'" + escapedString.replace(/'/g, '\\\'') + "'";
	}

Function : toJSON

Returns a JSON string

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

Function : unfilterJSON

Strips comment delimiters around Ajax JSON or JavaScript responses. This security method is called internally

	function unfilterJSON(filter) {
		return this.replace(filter || Prototype.JSONFilter, '$1');
	}

Function : isJSON

Check if the string is valid JSON by the use of regular expressions. This security method is called internally

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

Function : evalJSON

Evaluates the JSON in the string and returns the resulting object. If the optional sanitize parameter is set to true, the string is checked for possible malicious attempts and eval is not called if one is detected

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

Function : include

Check if the string contains a substring

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

Function : startsWith

Checks if the string starts with substring

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

Function : endsWith

Checks if the string ends with substring

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

Function : empty

Checks if the string is empty

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

Function : blank

Check if the string is 'blank', meaning either empty or containing only whitespace

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

Function : interpolate

Treats the string as a Template and fills it with object’s properties

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

Code : Assign methods

	return {
		gsub:           gsub,
		sub:            sub,
		scan:           scan,
		truncate:       truncate,
		strip:          String.prototype.trim ? String.prototype.trim : strip,
		stripTags:      stripTags,
		stripScripts:   stripScripts,
		extractScripts: extractScripts,
		evalScripts:    evalScripts,
		escapeHTML:     escapeHTML,
		unescapeHTML:   unescapeHTML,
		toQueryParams:  toQueryParams,
		parseQuery:     toQueryParams,
		toArray:        toArray,
		succ:           succ,
		times:          times,
		camelize:       camelize,
		capitalize:     capitalize,
		underscore:     underscore,
		dasherize:      dasherize,
		inspect:        inspect,
		toJSON:         toJSON,
		unfilterJSON:   unfilterJSON,
		isJSON:         isJSON,
		evalJSON:       evalJSON,
		include:        include,
		startsWith:     startsWith,
		endsWith:       endsWith,
		empty:          empty,
		blank:          blank,
		interpolate:    interpolate
	};
})());

Class : Template

The Template class is used to produce formatted output of text strings


var Template = Class.create({

Function : initialize

Constructor function

	initialize: function(template, pattern) {
		this.template = template.toString();
		this.pattern = pattern || Template.Pattern;
	},

Function : evaluate

Applies the template to the given object’s data, producing a formatted string with symbols replaced by corresponding object’s properties

	evaluate: function(object) {
		if (object && Object.isFunction(object.toTemplateReplacements))
			object = object.toTemplateReplacements();

		return this.template.gsub(this.pattern, function(match) {
			if (object == null) return (match[1] + '');

			var before = match[1] || '';
			if (before == '\\') return match[2];

			var ctx = object, expr = match[3];
			var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
			match = pattern.exec(expr);
			if (match == null) return before;

			while (match != null) {
				var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1];
				ctx = ctx[comp];
				if (null == ctx || '' == match[3]) break;
				expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
				match = pattern.exec(expr);
			}

			return before + String.interpret(ctx);
		});
	}
});

Properties : Pattern

	Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

Object : $break

Object

var $break = { };

Object : Enumberable

var Enumerable = (function() {

Function : each

The cornerstone of Enumerable

	function each(iterator, context) {
		var index = 0;
		try {
			this._each(function(value) {
				iterator.call(context, value, index++);
			});
		} catch (e) {
			if (e != $break) throw e;
		}
		return this;
	}

Function : eachSlice

Groups items in chunks based on a given size, with last chunk being possibly smaller

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

Function : all

Determines whether all the elements are boolean-equivalent to true, either directly or through computation by the provided iterator

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

Function : any

Determines whether at least one element is boolean-equivalent to true, either directly or through computation by the provided iterator

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

Function : collect

Returns the results of applying the iterator to each element

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

Function : detect

Finds the first element for which the iterator returns true

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

Function : findAll

Returns all the elements for which the iterator returned true

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

Function : grep

Returns all the elements that match the filter

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

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

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

Function : include

Determines whether a given object is in the Enumerable or not, based on the == comparison operator

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

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

Function : inGroupsOf

Groups items in fixed-size chunks, using a specific value to fill up the last chunk if necessary

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

Function : inject

Incrementally builds a result value based on the successive results of the iterator

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

Function : invoke

Invokes the same method, with the same arguments, for all items in a collection

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

Function : max

Returns the maximum element (or element-based computation), or undefined if the enumeration is empty

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

Function : min

Returns the minimum element (or element-based computation), or undefined if the enumeration is empty

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

Function : partition

Partitions the elements in two groups: those regarded as true, and those considered false

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

Function : pluck

Optimization for a common use-case of collect: fetching the same property for all the elements

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

Function : reject

Returns all the elements for which the iterator returned false

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

Function : sortBy

Provides a custom-sorted view of the elements based on the criteria computed, for each element, by the iterator

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

Function : toArray

Returns an Array representation of the enumeration

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

Function : zip

Zips together (think of the zip on a pair of trousers) 2+ sequences, providing an array of tuples

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

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

Function : size

Returns the size of the enumeration

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

Function : inspect

Debugging function

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

Code : Assign methods

	return {
		each:       each,
		eachSlice:  eachSlice,
		all:        all,
		every:      all,
		any:        any,
		some:       any,
		collect:    collect,
		map:        collect,
		detect:     detect,
		findAll:    findAll,
		select:     findAll,
		filter:     findAll,
		grep:       grep,
		include:    include,
		member:     include,
		inGroupsOf: inGroupsOf,
		inject:     inject,
		invoke:     invoke,
		max:        max,
		min:        min,
		partition:  partition,
		pluck:      pluck,
		reject:     reject,
		sortBy:     sortBy,
		toArray:    toArray,
		entries:    toArray,
		zip:        zip,
		size:       size,
		inspect:    inspect,
		find:       detect
	};
})();

Function : $A

Accepts an array-like collection (anything with numeric indices) and returns its equivalent as an actual Array object. This method is a convenience alias of Array.from, but is the preferred way of casting to an Array.

function $A(iterable) {
	if (!iterable) return [];
	if ('toArray' in Object(iterable)) return iterable.toArray();
	var length = iterable.length || 0, results = new Array(length);
	while (length--) results[length] = iterable[length];
	return results;
}

Function : $w

Splits a string into an Array, treating all whitespace as delimiters. Equivalent to Ruby's %w{foo bar} or Perl's qw(foo bar).

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

Properties : Array.from

Array.from = $A;

Extend : Array

Prototype extends all native Javascript arrays with quite a few powerful methods

(function() {

Code : Borrow native methods

	var arrayProto = Array.prototype,
		slice = arrayProto.slice,
		_each = arrayProto.forEach; // use native browser JS 1.6 implementation if available

Function : each

Iterates over the array in ascending numerical index order

	function each(iterator) {
		for (var i = 0, length = this.length; i < length; i++)
			iterator(this[i]);
	}

Properties : _each

	if (!_each) _each = each;

Function : clear

Clears the array (makes it empty)

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

Function : first

Returns the first item in the array, or undefined if the array is empty

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

Function : last

Returns the last item in the array, or undefined if the array is empty

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

Function : compact

Returns a new version of the array, without any null/undefined values

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

Function : flatten

Returns a “flat” (one-dimensional) version of the array

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

Function : without

Produces a new version of the array that does not contain any of the specified values

	function without() {
		var values = slice.call(arguments, 0);
		return this.select(function(value) {
			return !values.include(value);
		});
	}

Function : reverse

Returns the reversed version of the array

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

Function : uniq

Produces a duplicate-free version of an array

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

Function : intersect

Not available

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

Function : clone

Returns a duplicate of the array, leaving the original array intact

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

Function : size

Returns the size of the array

	function size() {
		return this.length;
	}

Function : inspect

Returns the debug-oriented string representation of an array

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

Function : toJSON

Returns a JSON string

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

Function : indexOf

Returns the position of the first occurrence of the argument within the array

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

Function : lastIndexOf

Returns the position of the last occurrence of the argument within the array

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

Function : concat

Concatenates two arrays, returning a new array

	function concat() {
		var array = slice.call(this, 0), item;
		for (var i = 0, length = arguments.length; i < length; i++) {
			item = arguments[i];
			if (Object.isArray(item) && !('callee' in item)) {
				for (var j = 0, arrayLength = item.length; j < arrayLength; j++)
					array.push(item[j]);
			} else {
				array.push(item);
			}
		}
		return array;
	}

Code : Assign methods

	Object.extend(arrayProto, Enumerable);

	if (!arrayProto._reverse)
		arrayProto._reverse = arrayProto.reverse;

	Object.extend(arrayProto, {
		_each:     _each,
		clear:     clear,
		first:     first,
		last:      last,
		compact:   compact,
		flatten:   flatten,
		without:   without,
		reverse:   reverse,
		uniq:      uniq,
		intersect: intersect,
		clone:     clone,
		toArray:   clone,
		size:      size,
		inspect:   inspect,
		toJSON:    toJSON
	});

	var CONCAT_ARGUMENTS_BUGGY = (function() {
		return [].concat(arguments)[0][0] !== 1;
	})(1,2)

	if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat;

	if (!arrayProto.indexOf) arrayProto.indexOf = indexOf;
	if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
})();

Function : $H

Creates a Hash (which is synonymous to “map” or “associative array” for our purposes). A convenience wrapper around the Hash constructor, with a safeguard that lets you pass an existing Hash object and get it back untouched (instead of uselessly cloning it).

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

Class : Hash

Hash can be thought of as an associative array, binding unique keys to values (which are not necessarily unique), though it can not guarantee consistent order its elements when iterating.


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

Function : initialize

Constructor function

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

Function : _each

Iterates over the name/value pairs in the hash

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

Function : set

Sets the hash’s key property to value and returns value

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

Function : get

Returns the value of the hash’s key property

	function get(key) {
		if (this._object[key] !== Object.prototype[key])
			return this._object[key];
	}

Function : unset

Deletes the hash’s key property and returns its value

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

Function : toObject

Returns a cloned, vanilla object

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

Function : keys

Provides an Array of keys (that is, property names) for the hash

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

Function : values

Collect the values of a hash and returns them in an array

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

Function : index

Description

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

Function : merge

Merges object to hash and returns the result of that merge

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

Function : update

Updates hash with the key/value pairs of object

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

Function : toQueryPair

Description

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

Function : toQueryString

Turns a hash into its URL-encoded query string representation

	function toQueryString() {
		return this.inject([], function(results, pair) {
			var key = encodeURIComponent(pair.key), values = pair.value;

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

Function : inspect

Returns the debug-oriented string representation of the hash

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

Function : toJSON

Returns a JSON string

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

Function : clone

Returns a clone of hash

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

Code : Assign methods

	return {
		initialize:             initialize,
		_each:                  _each,
		set:                    set,
		get:                    get,
		unset:                  unset,
		toObject:               toObject,
		toTemplateReplacements: toObject,
		keys:                   keys,
		values:                 values,
		index:                  index,
		merge:                  merge,
		update:                 update,
		toQueryString:          toQueryString,
		inspect:                inspect,
		toJSON:                 toJSON,
		clone:                  clone
	};
})());

Properties : Hash.from

Hash.from = $H;

Extend : Number

Prototype extends native JavaScript numbers

Object.extend(Number.prototype, (function() {

Function : toColorPart

Produces a 2-digit hexadecimal representation of the number (which is therefore assumed to be in the [0

	function toColorPart() {
		return this.toPaddedString(2, 16);
	}

Function : succ

Returns the successor of the current Number, as defined by current + 1

	function succ() {
		return this + 1;
	}

Function : times

Encapsulates a regular [0..n[ loop, Ruby-style

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

Function : toPaddedString

Converts the number into a string padded with 0s so that the string’s length is at least equal to length

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

Function : toJSON

Returns a JSON string

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

Function : abs

Returns the absolute value of the number

	function abs() {
		return Math.abs(this);
	}

Function : round

Rounds the number to the nearest integer

	function round() {
		return Math.round(this);
	}

Function : ceil

Returns the smallest integer greater than or equal to the number

	function ceil() {
		return Math.ceil(this);
	}

Function : floor

Returns the largest integer less than or equal to the number

	function floor() {
		return Math.floor(this);
	}

Code : Assign methods

	return {
		toColorPart:    toColorPart,
		succ:           succ,
		times:          times,
		toPaddedString: toPaddedString,
		toJSON:         toJSON,
		abs:            abs,
		round:          round,
		ceil:           ceil,
		floor:          floor
	};
})());

Function : $R

Creates a new ObjectRange object. This method is a convenience wrapper around the ObjectRange constructor, but $R is the preferred alias

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

Class : ObjectRange

Ranges represent an interval of values. The value type just needs to be “compatible,” that is, to implement a succ method letting us step from one value to the next (its successor)


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

Function : initialize

Constructor function

	function initialize(start, end, exclusive) {
		this.start = start;
		this.end = end;
		this.exclusive = exclusive;
	}

Function : _each

Internal function

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

Function : include

Determines whether the value is included in the range

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

Code : Assign methods

	return {
		initialize: initialize,
		_each:      _each,
		include:    include
	};
})());

Section : Ajax

Object : Ajax

Prototype offers three objects to deal with AJAX communication

var Ajax = {

Function : getTransport

Internal function

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

Properties : activeRequestCount

	activeRequestCount: 0
};

Object : Ajax.Responders

A repository of global listeners notified about every step of Prototype-based AJAX requests

Ajax.Responders = {

Properties : responders

	responders: [],

Function : _each

Internal function

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

Function : register

Registers a responder

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

Function : unregister

Unregisters a responder

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

Function : dispatch

Internal function

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

Code : Assign methods

Object.extend(Ajax.Responders, Enumerable);

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

Class : Ajax.Base

Ajax base class for all Ajax classes


Ajax.Base = Class.create({

Function : initialize

Constructor function

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

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

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

Class : Ajax.Request

Initiates and processes an AJAX request


Ajax.Request = Class.create(Ajax.Base, {

Properties : _complete

	_complete: false,

Function : initialize

Constrcutor function

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

Function : request

Internal function called by the constuctor that makes the request

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

		if (!['get', 'post'].include(this.method)) {
			params['_method'] = this.method;
			this.method = 'post';
		}

		this.parameters = params;

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

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

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

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

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

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

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

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

Function : onStateChange

Internal function

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

Function : setRequestHeaders

Internal function

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

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

			/* Force "Connection: close" for older Mozilla browsers to work
 			* around a bug where XMLHttpRequest sends an incorrect
 			* Content-length header. See Mozilla Bugzilla #246651.
 			*/
			if (this.transport.overrideMimeType &&
					(navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
						headers['Connection'] = 'close';
		}

		if (typeof this.options.requestHeaders == 'object') {
			var extras = this.options.requestHeaders;

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

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

Function : success

Internal function

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

Function : getStatus

Internal function

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

Function : respondToReadyState

Internal function

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

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

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

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

		if (state == 'Complete') {
			this.transport.onreadystatechange = Prototype.emptyFunction;
		}
	},

Function : isSameOrigin

Internal function

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

Function : getHeader

Internal function

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

Function : evalResponse

Internal function

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

Function : dispatchException

Internal function

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

Properties : Ajax.Request.Events

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

Class : Ajax.Response

The object passed as the first argument of all Ajax requests callbacks


Ajax.Response = Class.create({

Function : initialize

Constructor function

	initialize: function(request){
		this.request = request;
		var transport  = this.transport  = request.transport,
				readyState = this.readyState = transport.readyState;

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

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

Properties : status

	status:      0,

Properties : statusText

	statusText: '',

Function : getStatus

Description

	getStatus: Ajax.Request.prototype.getStatus,

Function : getStatusText

Description

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

Function : getHeader

Returns the value of the requested header if present. null otherwise. Does not throw errors on undefined headers like it’s native counterpart does

	getHeader: Ajax.Request.prototype.getHeader,

Function : getAllHeaders

Returns a string containing all headers separated by a line break. Does not throw errors if no headers are present like it’s native counterpart does

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

Function : getResponseHeader

Returns the value of the requested header if present. Throws an error otherwise. This is just a wrapper around the xmlHttpRequest object’s native method. Prefer it’s shorter counterpart getHeader

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

Function : getAllResponseHeaders

Returns a string containing all headers separated by a line break. Throws an error otherwise. This is just a wrapper around the xmlHttpRequest object’s native method. Prefer it’s shorter counterpart getAllHeaders

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

Function : _getHeaderJSON

Internal function

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

Function : _getResponseJSON

Internal function

	_getResponseJSON: function() {
		var options = this.request.options;
		if (!options.evalJSON || (options.evalJSON != 'force' &&
			!(this.getHeader('Content-type') || '').include('application/json')) ||

				this.responseText.blank())
					return null;
		try {
			return this.responseText.evalJSON(options.sanitizeJSON ||
				!this.request.isSameOrigin());
		} catch (e) {
			this.request.dispatchException(e);
		}
	}
});

Class : Ajax.Updater

Performs an AJAX request and updates a container’s contents based on the response text


Ajax.Updater = Class.create(Ajax.Request, {

Function : initialize

Constructor function

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

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

		$super(url, options);
	},

Function : updateContent

Internal function

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

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

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

Class : Ajax.PeriodicalUpdater

Periodically performs an AJAX request and updates a container’s contents based on the response text. Offers a mechanism for “decay,” which lets it trigger at widening intervals while the response is unchanged


Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {

Function : initialize

Constructor function

	initialize: function($super, container, url, options) {
		$super(options);
		this.onComplete = this.options.onComplete;

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

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

		this.start();
	},

Function : start

Internal function

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

Function : stop

Internal function

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

Function : updateComplete

Internal function

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

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

Function : onTimerEvent

Internal function

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

Section : Element

Function : $

If provided with a string, returns the element in the document with matching ID; otherwise returns the passed element. Takes in an arbitrary number of arguments. All elements returned by the function are extended with Prototype DOM extensions

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

Function : _getElementsByXPath

Description

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

Object : Node

DOM node constants

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

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

Object : Element

The Element object sports a flurry of powerful DOM methods which you can access either as methods of Element (but that’s rather old-fashioned now) or directly as methods of an extended element

(function(global) {

	var SETATTRIBUTE_IGNORES_NAME = (function(){
		var elForm = document.createElement("form");
		var elInput = document.createElement("input");
		var root = document.documentElement;
		elInput.setAttribute("name", "test");
		elForm.appendChild(elInput);
		root.appendChild(elForm);
		var isBuggy = elForm.elements
			? (typeof elForm.elements.test == "undefined")
			: null;
		root.removeChild(elForm);
		elForm = elInput = null;
		return isBuggy;
	})();

	var element = global.Element;
	global.Element = function(tagName, attributes) {
		attributes = attributes || { };
		tagName = tagName.toLowerCase();
		var cache = Element.cache;
		if (SETATTRIBUTE_IGNORES_NAME && attributes.name) {
			tagName = '<' + tagName + ' name="' + attributes.name + '">';
			delete attributes.name;
			return Element.writeAttribute(document.createElement(tagName), attributes);
		}
		if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
		return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
	};
	Object.extend(global.Element, element || { });
	if (element)global.Element.prototype = element.prototype;
})(this);

Properties : Element.cache & Element.idCounter

Element.cache = { };
Element.idCounter = 1;

Object : Element.methods

Element.Methods = {

Function : visible

Returns a Boolean indicating whether or not element is visible (i.e. whether its inline style property is set to "display: none;").

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

Function : toggle

Toggles the visibility of element

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

Function : hide

Hides and returns element

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

Function : show

Displays and returns element

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

Function : remove

Completely removes element from the document and returns it

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

Function : update

Replaces the content of element with the provided newContent argument and returns element

	update: (function(){

		var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){
			var el = document.createElement("select"),
					isBuggy = true;
			el.innerHTML = "<option value=\"test\">test</option>";
			if (el.options && el.options[0]) {
				isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION";
			}
			el = null;
			return isBuggy;
		})();

		var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){
			try {
				var el = document.createElement("table");
				if (el && el.tBodies) {
					el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>";
					var isBuggy = typeof el.tBodies[0] == "undefined";
					el = null;
					return isBuggy;
				}
			} catch (e) {
				return true;
			}
		})();

		var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () {
			var s = document.createElement("script"),
					isBuggy = false;
			try {
				s.appendChild(document.createTextNode(""));
				isBuggy = !s.firstChild ||
					s.firstChild && s.firstChild.nodeType !== 3;
			} catch (e) {
				isBuggy = true;
			}
			s = null;
			return isBuggy;
		})();

		function update(element, content) {
			element = $(element);

			if (content && content.toElement)
				content = content.toElement();

			if (Object.isElement(content))
				return element.update().insert(content);

			content = Object.toHTML(content);

			var tagName = element.tagName.toUpperCase();

			if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) {
				element.text = content;
				return element;
			}

			if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) {
				if (tagName in Element._insertionTranslations.tags) {
					while (element.firstChild) {
						element.removeChild(element.firstChild);
					}
					Element._getContentFromAnonymousElement(tagName, content.stripScripts())
						.each(function(node) {
							element.appendChild(node)
						});
				}
				else {
					element.innerHTML = content.stripScripts();
				}
			}
			else {
				element.innerHTML = content.stripScripts();
			}

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

		return update;
	})(),

Function : replace

Replaces element by the content of the html argument and returns the removed element

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

Function : insert

Inserts content before, after, at the top of, or at the bottom of element, as specified by the position property of the second argument

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

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

		var content, insert, tagName, childNodes;

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

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

			content = Object.toHTML(content);

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

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

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

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

		return element;
	},

Function : wrap

Wraps an element inside another, then returns the wrapper

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

Function : inspect

Returns the debug-oriented string representation of element

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

Function : recursivelyCollect

Recursively collects elements whose relationship is specified by property

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

Function : ancestors

Collects all of element’s ancestors and returns them as an array of extended elements

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

Function : descendants

Collects all of element’s descendants and returns them as an array of extended elements

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

Function : firstDescendant

Returns the first child that is an element

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

Function : immediateDescendants

Collects all of the element’s immediate descendants (i.e. children) and returns them as an array of extended elements

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

Function : previousSiblings

Collects all of element’s previous siblings and returns them as an array of extended elements

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

Function : nextSiblings

Collects all of element’s next siblings and returns them as an array of extended elements

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

Function : siblings

Collects all of element’s siblings and returns them as an array of extended elements

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

Function : match

Checks if element matches the given CSS selector

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

Function : up

Returns element’s first ancestor (or the index’th ancestor, if index is specified) that matches cssRule

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

Function : down

Returns element’s first descendant (or the n-th descendant if index is specified) that matches cssRule

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

Function : previous

Returns element's previous sibling (or the index'th one, if index is specified) that matches cssRule

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

Function : next

Returns element’s following sibling (or the index’th one, if index is specified) that matches cssRule

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

Function : select

Takes an arbitrary number of CSS selectors (strings) and returns an array of extended descendants of element that match any of them

	select: function(element) {
		var args = Array.prototype.slice.call(arguments, 1);
		return Selector.findChildElements(element, args);
	},

Function : adjacent

Finds all siblings of the current element that match the given selector(s)

	adjacent: function(element) {
		var args = Array.prototype.slice.call(arguments, 1);
		return Selector.findChildElements(element.parentNode, args).without(element);
	},

Function : identify

returns element’s id attribute if it exists, or sets and returns a unique, auto-generated id

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

Function : readAttribute

Returns the value of element's attribute or null if attribute has not been specified

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

Function : writeAttribute

Adds, specifies or removes attributes passed as either a hash or a name/value pair

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


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

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

Function : getHeight

Finds and returns the computed height of element

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

Function : getWidth

Finds and returns the computed width of element

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

Function : classNames

Returns a new instance of ClassNames, an Enumerable object used to read and write CSS class names of element

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

Function : hasClassName

Checks whether element has the given CSS className

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

Function : addClassName

Adds a CSS class to element

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

Function : removeClassName

Removes element’s CSS className and returns element

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

Function : toggleClassName

Toggles element’s CSS className and returns element

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

Function : cleanWhitespace

Removes all of element's text nodes which contain only whitespace

	cleanWhitespace: function(element) {
		element = $(element);
		var node = element.firstChild;
		while (node) {
			var nextNode = node.nextSibling;
			if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
				element.removeChild(node);
			node = nextNode;
		}
		return element;
	},

Function : empty

Tests whether element is empty (i.e. contains only whitespace)

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

Function : descendantOf

Checks if element is a descendant of ancestor

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

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

		if (ancestor.contains)
			return ancestor.contains(element) && ancestor !== element;

		while (element = element.parentNode)
			if (element == ancestor) return true;

		return false;
	},

Function : scrollTo

Scrolls the window so that element appears at the top of the viewport

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

Function : getStyle

Returns the given CSS property value of element

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

Function : getOpacity

Gets the visual opacity of an element while working around inconsistencies in various browsers

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

Function : setStyle

Modifies element’s CSS style properties

	setStyle: function(element, styles) {
		element = $(element);
		var elementStyle = element.style, match;
		if (Object.isString(styles)) {
			element.style.cssText += ';' + styles;
			return styles.include('opacity') ?
				element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
		}
		for (var property in styles)
			if (property == 'opacity') element.setOpacity(styles[property]);
			else
				elementStyle[(property == 'float' || property == 'cssFloat') ?
					(Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
						property] = styles[property];

		return element;
	},

Function : setOpacity

Sets the visual opacity of an element while working around inconsistencies in various browsers

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

Function : getDimensions

Finds the computed width and height of element and returns them as key/value pairs of an object

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

		var els = element.style;
		var originalVisibility = els.visibility;
		var originalPosition = els.position;
		var originalDisplay = els.display;
		els.visibility = 'hidden';
		if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari
			els.position = 'absolute';
		els.display = 'block';
		var originalWidth = element.clientWidth;
		var originalHeight = element.clientHeight;
		els.display = originalDisplay;
		els.position = originalPosition;
		els.visibility = originalVisibility;
		return {width: originalWidth, height: originalHeight};
	},

Function : makePositioned

Allows for the easy creation of CSS containing block by setting element's CSS position to 'relative' if its initial position is either 'static' or undefined

	makePositioned: function(element) {
		element = $(element);
		var pos = Element.getStyle(element, 'position');
		if (pos == 'static' || !pos) {
			element._madePositioned = true;
			element.style.position = 'relative';
			if (Prototype.Browser.Opera) {
				element.style.top = 0;
				element.style.left = 0;
			}
		}
		return element;
	},

Function : undoPositioned

Sets element back to the state it was before Element

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

Function : makeClipping

Simulates the poorly supported CSS clip property by setting element's overflow value to 'hidden'

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

Function : undoClipping

Sets element’s CSS overflow property back to the value it had before Element

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

Function : cumulativeOffset

Returns the offsets of element from the top left corner of the document

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

Function : positionedOffset

Returns element’s offset relative to its closest positioned ancestor (the element that would be returned by Element#getOffsetParent)

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

Function : absolutize

Turns element into an absolutely-positioned element without changing its position in the page layout

	absolutize: function(element) {
		element = $(element);
		if (Element.getStyle(element, 'position') == 'absolute') return element;

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

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

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

Function : relativize

Turns element into an relatively-positioned element without changing its position in the page layout

	relativize: function(element) {
		element = $(element);
		if (Element.getStyle(element, 'position') == 'relative') return element;

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


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

Function : cumulativeScrollOffset

Calculates the cumulative scroll offset of an element in nested scrolling containers

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

Function : getOffsetParent

Returns element’s closest positioned ancestor

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

		while ((element = element.parentNode) && element != document.body)
			if (Element.getStyle(element, 'position') != 'static')
				return $(element);

		return $(document.body);
	},

Function : viewportOffset

Returns the X/Y coordinates of element relative to the viewport

	viewportOffset: function(forElement) {
		var valueT = 0, valueL = 0;

		var element = forElement;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;

			if (element.offsetParent == document.body &&
				Element.getStyle(element, 'position') == 'absolute') break;

		} while (element = element.offsetParent);

		element = forElement;
		do {
			if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) {
				valueT -= element.scrollTop  || 0;
				valueL -= element.scrollLeft || 0;
			}
		} while (element = element.parentNode);

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

Function : clonePosition

Clones the position and/or dimensions of source onto element as defined by the optional argument options

	clonePosition: function(element, source) {
		var options = Object.extend({
			setLeft:    true,
			setTop:     true,
			setWidth:   true,
			setHeight:  true,
			offsetTop:  0,
			offsetLeft: 0
		}, arguments[2] || { });

		source = $(source);
		var p = Element.viewportOffset(source);

		element = $(element);
		var delta = [0, 0];
		var parent = null;
		if (Element.getStyle(element, 'position') == 'absolute') {
			parent = Element.getOffsetParent(element);
			delta = Element.viewportOffset(parent);
		}

		if (parent == document.body) {
			delta[0] -= document.body.offsetLeft;
			delta[1] -= document.body.offsetTop;
		}

		if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
		if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
		if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
		if (options.setHeight) element.style.height = source.offsetHeight + 'px';
		return element;
	}
};

Code : Assign methods

Object.extend(Element.Methods, {
	getElementsBySelector: Element.Methods.select,
	childElements: Element.Methods.immediateDescendants
});

Properties : _attributeTranslations

Element._attributeTranslations = {
	write: {
		names: {
			className: 'class',
			htmlFor:   'for'
		},
		values: { }
	}
};

Code : Opera-specific element code

if (Prototype.Browser.Opera) {
	Element.Methods.getStyle = Element.Methods.getStyle.wrap(
		function(proceed, element, style) {
			switch (style) {
				case 'left': case 'top': case 'right': case 'bottom':
					if (proceed(element, 'position') === 'static') return null;
				case 'height': case 'width':
					if (!Element.visible(element)) return null;

					var dim = parseInt(proceed(element, style), 10);

					if (dim !== element['offset' + style.capitalize()])
						return dim + 'px';

					var properties;
					if (style === 'height') {
						properties = ['border-top-width', 'padding-top',
 						'padding-bottom', 'border-bottom-width'];
					}
					else {
						properties = ['border-left-width', 'padding-left',
 						'padding-right', 'border-right-width'];
					}
					return properties.inject(dim, function(memo, property) {
						var val = proceed(element, property);
						return val === null ? memo : memo - parseInt(val, 10);
					}) + 'px';
				default: return proceed(element, style);
			}
		}
	);

	Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
		function(proceed, element, attribute) {
			if (attribute === 'title') return element.title;
			return proceed(element, attribute);
		}
	);
}

Code : IE-specific element code

else if (Prototype.Browser.IE) {
	Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
		function(proceed, element) {
			element = $(element);
			try { element.offsetParent }
			catch(e) { return $(document.body) }
			var position = element.getStyle('position');
			if (position !== 'static') return proceed(element);
			element.setStyle({ position: 'relative' });
			var value = proceed(element);
			element.setStyle({ position: position });
			return value;
		}
	);

	$w('positionedOffset viewportOffset').each(function(method) {
		Element.Methods[method] = Element.Methods[method].wrap(
			function(proceed, element) {
				element = $(element);
				try { element.offsetParent }
				catch(e) { return Element._returnOffset(0,0) }
				var position = element.getStyle('position');
				if (position !== 'static') return proceed(element);
				var offsetParent = element.getOffsetParent();
				if (offsetParent && offsetParent.getStyle('position') === 'fixed')
					offsetParent.setStyle({ zoom: 1 });
				element.setStyle({ position: 'relative' });
				var value = proceed(element);
				element.setStyle({ position: position });
				return value;
			}
		);
	});

	Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap(
		function(proceed, element) {
			try { element.offsetParent }
			catch(e) { return Element._returnOffset(0,0) }
			return proceed(element);
		}
	);

	Element.Methods.getStyle = function(element, style) {
		element = $(element);
		style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
		var value = element.style[style];
		if (!value && element.currentStyle) value = element.currentStyle[style];

		if (style == 'opacity') {
			if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
				if (value[1]) return parseFloat(value[1]) / 100;
			return 1.0;
		}

		if (value == 'auto') {
			if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
				return element['offset' + style.capitalize()] + 'px';
			return null;
		}
		return value;
	};

	Element.Methods.setOpacity = function(element, value) {
		function stripAlpha(filter){
			return filter.replace(/alpha\([^\)]*\)/gi,'');
		}
		element = $(element);
		var currentStyle = element.currentStyle;
		if ((currentStyle && !currentStyle.hasLayout) ||
			(!currentStyle && element.style.zoom == 'normal'))
				element.style.zoom = 1;

		var filter = element.getStyle('filter'), style = element.style;
		if (value == 1 || value === '') {
			(filter = stripAlpha(filter)) ?
				style.filter = filter : style.removeAttribute('filter');
			return element;
		} else if (value < 0.00001) value = 0;
		style.filter = stripAlpha(filter) +
			'alpha(opacity=' + (value * 100) + ')';
		return element;
	};

	Element._attributeTranslations = (function(){

		var classProp = 'className';
		var forProp = 'for';

		var el = document.createElement('div');

		el.setAttribute(classProp, 'x');

		if (el.className !== 'x') {
			el.setAttribute('class', 'x');
			if (el.className === 'x') {
				classProp = 'class';
			}
		}
		el = null;

		el = document.createElement('label');
		el.setAttribute(forProp, 'x');
		if (el.htmlFor !== 'x') {
			el.setAttribute('htmlFor', 'x');
			if (el.htmlFor === 'x') {
				forProp = 'htmlFor';
			}
		}
		el = null;

		return {
			read: {
				names: {
					'class':      classProp,
					'className':  classProp,
					'for':        forProp,
					'htmlFor':    forProp
				},
				values: {
					_getAttr: function(element, attribute) {
						return element.getAttribute(attribute);
					},
					_getAttr2: function(element, attribute) {
						return element.getAttribute(attribute, 2);
					},
					_getAttrNode: function(element, attribute) {
						var node = element.getAttributeNode(attribute);
						return node ? node.value : "";
					},
					_getEv: (function(){

						var el = document.createElement('div');
						el.onclick = Prototype.emptyFunction;
						var value = el.getAttribute('onclick');
						var f;

						if (String(value).indexOf('{') > -1) {
							f = function(element, attribute) {
								attribute = element.getAttribute(attribute);
								if (!attribute) return null;
								attribute = attribute.toString();
								attribute = attribute.split('{')[1];
								attribute = attribute.split('}')[0];
								return attribute.strip();
							};
						}
						else if (value === '') {
							f = function(element, attribute) {
								attribute = element.getAttribute(attribute);
								if (!attribute) return null;
								return attribute.strip();
							};
						}
						el = null;
						return f;
					})(),
					_flag: function(element, attribute) {
						return $(element).hasAttribute(attribute) ? attribute : null;
					},
					style: function(element) {
						return element.style.cssText.toLowerCase();
					},
					title: function(element) {
						return element.title;
					}
				}
			}
		}
	})();

	Element._attributeTranslations.write = {
		names: Object.extend({
			cellpadding: 'cellPadding',
			cellspacing: 'cellSpacing'
		}, Element._attributeTranslations.read.names),
		values: {
			checked: function(element, value) {
				element.checked = !!value;
			},

			style: function(element, value) {
				element.style.cssText = value ? value : '';
			}
		}
	};

	Element._attributeTranslations.has = {};

	$w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
			'encType maxLength readOnly longDesc frameBorder').each(function(attr) {
		Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
		Element._attributeTranslations.has[attr.toLowerCase()] = attr;
	});

	(function(v) {
		Object.extend(v, {
			href:        v._getAttr2,
			src:         v._getAttr2,
			type:        v._getAttr,
			action:      v._getAttrNode,
			disabled:    v._flag,
			checked:     v._flag,
			readonly:    v._flag,
			multiple:    v._flag,
			onload:      v._getEv,
			onunload:    v._getEv,
			onclick:     v._getEv,
			ondblclick:  v._getEv,
			onmousedown: v._getEv,
			onmouseup:   v._getEv,
			onmouseover: v._getEv,
			onmousemove: v._getEv,
			onmouseout:  v._getEv,
			onfocus:     v._getEv,
			onblur:      v._getEv,
			onkeypress:  v._getEv,
			onkeydown:   v._getEv,
			onkeyup:     v._getEv,
			onsubmit:    v._getEv,
			onreset:     v._getEv,
			onselect:    v._getEv,
			onchange:    v._getEv
		});
	})(Element._attributeTranslations.read.values);

	if (Prototype.BrowserFeatures.ElementExtensions) {
		(function() {
			function _descendants(element) {
				var nodes = element.getElementsByTagName('*'), results = [];
				for (var i = 0, node; node = nodes[i]; i++)
					if (node.tagName !== "!") // Filter out comment nodes.
						results.push(node);
				return results;
			}

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

}

Code : Geckospecific element code

else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
	Element.Methods.setOpacity = function(element, value) {
		element = $(element);
		element.style.opacity = (value == 1) ? 0.999999 :
			(value === '') ? '' : (value < 0.00001) ? 0 : value;
		return element;
	};
}

Code : Webkit-specific element code

else if (Prototype.Browser.WebKit) {
	Element.Methods.setOpacity = function(element, value) {
		element = $(element);
		element.style.opacity = (value == 1 || value === '') ? '' :
			(value < 0.00001) ? 0 : value;

		if (value == 1)
			if(element.tagName.toUpperCase() == 'IMG' && element.width) {
				element.width++; element.width--;
			} else try {
				var n = document.createTextNode(' ');
				element.appendChild(n);
				element.removeChild(n);
			} catch (e) { }

		return element;
	};

	Element.Methods.cumulativeOffset = function(element) {
		var valueT = 0, valueL = 0;
		do {
			valueT += element.offsetTop  || 0;
			valueL += element.offsetLeft || 0;
			if (element.offsetParent == document.body)
				if (Element.getStyle(element, 'position') == 'absolute') break;

			element = element.offsetParent;
		} while (element);

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

Code : replace

Replaces element by the content of the html argument and returns the removed element

if ('outerHTML' in document.documentElement) {
	Element.Methods.replace = function(element, content) {
		element = $(element);

		if (content && content.toElement) content = content.toElement();
		if (Object.isElement(content)) {
			element.parentNode.replaceChild(content, element);
			return element;
		}

		content = Object.toHTML(content);
		var parent = element.parentNode, tagName = parent.tagName.toUpperCase();

		if (Element._insertionTranslations.tags[tagName]) {
			var nextSibling = element.next();
			var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
			parent.removeChild(element);
			if (nextSibling)
				fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
			else
				fragments.each(function(node) { parent.appendChild(node) });
		}
		else element.outerHTML = content.stripScripts();

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

Function : Element._returnOffset

Internal function

Element._returnOffset = function(l, t) {
	var result = [l, t];
	result.left = l;
	result.top = t;
	return result;
};

Function : Element._getContentFromAnonymousElement

Internal function

Element._getContentFromAnonymousElement = function(tagName, html) {
	var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
	if (t) {
		div.innerHTML = t[0] + html + t[1];
		t[2].times(function() { div = div.firstChild });
	} else div.innerHTML = html;
	return $A(div.childNodes);
};

Object : _insertionTranslations

Description

Element._insertionTranslations = {
	before: function(element, node) {
		element.parentNode.insertBefore(node, element);
	},
	top: function(element, node) {
		element.insertBefore(node, element.firstChild);
	},
	bottom: function(element, node) {
		element.appendChild(node);
	},
	after: function(element, node) {
		element.parentNode.insertBefore(node, element.nextSibling);
	},
	tags: {
		TABLE:  ['<table>',                '</table>',                   1],
		TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
		TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
		TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
		SELECT: ['<select>',               '</select>',                  1]
	}
};

Code : Extend Element_insertionTranslations

(function() {
	var tags = Element._insertionTranslations.tags;
	Object.extend(tags, {
		THEAD: tags.TBODY,
		TFOOT: tags.TBODY,
		TH:    tags.TD
	});
})();

Function : Element.Methods.Simulated

Internal function

Element.Methods.Simulated = {
	hasAttribute: function(element, attribute) {
		attribute = Element._attributeTranslations.has[attribute] || attribute;
		var node = $(element).getAttributeNode(attribute);
		return !!(node && node.specified);
	}
};

Object : Element.Methods.ByTag

Object

Element.Methods.ByTag = { };

Code : Assign methods

Object.extend(Element, Element.Methods);

Code : Browser-features specific code

(function(div) {

	if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) {
		window.HTMLElement = { };
		window.HTMLElement.prototype = div['__proto__'];
		Prototype.BrowserFeatures.ElementExtensions = true;
	}

	div = null;

})(document.createElement('div'))

Function : Element.extend

Extends element with all of the methods contained in Element.Methods and Element.Methods.Simulated. If element is an input, textarea or select tag, it will also be extended with the methods from Form.Element.Methods. If it is a form tag, it will also be extended with Form.Methods

Element.extend = (function() {

	function checkDeficiency(tagName) {
		if (typeof window.Element != 'undefined') {
			var proto = window.Element.prototype;
			if (proto) {
				var id = '_' + (Math.random()+'').slice(2);
				var el = document.createElement(tagName);
				proto[id] = 'x';
				var isBuggy = (el[id] !== 'x');
				delete proto[id];
				el = null;
				return isBuggy;
			}
		}
		return false;
	}

	function extendElementWith(element, methods) {
		for (var property in methods) {
			var value = methods[property];
			if (Object.isFunction(value) && !(property in element))
				element[property] = value.methodize();
		}
	}

	var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object');

	if (Prototype.BrowserFeatures.SpecificElementExtensions) {
		if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) {
			return function(element) {
				if (element && typeof element._extendedByPrototype == 'undefined') {
					var t = element.tagName;
					if (t && (/^(?:object|applet|embed)$/i.test(t))) {
						extendElementWith(element, Element.Methods);
						extendElementWith(element, Element.Methods.Simulated);
						extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]);
					}
				}
				return element;
			}
		}
		return Prototype.K;
	}

	var Methods = { }, ByTag = Element.Methods.ByTag;

	var extend = Object.extend(function(element) {
		if (!element || typeof element._extendedByPrototype != 'undefined' ||
				element.nodeType != 1 || element == window) return element;

		var methods = Object.clone(Methods),
				tagName = element.tagName.toUpperCase();

		if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

		extendElementWith(element, methods);

		element._extendedByPrototype = Prototype.emptyFunction;
		return element;

	}, {
		refresh: function() {
			if (!Prototype.BrowserFeatures.ElementExtensions) {
				Object.extend(Methods, Element.Methods);
				Object.extend(Methods, Element.Methods.Simulated);
			}
		}
	});

	extend.refresh();
	return extend;
})();

Function : Element.hasAttribute

Internal function

Element.hasAttribute = function(element, attribute) {
	if (element.hasAttribute) return element.hasAttribute(attribute);
	return Element.Methods.Simulated.hasAttribute(element, attribute);
};

Function : Element.addMethods

Takes a hash of methods and makes them available as methods of extended elements and of the Element object. The second usage form is for targeting a specific HTML element

Element.addMethods = function(methods) {
	var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

	if (!methods) {
		Object.extend(Form, Form.Methods);
		Object.extend(Form.Element, Form.Element.Methods);
		Object.extend(Element.Methods.ByTag, {
			"FORM":     Object.clone(Form.Methods),
			"INPUT":    Object.clone(Form.Element.Methods),
			"SELECT":   Object.clone(Form.Element.Methods),
			"TEXTAREA": Object.clone(Form.Element.Methods)
		});
	}

	if (arguments.length == 2) {
		var tagName = methods;
		methods = arguments[1];
	}

	if (!tagName) Object.extend(Element.Methods, methods || { });
	else {
		if (Object.isArray(tagName)) tagName.each(extend);
		else extend(tagName);
	}

	function extend(tagName) {
		tagName = tagName.toUpperCase();
		if (!Element.Methods.ByTag[tagName])
			Element.Methods.ByTag[tagName] = { };
		Object.extend(Element.Methods.ByTag[tagName], methods);
	}

	function copy(methods, destination, onlyIfAbsent) {
		onlyIfAbsent = onlyIfAbsent || false;
		for (var property in methods) {
			var value = methods[property];
			if (!Object.isFunction(value)) continue;
			if (!onlyIfAbsent || !(property in destination))
				destination[property] = value.methodize();
		}
	}

	function findDOMClass(tagName) {
		var klass;
		var trans = {
			"OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
			"FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
			"DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
			"H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
			"INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
			"TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
			"TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
			"TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
			"FrameSet", "IFRAME": "IFrame"
		};
		if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
		if (window[klass]) return window[klass];
		klass = 'HTML' + tagName + 'Element';
		if (window[klass]) return window[klass];
		klass = 'HTML' + tagName.capitalize() + 'Element';
		if (window[klass]) return window[klass];

		var element = document.createElement(tagName);
		var proto = element['__proto__'] || element.constructor.prototype;
		element = null;
		return proto;
	}

	var elementPrototype = window.HTMLElement ? HTMLElement.prototype :
 	Element.prototype;

	if (F.ElementExtensions) {
		copy(Element.Methods, elementPrototype);
		copy(Element.Methods.Simulated, elementPrototype, true);
	}

	if (F.SpecificElementExtensions) {
		for (var tag in Element.Methods.ByTag) {
			var klass = findDOMClass(tag);
			if (Object.isUndefined(klass)) continue;
			copy(T[tag], klass.prototype);
		}
	}

	Object.extend(Element, Element.Methods);
	delete Element.ByTag;

	if (Element.extend.refresh) Element.extend.refresh();
	Element.cache = { };
};

Properties : document.viewport

document.viewport = {

	getDimensions: function() {
		return { width: this.getWidth(), height: this.getHeight() };
	},

	getScrollOffsets: function() {
		return Element._returnOffset(
			window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
			window.pageYOffset || document.documentElement.scrollTop  || document.body.scrollTop);
	}
};

Code : Browser-specific viewport code

(function(viewport) {
	var B = Prototype.Browser, doc = document, element, property = {};

	function getRootElement() {
		if (B.WebKit && !doc.evaluate)
			return document;

		if (B.Opera && window.parseFloat(window.opera.version()) < 9.5)
			return document.body;

		return document.documentElement;
	}

	function define(D) {
		if (!element) element = getRootElement();

		property[D] = 'client' + D;

		viewport['get' + D] = function() { return element[property[D]] };
		return viewport['get' + D]();
	}

	viewport.getWidth  = define.curry('Width');

	viewport.getHeight = define.curry('Height');
})(document.viewport);

Properties : Element.Storage

Element.Storage = {
	UID: 1
};

Code : Additional methods for Element

Element.addMethods({
	getStorage: function(element) {
		if (!(element = $(element))) return;

		var uid;
		if (element === window) {
			uid = 0;
		} else {
			if (typeof element._prototypeUID === "undefined")
				element._prototypeUID = [Element.Storage.UID++];
			uid = element._prototypeUID[0];
		}

		if (!Element.Storage[uid])
			Element.Storage[uid] = $H();

		return Element.Storage[uid];
	},

	store: function(element, key, value) {
		if (!(element = $(element))) return;

		if (arguments.length === 2) {
			Element.getStorage(element).update(key);
		} else {
			Element.getStorage(element).set(key, value);
		}

		return element;
	},

	retrieve: function(element, key, defaultValue) {
		if (!(element = $(element))) return;
		var hash = Element.getStorage(element), value = hash.get(key);

		if (Object.isUndefined(value)) {
			hash.set(key, defaultValue);
			value = defaultValue;
		}

		return value;
	},

	clone: function(element, deep) {
		if (!(element = $(element))) return;
		var clone = element.cloneNode(deep);
		clone._prototypeUID = void 0;
		if (deep) {
			var descendants = Element.select(clone, '*'),
					i = descendants.length;
			while (i--) {
				descendants[i]._prototypeUID = void 0;
			}
		}
		return Element.extend(clone);
	}
});

Class : Selector

Description


/* Portions of the Selector class are derived from Jack Slocum's DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector = Class.create({

Function : initialize

Constructor function


	initialize: function(expression) {
		this.expression = expression.strip();

		if (this.shouldUseSelectorsAPI()) {
			this.mode = 'selectorsAPI';
		} else if (this.shouldUseXPath()) {
			this.mode = 'xpath';
			this.compileXPathMatcher();
		} else {
			this.mode = "normal";
			this.compileMatcher();
		}

	},
						

Properties : shouldUseXPath

	shouldUseXPath: (function() {

		var IS_DESCENDANT_SELECTOR_BUGGY = (function(){
			var isBuggy = false;
			if (document.evaluate && window.XPathResult) {
				var el = document.createElement('div');
				el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>';

				var xpath = ".//*[local-name()='ul' or local-name()='UL']" +
					"//*[local-name()='li' or local-name()='LI']";

				var result = document.evaluate(xpath, el, null,
					XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

				isBuggy = (result.snapshotLength !== 2);
				el = null;
			}
			return isBuggy;
		})();

		return function() {
			if (!Prototype.BrowserFeatures.XPath) return false;

			var e = this.expression;

			if (Prototype.Browser.WebKit &&
 			(e.include("-of-type") || e.include(":empty")))
				return false;

			if ((/(\[[\w-]*?:|:checked)/).test(e))
				return false;

			if (IS_DESCENDANT_SELECTOR_BUGGY) return false;

			return true;
		}

	})(),	

Function : shouldUseSelectorsAPI

Internal function


	shouldUseSelectorsAPI: function() {
		if (!Prototype.BrowserFeatures.SelectorsAPI) return false;

		if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false;

		if (!Selector._div) Selector._div = new Element('div');

		try {
			Selector._div.querySelector(this.expression);
		} catch(e) {
			return false;
		}

		return true;
	},
						

Function : compileMatcher

Internal function


	compileMatcher: function() {
		var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
				c = Selector.criteria, le, p, m, len = ps.length, name;

		if (Selector._cache[e]) {
			this.matcher = Selector._cache[e];
			return;
		}

		this.matcher = ["this.matcher = function(root) {",
  									"var r = root, h = Selector.handlers, c = false, n;"];

		while (e && le != e && (/\S/).test(e)) {
			le = e;
			for (var i = 0; i<len; i++) {
				p = ps[i].re;
				name = ps[i].name;
				if (m = e.match(p)) {
					this.matcher.push(Object.isFunction(c[name]) ? c[name](m) :
						new Template(c[name]).evaluate(m));
					e = e.replace(m[0], '');
					break;
				}
			}
		}

		this.matcher.push("return h.unique(n);\n}");
		eval(this.matcher.join('\n'));
		Selector._cache[this.expression] = this.matcher;
	},
						

Function : compileXPathMatcher

Internal function


	compileXPathMatcher: function() {
		var e = this.expression, ps = Selector.patterns,
				x = Selector.xpath, le, m, len = ps.length, name;

		if (Selector._cache[e]) {
			this.xpath = Selector._cache[e]; return;
		}

		this.matcher = ['.//*'];
		while (e && le != e && (/\S/).test(e)) {
			le = e;
			for (var i = 0; i<len; i++) {
				name = ps[i].name;
				if (m = e.match(ps[i].re)) {
					this.matcher.push(Object.isFunction(x[name]) ? x[name](m) :
						new Template(x[name]).evaluate(m));
					e = e.replace(m[0], '');
					break;
				}
			}
		}

		this.xpath = this.matcher.join('');
		Selector._cache[this.expression] = this.xpath;
	},
						

Function : findElements

Searches the document for elements that match the instance's CSS selector.


	findElements: function(root) {
		root = root || document;
		var e = this.expression, results;

		switch (this.mode) {
			case 'selectorsAPI':
				if (root !== document) {
					var oldId = root.id, id = $(root).identify();
					id = id.replace(/([\.:])/g, "\\$1");
					e = "#" + id + " " + e;
				}

				results = $A(root.querySelectorAll(e)).map(Element.extend);
				root.id = oldId;

				return results;
			case 'xpath':
				return document._getElementsByXPath(this.xpath, root);
			default:
 			return this.matcher(root);
		}
	},
						

Function : match

Tests whether a element matches the instance's CSS selector.


	match: function(element) {
		this.tokens = [];

		var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
		var le, p, m, len = ps.length, name;

		while (e && le !== e && (/\S/).test(e)) {
			le = e;
			for (var i = 0; i<len; i++) {
				p = ps[i].re;
				name = ps[i].name;
				if (m = e.match(p)) {
					if (as[name]) {
						this.tokens.push([name, Object.clone(m)]);
						e = e.replace(m[0], '');
					} else {
						return this.findElements(document).include(element);
					}
				}
			}
		}

		var match = true, name, matches;
		for (var i = 0, token; token = this.tokens[i]; i++) {
			name = token[0], matches = token[1];
			if (!Selector.assertions[name](element, matches)) {
				match = false; break;
			}
		}

		return match;
	},

						

Function : toString

Returns the selector expression


	toString: function() {
		return this.expression;
	},
						

Function : inspect

Returns a debug-oriented output


	inspect: function() {
		return "#<Selector:" + this.expression.inspect() + ">";
	}
						
});

Code : Browser-specific selectors test

if (Prototype.BrowserFeatures.SelectorsAPI &&
 document.compatMode === 'BackCompat') {
	Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){
		var div = document.createElement('div'),
 		span = document.createElement('span');

		div.id = "prototype_test_id";
		span.className = 'Test';
		div.appendChild(span);
		var isIgnored = (div.querySelector('#prototype_test_id .test') !== null);
		div = span = null;
		return isIgnored;
	})();
}

Extend : Selector

A class that queries the document for elements that match a given CSS selector.

Object.extend(Selector, {

Properties : _cache

	_cache: { },

Object : xpath

	xpath: {
		descendant:   "//*",
		child:        "/*",
		adjacent:     "/following-sibling::*[1]",
		laterSibling: '/following-sibling::*',
		tagName:      function(m) {
			if (m[1] == '*') return '';
			return "[local-name()='" + m[1].toLowerCase() +
 						"' or local-name()='" + m[1].toUpperCase() + "']";
		},
		className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
		id:           "[@id='#{1}']",
		attrPresence: function(m) {
			m[1] = m[1].toLowerCase();
			return new Template("[@#{1}]").evaluate(m);
		},
		attr: function(m) {
			m[1] = m[1].toLowerCase();
			m[3] = m[5] || m[6];
			return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
		},
		pseudo: function(m) {
			var h = Selector.xpath.pseudos[m[1]];
			if (!h) return '';
			if (Object.isFunction(h)) return h(m);
			return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
		},
		operators: {
			'=':  "[@#{1}='#{3}']",
			'!=': "[@#{1}!='#{3}']",
			'^=': "[starts-with(@#{1}, '#{3}')]",
			'$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
			'*=': "[contains(@#{1}, '#{3}')]",
			'~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
			'|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
		},
		pseudos: {
			'first-child': '[not(preceding-sibling::*)]',
			'last-child':  '[not(following-sibling::*)]',
			'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
			'empty':       "[count(*) = 0 and (count(text()) = 0)]",
			'checked':     "[@checked]",
			'disabled':    "[(@disabled) and (@type!='hidden')]",
			'enabled':     "[not(@disabled) and (@type!='hidden')]",
			'not': function(m) {
				var e = m[6], p = Selector.patterns,
						x = Selector.xpath, le, v, len = p.length, name;

				var exclusion = [];
				while (e && le != e && (/\S/).test(e)) {
					le = e;
					for (var i = 0; i<len; i++) {
						name = p[i].name
						if (m = e.match(p[i].re)) {
							v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m);
							exclusion.push("(" + v.substring(1, v.length - 1) + ")");
							e = e.replace(m[0], '');
							break;
						}
					}
				}
				return "[not(" + exclusion.join(" and ") + ")]";
			},
			'nth-child':      function(m) {
				return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
			},
			'nth-last-child': function(m) {
				return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
			},
			'nth-of-type':    function(m) {
				return Selector.xpath.pseudos.nth("position() ", m);
			},
			'nth-last-of-type': function(m) {
				return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
			},
			'first-of-type':  function(m) {
				m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
			},
			'last-of-type':   function(m) {
				m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
			},
			'only-of-type':   function(m) {
				var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
			},
			nth: function(fragment, m) {
				var mm, formula = m[6], predicate;
				if (formula == 'even') formula = '2n+0';
				if (formula == 'odd')  formula = '2n+1';
				if (mm = formula.match(/^(\d+)$/)) // digit only
					return '[' + fragment + "= " + mm[1] + ']';
				if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
					if (mm[1] == "-") mm[1] = -1;
					var a = mm[1] ? Number(mm[1]) : 1;
					var b = mm[2] ? Number(mm[2]) : 0;
					predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
					"((#{fragment} - #{b}) div #{a} >= 0)]";
					return new Template(predicate).evaluate({
						fragment: fragment, a: a, b: b });
				}
			}
		}
	},

Object : criteria

	criteria: {
		tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
		className:    'n = h.className(n, r, "#{1}", c);    c = false;',
		id:           'n = h.id(n, r, "#{1}", c);           c = false;',
		attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
		attr: function(m) {
			m[3] = (m[5] || m[6]);
			return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
		},
		pseudo: function(m) {
			if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
			return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
		},
		descendant:   'c = "descendant";',
		child:        'c = "child";',
		adjacent:     'c = "adjacent";',
		laterSibling: 'c = "laterSibling";'
	},

Object : patterns

	patterns: [
		{ name: 'laterSibling', re: /^\s*~\s*/ },
		{ name: 'child',        re: /^\s*>\s*/ },
		{ name: 'adjacent',     re: /^\s*\+\s*/ },
		{ name: 'descendant',   re: /^\s/ },

		{ name: 'tagName',      re: /^\s*(\*|[\w\-]+)(\b|$)?/ },
		{ name: 'id',           re: /^#([\w\-\*]+)(\b|$)/ },
		{ name: 'className',    re: /^\.([\w\-\*]+)(\b|$)/ },
		{ name: 'pseudo',       re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ },
		{ name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ },
		{ name: 'attr',         re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }
	],

Object : assertions

	assertions: {
		tagName: function(element, matches) {
			return matches[1].toUpperCase() == element.tagName.toUpperCase();
		},

		className: function(element, matches) {
			return Element.hasClassName(element, matches[1]);
		},

		id: function(element, matches) {
			return element.id === matches[1];
		},

		attrPresence: function(element, matches) {
			return Element.hasAttribute(element, matches[1]);
		},

		attr: function(element, matches) {
			var nodeValue = Element.readAttribute(element, matches[1]);
			return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
		}
	},

Object : handlers

	handlers: {
		concat: function(a, b) {
			for (var i = 0, node; node = b[i]; i++)
				a.push(node);
			return a;
		},

		mark: function(nodes) {
			var _true = Prototype.emptyFunction;
			for (var i = 0, node; node = nodes[i]; i++)
				node._countedByPrototype = _true;
			return nodes;
		},

		unmark: (function(){

			var PROPERTIES_ATTRIBUTES_MAP = (function(){
				var el = document.createElement('div'),
						isBuggy = false,
						propName = '_countedByPrototype',
						value = 'x'
				el[propName] = value;
				isBuggy = (el.getAttribute(propName) === value);
				el = null;
				return isBuggy;
			})();

			return PROPERTIES_ATTRIBUTES_MAP ?
				function(nodes) {
					for (var i = 0, node; node = nodes[i]; i++)
						node.removeAttribute('_countedByPrototype');
					return nodes;
				} :
				function(nodes) {
					for (var i = 0, node; node = nodes[i]; i++)
						node._countedByPrototype = void 0;
					return nodes;
				}
		})(),

		index: function(parentNode, reverse, ofType) {
			parentNode._countedByPrototype = Prototype.emptyFunction;
			if (reverse) {
				for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
					var node = nodes[i];
					if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
				}
			} else {
				for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
					if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
			}
		},

		unique: function(nodes) {
			if (nodes.length == 0) return nodes;
			var results = [], n;
			for (var i = 0, l = nodes.length; i < l; i++)
				if (typeof (n = nodes[i])._countedByPrototype == 'undefined') {
					n._countedByPrototype = Prototype.emptyFunction;
					results.push(Element.extend(n));
				}
			return Selector.handlers.unmark(results);
		},

		descendant: function(nodes) {
			var h = Selector.handlers;
			for (var i = 0, results = [], node; node = nodes[i]; i++)
				h.concat(results, node.getElementsByTagName('*'));
			return results;
		},

		child: function(nodes) {
			var h = Selector.handlers;
			for (var i = 0, results = [], node; node = nodes[i]; i++) {
				for (var j = 0, child; child = node.childNodes[j]; j++)
					if (child.nodeType == 1 && child.tagName != '!') results.push(child);
			}
			return results;
		},

		adjacent: function(nodes) {
			for (var i = 0, results = [], node; node = nodes[i]; i++) {
				var next = this.nextElementSibling(node);
				if (next) results.push(next);
			}
			return results;
		},

		laterSibling: function(nodes) {
			var h = Selector.handlers;
			for (var i = 0, results = [], node; node = nodes[i]; i++)
				h.concat(results, Element.nextSiblings(node));
			return results;
		},

		nextElementSibling: function(node) {
			while (node = node.nextSibling)
				if (node.nodeType == 1) return node;
			return null;
		},

		previousElementSibling: function(node) {
			while (node = node.previousSibling)
				if (node.nodeType == 1) return node;
			return null;
		},

		tagName: function(nodes, root, tagName, combinator) {
			var uTagName = tagName.toUpperCase();
			var results = [], h = Selector.handlers;
			if (nodes) {
				if (combinator) {
					if (combinator == "descendant") {
						for (var i = 0, node; node = nodes[i]; i++)
							h.concat(results, node.getElementsByTagName(tagName));
						return results;
					} else nodes = this[combinator](nodes);
					if (tagName == "*") return nodes;
				}
				for (var i = 0, node; node = nodes[i]; i++)
					if (node.tagName.toUpperCase() === uTagName) results.push(node);
				return results;
			} else return root.getElementsByTagName(tagName);
		},

		id: function(nodes, root, id, combinator) {
			var targetNode = $(id), h = Selector.handlers;

			if (root == document) {
				if (!targetNode) return [];
				if (!nodes) return [targetNode];
			} else {
				if (!root.sourceIndex || root.sourceIndex < 1) {
					var nodes = root.getElementsByTagName('*');
					for (var j = 0, node; node = nodes[j]; j++) {
						if (node.id === id) return [node];
					}
				}
			}

			if (nodes) {
				if (combinator) {
					if (combinator == 'child') {
						for (var i = 0, node; node = nodes[i]; i++)
							if (targetNode.parentNode == node) return [targetNode];
					} else if (combinator == 'descendant') {
						for (var i = 0, node; node = nodes[i]; i++)
							if (Element.descendantOf(targetNode, node)) return [targetNode];
					} else if (combinator == 'adjacent') {
						for (var i = 0, node; node = nodes[i]; i++)
							if (Selector.handlers.previousElementSibling(targetNode) == node)
								return [targetNode];
					} else nodes = h[combinator](nodes);
				}
				for (var i = 0, node; node = nodes[i]; i++)
					if (node == targetNode) return [targetNode];
				return [];
			}
			return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
		},

		className: function(nodes, root, className, combinator) {
			if (nodes && combinator) nodes = this[combinator](nodes);
			return Selector.handlers.byClassName(nodes, root, className);
		},

		byClassName: function(nodes, root, className) {
			if (!nodes) nodes = Selector.handlers.descendant([root]);
			var needle = ' ' + className + ' ';
			for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
				nodeClassName = node.className;
				if (nodeClassName.length == 0) continue;
				if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
					results.push(node);
			}
			return results;
		},

		attrPresence: function(nodes, root, attr, combinator) {
			if (!nodes) nodes = root.getElementsByTagName("*");
			if (nodes && combinator) nodes = this[combinator](nodes);
			var results = [];
			for (var i = 0, node; node = nodes[i]; i++)
				if (Element.hasAttribute(node, attr)) results.push(node);
			return results;
		},

		attr: function(nodes, root, attr, value, operator, combinator) {
			if (!nodes) nodes = root.getElementsByTagName("*");
			if (nodes && combinator) nodes = this[combinator](nodes);
			var handler = Selector.operators[operator], results = [];
			for (var i = 0, node; node = nodes[i]; i++) {
				var nodeValue = Element.readAttribute(node, attr);
				if (nodeValue === null) continue;
				if (handler(nodeValue, value)) results.push(node);
			}
			return results;
		},

		pseudo: function(nodes, name, value, root, combinator) {
			if (nodes && combinator) nodes = this[combinator](nodes);
			if (!nodes) nodes = root.getElementsByTagName("*");
			return Selector.pseudos[name](nodes, value, root);
		}
	},

Object : pseudos

	pseudos: {
		'first-child': function(nodes, value, root) {
			for (var i = 0, results = [], node; node = nodes[i]; i++) {
				if (Selector.handlers.previousElementSibling(node)) continue;
					results.push(node);
			}
			return results;
		},
		'last-child': function(nodes, value, root) {
			for (var i = 0, results = [], node; node = nodes[i]; i++) {
				if (Selector.handlers.nextElementSibling(node)) continue;
					results.push(node);
			}
			return results;
		},
		'only-child': function(nodes, value, root) {
			var h = Selector.handlers;
			for (var i = 0, results = [], node; node = nodes[i]; i++)
				if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
					results.push(node);
			return results;
		},
		'nth-child':        function(nodes, formula, root) {
			return Selector.pseudos.nth(nodes, formula, root);
		},
		'nth-last-child':   function(nodes, formula, root) {
			return Selector.pseudos.nth(nodes, formula, root, true);
		},
		'nth-of-type':      function(nodes, formula, root) {
			return Selector.pseudos.nth(nodes, formula, root, false, true);
		},
		'nth-last-of-type': function(nodes, formula, root) {
			return Selector.pseudos.nth(nodes, formula, root, true, true);
		},
		'first-of-type':    function(nodes, formula, root) {
			return Selector.pseudos.nth(nodes, "1", root, false, true);
		},
		'last-of-type':     function(nodes, formula, root) {
			return Selector.pseudos.nth(nodes, "1", root, true, true);
		},
		'only-of-type':     function(nodes, formula, root) {
			var p = Selector.pseudos;
			return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
		},

		getIndices: function(a, b, total) {
			if (a == 0) return b > 0 ? [b] : [];
			return $R(1, total).inject([], function(memo, i) {
				if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
				return memo;
			});
		},

		nth: function(nodes, formula, root, reverse, ofType) {
			if (nodes.length == 0) return [];
			if (formula == 'even') formula = '2n+0';
			if (formula == 'odd')  formula = '2n+1';
			var h = Selector.handlers, results = [], indexed = [], m;
			h.mark(nodes);
			for (var i = 0, node; node = nodes[i]; i++) {
				if (!node.parentNode._countedByPrototype) {
					h.index(node.parentNode, reverse, ofType);
					indexed.push(node.parentNode);
				}
			}
			if (formula.match(/^\d+$/)) { // just a number
				formula = Number(formula);
				for (var i = 0, node; node = nodes[i]; i++)
					if (node.nodeIndex == formula) results.push(node);
			} else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
				if (m[1] == "-") m[1] = -1;
				var a = m[1] ? Number(m[1]) : 1;
				var b = m[2] ? Number(m[2]) : 0;
				var indices = Selector.pseudos.getIndices(a, b, nodes.length);
				for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
					for (var j = 0; j < l; j++)
						if (node.nodeIndex == indices[j]) results.push(node);
				}
			}
			h.unmark(nodes);
			h.unmark(indexed);
			return results;
		},

		'empty': function(nodes, value, root) {
			for (var i = 0, results = [], node; node = nodes[i]; i++) {
				if (node.tagName == '!' || node.firstChild) continue;

				results.push(node);
			}
			return results;
		},

		'not': function(nodes, selector, root) {
			var h = Selector.handlers, selectorType, m;
			var exclusions = new Selector(selector).findElements(root);
			h.mark(exclusions);
			for (var i = 0, results = [], node; node = nodes[i]; i++)
				if (!node._countedByPrototype) results.push(node);
			h.unmark(exclusions);
			return results;
		},

		'enabled': function(nodes, value, root) {
			for (var i = 0, results = [], node; node = nodes[i]; i++)
				if (!node.disabled && (!node.type || node.type !== 'hidden'))
					results.push(node);
			return results;
		},

		'disabled': function(nodes, value, root) {
			for (var i = 0, results = [], node; node = nodes[i]; i++)
				if (node.disabled) results.push(node);
			return results;
		},

		'checked': function(nodes, value, root) {
			for (var i = 0, results = [], node; node = nodes[i]; i++)
				if (node.checked) results.push(node);
			return results;
		}
	},

Object : operators

	operators: {
		'=':  function(nv, v) { return nv == v; },
		'!=': function(nv, v) { return nv != v; },
		'^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); },
		'$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); },
		'*=': function(nv, v) { return nv == v || nv && nv.include(v); },
		'~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
		'|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() +
 		'-').include('-' + (v || "").toUpperCase() + '-'); }
	},

Function : split

Internal function

	split: function(expression) {
		var expressions = [];
		expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
			expressions.push(m[1].strip());
		});
		return expressions;
	},

Function : matchElements

Internal function

	matchElements: function(elements, expression) {
		var matches = $$(expression), h = Selector.handlers;
		h.mark(matches);
		for (var i = 0, results = [], element; element = elements[i]; i++)
			if (element._countedByPrototype) results.push(element);
		h.unmark(matches);
		return results;
	},

Function : findElement

Returns the indexth element in the collection that matches expression. Returns the indexth element overall if expression is not given

	findElement: function(elements, expression, index) {
		if (Object.isNumber(expression)) {
			index = expression; expression = false;
		}
		return Selector.matchElements(elements, expression || '*')[index || 0];
	},

Function : findChildElements

Searches beneath element for any elements that match the selector (or selectors) specified in expressions


	findChildElements: function(element, expressions) {
		expressions = Selector.split(expressions.join(','));
		var results = [], h = Selector.handlers;
		for (var i = 0, l = expressions.length, selector; i < l; i++) {
			selector = new Selector(expressions[i].strip());
			h.concat(results, selector.findElements(element));
		}
		return (l > 1) ? h.unique(results) : results;
	}
});

Code : IE-specific code

if (Prototype.Browser.IE) {
	Object.extend(Selector.handlers, {
		concat: function(a, b) {
			for (var i = 0, node; node = b[i]; i++)
				if (node.tagName !== "!") a.push(node);
			return a;
		}
	});
}

Function : $$

Takes an arbitrary number of CSS selectors (strings) and returns a document-order array of extended DOM elements that match any of them

function $$() {
	return Selector.findChildElements(document, $A(arguments));
}

Section : Form

Object : Form

Form is a namespace and a module for all things form-related, packed with form manipulation and serialization goodness. While it holds methods dealing with forms as whole, its submodule Form.Element deals with specific form controls

var Form = {

Function : reset

Resets a form to its default values

	reset: function(form) {
		form = $(form);
		form.reset();
		return form;
	},

Function : serializeElements

Serialize an array of form elements to a string suitable for Ajax requests (default behavior) or, if optional getHash evaluates to true, an object hash where keys are form control names and values are data

	serializeElements: function(elements, options) {
		if (typeof options != 'object') options = { hash: !!options };
		else if (Object.isUndefined(options.hash)) options.hash = true;
		var key, value, submitted = false, submit = options.submit;

		var data = elements.inject({ }, function(result, element) {
			if (!element.disabled && element.name) {
				key = element.name; value = $(element).getValue();
				if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted &&
						submit !== false && (!submit || key == submit) && (submitted = true)))) {
					if (key in result) {
						if (!Object.isArray(result[key])) result[key] = [result[key]];
						result[key].push(value);
					}
					else result[key] = value;
				}
			}
			return result;
		});

		return options.hash ? data : Object.toQueryString(data);
	}
};

Object : Form Methods

Most of these methods are also available directly on FORM elements that have been extended

Form.Methods = {

Function : serialize

Serialize form data to a string suitable for Ajax requests (default behavior) or, if optional getHash evaluates to true, an object hash where keys are form control names and values are data

	serialize: function(form, options) {
		return Form.serializeElements(Form.getElements(form), options);
	},

Function : getElements

Returns a collection of all form controls within a form

	getElements: function(form) {
		var elements = $(form).getElementsByTagName('*'),
				element,
				arr = [ ],
				serializers = Form.Element.Serializers;
		for (var i = 0; element = elements[i]; i++) {
			arr.push(element);
		}
		return arr.inject([], function(elements, child) {
			if (serializers[child.tagName.toLowerCase()])
				elements.push(Element.extend(child));
			return elements;
		})
	},

Function : getInputs

Returns a collection of all INPUT elements in a form

	getInputs: function(form, typeName, name) {
		form = $(form);
		var inputs = form.getElementsByTagName('input');

		if (!typeName && !name) return $A(inputs).map(Element.extend);

		for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
			var input = inputs[i];
			if ((typeName && input.type != typeName) || (name && input.name != name))
				continue;
			matchingInputs.push(Element.extend(input));
		}

		return matchingInputs;
	},

Function : disable

Disables the form as whole

	disable: function(form) {
		form = $(form);
		Form.getElements(form).invoke('disable');
		return form;
	},

Function : enable

Enables a fully or partially disabled form

	enable: function(form) {
		form = $(form);
		Form.getElements(form).invoke('enable');
		return form;
	},

Function : findFirstElement

Finds first non-hidden, non-disabled form control

	findFirstElement: function(form) {
		var elements = $(form).getElements().findAll(function(element) {
			return 'hidden' != element.type && !element.disabled;
		});
		var firstByIndex = elements.findAll(function(element) {
			return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
		}).sortBy(function(element) { return element.tabIndex }).first();

		return firstByIndex ? firstByIndex : elements.find(function(element) {
			return /^(?:input|select|textarea)$/i.test(element.tagName);
		});
	},

Function : focusFirstElement

Gives keyboard focus to the first element of the form

	focusFirstElement: function(form) {
		form = $(form);
		form.findFirstElement().activate();
		return form;
	},

Function : request

A convenience method for serializing and submitting the form via an Ajax

	request: function(form, options) {
		form = $(form), options = Object.clone(options || { });

		var params = options.parameters, action = form.readAttribute('action') || '';
		if (action.blank()) action = window.location.href;
		options.parameters = form.serialize(true);

		if (params) {
			if (Object.isString(params)) params = params.toQueryParams();
			Object.extend(options.parameters, params);
		}

		if (form.hasAttribute('method') && !options.method)
			options.method = form.method;

		return new Ajax.Request(action, options);
	}
};

Object : Form Element

This is a collection of methods that assist in dealing with form controls. They provide ways to focus, serialize, disable/enable or extract current value from a specific control.

Form.Element = {

Function : focus

Gives keyboard focus to an element

	focus: function(element) {
		$(element).focus();
		return element;
	},

Function : select

Selects the current text in a text input

	select: function(element) {
		$(element).select();
		return element;
	}
};

Object : Form.Element.Methods

Description

Form.Element.Methods = {

Function : serialize

Creates an URL-encoded string representation of a form control in the name=value format

	serialize: function(element) {
		element = $(element);
		if (!element.disabled && element.name) {
			var value = element.getValue();
			if (value != undefined) {
				var pair = { };
				pair[element.name] = value;
				return Object.toQueryString(pair);
			}
		}
		return '';
	},

Function : getValue

Returns the current value of a form control

	getValue: function(element) {
		element = $(element);
		var method = element.tagName.toLowerCase();
		return Form.Element.Serializers[method](element);
	},

Function : setValue

Sets the current value of a form control

	setValue: function(element, value) {
		element = $(element);
		var method = element.tagName.toLowerCase();
		Form.Element.Serializers[method](element, value);
		return element;
	},

Function : clear

Clears the contents of a text input

	clear: function(element) {
		$(element).value = '';
		return element;
	},

Function : present

Returns true if a text input has contents, false otherwise

	present: function(element) {
		return $(element).value != '';
	},

Function : activate

Gives focus to a form control and selects its contents if it is a text input

	activate: function(element) {
		element = $(element);
		try {
			element.focus();
			if (element.select && (element.tagName.toLowerCase() != 'input' ||
					!(/^(?:button|reset|submit)$/i.test(element.type))))
				element.select();
		} catch (e) { }
		return element;
	},

Function : disable

Disables a form control, effectively preventing its value to be changed until it is enabled again

	disable: function(element) {
		element = $(element);
		element.disabled = true;
		return element;
	},

Function : enable

Enables a previously disabled form control

	enable: function(element) {
		element = $(element);
		element.disabled = false;
		return element;
	}
};

Object : Field

An alias for Form.Element

var Field = Form.Element;

Function : $F

Returns the value of a form control. This is a convenience alias of Form.Element.getValue. Refer to it for full details

var $F = Form.Element.Methods.getValue;

Object : Form.Element.Serializers

Form serialization functions

Form.Element.Serializers = {

	input: function(element, value) {
		switch (element.type.toLowerCase()) {
			case 'checkbox':
			case 'radio':
				return Form.Element.Serializers.inputSelector(element, value);
			default:
				return Form.Element.Serializers.textarea(element, value);
		}
	},
	
	inputSelector: function(element, value) {
		if (Object.isUndefined(value)) return element.checked ? element.value : null;
		else element.checked = !!value;
	},
	
	textarea: function(element, value) {
		if (Object.isUndefined(value)) return element.value;
		else element.value = value;
	},
	
	select: function(element, value) {
		if (Object.isUndefined(value))
			return this[element.type == 'select-one' ?
				'selectOne' : 'selectMany'](element);
		else {
			var opt, currentValue, single = !Object.isArray(value);
			for (var i = 0, length = element.length; i < length; i++) {
				opt = element.options[i];
				currentValue = this.optionValue(opt);
				if (single) {
					if (currentValue == value) {
						opt.selected = true;
						return;
					}
				}
				else opt.selected = value.include(currentValue);
			}
		}
	},
	
	selectOne: function(element) {
		var index = element.selectedIndex;
		return index >= 0 ? this.optionValue(element.options[index]) : null;
	},
	
	selectMany: function(element) {
		var values, length = element.length;
		if (!length) return null;

		for (var i = 0, values = []; i < length; i++) {
			var opt = element.options[i];
			if (opt.selected) values.push(this.optionValue(opt));
		}
		return values;

	},
	
	optionValue: function(opt) {
		return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
	}
};

Object : Timed Observer

An abstract observer class which instances can be used to periodically check some value and trigger a callback when the value has changed. The frequency is in seconds

Abstract.TimedObserver = Class.create(PeriodicalExecuter, {

Function : initialize

Constructor function

	initialize: function($super, element, frequency, callback) {
		$super(callback, frequency);
		this.element   = $(element);
		this.lastValue = this.getValue();
	},

Function : execute

Internal function

	execute: function() {
		var value = this.getValue();
		if (Object.isString(this.lastValue) && Object.isString(value) ?
				this.lastValue != value : String(this.lastValue) != String(value)) {
			this.callback(this.element, value);
			this.lastValue = value;
		}
	}
});

Class : Form.Element.Observer

orm.Element observer implements the getValue() method using Form.Element.getValue() on the given element. See Abstract.TimedObserver for general documentation on timed observers


Form.Element.Observer = Class.create(Abstract.TimedObserver, {

Function : getValue

Returns the current value of a form control.

	getValue: function() {
		return Form.Element.getValue(this.element);
	}	
});

Class : Form.Observer

Form observer implements the getValue() method using Form.serialize() on the element from the first argument. See Abstract.TimedObserver for general documentation on timed observers


Form.Observer = Class.create(Abstract.TimedObserver, {

Function : getValue

Serialize the form data to a string


	getValue: function() {
		return Form.serialize(this.element);
	}
	
});

Class : Abstract.EventObserver

No info


Abstract.EventObserver = Class.create({

Function : initialize

Constructor function

	initialize: function(element, callback) {
		this.element  = $(element);
		this.callback = callback;

		this.lastValue = this.getValue();
		if (this.element.tagName.toLowerCase() == 'form')
			this.registerFormCallbacks();
		else
			this.registerCallback(this.element);
	},

Function : onElementEvent

Internal function

	onElementEvent: function() {
		var value = this.getValue();
		if (this.lastValue != value) {
			this.callback(this.element, value);
			this.lastValue = value;
		}
	},

Function : registerFormCallbacks

Internal function

	registerFormCallbacks: function() {
		Form.getElements(this.element).each(this.registerCallback, this);
	},

Function : registerCallback

Internal function

	registerCallback: function(element) {
		if (element.type) {
			switch (element.type.toLowerCase()) {
				case 'checkbox':
				case 'radio':
					Event.observe(element, 'click', this.onElementEvent.bind(this));
					break;
				default:
					Event.observe(element, 'change', this.onElementEvent.bind(this));
					break;
			}
		}
	}
});

Class : Form.Element.EventObserver

No info


Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
	getValue: function() {
		return Form.Element.getValue(this.element);
	}
});

Class : Form.EventObserver

No info


Form.EventObserver = Class.create(Abstract.EventObserver, {
	getValue: function() {
		return Form.serialize(this.element);
	}
});

Section : Event

Code : Self-executing anonymous function

(function() {

object : Event

Event management class

	var Event = {
		KEY_BACKSPACE: 8,
		KEY_TAB:       9,
		KEY_RETURN:   13,
		KEY_ESC:      27,
		KEY_LEFT:     37,
		KEY_UP:       38,
		KEY_RIGHT:    39,
		KEY_DOWN:     40,
		KEY_DELETE:   46,
		KEY_HOME:     36,
		KEY_END:      35,
		KEY_PAGEUP:   33,
		KEY_PAGEDOWN: 34,
		KEY_INSERT:   45,

		cache: {}
	};

Code : Browser-specific mouse event code

	var docEl = document.documentElement;
	var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl
		&& 'onmouseleave' in docEl;

	var _isButton;
	if (Prototype.Browser.IE) {
		var buttonMap = { 0: 1, 1: 4, 2: 2 };
		_isButton = function(event, code) {
			return event.button === buttonMap[code];
		};
	} else if (Prototype.Browser.WebKit) {
		_isButton = function(event, code) {
			switch (code) {
				case 0: return event.which == 1 && !event.metaKey;
				case 1: return event.which == 1 && event.metaKey;
				default: return false;
			}
		};
	} else {
		_isButton = function(event, code) {
			return event.which ? (event.which === code + 1) : (event.button === code);
		};
	}

Function : isLeftClick

Internal function

	function isLeftClick(event)   { return _isButton(event, 0) }

Function : isMiddleClick

Internal function

	function isMiddleClick(event) { return _isButton(event, 1) }

Function : isRightClick

Internal function

	function isRightClick(event)  { return _isButton(event, 2) }

Function : element

Returns the DOM element on which the event occurred

	function element(event) {
		event = Event.extend(event);

		var node = event.target, type = event.type,
 		currentTarget = event.currentTarget;

		if (currentTarget && currentTarget.tagName) {
			if (type === 'load' || type === 'error' ||
				(type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
					&& currentTarget.type === 'radio'))
						node = currentTarget;
		}

		if (node.nodeType == Node.TEXT_NODE)
			node = node.parentNode;

		return Element.extend(node);
	}

Function : findElement

Returns the first DOM element with a given tag name, upwards from the one on which the event occurred

	function findElement(event, expression) {
		var element = Event.element(event);
		if (!expression) return element;
		var elements = [element].concat(element.ancestors());
		return Selector.findElement(elements, expression, 0);
	}

Function : pointer

Internal function

	function pointer(event) {
		return { x: pointerX(event), y: pointerY(event) };
	}

Function : pointerX

Returns the absolute horizontal position for a mouse event

	function pointerX(event) {
		var docElement = document.documentElement,
 		body = document.body || { scrollLeft: 0 };

		return event.pageX || (event.clientX +
			(docElement.scrollLeft || body.scrollLeft) -
			(docElement.clientLeft || 0));
	}

Function : pointerY

Returns the absolute vertical position for a mouse event

	function pointerY(event) {
		var docElement = document.documentElement,
 		body = document.body || { scrollTop: 0 };

		return  event.pageY || (event.clientY +
 			(docElement.scrollTop || body.scrollTop) -
 			(docElement.clientTop || 0));
	}

Function : stop

Stops the event’s propagation and prevents its default action from being triggered eventually

	function stop(event) {
		Event.extend(event);
		event.preventDefault();
		event.stopPropagation();

		event.stopped = true;
	}

Object : Event.Methods

Description

	Event.Methods = {
		isLeftClick: isLeftClick,
		isMiddleClick: isMiddleClick,
		isRightClick: isRightClick,

		element: element,
		findElement: findElement,

		pointer: pointer,
		pointerX: pointerX,
		pointerY: pointerY,

		stop: stop
	};

Code : Assign event methods to Event object

	var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
		m[name] = Event.Methods[name].methodize();
		return m;
	});

	if (Prototype.Browser.IE) {
		function _relatedTarget(event) {
			var element;
			switch (event.type) {
				case 'mouseover': element = event.fromElement; break;
				case 'mouseout':  element = event.toElement;   break;
				default: return null;
			}
			return Element.extend(element);
		}

		Object.extend(methods, {
			stopPropagation: function() { this.cancelBubble = true },
			preventDefault:  function() { this.returnValue = false },
			inspect: function() { return '[object Event]' }
		});

		Event.extend = function(event, element) {
			if (!event) return false;
			if (event._extendedByPrototype) return event;

			event._extendedByPrototype = Prototype.emptyFunction;
			var pointer = Event.pointer(event);

			Object.extend(event, {
				target: event.srcElement || element,
				relatedTarget: _relatedTarget(event),
				pageX:  pointer.x,
				pageY:  pointer.y
			});

			return Object.extend(event, methods);
		};
	} else {
		Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__;
		Object.extend(Event.prototype, methods);
		Event.extend = Prototype.K;
	}

Function : _createResponder

Internal function

function _createResponder(element, eventName, handler) {
		var registry = Element.retrieve(element, 'prototype_event_registry');

		if (Object.isUndefined(registry)) {
			CACHE.push(element);
			registry = Element.retrieve(element, 'prototype_event_registry', $H());
		}

		var respondersForEvent = registry.get(eventName);
		if (Object.isUndefined(respondersForEvent)) {
			respondersForEvent = [];
			registry.set(eventName, respondersForEvent);
		}

		if (respondersForEvent.pluck('handler').include(handler)) return false;

		var responder;
		if (eventName.include(":")) {
			responder = function(event) {
				if (Object.isUndefined(event.eventName))
					return false;

				if (event.eventName !== eventName)
					return false;

				Event.extend(event, element);
				handler.call(element, event);
			};
		} else {
			if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED &&
 			(eventName === "mouseenter" || eventName === "mouseleave")) {
				if (eventName === "mouseenter" || eventName === "mouseleave") {
					responder = function(event) {
						Event.extend(event, element);

						var parent = event.relatedTarget;
						while (parent && parent !== element) {
							try { parent = parent.parentNode; }
							catch(e) { parent = element; }
						}

						if (parent === element) return;

						handler.call(element, event);
					};
				}
			} else {
				responder = function(event) {
					Event.extend(event, element);
					handler.call(element, event);
				};
			}
		}

		responder.handler = handler;
		respondersForEvent.push(responder);
		return responder;
	}

Function : _destroyCache

Internal function

function _destroyCache() {
		for (var i = 0, length = CACHE.length; i < length; i++) {
			Event.stopObserving(CACHE[i]);
			CACHE[i] = null;
		}
	}

Properties : cache

	var CACHE = [];

Code : Browser-specific event code

	if (Prototype.Browser.IE)
		window.attachEvent('onunload', _destroyCache);

	if (Prototype.Browser.WebKit)
		window.addEventListener('unload', Prototype.emptyFunction, false);


	var _getDOMEventName = Prototype.K;

	if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
		_getDOMEventName = function(eventName) {
			var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
			return eventName in translations ? translations[eventName] : eventName;
		};
	}

Function : observe

Registers an event handler on a DOM element

	function observe(element, eventName, handler) {
		element = $(element);

		var responder = _createResponder(element, eventName, handler);

		if (!responder) return element;

		if (eventName.include(':')) {
			if (element.addEventListener)
				element.addEventListener("dataavailable", responder, false);
			else {
				element.attachEvent("ondataavailable", responder);
				element.attachEvent("onfilterchange", responder);
			}
		} else {
			var actualEventName = _getDOMEventName(eventName);

			if (element.addEventListener)
				element.addEventListener(actualEventName, responder, false);
			else
				element.attachEvent("on" + actualEventName, responder);
		}

		return element;
	}

Function : stopObserving

Unregisters one or more event handlers

	function stopObserving(element, eventName, handler) {
		element = $(element);

		var registry = Element.retrieve(element, 'prototype_event_registry');

		if (Object.isUndefined(registry)) return element;

		if (eventName && !handler) {
			var responders = registry.get(eventName);

			if (Object.isUndefined(responders)) return element;

			responders.each( function(r) {
				Element.stopObserving(element, eventName, r.handler);
			});
			return element;
		} else if (!eventName) {
			registry.each( function(pair) {
				var eventName = pair.key, responders = pair.value;

				responders.each( function(r) {
					Element.stopObserving(element, eventName, r.handler);
				});
			});
			return element;
		}

		var responders = registry.get(eventName);

		if (!responders) return;

		var responder = responders.find( function(r) { return r.handler === handler; });
		if (!responder) return element;

		var actualEventName = _getDOMEventName(eventName);

		if (eventName.include(':')) {
			if (element.removeEventListener)
				element.removeEventListener("dataavailable", responder, false);
			else {
				element.detachEvent("ondataavailable", responder);
				element.detachEvent("onfilterchange",  responder);
			}
		} else {
			if (element.removeEventListener)
				element.removeEventListener(actualEventName, responder, false);
			else
				element.detachEvent('on' + actualEventName, responder);
		}

		registry.set(eventName, responders.without(responder));

		return element;
	}

Function : fire

Internal function

	function fire(element, eventName, memo, bubble) {
		element = $(element);

		if (Object.isUndefined(bubble))
			bubble = true;

		if (element == document && document.createEvent && !element.dispatchEvent)
			element = document.documentElement;

		var event;
		if (document.createEvent) {
			event = document.createEvent('HTMLEvents');
			event.initEvent('dataavailable', true, true);
		} else {
			event = document.createEventObject();
			event.eventType = bubble ? 'ondataavailable' : 'onfilterchange';
		}

		event.eventName = eventName;
		event.memo = memo || { };

		if (document.createEvent)
			element.dispatchEvent(event);
		else
			element.fireEvent(event.eventType, event);

		return Event.extend(event);
	}

Code : Assign events

	Object.extend(Event, Event.Methods);
	
	Object.extend(Event, {
		fire:          fire,
		observe:       observe,
		stopObserving: stopObserving
	});
	
	Element.addMethods({
		fire:          fire,
		observe:       observe,
		stopObserving: stopObserving
	});
	
	Object.extend(document, {
		fire:          fire.methodize(),
		observe:       observe.methodize(),
		stopObserving: stopObserving.methodize(),
		loaded:        false
	});
	
	if (window.Event) Object.extend(window.Event, Event);
	else window.Event = Event;
})();

Code : Browser-specific DOM content loaded code

(function() {
	/* Support for the DOMContentLoaded event is based on work by Dan Webb,
 		Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */

	var timer;

	function fireContentLoadedEvent() {
		if (document.loaded) return;
		if (timer) window.clearTimeout(timer);
		document.loaded = true;
		document.fire('dom:loaded');
	}

	function checkReadyState() {
		if (document.readyState === 'complete') {
			document.stopObserving('readystatechange', checkReadyState);
			fireContentLoadedEvent();
		}
	}

	function pollDoScroll() {
		try { document.documentElement.doScroll('left'); }
		catch(e) {
			timer = pollDoScroll.defer();
			return;
		}
		fireContentLoadedEvent();
	}

	if (document.addEventListener) {
		document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false);
	} else {
		document.observe('readystatechange', checkReadyState);
		if (window == top)
			timer = pollDoScroll.defer();
	}

	Event.observe(window, 'load', fireContentLoadedEvent);
})();

Code : Empty method call

Element.addMethods();