.

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 :

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: ']*>([\\S\\s]*?)<\/script>',

Properties : JSONFilter

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

Function :

The emptyFunction does nothing… and returns nothing!

        emptyFunction: function() { },

Function :

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 :

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 :

Prototype’s object for class-based OOP

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

var Class = (function() {

Function : subclass

Internal function

        function subclass() {};

Function :

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 :

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 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 :

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 :

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 :

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 :

Turns an object into its URL-encoded query string representation

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

Function :

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 :

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 :

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 :

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

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

Function :

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

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

Function :

Returns true if obj is an array, false otherwise

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

Function :

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

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

Function :

Returns true if obj is of type function, false otherwise

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

Function :

Returns true if obj is of type string, false otherwise

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

Function :

Returns true if obj is of type number, false otherwise

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

Function :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

Strips all leading and trailing whitespace from a string

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

Function :

Strips a string of any HTML tag

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

Function :

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

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

Function :

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 :

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 :

Converts HTML special characters to their entity equivalents

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

Function :

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

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

Function :

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 :

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

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

Function :

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 :

Concatenates the string count times

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

Function :

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 :

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

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

Function :

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 :

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

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

Function :

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 :

Returns a JSON string

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

Function :

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 :

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 :

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 :

Check if the string contains a substring

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

Function :

Checks if the string starts with substring

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

Function :

Checks if the string ends with substring

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

Function :

Checks if the string is empty

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

Function :

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

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

Function :

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
        };
})());

Section :

Class :

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 :

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 :

var Enumerable = (function() {

Function :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

Returns an Array representation of the enumeration

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

Function :

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 :

Returns the size of the enumeration

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

Function :

Debugging function

        function inspect() {
                return '#';
        }

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 :

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 :

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 :

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 :

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 :

Clears the array (makes it empty)

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

Function :

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

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

Function :

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

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

Function :

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

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

Function :

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 :

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 :

Returns the reversed version of the array

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

Function :

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 :

Not available

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

Function :

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

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

Function :

Returns the size of the array

        function size() {
                return this.length;
        }

Function :

Returns the debug-oriented string representation of an array

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

Function :

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 :

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 :

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 :

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 :

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 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 :

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

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

Function :

Returns the value of the hash’s key property

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

Function :

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 :

Returns a cloned, vanilla object

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

Function :

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

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

Function :

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

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

Function :

Description

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

Function :

Merges object to hash and returns the result of that merge

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

Function :

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 :

Description

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

Function :

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 :

Returns the debug-oriented string representation of the hash

        function inspect() {
                return '#';
        }

Function :

Returns a JSON string

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

Function :

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 :

Prototype extends native JavaScript numbers

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

Function :

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 :

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

        function succ() {
                return this + 1;
        }

Function :

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

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

Function :

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 :

Returns a JSON string

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

Function :

Returns the absolute value of the number

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

Function :

Rounds the number to the nearest integer

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

Function :

Returns the smallest integer greater than or equal to the number

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

Function :

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 :

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 :

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 :

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 :

Object :

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 :

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 :

Registers a responder

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

Function :

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 :

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 :

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 :

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 :

Description

        getStatus: Ajax.Request.prototype.getStatus,

Function :

Description

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

Function :

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 :

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 :

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 :

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 :

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 :

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 :

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 = {

Function :

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 :

Toggles the visibility of element

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

Function :

Hides and returns element

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

Function :

Displays and returns element

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

Function :

Completely removes element from the document and returns it

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

Function :

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 = "";
                        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 = "test";
                                        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 :

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 :

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 :

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 :

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 :

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 :

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

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

Function :

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

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

Function :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

Finds and returns the computed height of element

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

Function :

Finds and returns the computed width of element

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

Function :

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 :

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 :

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 :

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 :

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 :

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 :

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

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

Function :

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 :

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 :

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 :

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

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

Function :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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:  ['',                '
', 1], TBODY: ['', '
', 2], TR: ['', '
', 3], TD: ['
', '
', 4], 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 :

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 :

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 :

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 = '
'; 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
            

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
            

Function :

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 :

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
            

Function : toString

Returns the selector expression


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

Function : inspect

Returns a debug-oriented output


        inspect: function() {
                return "#";
        }
                                                
});

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 :

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= 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 :

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 :

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 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 :

Resets a form to its default values

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

Function :

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 :

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

Form.Methods = {

Function :

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 :

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 :

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 :

Disables the form as whole

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

Function :

Enables a fully or partially disabled form

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

Function :

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 :

Gives keyboard focus to the first element of the form

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

Function :

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 :

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 :

Gives keyboard focus to an element

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

Function :

Selects the current text in a text input

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

Object :

Description

Form.Element.Methods = {

Function :

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 :

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 :

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 :

Clears the contents of a text input

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

Function :

Returns true if a text input has contents, false otherwise

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

Function :

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 :

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 :

Enables a previously disabled form control

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

Object :

An alias for Form.Element

var Field = Form.Element;

Function :

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 :

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 :

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 :

Returns the current value of a form control.

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

Class :

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 :

Serialize the form data to a string


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

Class :

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 :

No info


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

Class :

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 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 :

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 :

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 :

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 :

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 :

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 :

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 :

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 :

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();