|
|||||||||||||||||||||
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
|
||||||||||||||||||||
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://prototypejs.org/ * *--------------------------------------------------------------------------*/
Section : Core functionality
Object : Prototype
The Prototype namespace provides fundamental information about the Prototype library you’re using, as well as a central repository for default iterators or functions
var Prototype = {
Properties : Version
Version: '1.6.1',
Properties : Browser
Browser: (function(){ var ua = navigator.userAgent; var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]'; return { IE: !!window.attachEvent && !isOpera, Opera: isOpera, WebKit: ua.indexOf('AppleWebKit/') > -1, Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, MobileSafari: /Apple.*Mobile.*Safari/.test(ua) } })(),
Properties : BrowserFeatures
BrowserFeatures: { XPath: !!document.evaluate, SelectorsAPI: !!document.querySelector, ElementExtensions: (function() { var constructor = window.Element || window.HTMLElement; return !!(constructor && constructor.prototype); })(), SpecificElementExtensions: (function() { if (typeof window.HTMLDivElement !== 'undefined') return true; var div = document.createElement('div'); var form = document.createElement('form'); var isSupported = false; if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { isSupported = true; } div = form = null; return isSupported; })() },
Properties : ScriptFragment
ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
Properties : JSONFilter
JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
Function : emptyFunction
The emptyFunction does nothing… and returns nothing!
emptyFunction: function() { },
Function : K
K is Prototype’s very own identity function, i.e. it returns its argument untouched.
K: function(x) { return x }
};
Code : Mobile Safari-specific code
if (Prototype.Browser.MobileSafari) Prototype.BrowserFeatures.SpecificElementExtensions = false;
Properties : Abstract
var Abstract = { };
Function : Try.these
Accepts an arbitrary number of functions and returns the result of the first one that doesn't throw an error
var Try = { these: function() { var returnValue; for (var i = 0, length = arguments.length; i < length; i++) { var lambda = arguments[i]; try { returnValue = lambda(); break; } catch (e) { } } return returnValue; }};
Object : Class
Prototype’s object for class-based OOP
/* Based on Alex Arnell's inheritance implementation. */ var Class = (function() {
Function : create
Creates a class
function create() { var parent = null, properties = $A(arguments); if (Object.isFunction(properties[0])) parent = properties.shift(); function klass() { this.initialize.apply(this, arguments); } Object.extend(klass, Class.Methods); klass.superclass = parent; klass.subclasses = []; if (parent) { subclass.prototype = parent.prototype; klass.prototype = new subclass; parent.subclasses.push(klass); } for (var i = 0; i < properties.length; i++) klass.addMethods(properties[i]); if (!klass.prototype.initialize) klass.prototype.initialize = Prototype.emptyFunction; klass.prototype.constructor = klass; return klass; }
Function : addMethods
Adds methods to an existing class
function addMethods(source) { var ancestor = this.superclass && this.superclass.prototype; var properties = Object.keys(source); if (!Object.keys({ toString: true }).length) { if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) properties.push("valueOf"); } for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; if (ancestor && Object.isFunction(value) && value.argumentNames().first() == "$super") { var method = value; value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); value.valueOf = method.valueOf.bind(method); value.toString = method.toString.bind(method); } this.prototype[property] = value; } return this; }
}; })();
Section : Extend native classes
Extend : Object
Object is used by Prototype as a namespace; that is, it just keeps a few new methods together, which are intended for namespaced access
(function() {
Function : extend
Copies all properties from the source to the destination object. Used by Prototype to simulate inheritance (rather statically) by copying to prototypes
function extend(destination, source) { for (var property in source) destination[property] = source[property]; return destination; }
Function : inspect
Returns the debug-oriented string representation of the object
function inspect(object) { try { if (isUndefined(object)) return 'undefined'; if (object === null) return 'null'; return object.inspect ? object.inspect() : String(object); } catch (e) { if (e instanceof RangeError) return '...'; throw e; } }
Function : toJSON
Returns a JSON string
function toJSON(object) { var type = typeof object; switch (type) { case 'undefined': case 'function': case 'unknown': return; case 'boolean': return object.toString(); } if (object === null) return 'null'; if (object.toJSON) return object.toJSON(); if (isElement(object)) return; var results = []; for (var property in object) { var value = toJSON(object[property]); if (!isUndefined(value)) results.push(property.toJSON() + ': ' + value); } return '{' + results.join(', ') + '}'; }
Function : toQueryString
Turns an object into its URL-encoded query string representation
function toQueryString(object) { return $H(object).toQueryString(); }
Function : toHTML
Returns the return value of obj’s toHTML method if it exists, else runs obj through String.interpret
function toHTML(object) { return object && object.toHTML ? object.toHTML() : String.interpret(object); }
Function : keys
reats any object as a Hash and fetches the list of its property names
function keys(object) { var results = []; for (var property in object) results.push(property); return results; }
Function : values
Treats any object as a Hash and fetches the list of its property values
function values(object) { var results = []; for (var property in object) results.push(object[property]); return results; }
Function : clone
Clones the passed object using shallow copy (copies all the original’s properties to the result)
function clone(object) { return extend({ }, object); }
Function : isElement
Returns true if obj is a DOM node of type 1, false otherwise
function isElement(object) { return !!(object && object.nodeType == 1); }
Function : isArray
Returns true if obj is an array, false otherwise
function isArray(object) { return _toString.call(object) == "[object Array]"; }
Function : isHash
Returns true if obj is an instance of the Hash class, false otherwise
function isHash(object) { return object instanceof Hash; }
Function : isFunction
Returns true if obj is of type function, false otherwise
function isFunction(object) { return typeof object === "function"; }
Function : isString
Returns true if obj is of type string, false otherwise
function isString(object) { return _toString.call(object) == "[object String]"; }
Function : isNumber
Returns true if obj is of type number, false otherwise
function isNumber(object) { return _toString.call(object) == "[object Number]"; }
Function : isUndefined
Returns true if obj is of type undefined, false otherwise
function isUndefined(object) { return typeof object === "undefined"; }
Code : Assign methods
extend(Object, { extend: extend, inspect: inspect, toJSON: toJSON, toQueryString: toQueryString, toHTML: toHTML, keys: keys, values: values, clone: clone, isElement: isElement, isArray: isArray, isHash: isHash, isFunction: isFunction, isString: isString, isNumber: isNumber, isUndefined: isUndefined });
})();
Extend : Function
Prototype's extended Function object
Object.extend(Function.prototype, (function() {
Properties : slice
var slice = Array.prototype.slice;
Function : update
Internal function
function update(array, args) { var arrayLength = array.length, length = args.length; while (length--) array[arrayLength + length] = args[length]; return array; }
Function : merge
Internal function
function merge(array, args) { array = slice.call(array, 0); return update(array, args); }
Function : argumentNames
Reads the argument names as defined in the function definition and returns the values as an array of strings, or an empty array if the function is defined without parameters
function argumentNames() { var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') .replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; }
Function : bind
Wraps the function in another, locking its execution scope to an object specified by thisObj
function bind(context) { if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; var __method = this, args = slice.call(arguments, 1); return function() { var a = merge(args, arguments); return __method.apply(context, a); } }
Function : bindAsEventListener
An event-specific variant of bind which makes sure the function will recieve the current event object as the first argument when executing
function bindAsEventListener(context) { var __method = this, args = slice.call(arguments, 1); return function(event) { var a = update([event || window.event], args); return __method.apply(context, a); } }
Function : curry
artially applies the function, returning a function with one or more arguments already “filled in.”
function curry() { if (!arguments.length) return this; var __method = this, args = slice.call(arguments, 0); return function() { var a = merge(args, arguments); return __method.apply(this, a); } }
Function : delay
chedules the function to run after the specified amount of time, passing any arguments given
function delay(timeout) { var __method = this, args = slice.call(arguments, 1); timeout = timeout * 1000 return window.setTimeout(function() { return __method.apply(__method, args); }, timeout); }
Function : defer
Schedules the function to run as soon as the interpreter is idle
function defer() { var args = update([0.01], arguments); return this.delay.apply(this, args); }
Function : wrap
Returns a function “wrapped” around the original function
function wrap(wrapper) { var __method = this; return function() { var a = update([__method.bind(this)], arguments); return wrapper.apply(this, a); } }
Function : methodize
Takes a function and wraps it in another function that, at call time, pushes this to the original function as the first argument
function methodize() { if (this._methodized) return this._methodized; var __method = this; return this._methodized = function() { var a = update([this], arguments); return __method.apply(null, a); }; }
Code : Assign methods
return { argumentNames: argumentNames, bind: bind, bindAsEventListener: bindAsEventListener, curry: curry, delay: delay, defer: defer, wrap: wrap, methodize: methodize }
})());
Properties : Date.prototype.toJSON
Date.prototype.toJSON = function() { return '"' + this.getUTCFullYear() + '-' + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + this.getUTCDate().toPaddedString(2) + 'T' + this.getUTCHours().toPaddedString(2) + ':' + this.getUTCMinutes().toPaddedString(2) + ':' + this.getUTCSeconds().toPaddedString(2) + 'Z"'; };
Properties : RegExp.prototype.match
RegExp.prototype.match = RegExp.prototype.test;
Properties : RegExp.escape
RegExp.escape = function(str) { return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); };
Class : PeriodicalExecuter
This is a simple facility for periodical execution of a function. This essentially encapsulates the native clearInterval/setInterval mechanism found in native Window objects
var PeriodicalExecuter = Class.create({
Function : initialize
Constructor function
initialize: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.currentlyExecuting = false; this.registerCallback(); },
Function : registerCallback
Internal function
registerCallback: function() { this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); },
Function : stop
Stops the periodical executer (there will be no further triggers)
stop: function() { if (!this.timer) return; clearInterval(this.timer); this.timer = null; },
Function : onTimerEvent
Internal function
onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; this.execute(); this.currentlyExecuting = false; } catch(e) { this.currentlyExecuting = false; throw e; } } }
});
Extend : String
Prototype enhances the String object with a series of useful methods for String.prototype ranging from the trivial to the complex
Object.extend(String, { interpret: function(value) { return value == null ? '' : String(value); }, specialChar: { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\\': '\\\\' } });
Extend : String.prototype
Prototype enhances the String object with a series of useful methods for String.prototype ranging from the trivial to the complex
Object.extend(String.prototype, (function() {
Function : prepareReplacement
Internal function
function prepareReplacement(replacement) { if (Object.isFunction(replacement)) return replacement; var template = new Template(replacement); return function(match) { return template.evaluate(match) }; }
Function : gsub
Returns the string with every occurence of a given pattern replaced by either a regular string, the returned value of a function or a Template string
function gsub(pattern, replacement) { var result = '', source = this, match; replacement = prepareReplacement(replacement); if (Object.isString(pattern)) pattern = RegExp.escape(pattern); if (!(pattern.length || pattern.source)) { replacement = replacement(''); return replacement + source.split('').join(replacement) + replacement; } while (source.length > 0) { if (match = source.match(pattern)) { result += source.slice(0, match.index); result += String.interpret(replacement(match)); source = source.slice(match.index + match[0].length); } else { result += source, source = ''; } } return result; }
Function : sub
Returns a string with the first count occurrences of pattern replaced by either a regular string, the returned value of a function or a Template string
function sub(pattern, replacement, count) { replacement = prepareReplacement(replacement); count = Object.isUndefined(count) ? 1 : count; return this.gsub(pattern, function(match) { if (--count < 0) return match[0]; return replacement(match); }); }
Function : scan
Allows iterating over every occurrence of the given pattern (which can be a string or a regular expression). Returns the original string
function scan(pattern, iterator) { this.gsub(pattern, iterator); return String(this); }
Function : truncate
Truncates a string to the given length and appends a suffix to it (indicating that it is only an excerpt)
function truncate(length, truncation) { length = length || 30; truncation = Object.isUndefined(truncation) ? '...' : truncation; return this.length > length ? this.slice(0, length - truncation.length) + truncation : String(this); }
Function : strip
Strips all leading and trailing whitespace from a string
function strip() { return this.replace(/^\s+/, '').replace(/\s+$/, ''); }
Function : stripTags
Strips a string of any HTML tag
function stripTags() { return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi, ''); }
Function : stripScripts
Strips a string of anything that looks like an HTML script block
function stripScripts() { return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); }
Function : extractScripts
Exctracts the content of any script block present in the string and returns them as an array of strings
function extractScripts() { var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); return (this.match(matchAll) || []).map(function(scriptTag) { return (scriptTag.match(matchOne) || ['', ''])[1]; }); }
Function : evalScripts
Evaluates the content of any script block present in the string. Returns an array containing the value returned by each script
function evalScripts() { return this.extractScripts().map(function(script) { return eval(script) }); }
Function : escapeHTML
Converts HTML special characters to their entity equivalents
function escapeHTML() { return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
Function : unescapeHTML
Strips tags and converts the entity forms of special HTML characters to their normal form
function unescapeHTML() { return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); }
Function : toQueryParams
Parses a URI-like query string and returns an object composed of parameter/value pairs
function toQueryParams(separator) { var match = this.strip().match(/([^?#]*)(#.*)?$/); if (!match) return { }; return match[1].split(separator || '&').inject({ }, function(hash, pair) { if ((pair = pair.split('='))[0]) { var key = decodeURIComponent(pair.shift()); var value = pair.length > 1 ? pair.join('=') : pair[0]; if (value != undefined) value = decodeURIComponent(value); if (key in hash) { if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; hash[key].push(value); } else hash[key] = value; } return hash; }); }
Function : toArray
Splits the string character-by-character and returns an array with the result
function toArray() { return this.split(''); }
Function : succ
Used internally by ObjectRange. Converts the last character of the string to the following character in the Unicode alphabet
function succ() { return this.slice(0, this.length - 1) + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); }
Function : times
Concatenates the string count times
function times(count) { return count < 1 ? '' : new Array(count + 1).join(this); }
Function : camelize
Converts a string separated by dashes into a camelCase equivalent. For instance, 'foo-bar' would be converted to 'fooBar'
function camelize() { var parts = this.split('-'), len = parts.length; if (len == 1) return parts[0]; var camelized = this.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) : parts[0]; for (var i = 1; i < len; i++) camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); return camelized; }
Function : capitalize
Capitalizes the first letter of a string and downcases all the others
function capitalize() { return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); }
Function : underscore
Converts a camelized string into a series of words separated by an underscore ("_")
function underscore() { return this.replace(/::/g, '/') .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') .replace(/([a-z\d])([A-Z])/g, '$1_$2') .replace(/-/g, '_') .toLowerCase(); }
Function : dasherize
Replaces every instance of the underscore character ("_") by a dash ("-")
function dasherize() { return this.replace(/_/g, '-'); }
Function : inspect
Returns a debug-oriented version of the string (i.e. wrapped in single or double quotes, with backslashes and quotes escaped)
function inspect(useDoubleQuotes) { var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { if (character in String.specialChar) { return String.specialChar[character]; } return '\\u00' + character.charCodeAt().toPaddedString(2, 16); }); if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; return "'" + escapedString.replace(/'/g, '\\\'') + "'"; }
Function : unfilterJSON
Strips comment delimiters around Ajax JSON or JavaScript responses. This security method is called internally
function unfilterJSON(filter) { return this.replace(filter || Prototype.JSONFilter, '$1'); }
Function : isJSON
Check if the string is valid JSON by the use of regular expressions. This security method is called internally
function isJSON() { var str = this; if (str.blank()) return false; str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); }
Function : evalJSON
Evaluates the JSON in the string and returns the resulting object. If the optional sanitize parameter is set to true, the string is checked for possible malicious attempts and eval is not called if one is detected
function evalJSON(sanitize) { var json = this.unfilterJSON(); try { if (!sanitize || json.isJSON()) return eval('(' + json + ')'); } catch (e) { } throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); }
Function : include
Check if the string contains a substring
function include(pattern) { return this.indexOf(pattern) > -1; }
Function : startsWith
Checks if the string starts with substring
function startsWith(pattern) { return this.indexOf(pattern) === 0; }
Function : endsWith
Checks if the string ends with substring
function endsWith(pattern) { var d = this.length - pattern.length; return d >= 0 && this.lastIndexOf(pattern) === d; }
Function : blank
Check if the string is 'blank', meaning either empty or containing only whitespace
function blank() { return /^\s*$/.test(this); }
Function : interpolate
Treats the string as a Template and fills it with object’s properties
function interpolate(object, pattern) { return new Template(this, pattern).evaluate(object); }
Code : Assign methods
return { gsub: gsub, sub: sub, scan: scan, truncate: truncate, strip: String.prototype.trim ? String.prototype.trim : strip, stripTags: stripTags, stripScripts: stripScripts, extractScripts: extractScripts, evalScripts: evalScripts, escapeHTML: escapeHTML, unescapeHTML: unescapeHTML, toQueryParams: toQueryParams, parseQuery: toQueryParams, toArray: toArray, succ: succ, times: times, camelize: camelize, capitalize: capitalize, underscore: underscore, dasherize: dasherize, inspect: inspect, toJSON: toJSON, unfilterJSON: unfilterJSON, isJSON: isJSON, evalJSON: evalJSON, include: include, startsWith: startsWith, endsWith: endsWith, empty: empty, blank: blank, interpolate: interpolate };
})());
Section : Additional Prototype classes
Class : Template
The Template class is used to produce formatted output of text strings
var Template = Class.create({
Function : initialize
Constructor function
initialize: function(template, pattern) { this.template = template.toString(); this.pattern = pattern || Template.Pattern; },
Function : evaluate
Applies the template to the given object’s data, producing a formatted string with symbols replaced by corresponding object’s properties
evaluate: function(object) { if (object && Object.isFunction(object.toTemplateReplacements)) object = object.toTemplateReplacements(); return this.template.gsub(this.pattern, function(match) { if (object == null) return (match[1] + ''); var before = match[1] || ''; if (before == '\\') return match[2]; var ctx = object, expr = match[3]; var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; match = pattern.exec(expr); if (match == null) return before; while (match != null) { var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; ctx = ctx[comp]; if (null == ctx || '' == match[3]) break; expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); match = pattern.exec(expr); } return before + String.interpret(ctx); }); }
});
Properties : Pattern
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
Object : Enumberable
var Enumerable = (function() {
Function : each
The cornerstone of Enumerable
function each(iterator, context) { var index = 0; try { this._each(function(value) { iterator.call(context, value, index++); }); } catch (e) { if (e != $break) throw e; } return this; }
Function : eachSlice
Groups items in chunks based on a given size, with last chunk being possibly smaller
function eachSlice(number, iterator, context) { var index = -number, slices = [], array = this.toArray(); if (number < 1) return array; while ((index += number) < array.length) slices.push(array.slice(index, index+number)); return slices.collect(iterator, context); }
Function : all
Determines whether all the elements are boolean-equivalent to true, either directly or through computation by the provided iterator
function all(iterator, context) { iterator = iterator || Prototype.K; var result = true; this.each(function(value, index) { result = result && !!iterator.call(context, value, index); if (!result) throw $break; }); return result; }
Function : any
Determines whether at least one element is boolean-equivalent to true, either directly or through computation by the provided iterator
function any(iterator, context) { iterator = iterator || Prototype.K; var result = false; this.each(function(value, index) { if (result = !!iterator.call(context, value, index)) throw $break; }); return result; }
Function : collect
Returns the results of applying the iterator to each element
function collect(iterator, context) { iterator = iterator || Prototype.K; var results = []; this.each(function(value, index) { results.push(iterator.call(context, value, index)); }); return results; }
Function : detect
Finds the first element for which the iterator returns true
function detect(iterator, context) { var result; this.each(function(value, index) { if (iterator.call(context, value, index)) { result = value; throw $break; } }); return result; }
Function : findAll
Returns all the elements for which the iterator returned true
function findAll(iterator, context) { var results = []; this.each(function(value, index) { if (iterator.call(context, value, index)) results.push(value); }); return results; }
Function : grep
Returns all the elements that match the filter
function grep(filter, iterator, context) { iterator = iterator || Prototype.K; var results = []; if (Object.isString(filter)) filter = new RegExp(RegExp.escape(filter)); this.each(function(value, index) { if (filter.match(value)) results.push(iterator.call(context, value, index)); }); return results; }
Function : include
Determines whether a given object is in the Enumerable or not, based on the == comparison operator
function include(object) { if (Object.isFunction(this.indexOf)) if (this.indexOf(object) != -1) return true; var found = false; this.each(function(value) { if (value == object) { found = true; throw $break; } }); return found; }
Function : inGroupsOf
Groups items in fixed-size chunks, using a specific value to fill up the last chunk if necessary
function inGroupsOf(number, fillWith) { fillWith = Object.isUndefined(fillWith) ? null : fillWith; return this.eachSlice(number, function(slice) { while(slice.length < number) slice.push(fillWith); return slice; }); }
Function : inject
Incrementally builds a result value based on the successive results of the iterator
function inject(memo, iterator, context) { this.each(function(value, index) { memo = iterator.call(context, memo, value, index); }); return memo; }
Function : invoke
Invokes the same method, with the same arguments, for all items in a collection
function invoke(method) { var args = $A(arguments).slice(1); return this.map(function(value) { return value[method].apply(value, args); }); }
Function : max
Returns the maximum element (or element-based computation), or undefined if the enumeration is empty
function max(iterator, context) { iterator = iterator || Prototype.K; var result; this.each(function(value, index) { value = iterator.call(context, value, index); if (result == null || value >= result) result = value; }); return result; }
Function : min
Returns the minimum element (or element-based computation), or undefined if the enumeration is empty
function min(iterator, context) { iterator = iterator || Prototype.K; var result; this.each(function(value, index) { value = iterator.call(context, value, index); if (result == null || value < result) result = value; }); return result; }
Function : partition
Partitions the elements in two groups: those regarded as true, and those considered false
function partition(iterator, context) { iterator = iterator || Prototype.K; var trues = [], falses = []; this.each(function(value, index) { (iterator.call(context, value, index) ? trues : falses).push(value); }); return [trues, falses]; }
Function : pluck
Optimization for a common use-case of collect: fetching the same property for all the elements
function pluck(property) { var results = []; this.each(function(value) { results.push(value[property]); }); return results; }
Function : reject
Returns all the elements for which the iterator returned false
function reject(iterator, context) { var results = []; this.each(function(value, index) { if (!iterator.call(context, value, index)) results.push(value); }); return results; }
Function : sortBy
Provides a custom-sorted view of the elements based on the criteria computed, for each element, by the iterator
function sortBy(iterator, context) { return this.map(function(value, index) { return { value: value, criteria: iterator.call(context, value, index) }; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; }).pluck('value'); }
Function : toArray
Returns an Array representation of the enumeration
function toArray() { return this.map(); }
Function : zip
Zips together (think of the zip on a pair of trousers) 2+ sequences, providing an array of tuples
function zip() { var iterator = Prototype.K, args = $A(arguments); if (Object.isFunction(args.last())) iterator = args.pop(); var collections = [this].concat(args).map($A); return this.map(function(value, index) { return iterator(collections.pluck(index)); }); }
Function : size
Returns the size of the enumeration
function size() { return this.toArray().length; }
Function : inspect
Debugging function
function inspect() { return '#<Enumerable:' + this.toArray().inspect() + '>'; }
Code : Assign methods
return { each: each, eachSlice: eachSlice, all: all, every: all, any: any, some: any, collect: collect, map: collect, detect: detect, findAll: findAll, select: findAll, filter: findAll, grep: grep, include: include, member: include, inGroupsOf: inGroupsOf, inject: inject, invoke: invoke, max: max, min: min, partition: partition, pluck: pluck, reject: reject, sortBy: sortBy, toArray: toArray, entries: toArray, zip: zip, size: size, inspect: inspect, find: detect };
})();
Function : $A
Accepts an array-like collection (anything with numeric indices) and returns its equivalent as an actual Array object. This method is a convenience alias of Array.from, but is the preferred way of casting to an Array.
function $A(iterable) { if (!iterable) return []; if ('toArray' in Object(iterable)) return iterable.toArray(); var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; }
Function : $w
Splits a string into an Array, treating all whitespace as delimiters. Equivalent to Ruby's %w{foo bar} or Perl's qw(foo bar).
function $w(string) { if (!Object.isString(string)) return []; string = string.strip(); return string ? string.split(/\s+/) : []; }
Properties : Array.from
Array.from = $A;
Extend : Array
Prototype extends all native Javascript arrays with quite a few powerful methods
(function() {
Code : Borrow native methods
var arrayProto = Array.prototype, slice = arrayProto.slice, _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available
Function : each
Iterates over the array in ascending numerical index order
function each(iterator) { for (var i = 0, length = this.length; i < length; i++) iterator(this[i]); }
Properties : _each
if (!_each) _each = each;
Function : clear
Clears the array (makes it empty)
function clear() { this.length = 0; return this; }
Function : first
Returns the first item in the array, or undefined if the array is empty
function first() { return this[0]; }
Function : last
Returns the last item in the array, or undefined if the array is empty
function last() { return this[this.length - 1]; }
Function : compact
Returns a new version of the array, without any null/undefined values
function compact() { return this.select(function(value) { return value != null; }); }
Function : flatten
Returns a “flat” (one-dimensional) version of the array
function flatten() { return this.inject([], function(array, value) { if (Object.isArray(value)) return array.concat(value.flatten()); array.push(value); return array; }); }
Function : without
Produces a new version of the array that does not contain any of the specified values
function without() { var values = slice.call(arguments, 0); return this.select(function(value) { return !values.include(value); }); }
Function : reverse
Returns the reversed version of the array
function reverse(inline) { return (inline !== false ? this : this.toArray())._reverse(); }
Function : uniq
Produces a duplicate-free version of an array
function uniq(sorted) { return this.inject([], function(array, value, index) { if (0 == index || (sorted ? array.last() != value : !array.include(value))) array.push(value); return array; }); }
Function : intersect
Not available
function intersect(array) { return this.uniq().findAll(function(item) { return array.detect(function(value) { return item === value }); }); }
Function : clone
Returns a duplicate of the array, leaving the original array intact
function clone() { return slice.call(this, 0); }
Function : inspect
Returns the debug-oriented string representation of an array
function inspect() { return '[' + this.map(Object.inspect).join(', ') + ']'; }
Function : toJSON
Returns a JSON string
function toJSON() { var results = []; this.each(function(object) { var value = Object.toJSON(object); if (!Object.isUndefined(value)) results.push(value); }); return '[' + results.join(', ') + ']'; }
Function : indexOf
Returns the position of the first occurrence of the argument within the array
function indexOf(item, i) { i || (i = 0); var length = this.length; if (i < 0) i = length + i; for (; i < length; i++) if (this[i] === item) return i; return -1; }
Function : lastIndexOf
Returns the position of the last occurrence of the argument within the array
function lastIndexOf(item, i) { i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; var n = this.slice(0, i).reverse().indexOf(item); return (n < 0) ? n : i - n - 1; }
Function : concat
Concatenates two arrays, returning a new array
function concat() { var array = slice.call(this, 0), item; for (var i = 0, length = arguments.length; i < length; i++) { item = arguments[i]; if (Object.isArray(item) && !('callee' in item)) { for (var j = 0, arrayLength = item.length; j < arrayLength; j++) array.push(item[j]); } else { array.push(item); } } return array; }
Code : Assign methods
Object.extend(arrayProto, Enumerable); if (!arrayProto._reverse) arrayProto._reverse = arrayProto.reverse; Object.extend(arrayProto, { _each: _each, clear: clear, first: first, last: last, compact: compact, flatten: flatten, without: without, reverse: reverse, uniq: uniq, intersect: intersect, clone: clone, toArray: clone, size: size, inspect: inspect, toJSON: toJSON }); var CONCAT_ARGUMENTS_BUGGY = (function() { return [].concat(arguments)[0][0] !== 1; })(1,2) if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf;
})();
Function : $H
Creates a Hash (which is synonymous to “map” or “associative array” for our purposes). A convenience wrapper around the Hash constructor, with a safeguard that lets you pass an existing Hash object and get it back untouched (instead of uselessly cloning it).
function $H(object) { return new Hash(object); };
Class : Hash
Hash can be thought of as an associative array, binding unique keys to values (which are not necessarily unique), though it can not guarantee consistent order its elements when iterating.
var Hash = Class.create(Enumerable, (function() {
Function : initialize
Constructor function
function initialize(object) { this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); }
Function : _each
Iterates over the name/value pairs in the hash
function _each(iterator) { for (var key in this._object) { var value = this._object[key], pair = [key, value]; pair.key = key; pair.value = value; iterator(pair); } }
Function : set
Sets the hash’s key property to value and returns value
function set(key, value) { return this._object[key] = value; }
Function : get
Returns the value of the hash’s key property
function get(key) { if (this._object[key] !== Object.prototype[key]) return this._object[key]; }
Function : unset
Deletes the hash’s key property and returns its value
function unset(key) { var value = this._object[key]; delete this._object[key]; return value; }
Function : toObject
Returns a cloned, vanilla object
function toObject() { return Object.clone(this._object); }
Function : keys
Provides an Array of keys (that is, property names) for the hash
function keys() { return this.pluck('key'); }
Function : values
Collect the values of a hash and returns them in an array
function values() { return this.pluck('value'); }
Function : index
Description
function index(value) { var match = this.detect(function(pair) { return pair.value === value; }); return match && match.key; }
Function : merge
Merges object to hash and returns the result of that merge
function merge(object) { return this.clone().update(object); }
Function : update
Updates hash with the key/value pairs of object
function update(object) { return new Hash(object).inject(this, function(result, pair) { result.set(pair.key, pair.value); return result; }); }
Function : toQueryPair
Description
function toQueryPair(key, value) { if (Object.isUndefined(value)) return key; return key + '=' + encodeURIComponent(String.interpret(value)); }
Function : toQueryString
Turns a hash into its URL-encoded query string representation
function toQueryString() { return this.inject([], function(results, pair) { var key = encodeURIComponent(pair.key), values = pair.value; if (values && typeof values == 'object') { if (Object.isArray(values)) return results.concat(values.map(toQueryPair.curry(key))); } else results.push(toQueryPair(key, values)); return results; }).join('&'); }
Function : inspect
Returns the debug-oriented string representation of the hash
function inspect() { return '#<Hash:{' + this.map(function(pair) { return pair.map(Object.inspect).join(': '); }).join(', ') + '}>'; }
Function : toJSON
Returns a JSON string
function toJSON() { return Object.toJSON(this.toObject()); }
Code : Assign methods
return { initialize: initialize, _each: _each, set: set, get: get, unset: unset, toObject: toObject, toTemplateReplacements: toObject, keys: keys, values: values, index: index, merge: merge, update: update, toQueryString: toQueryString, inspect: inspect, toJSON: toJSON, clone: clone };
})());
Properties : Hash.from
Hash.from = $H;
Extend : Number
Prototype extends native JavaScript numbers
Object.extend(Number.prototype, (function() {
Function : toColorPart
Produces a 2-digit hexadecimal representation of the number (which is therefore assumed to be in the [0
function toColorPart() { return this.toPaddedString(2, 16); }
Function : succ
Returns the successor of the current Number, as defined by current + 1
function succ() { return this + 1; }
Function : times
Encapsulates a regular [0..n[ loop, Ruby-style
function times(iterator, context) { $R(0, this, true).each(iterator, context); return this; }
Function : toPaddedString
Converts the number into a string padded with 0s so that the string’s length is at least equal to length
function toPaddedString(length, radix) { var string = this.toString(radix || 10); return '0'.times(length - string.length) + string; }
Function : toJSON
Returns a JSON string
function toJSON() { return isFinite(this) ? this.toString() : 'null'; }
Function : round
Rounds the number to the nearest integer
function round() { return Math.round(this); }
Function : ceil
Returns the smallest integer greater than or equal to the number
function ceil() { return Math.ceil(this); }
Function : floor
Returns the largest integer less than or equal to the number
function floor() { return Math.floor(this); }
Code : Assign methods
return { toColorPart: toColorPart, succ: succ, times: times, toPaddedString: toPaddedString, toJSON: toJSON, abs: abs, round: round, ceil: ceil, floor: floor };
})());
Function : $R
Creates a new ObjectRange object. This method is a convenience wrapper around the ObjectRange constructor, but $R is the preferred alias
function $R(start, end, exclusive) { return new ObjectRange(start, end, exclusive); }
Class : ObjectRange
Ranges represent an interval of values. The value type just needs to be “compatible,” that is, to implement a succ method letting us step from one value to the next (its successor)
var ObjectRange = Class.create(Enumerable, (function() {
Function : initialize
Constructor function
function initialize(start, end, exclusive) { this.start = start; this.end = end; this.exclusive = exclusive; }
Function : _each
Internal function
function _each(iterator) { var value = this.start; while (this.include(value)) { iterator(value); value = value.succ(); } }
Function : include
Determines whether the value is included in the range
function include(value) { if (value < this.start) return false; if (this.exclusive) return value < this.end; return value <= this.end; }
})());
Section : Ajax
Object : Ajax
Prototype offers three objects to deal with AJAX communication
var Ajax = {
Function : getTransport
Internal function
getTransport: function() { return Try.these( function() {return new XMLHttpRequest()}, function() {return new ActiveXObject('Msxml2.XMLHTTP')}, function() {return new ActiveXObject('Microsoft.XMLHTTP')} ) || false; },
Properties : activeRequestCount
activeRequestCount: 0
};
Object : Ajax.Responders
A repository of global listeners notified about every step of Prototype-based AJAX requests
Ajax.Responders = {
Properties : responders
responders: [],
Function : register
Registers a responder
register: function(responder) { if (!this.include(responder)) this.responders.push(responder); },
Function : unregister
Unregisters a responder
unregister: function(responder) { this.responders = this.responders.without(responder); },
Function : dispatch
Internal function
dispatch: function(callback, request, transport, json) { this.each(function(responder) { if (Object.isFunction(responder[callback])) { try { responder[callback].apply(responder, [request, transport, json]); } catch (e) { } } }); }
};
Code : Assign methods
Object.extend(Ajax.Responders, Enumerable); Ajax.Responders.register({ onCreate: function() { Ajax.activeRequestCount++ }, onComplete: function() { Ajax.activeRequestCount-- } });
Class : Ajax.Base
Ajax base class for all Ajax classes
Ajax.Base = Class.create({
Function : initialize
Constructor function
initialize: function(options) { this.options = { method: 'post', asynchronous: true, contentType: 'application/x-www-form-urlencoded', encoding: 'UTF-8', parameters: '', evalJSON: true, evalJS: true }; Object.extend(this.options, options || { }); this.options.method = this.options.method.toLowerCase(); if (Object.isString(this.options.parameters)) this.options.parameters = this.options.parameters.toQueryParams(); else if (Object.isHash(this.options.parameters)) this.options.parameters = this.options.parameters.toObject(); }
});
Class : Ajax.Request
Initiates and processes an AJAX request
Ajax.Request = Class.create(Ajax.Base, {
Properties : _complete
_complete: false,
Function : initialize
Constrcutor function
initialize: function($super, url, options) { $super(options); this.transport = Ajax.getTransport(); this.request(url); },
Function : request
Internal function called by the constuctor that makes the request
request: function(url) { this.url = url; this.method = this.options.method; var params = Object.clone(this.options.parameters); if (!['get', 'post'].include(this.method)) { params['_method'] = this.method; this.method = 'post'; } this.parameters = params; if (params = Object.toQueryString(params)) { if (this.method == 'get') this.url += (this.url.include('?') ? '&' : '?') + params; else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='; } try { var response = new Ajax.Response(this); if (this.options.onCreate) this.options.onCreate(response); Ajax.Responders.dispatch('onCreate', this, response); this.transport.open(this.method.toUpperCase(), this.url, this.options.asynchronous); if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); this.transport.onreadystatechange = this.onStateChange.bind(this); this.setRequestHeaders(); this.body = this.method == 'post' ? (this.options.postBody || params) : null; this.transport.send(this.body); /* Force Firefox to handle ready state 4 for synchronous requests */ if (!this.options.asynchronous && this.transport.overrideMimeType) this.onStateChange(); } catch (e) { this.dispatchException(e); } },
Function : onStateChange
Internal function
onStateChange: function() { var readyState = this.transport.readyState; if (readyState > 1 && !((readyState == 4) && this._complete)) this.respondToReadyState(this.transport.readyState); },
Function : setRequestHeaders
Internal function
setRequestHeaders: function() { var headers = { 'X-Requested-With': 'XMLHttpRequest', 'X-Prototype-Version': Prototype.Version, 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' }; if (this.method == 'post') { headers['Content-type'] = this.options.contentType + (this.options.encoding ? '; charset=' + this.options.encoding : ''); /* Force "Connection: close" for older Mozilla browsers to work * around a bug where XMLHttpRequest sends an incorrect * Content-length header. See Mozilla Bugzilla #246651. */ if (this.transport.overrideMimeType && (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) headers['Connection'] = 'close'; } if (typeof this.options.requestHeaders == 'object') { var extras = this.options.requestHeaders; if (Object.isFunction(extras.push)) for (var i = 0, length = extras.length; i < length; i += 2) headers[extras[i]] = extras[i+1]; else $H(extras).each(function(pair) { headers[pair.key] = pair.value }); } for (var name in headers) this.transport.setRequestHeader(name, headers[name]); },
Function : success
Internal function
success: function() { var status = this.getStatus(); return !status || (status >= 200 && status < 300); },
Function : getStatus
Internal function
getStatus: function() { try { return this.transport.status || 0; } catch (e) { return 0 } },
Function : respondToReadyState
Internal function
respondToReadyState: function(readyState) { var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); if (state == 'Complete') { try { this._complete = true; (this.options['on' + response.status] || this.options['on' + (this.success() ? 'Success' : 'Failure')] || Prototype.emptyFunction)(response, response.headerJSON); } catch (e) { this.dispatchException(e); } var contentType = response.getHeader('Content-type'); if (this.options.evalJS == 'force' || (this.options.evalJS && this.isSameOrigin() && contentType && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) this.evalResponse(); } try { (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); } catch (e) { this.dispatchException(e); } if (state == 'Complete') { this.transport.onreadystatechange = Prototype.emptyFunction; } },
Function : isSameOrigin
Internal function
isSameOrigin: function() { var m = this.url.match(/^\s*https?:\/\/[^\/]*/); return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ protocol: location.protocol, domain: document.domain, port: location.port ? ':' + location.port : '' })); },
Function : getHeader
Internal function
getHeader: function(name) { try { return this.transport.getResponseHeader(name) || null; } catch (e) { return null; } },
Function : evalResponse
Internal function
evalResponse: function() { try { return eval((this.transport.responseText || '').unfilterJSON()); } catch (e) { this.dispatchException(e); } },
Function : dispatchException
Internal function
dispatchException: function(exception) { (this.options.onException || Prototype.emptyFunction)(this, exception); Ajax.Responders.dispatch('onException', this, exception); }
});
Properties : Ajax.Request.Events
Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
Class : Ajax.Response
The object passed as the first argument of all Ajax requests callbacks
Ajax.Response = Class.create({
Function : initialize
Constructor function
initialize: function(request){ this.request = request; var transport = this.transport = request.transport, readyState = this.readyState = transport.readyState; if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { this.status = this.getStatus(); this.statusText = this.getStatusText(); this.responseText = String.interpret(transport.responseText); this.headerJSON = this._getHeaderJSON(); } if(readyState == 4) { var xml = transport.responseXML; this.responseXML = Object.isUndefined(xml) ? null : xml; this.responseJSON = this._getResponseJSON(); } },
Properties : status
status: 0,
Properties : statusText
statusText: '',
Function : getStatusText
Description
getStatusText: function() { try { return this.transport.statusText || ''; } catch (e) { return '' } },
Function : getHeader
Returns the value of the requested header if present. null otherwise. Does not throw errors on undefined headers like it’s native counterpart does
getHeader: Ajax.Request.prototype.getHeader,
Function : getAllHeaders
Returns a string containing all headers separated by a line break. Does not throw errors if no headers are present like it’s native counterpart does
getAllHeaders: function() { try { return this.getAllResponseHeaders(); } catch (e) { return null } },
Function : getResponseHeader
Returns the value of the requested header if present. Throws an error otherwise. This is just a wrapper around the xmlHttpRequest object’s native method. Prefer it’s shorter counterpart getHeader
getResponseHeader: function(name) { return this.transport.getResponseHeader(name); },
Function : getAllResponseHeaders
Returns a string containing all headers separated by a line break. Throws an error otherwise. This is just a wrapper around the xmlHttpRequest object’s native method. Prefer it’s shorter counterpart getAllHeaders
getAllResponseHeaders: function() { return this.transport.getAllResponseHeaders(); },
Function : _getHeaderJSON
Internal function
_getHeaderJSON: function() { var json = this.getHeader('X-JSON'); if (!json) return null; json = decodeURIComponent(escape(json)); try { return json.evalJSON(this.request.options.sanitizeJSON || !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } },
Function : _getResponseJSON
Internal function
_getResponseJSON: function() { var options = this.request.options; if (!options.evalJSON || (options.evalJSON != 'force' && !(this.getHeader('Content-type') || '').include('application/json')) || this.responseText.blank()) return null; try { return this.responseText.evalJSON(options.sanitizeJSON || !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } }
});
Class : Ajax.Updater
Performs an AJAX request and updates a container’s contents based on the response text
Ajax.Updater = Class.create(Ajax.Request, {
Function : initialize
Constructor function
initialize: function($super, container, url, options) { this.container = { success: (container.success || container), failure: (container.failure || (container.success ? null : container)) }; options = Object.clone(options); var onComplete = options.onComplete; options.onComplete = (function(response, json) { this.updateContent(response.responseText); if (Object.isFunction(onComplete)) onComplete(response, json); }).bind(this); $super(url, options); },
Function : updateContent
Internal function
updateContent: function(responseText) { var receiver = this.container[this.success() ? 'success' : 'failure'], options = this.options; if (!options.evalScripts) responseText = responseText.stripScripts(); if (receiver = $(receiver)) { if (options.insertion) { if (Object.isString(options.insertion)) { var insertion = { }; insertion[options.insertion] = responseText; receiver.insert(insertion); } else options.insertion(receiver, responseText); } else receiver.update(responseText); } }
});
Class : Ajax.PeriodicalUpdater
Periodically performs an AJAX request and updates a container’s contents based on the response text. Offers a mechanism for “decay,” which lets it trigger at widening intervals while the response is unchanged
Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
Function : initialize
Constructor function
initialize: function($super, container, url, options) { $super(options); this.onComplete = this.options.onComplete; this.frequency = (this.options.frequency || 2); this.decay = (this.options.decay || 1); this.updater = { }; this.container = container; this.url = url; this.start(); },
Function : start
Internal function
start: function() { this.options.onComplete = this.updateComplete.bind(this); this.onTimerEvent(); },
Function : stop
Internal function
stop: function() { this.updater.options.onComplete = undefined; clearTimeout(this.timer); (this.onComplete || Prototype.emptyFunction).apply(this, arguments); },
Function : updateComplete
Internal function
updateComplete: function(response) { if (this.options.decay) { this.decay = (response.responseText == this.lastText ? this.decay * this.options.decay : 1); this.lastText = response.responseText; } this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); },
Function : onTimerEvent
Internal function
onTimerEvent: function() { this.updater = new Ajax.Updater(this.container, this.url, this.options); }
});
Section : Element
Function : $
If provided with a string, returns the element in the document with matching ID; otherwise returns the passed element. Takes in an arbitrary number of arguments. All elements returned by the function are extended with Prototype DOM extensions
function $(element) { if (arguments.length > 1) { for (var i = 0, elements = [], length = arguments.length; i < length; i++) elements.push($(arguments[i])); return elements; } if (Object.isString(element)) element = document.getElementById(element); return Element.extend(element); }
Function : _getElementsByXPath
Description
if (Prototype.BrowserFeatures.XPath) { document._getElementsByXPath = function(expression, parentElement) { var results = []; var query = document.evaluate(expression, $(parentElement) || document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for (var i = 0, length = query.snapshotLength; i < length; i++) results.push(Element.extend(query.snapshotItem(i))); return results; }; }
Object : Node
DOM node constants
if (!window.Node) var Node = { }; if (!Node.ELEMENT_NODE) { Object.extend(Node, { ELEMENT_NODE: 1, ATTRIBUTE_NODE: 2, TEXT_NODE: 3, CDATA_SECTION_NODE: 4, ENTITY_REFERENCE_NODE: 5, ENTITY_NODE: 6, PROCESSING_INSTRUCTION_NODE: 7, COMMENT_NODE: 8, DOCUMENT_NODE: 9, DOCUMENT_TYPE_NODE: 10, DOCUMENT_FRAGMENT_NODE: 11, NOTATION_NODE: 12 }); }
Object : Element
The Element object sports a flurry of powerful DOM methods which you can access either as methods of Element (but that’s rather old-fashioned now) or directly as methods of an extended element
(function(global) { var SETATTRIBUTE_IGNORES_NAME = (function(){ var elForm = document.createElement("form"); var elInput = document.createElement("input"); var root = document.documentElement; elInput.setAttribute("name", "test"); elForm.appendChild(elInput); root.appendChild(elForm); var isBuggy = elForm.elements ? (typeof elForm.elements.test == "undefined") : null; root.removeChild(elForm); elForm = elInput = null; return isBuggy; })(); var element = global.Element; global.Element = function(tagName, attributes) { attributes = attributes || { }; tagName = tagName.toLowerCase(); var cache = Element.cache; if (SETATTRIBUTE_IGNORES_NAME && attributes.name) { tagName = '<' + tagName + ' name="' + attributes.name + '">'; delete attributes.name; return Element.writeAttribute(document.createElement(tagName), attributes); } if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); }; Object.extend(global.Element, element || { }); if (element)global.Element.prototype = element.prototype; })(this);
Properties : Element.cache & Element.idCounter
Element.cache = { }; Element.idCounter = 1;
Object : Element.methods
Element.Methods = {
Function : visible
Returns a Boolean indicating whether or not element is visible (i.e. whether its inline style property is set to "display: none;").
visible: function(element) { return $(element).style.display != 'none'; },
Function : toggle
Toggles the visibility of element
toggle: function(element) { element = $(element); Element[Element.visible(element) ? 'hide' : 'show'](element); return element; },
Function : hide
Hides and returns element
hide: function(element) { element = $(element); element.style.display = 'none'; return element; },
Function : show
Displays and returns element
show: function(element) { element = $(element); element.style.display = ''; return element; },
Function : remove
Completely removes element from the document and returns it
remove: function(element) { element = $(element); element.parentNode.removeChild(element); return element; },
Function : update
Replaces the content of element with the provided newContent argument and returns element
update: (function(){ var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ var el = document.createElement("select"), isBuggy = true; el.innerHTML = "<option value=\"test\">test</option>"; if (el.options && el.options[0]) { isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; } el = null; return isBuggy; })(); var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ try { var el = document.createElement("table"); if (el && el.tBodies) { el.innerHTML = "<tbody><tr><td>test</td></tr></tbody>"; var isBuggy = typeof el.tBodies[0] == "undefined"; el = null; return isBuggy; } } catch (e) { return true; } })(); var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { var s = document.createElement("script"), isBuggy = false; try { s.appendChild(document.createTextNode("")); isBuggy = !s.firstChild || s.firstChild && s.firstChild.nodeType !== 3; } catch (e) { isBuggy = true; } s = null; return isBuggy; })(); function update(element, content) { element = $(element); if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) return element.update().insert(content); content = Object.toHTML(content); var tagName = element.tagName.toUpperCase(); if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { element.text = content; return element; } if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) { if (tagName in Element._insertionTranslations.tags) { while (element.firstChild) { element.removeChild(element.firstChild); } Element._getContentFromAnonymousElement(tagName, content.stripScripts()) .each(function(node) { element.appendChild(node) }); } else { element.innerHTML = content.stripScripts(); } } else { element.innerHTML = content.stripScripts(); } content.evalScripts.bind(content).defer(); return element; } return update; })(),
Function : replace
Replaces element by the content of the html argument and returns the removed element
replace: function(element, content) { element = $(element); if (content && content.toElement) content = content.toElement(); else if (!Object.isElement(content)) { content = Object.toHTML(content); var range = element.ownerDocument.createRange(); range.selectNode(element); content.evalScripts.bind(content).defer(); content = range.createContextualFragment(content.stripScripts()); } element.parentNode.replaceChild(content, element); return element; },
Function : insert
Inserts content before, after, at the top of, or at the bottom of element, as specified by the position property of the second argument
insert: function(element, insertions) { element = $(element); if (Object.isString(insertions) || Object.isNumber(insertions) || Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) insertions = {bottom:insertions}; var content, insert, tagName, childNodes; for (var position in insertions) { content = insertions[position]; position = position.toLowerCase(); insert = Element._insertionTranslations[position]; if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) { insert(element, content); continue; } content = Object.toHTML(content); tagName = ((position == 'before' || position == 'after') ? element.parentNode : element).tagName.toUpperCase(); childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); if (position == 'top' || position == 'after') childNodes.reverse(); childNodes.each(insert.curry(element)); content.evalScripts.bind(content).defer(); } return element; },
Function : wrap
Wraps an element inside another, then returns the wrapper
wrap: function(element, wrapper, attributes) { element = $(element); if (Object.isElement(wrapper)) $(wrapper).writeAttribute(attributes || { }); else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); else wrapper = new Element('div', wrapper); if (element.parentNode) element.parentNode.replaceChild(wrapper, element); wrapper.appendChild(element); return wrapper; },
Function : inspect
Returns the debug-oriented string representation of element
inspect: function(element) { element = $(element); var result = '<' + element.tagName.toLowerCase(); $H({'id': 'id', 'className': 'class'}).each(function(pair) { var property = pair.first(), attribute = pair.last(); var value = (element[property] || '').toString(); if (value) result += ' ' + attribute + '=' + value.inspect(true); }); return result + '>'; },
Function : recursivelyCollect
Recursively collects elements whose relationship is specified by property
recursivelyCollect: function(element, property) { element = $(element); var elements = []; while (element = element[property]) if (element.nodeType == 1) elements.push(Element.extend(element)); return elements; },
Function : ancestors
Collects all of element’s ancestors and returns them as an array of extended elements
ancestors: function(element) { return Element.recursivelyCollect(element, 'parentNode'); },
Function : descendants
Collects all of element’s descendants and returns them as an array of extended elements
descendants: function(element) { return Element.select(element, "*"); },
Function : firstDescendant
Returns the first child that is an element
firstDescendant: function(element) { element = $(element).firstChild; while (element && element.nodeType != 1) element = element.nextSibling; return $(element); },
Function : immediateDescendants
Collects all of the element’s immediate descendants (i.e. children) and returns them as an array of extended elements
immediateDescendants: function(element) { if (!(element = $(element).firstChild)) return []; while (element && element.nodeType != 1) element = element.nextSibling; if (element) return [element].concat($(element).nextSiblings()); return []; },
Function : previousSiblings
Collects all of element’s previous siblings and returns them as an array of extended elements
previousSiblings: function(element) { return Element.recursivelyCollect(element, 'previousSibling'); },
Function : nextSiblings
Collects all of element’s next siblings and returns them as an array of extended elements
nextSiblings: function(element) { return Element.recursivelyCollect(element, 'nextSibling'); },
Function : siblings
Collects all of element’s siblings and returns them as an array of extended elements
siblings: function(element) { element = $(element); return Element.previousSiblings(element).reverse() .concat(Element.nextSiblings(element)); },
Function : match
Checks if element matches the given CSS selector
match: function(element, selector) { if (Object.isString(selector)) selector = new Selector(selector); return selector.match($(element)); },
Function : up
Returns element’s first ancestor (or the index’th ancestor, if index is specified) that matches cssRule
up: function(element, expression, index) { element = $(element); if (arguments.length == 1) return $(element.parentNode); var ancestors = Element.ancestors(element); return Object.isNumber(expression) ? ancestors[expression] : Selector.findElement(ancestors, expression, index); },
Function : down
Returns element’s first descendant (or the n-th descendant if index is specified) that matches cssRule
down: function(element, expression, index) { element = $(element); if (arguments.length == 1) return Element.firstDescendant(element); return Object.isNumber(expression) ? Element.descendants(element)[expression] : Element.select(element, expression)[index || 0]; },
Function : previous
Returns element's previous sibling (or the index'th one, if index is specified) that matches cssRule
previous: function(element, expression, index) { element = $(element); if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); var previousSiblings = Element.previousSiblings(element); return Object.isNumber(expression) ? previousSiblings[expression] : Selector.findElement(previousSiblings, expression, index); },
Function : next
Returns element’s following sibling (or the index’th one, if index is specified) that matches cssRule
next: function(element, expression, index) { element = $(element); if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); var nextSiblings = Element.nextSiblings(element); return Object.isNumber(expression) ? nextSiblings[expression] : Selector.findElement(nextSiblings, expression, index); },
Function : select
Takes an arbitrary number of CSS selectors (strings) and returns an array of extended descendants of element that match any of them
select: function(element) { var args = Array.prototype.slice.call(arguments, 1); return Selector.findChildElements(element, args); },
Function : adjacent
Finds all siblings of the current element that match the given selector(s)
adjacent: function(element) { var args = Array.prototype.slice.call(arguments, 1); return Selector.findChildElements(element.parentNode, args).without(element); },
Function : identify
returns element’s id attribute if it exists, or sets and returns a unique, auto-generated id
identify: function(element) { element = $(element); var id = Element.readAttribute(element, 'id'); if (id) return id; do { id = 'anonymous_element_' + Element.idCounter++ } while ($(id)); Element.writeAttribute(element, 'id', id); return id; },
Function : readAttribute
Returns the value of element's attribute or null if attribute has not been specified
readAttribute: function(element, name) { element = $(element); if (Prototype.Browser.IE) { var t = Element._attributeTranslations.read; if (t.values[name]) return t.values[name](element, name); if (t.names[name]) name = t.names[name]; if (name.include(':')) { return (!element.attributes || !element.attributes[name]) ? null : element.attributes[name].value; } } return element.getAttribute(name); },
Function : writeAttribute
Adds, specifies or removes attributes passed as either a hash or a name/value pair
writeAttribute: function(element, name, value) { element = $(element); var attributes = { }, t = Element._attributeTranslations.write; if (typeof name == 'object') attributes = name; else attributes[name] = Object.isUndefined(value) ? true : value; for (var attr in attributes) { name = t.names[attr] || attr; value = attributes[attr]; if (t.values[attr]) name = t.values[attr](element, value); if (value === false || value === null) element.removeAttribute(name); else if (value === true) element.setAttribute(name, name); else element.setAttribute(name, value); } return element; },
Function : getHeight
Finds and returns the computed height of element
getHeight: function(element) { return Element.getDimensions(element).height; },
Function : getWidth
Finds and returns the computed width of element
getWidth: function(element) { return Element.getDimensions(element).width; },
Function : classNames
Returns a new instance of ClassNames, an Enumerable object used to read and write CSS class names of element
classNames: function(element) { return new Element.ClassNames(element); },
Function : hasClassName
Checks whether element has the given CSS className
hasClassName: function(element, className) { if (!(element = $(element))) return; var elementClassName = element.className; return (elementClassName.length > 0 && (elementClassName == className || new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); },
Function : addClassName
Adds a CSS class to element
addClassName: function(element, className) { if (!(element = $(element))) return; if (!Element.hasClassName(element, className)) element.className += (element.className ? ' ' : '') + className; return element; },
Function : removeClassName
Removes element’s CSS className and returns element
removeClassName: function(element, className) { if (!(element = $(element))) return; element.className = element.className.replace( new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); return element; },
Function : toggleClassName
Toggles element’s CSS className and returns element
toggleClassName: function(element, className) { if (!(element = $(element))) return; return Element[Element.hasClassName(element, className) ? 'removeClassName' : 'addClassName'](element, className); },
Function : cleanWhitespace
Removes all of element's text nodes which contain only whitespace
cleanWhitespace: function(element) { element = $(element); var node = element.firstChild; while (node) { var nextNode = node.nextSibling; if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) element.removeChild(node); node = nextNode; } return element; },
Function : empty
Tests whether element is empty (i.e. contains only whitespace)
empty: function(element) { return $(element).innerHTML.blank(); },
Function : descendantOf
Checks if element is a descendant of ancestor
descendantOf: function(element, ancestor) { element = $(element), ancestor = $(ancestor); if (element.compareDocumentPosition) return (element.compareDocumentPosition(ancestor) & 8) === 8; if (ancestor.contains) return ancestor.contains(element) && ancestor !== element; while (element = element.parentNode) if (element == ancestor) return true; return false; },
Function : scrollTo
Scrolls the window so that element appears at the top of the viewport
scrollTo: function(element) { element = $(element); var pos = Element.cumulativeOffset(element); window.scrollTo(pos[0], pos[1]); return element; },
Function : getStyle
Returns the given CSS property value of element
getStyle: function(element, style) { element = $(element); style = style == 'float' ? 'cssFloat' : style.camelize(); var value = element.style[style]; if (!value || value == 'auto') { var css = document.defaultView.getComputedStyle(element, null); value = css ? css[style] : null; } if (style == 'opacity') return value ? parseFloat(value) : 1.0; return value == 'auto' ? null : value; },
Function : getOpacity
Gets the visual opacity of an element while working around inconsistencies in various browsers
getOpacity: function(element) { return $(element).getStyle('opacity'); },
Function : setStyle
Modifies element’s CSS style properties
setStyle: function(element, styles) { element = $(element); var elementStyle = element.style, match; if (Object.isString(styles)) { element.style.cssText += ';' + styles; return styles.include('opacity') ? element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; } for (var property in styles) if (property == 'opacity') element.setOpacity(styles[property]); else elementStyle[(property == 'float' || property == 'cssFloat') ? (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : property] = styles[property]; return element; },
Function : setOpacity
Sets the visual opacity of an element while working around inconsistencies in various browsers
setOpacity: function(element, value) { element = $(element); element.style.opacity = (value == 1 || value === '') ? '' : (value < 0.00001) ? 0 : value; return element; },
Function : getDimensions
Finds the computed width and height of element and returns them as key/value pairs of an object
getDimensions: function(element) { element = $(element); var display = Element.getStyle(element, 'display'); if (display != 'none' && display != null) // Safari bug return {width: element.offsetWidth, height: element.offsetHeight}; var els = element.style; var originalVisibility = els.visibility; var originalPosition = els.position; var originalDisplay = els.display; els.visibility = 'hidden'; if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari els.position = 'absolute'; els.display = 'block'; var originalWidth = element.clientWidth; var originalHeight = element.clientHeight; els.display = originalDisplay; els.position = originalPosition; els.visibility = originalVisibility; return {width: originalWidth, height: originalHeight}; },
Function : makePositioned
Allows for the easy creation of CSS containing block by setting element's CSS position to 'relative' if its initial position is either 'static' or undefined
makePositioned: function(element) { element = $(element); var pos = Element.getStyle(element, 'position'); if (pos == 'static' || !pos) { element._madePositioned = true; element.style.position = 'relative'; if (Prototype.Browser.Opera) { element.style.top = 0; element.style.left = 0; } } return element; },
Function : undoPositioned
Sets element back to the state it was before Element
undoPositioned: function(element) { element = $(element); if (element._madePositioned) { element._madePositioned = undefined; element.style.position = element.style.top = element.style.left = element.style.bottom = element.style.right = ''; } return element; },
Function : makeClipping
Simulates the poorly supported CSS clip property by setting element's overflow value to 'hidden'
makeClipping: function(element) { element = $(element); if (element._overflow) return element; element._overflow = Element.getStyle(element, 'overflow') || 'auto'; if (element._overflow !== 'hidden') element.style.overflow = 'hidden'; return element; },
Function : undoClipping
Sets element’s CSS overflow property back to the value it had before Element
undoClipping: function(element) { element = $(element); if (!element._overflow) return element; element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; element._overflow = null; return element; },
Function : cumulativeOffset
Returns the offsets of element from the top left corner of the document
cumulativeOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; } while (element); return Element._returnOffset(valueL, valueT); },
Function : positionedOffset
Returns element’s offset relative to its closest positioned ancestor (the element that would be returned by Element#getOffsetParent)
positionedOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; element = element.offsetParent; if (element) { if (element.tagName.toUpperCase() == 'BODY') break; var p = Element.getStyle(element, 'position'); if (p !== 'static') break; } } while (element); return Element._returnOffset(valueL, valueT); },
Function : absolutize
Turns element into an absolutely-positioned element without changing its position in the page layout
absolutize: function(element) { element = $(element); if (Element.getStyle(element, 'position') == 'absolute') return element; var offsets = Element.positionedOffset(element); var top = offsets[1]; var left = offsets[0]; var width = element.clientWidth; var height = element.clientHeight; element._originalLeft = left - parseFloat(element.style.left || 0); element._originalTop = top - parseFloat(element.style.top || 0); element._originalWidth = element.style.width; element._originalHeight = element.style.height; element.style.position = 'absolute'; element.style.top = top + 'px'; element.style.left = left + 'px'; element.style.width = width + 'px'; element.style.height = height + 'px'; return element; },
Function : relativize
Turns element into an relatively-positioned element without changing its position in the page layout
relativize: function(element) { element = $(element); if (Element.getStyle(element, 'position') == 'relative') return element; element.style.position = 'relative'; var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); element.style.top = top + 'px'; element.style.left = left + 'px'; element.style.height = element._originalHeight; element.style.width = element._originalWidth; return element; },
Function : cumulativeScrollOffset
Calculates the cumulative scroll offset of an element in nested scrolling containers
cumulativeScrollOffset: function(element) { var valueT = 0, valueL = 0; do { valueT += element.scrollTop || 0; valueL += element.scrollLeft || 0; element = element.parentNode; } while (element); return Element._returnOffset(valueL, valueT); },
Function : getOffsetParent
Returns element’s closest positioned ancestor
getOffsetParent: function(element) { if (element.offsetParent) return $(element.offsetParent); if (element == document.body) return $(element); while ((element = element.parentNode) && element != document.body) if (Element.getStyle(element, 'position') != 'static') return $(element); return $(document.body); },
Function : viewportOffset
Returns the X/Y coordinates of element relative to the viewport
viewportOffset: function(forElement) { var valueT = 0, valueL = 0; var element = forElement; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; if (element.offsetParent == document.body && Element.getStyle(element, 'position') == 'absolute') break; } while (element = element.offsetParent); element = forElement; do { if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { valueT -= element.scrollTop || 0; valueL -= element.scrollLeft || 0; } } while (element = element.parentNode); return Element._returnOffset(valueL, valueT); },
Function : clonePosition
Clones the position and/or dimensions of source onto element as defined by the optional argument options
clonePosition: function(element, source) { var options = Object.extend({ setLeft: true, setTop: true, setWidth: true, setHeight: true, offsetTop: 0, offsetLeft: 0 }, arguments[2] || { }); source = $(source); var p = Element.viewportOffset(source); element = $(element); var delta = [0, 0]; var parent = null; if (Element.getStyle(element, 'position') == 'absolute') { parent = Element.getOffsetParent(element); delta = Element.viewportOffset(parent); } if (parent == document.body) { delta[0] -= document.body.offsetLeft; delta[1] -= document.body.offsetTop; } if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; if (options.setWidth) element.style.width = source.offsetWidth + 'px'; if (options.setHeight) element.style.height = source.offsetHeight + 'px'; return element; }
};
Code : Assign methods
Object.extend(Element.Methods, { getElementsBySelector: Element.Methods.select, childElements: Element.Methods.immediateDescendants });
Properties : _attributeTranslations
Element._attributeTranslations = { write: { names: { className: 'class', htmlFor: 'for' }, values: { } } };
Code : Opera-specific element code
if (Prototype.Browser.Opera) { Element.Methods.getStyle = Element.Methods.getStyle.wrap( function(proceed, element, style) { switch (style) { case 'left': case 'top': case 'right': case 'bottom': if (proceed(element, 'position') === 'static') return null; case 'height': case 'width': if (!Element.visible(element)) return null; var dim = parseInt(proceed(element, style), 10); if (dim !== element['offset' + style.capitalize()]) return dim + 'px'; var properties; if (style === 'height') { properties = ['border-top-width', 'padding-top', 'padding-bottom', 'border-bottom-width']; } else { properties = ['border-left-width', 'padding-left', 'padding-right', 'border-right-width']; } return properties.inject(dim, function(memo, property) { var val = proceed(element, property); return val === null ? memo : memo - parseInt(val, 10); }) + 'px'; default: return proceed(element, style); } } ); Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( function(proceed, element, attribute) { if (attribute === 'title') return element.title; return proceed(element, attribute); } ); }
Code : IE-specific element code
else if (Prototype.Browser.IE) { Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( function(proceed, element) { element = $(element); try { element.offsetParent } catch(e) { return $(document.body) } var position = element.getStyle('position'); if (position !== 'static') return proceed(element); element.setStyle({ position: 'relative' }); var value = proceed(element); element.setStyle({ position: position }); return value; } ); $w('positionedOffset viewportOffset').each(function(method) { Element.Methods[method] = Element.Methods[method].wrap( function(proceed, element) { element = $(element); try { element.offsetParent } catch(e) { return Element._returnOffset(0,0) } var position = element.getStyle('position'); if (position !== 'static') return proceed(element); var offsetParent = element.getOffsetParent(); if (offsetParent && offsetParent.getStyle('position') === 'fixed') offsetParent.setStyle({ zoom: 1 }); element.setStyle({ position: 'relative' }); var value = proceed(element); element.setStyle({ position: position }); return value; } ); }); Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( function(proceed, element) { try { element.offsetParent } catch(e) { return Element._returnOffset(0,0) } return proceed(element); } ); Element.Methods.getStyle = function(element, style) { element = $(element); style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); var value = element.style[style]; if (!value && element.currentStyle) value = element.currentStyle[style]; if (style == 'opacity') { if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) if (value[1]) return parseFloat(value[1]) / 100; return 1.0; } if (value == 'auto') { if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) return element['offset' + style.capitalize()] + 'px'; return null; } return value; }; Element.Methods.setOpacity = function(element, value) { function stripAlpha(filter){ return filter.replace(/alpha\([^\)]*\)/gi,''); } element = $(element); var currentStyle = element.currentStyle; if ((currentStyle && !currentStyle.hasLayout) || (!currentStyle && element.style.zoom == 'normal')) element.style.zoom = 1; var filter = element.getStyle('filter'), style = element.style; if (value == 1 || value === '') { (filter = stripAlpha(filter)) ? style.filter = filter : style.removeAttribute('filter'); return element; } else if (value < 0.00001) value = 0; style.filter = stripAlpha(filter) + 'alpha(opacity=' + (value * 100) + ')'; return element; }; Element._attributeTranslations = (function(){ var classProp = 'className'; var forProp = 'for'; var el = document.createElement('div'); el.setAttribute(classProp, 'x'); if (el.className !== 'x') { el.setAttribute('class', 'x'); if (el.className === 'x') { classProp = 'class'; } } el = null; el = document.createElement('label'); el.setAttribute(forProp, 'x'); if (el.htmlFor !== 'x') { el.setAttribute('htmlFor', 'x'); if (el.htmlFor === 'x') { forProp = 'htmlFor'; } } el = null; return { read: { names: { 'class': classProp, 'className': classProp, 'for': forProp, 'htmlFor': forProp }, values: { _getAttr: function(element, attribute) { return element.getAttribute(attribute); }, _getAttr2: function(element, attribute) { return element.getAttribute(attribute, 2); }, _getAttrNode: function(element, attribute) { var node = element.getAttributeNode(attribute); return node ? node.value : ""; }, _getEv: (function(){ var el = document.createElement('div'); el.onclick = Prototype.emptyFunction; var value = el.getAttribute('onclick'); var f; if (String(value).indexOf('{') > -1) { f = function(element, attribute) { attribute = element.getAttribute(attribute); if (!attribute) return null; attribute = attribute.toString(); attribute = attribute.split('{')[1]; attribute = attribute.split('}')[0]; return attribute.strip(); }; } else if (value === '') { f = function(element, attribute) { attribute = element.getAttribute(attribute); if (!attribute) return null; return attribute.strip(); }; } el = null; return f; })(), _flag: function(element, attribute) { return $(element).hasAttribute(attribute) ? attribute : null; }, style: function(element) { return element.style.cssText.toLowerCase(); }, title: function(element) { return element.title; } } } } })(); Element._attributeTranslations.write = { names: Object.extend({ cellpadding: 'cellPadding', cellspacing: 'cellSpacing' }, Element._attributeTranslations.read.names), values: { checked: function(element, value) { element.checked = !!value; }, style: function(element, value) { element.style.cssText = value ? value : ''; } } }; Element._attributeTranslations.has = {}; $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + 'encType maxLength readOnly longDesc frameBorder').each(function(attr) { Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; Element._attributeTranslations.has[attr.toLowerCase()] = attr; }); (function(v) { Object.extend(v, { href: v._getAttr2, src: v._getAttr2, type: v._getAttr, action: v._getAttrNode, disabled: v._flag, checked: v._flag, readonly: v._flag, multiple: v._flag, onload: v._getEv, onunload: v._getEv, onclick: v._getEv, ondblclick: v._getEv, onmousedown: v._getEv, onmouseup: v._getEv, onmouseover: v._getEv, onmousemove: v._getEv, onmouseout: v._getEv, onfocus: v._getEv, onblur: v._getEv, onkeypress: v._getEv, onkeydown: v._getEv, onkeyup: v._getEv, onsubmit: v._getEv, onreset: v._getEv, onselect: v._getEv, onchange: v._getEv }); })(Element._attributeTranslations.read.values); if (Prototype.BrowserFeatures.ElementExtensions) { (function() { function _descendants(element) { var nodes = element.getElementsByTagName('*'), results = []; for (var i = 0, node; node = nodes[i]; i++) if (node.tagName !== "!") // Filter out comment nodes. results.push(node); return results; } Element.Methods.down = function(element, expression, index) { element = $(element); if (arguments.length == 1) return element.firstDescendant(); return Object.isNumber(expression) ? _descendants(element)[expression] : Element.select(element, expression)[index || 0]; } })(); } }
Code : Geckospecific element code
else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { Element.Methods.setOpacity = function(element, value) { element = $(element); element.style.opacity = (value == 1) ? 0.999999 : (value === '') ? '' : (value < 0.00001) ? 0 : value; return element; }; }
Code : Webkit-specific element code
else if (Prototype.Browser.WebKit) { Element.Methods.setOpacity = function(element, value) { element = $(element); element.style.opacity = (value == 1 || value === '') ? '' : (value < 0.00001) ? 0 : value; if (value == 1) if(element.tagName.toUpperCase() == 'IMG' && element.width) { element.width++; element.width--; } else try { var n = document.createTextNode(' '); element.appendChild(n); element.removeChild(n); } catch (e) { } return element; }; Element.Methods.cumulativeOffset = function(element) { var valueT = 0, valueL = 0; do { valueT += element.offsetTop || 0; valueL += element.offsetLeft || 0; if (element.offsetParent == document.body) if (Element.getStyle(element, 'position') == 'absolute') break; element = element.offsetParent; } while (element); return Element._returnOffset(valueL, valueT); }; }
Code : replace
Replaces element by the content of the html argument and returns the removed element
if ('outerHTML' in document.documentElement) { Element.Methods.replace = function(element, content) { element = $(element); if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) { element.parentNode.replaceChild(content, element); return element; } content = Object.toHTML(content); var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); if (Element._insertionTranslations.tags[tagName]) { var nextSibling = element.next(); var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); parent.removeChild(element); if (nextSibling) fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); else fragments.each(function(node) { parent.appendChild(node) }); } else element.outerHTML = content.stripScripts(); content.evalScripts.bind(content).defer(); return element; }; }
Function : Element._returnOffset
Internal function
Element._returnOffset = function(l, t) { var result = [l, t]; result.left = l; result.top = t; return result; };
Function : Element._getContentFromAnonymousElement
Internal function
Element._getContentFromAnonymousElement = function(tagName, html) { var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; if (t) { div.innerHTML = t[0] + html + t[1]; t[2].times(function() { div = div.firstChild }); } else div.innerHTML = html; return $A(div.childNodes); };
Object : _insertionTranslations
Description
Element._insertionTranslations = { before: function(element, node) { element.parentNode.insertBefore(node, element); }, top: function(element, node) { element.insertBefore(node, element.firstChild); }, bottom: function(element, node) { element.appendChild(node); }, after: function(element, node) { element.parentNode.insertBefore(node, element.nextSibling); }, tags: { TABLE: ['<table>', '</table>', 1], TBODY: ['<table><tbody>', '</tbody></table>', 2], TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3], TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4], SELECT: ['<select>', '</select>', 1] } };
Code : Extend Element_insertionTranslations
(function() { var tags = Element._insertionTranslations.tags; Object.extend(tags, { THEAD: tags.TBODY, TFOOT: tags.TBODY, TH: tags.TD }); })();
Function : Element.Methods.Simulated
Internal function
Element.Methods.Simulated = { hasAttribute: function(element, attribute) { attribute = Element._attributeTranslations.has[attribute] || attribute; var node = $(element).getAttributeNode(attribute); return !!(node && node.specified); } };
Code : Browser-features specific code
(function(div) { if (!Prototype.BrowserFeatures.ElementExtensions && div['__proto__']) { window.HTMLElement = { }; window.HTMLElement.prototype = div['__proto__']; Prototype.BrowserFeatures.ElementExtensions = true; } div = null; })(document.createElement('div'))
Function : Element.extend
Extends element with all of the methods contained in Element.Methods and Element.Methods.Simulated. If element is an input, textarea or select tag, it will also be extended with the methods from Form.Element.Methods. If it is a form tag, it will also be extended with Form.Methods
Element.extend = (function() { function checkDeficiency(tagName) { if (typeof window.Element != 'undefined') { var proto = window.Element.prototype; if (proto) { var id = '_' + (Math.random()+'').slice(2); var el = document.createElement(tagName); proto[id] = 'x'; var isBuggy = (el[id] !== 'x'); delete proto[id]; el = null; return isBuggy; } } return false; } function extendElementWith(element, methods) { for (var property in methods) { var value = methods[property]; if (Object.isFunction(value) && !(property in element)) element[property] = value.methodize(); } } var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = checkDeficiency('object'); if (Prototype.BrowserFeatures.SpecificElementExtensions) { if (HTMLOBJECTELEMENT_PROTOTYPE_BUGGY) { return function(element) { if (element && typeof element._extendedByPrototype == 'undefined') { var t = element.tagName; if (t && (/^(?:object|applet|embed)$/i.test(t))) { extendElementWith(element, Element.Methods); extendElementWith(element, Element.Methods.Simulated); extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); } } return element; } } return Prototype.K; } var Methods = { }, ByTag = Element.Methods.ByTag; var extend = Object.extend(function(element) { if (!element || typeof element._extendedByPrototype != 'undefined' || element.nodeType != 1 || element == window) return element; var methods = Object.clone(Methods), tagName = element.tagName.toUpperCase(); if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); extendElementWith(element, methods); element._extendedByPrototype = Prototype.emptyFunction; return element; }, { refresh: function() { if (!Prototype.BrowserFeatures.ElementExtensions) { Object.extend(Methods, Element.Methods); Object.extend(Methods, Element.Methods.Simulated); } } }); extend.refresh(); return extend; })();
Function : Element.hasAttribute
Internal function
Element.hasAttribute = function(element, attribute) { if (element.hasAttribute) return element.hasAttribute(attribute); return Element.Methods.Simulated.hasAttribute(element, attribute); };
Function : Element.addMethods
Takes a hash of methods and makes them available as methods of extended elements and of the Element object. The second usage form is for targeting a specific HTML element
Element.addMethods = function(methods) { var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; if (!methods) { Object.extend(Form, Form.Methods); Object.extend(Form.Element, Form.Element.Methods); Object.extend(Element.Methods.ByTag, { "FORM": Object.clone(Form.Methods), "INPUT": Object.clone(Form.Element.Methods), "SELECT": Object.clone(Form.Element.Methods), "TEXTAREA": Object.clone(Form.Element.Methods) }); } if (arguments.length == 2) { var tagName = methods; methods = arguments[1]; } if (!tagName) Object.extend(Element.Methods, methods || { }); else { if (Object.isArray(tagName)) tagName.each(extend); else extend(tagName); } function extend(tagName) { tagName = tagName.toUpperCase(); if (!Element.Methods.ByTag[tagName]) Element.Methods.ByTag[tagName] = { }; Object.extend(Element.Methods.ByTag[tagName], methods); } function copy(methods, destination, onlyIfAbsent) { onlyIfAbsent = onlyIfAbsent || false; for (var property in methods) { var value = methods[property]; if (!Object.isFunction(value)) continue; if (!onlyIfAbsent || !(property in destination)) destination[property] = value.methodize(); } } function findDOMClass(tagName) { var klass; var trans = { "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": "FrameSet", "IFRAME": "IFrame" }; if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; if (window[klass]) return window[klass]; klass = 'HTML' + tagName + 'Element'; if (window[klass]) return window[klass]; klass = 'HTML' + tagName.capitalize() + 'Element'; if (window[klass]) return window[klass]; var element = document.createElement(tagName); var proto = element['__proto__'] || element.constructor.prototype; element = null; return proto; } var elementPrototype = window.HTMLElement ? HTMLElement.prototype : Element.prototype; if (F.ElementExtensions) { copy(Element.Methods, elementPrototype); copy(Element.Methods.Simulated, elementPrototype, true); } if (F.SpecificElementExtensions) { for (var tag in Element.Methods.ByTag) { var klass = findDOMClass(tag); if (Object.isUndefined(klass)) continue; copy(T[tag], klass.prototype); } } Object.extend(Element, Element.Methods); delete Element.ByTag; if (Element.extend.refresh) Element.extend.refresh(); Element.cache = { }; };
Properties : document.viewport
document.viewport = { getDimensions: function() { return { width: this.getWidth(), height: this.getHeight() }; }, getScrollOffsets: function() { return Element._returnOffset( window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); } };
Code : Browser-specific viewport code
(function(viewport) { var B = Prototype.Browser, doc = document, element, property = {}; function getRootElement() { if (B.WebKit && !doc.evaluate) return document; if (B.Opera && window.parseFloat(window.opera.version()) < 9.5) return document.body; return document.documentElement; } function define(D) { if (!element) element = getRootElement(); property[D] = 'client' + D; viewport['get' + D] = function() { return element[property[D]] }; return viewport['get' + D](); } viewport.getWidth = define.curry('Width'); viewport.getHeight = define.curry('Height'); })(document.viewport);
Properties : Element.Storage
Element.Storage = { UID: 1 };
Code : Additional methods for Element
Element.addMethods({ getStorage: function(element) { if (!(element = $(element))) return; var uid; if (element === window) { uid = 0; } else { if (typeof element._prototypeUID === "undefined") element._prototypeUID = [Element.Storage.UID++]; uid = element._prototypeUID[0]; } if (!Element.Storage[uid]) Element.Storage[uid] = $H(); return Element.Storage[uid]; }, store: function(element, key, value) { if (!(element = $(element))) return; if (arguments.length === 2) { Element.getStorage(element).update(key); } else { Element.getStorage(element).set(key, value); } return element; }, retrieve: function(element, key, defaultValue) { if (!(element = $(element))) return; var hash = Element.getStorage(element), value = hash.get(key); if (Object.isUndefined(value)) { hash.set(key, defaultValue); value = defaultValue; } return value; }, clone: function(element, deep) { if (!(element = $(element))) return; var clone = element.cloneNode(deep); clone._prototypeUID = void 0; if (deep) { var descendants = Element.select(clone, '*'), i = descendants.length; while (i--) { descendants[i]._prototypeUID = void 0; } } return Element.extend(clone); } });
Class : Selector
Description
/* Portions of the Selector class are derived from Jack Slocum's DomQuery, * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style * license. Please see http://yui-ext.com/ for more information. */ var Selector = Class.create({
Function : initialize
Constructor function
initialize: function(expression) { this.expression = expression.strip(); if (this.shouldUseSelectorsAPI()) { this.mode = 'selectorsAPI'; } else if (this.shouldUseXPath()) { this.mode = 'xpath'; this.compileXPathMatcher(); } else { this.mode = "normal"; this.compileMatcher(); } },
Properties : shouldUseXPath
shouldUseXPath: (function() { var IS_DESCENDANT_SELECTOR_BUGGY = (function(){ var isBuggy = false; if (document.evaluate && window.XPathResult) { var el = document.createElement('div'); el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>'; var xpath = ".//*[local-name()='ul' or local-name()='UL']" + "//*[local-name()='li' or local-name()='LI']"; var result = document.evaluate(xpath, el, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); isBuggy = (result.snapshotLength !== 2); el = null; } return isBuggy; })(); return function() { if (!Prototype.BrowserFeatures.XPath) return false; var e = this.expression; if (Prototype.Browser.WebKit && (e.include("-of-type") || e.include(":empty"))) return false; if ((/(\[[\w-]*?:|:checked)/).test(e)) return false; if (IS_DESCENDANT_SELECTOR_BUGGY) return false; return true; } })(),
Function : shouldUseSelectorsAPI
Internal function
shouldUseSelectorsAPI: function() { if (!Prototype.BrowserFeatures.SelectorsAPI) return false; if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; if (!Selector._div) Selector._div = new Element('div'); try { Selector._div.querySelector(this.expression); } catch(e) { return false; } return true; },
Function : compileMatcher
Internal function
compileMatcher: function() { var e = this.expression, ps = Selector.patterns, h = Selector.handlers, c = Selector.criteria, le, p, m, len = ps.length, name; if (Selector._cache[e]) { this.matcher = Selector._cache[e]; return; } this.matcher = ["this.matcher = function(root) {", "var r = root, h = Selector.handlers, c = false, n;"]; while (e && le != e && (/\S/).test(e)) { le = e; for (var i = 0; i<len; i++) { p = ps[i].re; name = ps[i].name; if (m = e.match(p)) { this.matcher.push(Object.isFunction(c[name]) ? c[name](m) : new Template(c[name]).evaluate(m)); e = e.replace(m[0], ''); break; } } } this.matcher.push("return h.unique(n);\n}"); eval(this.matcher.join('\n')); Selector._cache[this.expression] = this.matcher; },
Function : compileXPathMatcher
Internal function
compileXPathMatcher: function() { var e = this.expression, ps = Selector.patterns, x = Selector.xpath, le, m, len = ps.length, name; if (Selector._cache[e]) { this.xpath = Selector._cache[e]; return; } this.matcher = ['.//*']; while (e && le != e && (/\S/).test(e)) { le = e; for (var i = 0; i<len; i++) { name = ps[i].name; if (m = e.match(ps[i].re)) { this.matcher.push(Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m)); e = e.replace(m[0], ''); break; } } } this.xpath = this.matcher.join(''); Selector._cache[this.expression] = this.xpath; },
Function : findElements
Searches the document for elements that match the instance's CSS selector.
findElements: function(root) { root = root || document; var e = this.expression, results; switch (this.mode) { case 'selectorsAPI': if (root !== document) { var oldId = root.id, id = $(root).identify(); id = id.replace(/([\.:])/g, "\\$1"); e = "#" + id + " " + e; } results = $A(root.querySelectorAll(e)).map(Element.extend); root.id = oldId; return results; case 'xpath': return document._getElementsByXPath(this.xpath, root); default: return this.matcher(root); } },
Function : match
Tests whether a element matches the instance's CSS selector.
match: function(element) { this.tokens = []; var e = this.expression, ps = Selector.patterns, as = Selector.assertions; var le, p, m, len = ps.length, name; while (e && le !== e && (/\S/).test(e)) { le = e; for (var i = 0; i<len; i++) { p = ps[i].re; name = ps[i].name; if (m = e.match(p)) { if (as[name]) { this.tokens.push([name, Object.clone(m)]); e = e.replace(m[0], ''); } else { return this.findElements(document).include(element); } } } } var match = true, name, matches; for (var i = 0, token; token = this.tokens[i]; i++) { name = token[0], matches = token[1]; if (!Selector.assertions[name](element, matches)) { match = false; break; } } return match; },
Function : toString
Returns the selector expression
toString: function() { return this.expression; },
Function : inspect
Returns a debug-oriented output
inspect: function() { return "#<Selector:" + this.expression.inspect() + ">"; }
});
Code : Browser-specific selectors test
if (Prototype.BrowserFeatures.SelectorsAPI && document.compatMode === 'BackCompat') { Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){ var div = document.createElement('div'), span = document.createElement('span'); div.id = "prototype_test_id"; span.className = 'Test'; div.appendChild(span); var isIgnored = (div.querySelector('#prototype_test_id .test') !== null); div = span = null; return isIgnored; })(); }
Extend : Selector
A class that queries the document for elements that match a given CSS selector.
Object.extend(Selector, {
Properties : _cache
_cache: { },
Object : xpath
xpath: { descendant: "//*", child: "/*", adjacent: "/following-sibling::*[1]", laterSibling: '/following-sibling::*', tagName: function(m) { if (m[1] == '*') return ''; return "[local-name()='" + m[1].toLowerCase() + "' or local-name()='" + m[1].toUpperCase() + "']"; }, className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", id: "[@id='#{1}']", attrPresence: function(m) { m[1] = m[1].toLowerCase(); return new Template("[@#{1}]").evaluate(m); }, attr: function(m) { m[1] = m[1].toLowerCase(); m[3] = m[5] || m[6]; return new Template(Selector.xpath.operators[m[2]]).evaluate(m); }, pseudo: function(m) { var h = Selector.xpath.pseudos[m[1]]; if (!h) return ''; if (Object.isFunction(h)) return h(m); return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); }, operators: { '=': "[@#{1}='#{3}']", '!=': "[@#{1}!='#{3}']", '^=': "[starts-with(@#{1}, '#{3}')]", '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", '*=': "[contains(@#{1}, '#{3}')]", '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" }, pseudos: { 'first-child': '[not(preceding-sibling::*)]', 'last-child': '[not(following-sibling::*)]', 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', 'empty': "[count(*) = 0 and (count(text()) = 0)]", 'checked': "[@checked]", 'disabled': "[(@disabled) and (@type!='hidden')]", 'enabled': "[not(@disabled) and (@type!='hidden')]", 'not': function(m) { var e = m[6], p = Selector.patterns, x = Selector.xpath, le, v, len = p.length, name; var exclusion = []; while (e && le != e && (/\S/).test(e)) { le = e; for (var i = 0; i<len; i++) { name = p[i].name if (m = e.match(p[i].re)) { v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m); exclusion.push("(" + v.substring(1, v.length - 1) + ")"); e = e.replace(m[0], ''); break; } } } return "[not(" + exclusion.join(" and ") + ")]"; }, 'nth-child': function(m) { return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); }, 'nth-last-child': function(m) { return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); }, 'nth-of-type': function(m) { return Selector.xpath.pseudos.nth("position() ", m); }, 'nth-last-of-type': function(m) { return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); }, 'first-of-type': function(m) { m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); }, 'last-of-type': function(m) { m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); }, 'only-of-type': function(m) { var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); }, nth: function(fragment, m) { var mm, formula = m[6], predicate; if (formula == 'even') formula = '2n+0'; if (formula == 'odd') formula = '2n+1'; if (mm = formula.match(/^(\d+)$/)) // digit only return '[' + fragment + "= " + mm[1] + ']'; if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b if (mm[1] == "-") mm[1] = -1; var a = mm[1] ? Number(mm[1]) : 1; var b = mm[2] ? Number(mm[2]) : 0; predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + "((#{fragment} - #{b}) div #{a} >= 0)]"; return new Template(predicate).evaluate({ fragment: fragment, a: a, b: b }); } } } },
Object : criteria
criteria: { tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', className: 'n = h.className(n, r, "#{1}", c); c = false;', id: 'n = h.id(n, r, "#{1}", c); c = false;', attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', attr: function(m) { m[3] = (m[5] || m[6]); return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); }, pseudo: function(m) { if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); }, descendant: 'c = "descendant";', child: 'c = "child";', adjacent: 'c = "adjacent";', laterSibling: 'c = "laterSibling";' },
Object : patterns
patterns: [ { name: 'laterSibling', re: /^\s*~\s*/ }, { name: 'child', re: /^\s*>\s*/ }, { name: 'adjacent', re: /^\s*\+\s*/ }, { name: 'descendant', re: /^\s/ }, { name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ }, { name: 'id', re: /^#([\w\-\*]+)(\b|$)/ }, { name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ }, { name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ }, { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ }, { name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ } ],
Object : assertions
assertions: { tagName: function(element, matches) { return matches[1].toUpperCase() == element.tagName.toUpperCase(); }, className: function(element, matches) { return Element.hasClassName(element, matches[1]); }, id: function(element, matches) { return element.id === matches[1]; }, attrPresence: function(element, matches) { return Element.hasAttribute(element, matches[1]); }, attr: function(element, matches) { var nodeValue = Element.readAttribute(element, matches[1]); return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); } },
Object : handlers
handlers: { concat: function(a, b) { for (var i = 0, node; node = b[i]; i++) a.push(node); return a; }, mark: function(nodes) { var _true = Prototype.emptyFunction; for (var i = 0, node; node = nodes[i]; i++) node._countedByPrototype = _true; return nodes; }, unmark: (function(){ var PROPERTIES_ATTRIBUTES_MAP = (function(){ var el = document.createElement('div'), isBuggy = false, propName = '_countedByPrototype', value = 'x' el[propName] = value; isBuggy = (el.getAttribute(propName) === value); el = null; return isBuggy; })(); return PROPERTIES_ATTRIBUTES_MAP ? function(nodes) { for (var i = 0, node; node = nodes[i]; i++) node.removeAttribute('_countedByPrototype'); return nodes; } : function(nodes) { for (var i = 0, node; node = nodes[i]; i++) node._countedByPrototype = void 0; return nodes; } })(), index: function(parentNode, reverse, ofType) { parentNode._countedByPrototype = Prototype.emptyFunction; if (reverse) { for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { var node = nodes[i]; if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; } } else { for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; } }, unique: function(nodes) { if (nodes.length == 0) return nodes; var results = [], n; for (var i = 0, l = nodes.length; i < l; i++) if (typeof (n = nodes[i])._countedByPrototype == 'undefined') { n._countedByPrototype = Prototype.emptyFunction; results.push(Element.extend(n)); } return Selector.handlers.unmark(results); }, descendant: function(nodes) { var h = Selector.handlers; for (var i = 0, results = [], node; node = nodes[i]; i++) h.concat(results, node.getElementsByTagName('*')); return results; }, child: function(nodes) { var h = Selector.handlers; for (var i = 0, results = [], node; node = nodes[i]; i++) { for (var j = 0, child; child = node.childNodes[j]; j++) if (child.nodeType == 1 && child.tagName != '!') results.push(child); } return results; }, adjacent: function(nodes) { for (var i = 0, results = [], node; node = nodes[i]; i++) { var next = this.nextElementSibling(node); if (next) results.push(next); } return results; }, laterSibling: function(nodes) { var h = Selector.handlers; for (var i = 0, results = [], node; node = nodes[i]; i++) h.concat(results, Element.nextSiblings(node)); return results; }, nextElementSibling: function(node) { while (node = node.nextSibling) if (node.nodeType == 1) return node; return null; }, previousElementSibling: function(node) { while (node = node.previousSibling) if (node.nodeType == 1) return node; return null; }, tagName: function(nodes, root, tagName, combinator) { var uTagName = tagName.toUpperCase(); var results = [], h = Selector.handlers; if (nodes) { if (combinator) { if (combinator == "descendant") { for (var i = 0, node; node = nodes[i]; i++) h.concat(results, node.getElementsByTagName(tagName)); return results; } else nodes = this[combinator](nodes); if (tagName == "*") return nodes; } for (var i = 0, node; node = nodes[i]; i++) if (node.tagName.toUpperCase() === uTagName) results.push(node); return results; } else return root.getElementsByTagName(tagName); }, id: function(nodes, root, id, combinator) { var targetNode = $(id), h = Selector.handlers; if (root == document) { if (!targetNode) return []; if (!nodes) return [targetNode]; } else { if (!root.sourceIndex || root.sourceIndex < 1) { var nodes = root.getElementsByTagName('*'); for (var j = 0, node; node = nodes[j]; j++) { if (node.id === id) return [node]; } } } if (nodes) { if (combinator) { if (combinator == 'child') { for (var i = 0, node; node = nodes[i]; i++) if (targetNode.parentNode == node) return [targetNode]; } else if (combinator == 'descendant') { for (var i = 0, node; node = nodes[i]; i++) if (Element.descendantOf(targetNode, node)) return [targetNode]; } else if (combinator == 'adjacent') { for (var i = 0, node; node = nodes[i]; i++) if (Selector.handlers.previousElementSibling(targetNode) == node) return [targetNode]; } else nodes = h[combinator](nodes); } for (var i = 0, node; node = nodes[i]; i++) if (node == targetNode) return [targetNode]; return []; } return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; }, className: function(nodes, root, className, combinator) { if (nodes && combinator) nodes = this[combinator](nodes); return Selector.handlers.byClassName(nodes, root, className); }, byClassName: function(nodes, root, className) { if (!nodes) nodes = Selector.handlers.descendant([root]); var needle = ' ' + className + ' '; for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { nodeClassName = node.className; if (nodeClassName.length == 0) continue; if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) results.push(node); } return results; }, attrPresence: function(nodes, root, attr, combinator) { if (!nodes) nodes = root.getElementsByTagName("*"); if (nodes && combinator) nodes = this[combinator](nodes); var results = []; for (var i = 0, node; node = nodes[i]; i++) if (Element.hasAttribute(node, attr)) results.push(node); return results; }, attr: function(nodes, root, attr, value, operator, combinator) { if (!nodes) nodes = root.getElementsByTagName("*"); if (nodes && combinator) nodes = this[combinator](nodes); var handler = Selector.operators[operator], results = []; for (var i = 0, node; node = nodes[i]; i++) { var nodeValue = Element.readAttribute(node, attr); if (nodeValue === null) continue; if (handler(nodeValue, value)) results.push(node); } return results; }, pseudo: function(nodes, name, value, root, combinator) { if (nodes && combinator) nodes = this[combinator](nodes); if (!nodes) nodes = root.getElementsByTagName("*"); return Selector.pseudos[name](nodes, value, root); } },
Object : pseudos
pseudos: { 'first-child': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) { if (Selector.handlers.previousElementSibling(node)) continue; results.push(node); } return results; }, 'last-child': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) { if (Selector.handlers.nextElementSibling(node)) continue; results.push(node); } return results; }, 'only-child': function(nodes, value, root) { var h = Selector.handlers; for (var i = 0, results = [], node; node = nodes[i]; i++) if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) results.push(node); return results; }, 'nth-child': function(nodes, formula, root) { return Selector.pseudos.nth(nodes, formula, root); }, 'nth-last-child': function(nodes, formula, root) { return Selector.pseudos.nth(nodes, formula, root, true); }, 'nth-of-type': function(nodes, formula, root) { return Selector.pseudos.nth(nodes, formula, root, false, true); }, 'nth-last-of-type': function(nodes, formula, root) { return Selector.pseudos.nth(nodes, formula, root, true, true); }, 'first-of-type': function(nodes, formula, root) { return Selector.pseudos.nth(nodes, "1", root, false, true); }, 'last-of-type': function(nodes, formula, root) { return Selector.pseudos.nth(nodes, "1", root, true, true); }, 'only-of-type': function(nodes, formula, root) { var p = Selector.pseudos; return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); }, getIndices: function(a, b, total) { if (a == 0) return b > 0 ? [b] : []; return $R(1, total).inject([], function(memo, i) { if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); return memo; }); }, nth: function(nodes, formula, root, reverse, ofType) { if (nodes.length == 0) return []; if (formula == 'even') formula = '2n+0'; if (formula == 'odd') formula = '2n+1'; var h = Selector.handlers, results = [], indexed = [], m; h.mark(nodes); for (var i = 0, node; node = nodes[i]; i++) { if (!node.parentNode._countedByPrototype) { h.index(node.parentNode, reverse, ofType); indexed.push(node.parentNode); } } if (formula.match(/^\d+$/)) { // just a number formula = Number(formula); for (var i = 0, node; node = nodes[i]; i++) if (node.nodeIndex == formula) results.push(node); } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b if (m[1] == "-") m[1] = -1; var a = m[1] ? Number(m[1]) : 1; var b = m[2] ? Number(m[2]) : 0; var indices = Selector.pseudos.getIndices(a, b, nodes.length); for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { for (var j = 0; j < l; j++) if (node.nodeIndex == indices[j]) results.push(node); } } h.unmark(nodes); h.unmark(indexed); return results; }, 'empty': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) { if (node.tagName == '!' || node.firstChild) continue; results.push(node); } return results; }, 'not': function(nodes, selector, root) { var h = Selector.handlers, selectorType, m; var exclusions = new Selector(selector).findElements(root); h.mark(exclusions); for (var i = 0, results = [], node; node = nodes[i]; i++) if (!node._countedByPrototype) results.push(node); h.unmark(exclusions); return results; }, 'enabled': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) if (!node.disabled && (!node.type || node.type !== 'hidden')) results.push(node); return results; }, 'disabled': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) if (node.disabled) results.push(node); return results; }, 'checked': function(nodes, value, root) { for (var i = 0, results = [], node; node = nodes[i]; i++) if (node.checked) results.push(node); return results; } },
Object : operators
operators: { '=': function(nv, v) { return nv == v; }, '!=': function(nv, v) { return nv != v; }, '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); }, '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + '-').include('-' + (v || "").toUpperCase() + '-'); } },
Function : split
Internal function
split: function(expression) { var expressions = []; expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { expressions.push(m[1].strip()); }); return expressions; },
Function : matchElements
Internal function
matchElements: function(elements, expression) { var matches = $$(expression), h = Selector.handlers; h.mark(matches); for (var i = 0, results = [], element; element = elements[i]; i++) if (element._countedByPrototype) results.push(element); h.unmark(matches); return results; },
Function : findElement
Returns the indexth element in the collection that matches expression. Returns the indexth element overall if expression is not given
findElement: function(elements, expression, index) { if (Object.isNumber(expression)) { index = expression; expression = false; } return Selector.matchElements(elements, expression || '*')[index || 0]; },
Function : findChildElements
Searches beneath element for any elements that match the selector (or selectors) specified in expressions
findChildElements: function(element, expressions) { expressions = Selector.split(expressions.join(',')); var results = [], h = Selector.handlers; for (var i = 0, l = expressions.length, selector; i < l; i++) { selector = new Selector(expressions[i].strip()); h.concat(results, selector.findElements(element)); } return (l > 1) ? h.unique(results) : results; }
});
Code : IE-specific code
if (Prototype.Browser.IE) { Object.extend(Selector.handlers, { concat: function(a, b) { for (var i = 0, node; node = b[i]; i++) if (node.tagName !== "!") a.push(node); return a; } }); }
Function : $$
Takes an arbitrary number of CSS selectors (strings) and returns a document-order array of extended DOM elements that match any of them
function $$() { return Selector.findChildElements(document, $A(arguments)); }
Section : Form
Object : Form
Form is a namespace and a module for all things form-related, packed with form manipulation and serialization goodness. While it holds methods dealing with forms as whole, its submodule Form.Element deals with specific form controls
var Form = {
Function : reset
Resets a form to its default values
reset: function(form) { form = $(form); form.reset(); return form; },
Function : serializeElements
Serialize an array of form elements to a string suitable for Ajax requests (default behavior) or, if optional getHash evaluates to true, an object hash where keys are form control names and values are data
serializeElements: function(elements, options) { if (typeof options != 'object') options = { hash: !!options }; else if (Object.isUndefined(options.hash)) options.hash = true; var key, value, submitted = false, submit = options.submit; var data = elements.inject({ }, function(result, element) { if (!element.disabled && element.name) { key = element.name; value = $(element).getValue(); if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && submit !== false && (!submit || key == submit) && (submitted = true)))) { if (key in result) { if (!Object.isArray(result[key])) result[key] = [result[key]]; result[key].push(value); } else result[key] = value; } } return result; }); return options.hash ? data : Object.toQueryString(data); }
};
Object : Form Methods
Most of these methods are also available directly on FORM elements that have been extended
Form.Methods = {
Function : serialize
Serialize form data to a string suitable for Ajax requests (default behavior) or, if optional getHash evaluates to true, an object hash where keys are form control names and values are data
serialize: function(form, options) { return Form.serializeElements(Form.getElements(form), options); },
Function : getElements
Returns a collection of all form controls within a form
getElements: function(form) { var elements = $(form).getElementsByTagName('*'), element, arr = [ ], serializers = Form.Element.Serializers; for (var i = 0; element = elements[i]; i++) { arr.push(element); } return arr.inject([], function(elements, child) { if (serializers[child.tagName.toLowerCase()]) elements.push(Element.extend(child)); return elements; }) },
Function : getInputs
Returns a collection of all INPUT elements in a form
getInputs: function(form, typeName, name) { form = $(form); var inputs = form.getElementsByTagName('input'); if (!typeName && !name) return $A(inputs).map(Element.extend); for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { var input = inputs[i]; if ((typeName && input.type != typeName) || (name && input.name != name)) continue; matchingInputs.push(Element.extend(input)); } return matchingInputs; },
Function : disable
Disables the form as whole
disable: function(form) { form = $(form); Form.getElements(form).invoke('disable'); return form; },
Function : enable
Enables a fully or partially disabled form
enable: function(form) { form = $(form); Form.getElements(form).invoke('enable'); return form; },
Function : findFirstElement
Finds first non-hidden, non-disabled form control
findFirstElement: function(form) { var elements = $(form).getElements().findAll(function(element) { return 'hidden' != element.type && !element.disabled; }); var firstByIndex = elements.findAll(function(element) { return element.hasAttribute('tabIndex') && element.tabIndex >= 0; }).sortBy(function(element) { return element.tabIndex }).first(); return firstByIndex ? firstByIndex : elements.find(function(element) { return /^(?:input|select|textarea)$/i.test(element.tagName); }); },
Function : focusFirstElement
Gives keyboard focus to the first element of the form
focusFirstElement: function(form) { form = $(form); form.findFirstElement().activate(); return form; },
Function : request
A convenience method for serializing and submitting the form via an Ajax
request: function(form, options) { form = $(form), options = Object.clone(options || { }); var params = options.parameters, action = form.readAttribute('action') || ''; if (action.blank()) action = window.location.href; options.parameters = form.serialize(true); if (params) { if (Object.isString(params)) params = params.toQueryParams(); Object.extend(options.parameters, params); } if (form.hasAttribute('method') && !options.method) options.method = form.method; return new Ajax.Request(action, options); }
};
Object : Form Element
This is a collection of methods that assist in dealing with form controls. They provide ways to focus, serialize, disable/enable or extract current value from a specific control.
Form.Element = {
Function : focus
Gives keyboard focus to an element
focus: function(element) { $(element).focus(); return element; },
Function : select
Selects the current text in a text input
select: function(element) { $(element).select(); return element; }
};
Object : Form.Element.Methods
Description
Form.Element.Methods = {
Function : serialize
Creates an URL-encoded string representation of a form control in the name=value format
serialize: function(element) { element = $(element); if (!element.disabled && element.name) { var value = element.getValue(); if (value != undefined) { var pair = { }; pair[element.name] = value; return Object.toQueryString(pair); } } return ''; },
Function : getValue
Returns the current value of a form control
getValue: function(element) { element = $(element); var method = element.tagName.toLowerCase(); return Form.Element.Serializers[method](element); },
Function : setValue
Sets the current value of a form control
setValue: function(element, value) { element = $(element); var method = element.tagName.toLowerCase(); Form.Element.Serializers[method](element, value); return element; },
Function : clear
Clears the contents of a text input
clear: function(element) { $(element).value = ''; return element; },
Function : present
Returns true if a text input has contents, false otherwise
present: function(element) { return $(element).value != ''; },
Function : activate
Gives focus to a form control and selects its contents if it is a text input
activate: function(element) { element = $(element); try { element.focus(); if (element.select && (element.tagName.toLowerCase() != 'input' || !(/^(?:button|reset|submit)$/i.test(element.type)))) element.select(); } catch (e) { } return element; },
Function : disable
Disables a form control, effectively preventing its value to be changed until it is enabled again
disable: function(element) { element = $(element); element.disabled = true; return element; },
Function : enable
Enables a previously disabled form control
enable: function(element) { element = $(element); element.disabled = false; return element; }
};
Function : $F
Returns the value of a form control. This is a convenience alias of Form.Element.getValue. Refer to it for full details
var $F = Form.Element.Methods.getValue;
Object : Form.Element.Serializers
Form serialization functions
Form.Element.Serializers = { input: function(element, value) { switch (element.type.toLowerCase()) { case 'checkbox': case 'radio': return Form.Element.Serializers.inputSelector(element, value); default: return Form.Element.Serializers.textarea(element, value); } }, inputSelector: function(element, value) { if (Object.isUndefined(value)) return element.checked ? element.value : null; else element.checked = !!value; }, textarea: function(element, value) { if (Object.isUndefined(value)) return element.value; else element.value = value; }, select: function(element, value) { if (Object.isUndefined(value)) return this[element.type == 'select-one' ? 'selectOne' : 'selectMany'](element); else { var opt, currentValue, single = !Object.isArray(value); for (var i = 0, length = element.length; i < length; i++) { opt = element.options[i]; currentValue = this.optionValue(opt); if (single) { if (currentValue == value) { opt.selected = true; return; } } else opt.selected = value.include(currentValue); } } }, selectOne: function(element) { var index = element.selectedIndex; return index >= 0 ? this.optionValue(element.options[index]) : null; }, selectMany: function(element) { var values, length = element.length; if (!length) return null; for (var i = 0, values = []; i < length; i++) { var opt = element.options[i]; if (opt.selected) values.push(this.optionValue(opt)); } return values; }, optionValue: function(opt) { return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; }
};
Object : Timed Observer
An abstract observer class which instances can be used to periodically check some value and trigger a callback when the value has changed. The frequency is in seconds
Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
Function : initialize
Constructor function
initialize: function($super, element, frequency, callback) { $super(callback, frequency); this.element = $(element); this.lastValue = this.getValue(); },
Function : execute
Internal function
execute: function() { var value = this.getValue(); if (Object.isString(this.lastValue) && Object.isString(value) ? this.lastValue != value : String(this.lastValue) != String(value)) { this.callback(this.element, value); this.lastValue = value; } }
});
Class : Form.Element.Observer
orm.Element observer implements the getValue() method using Form.Element.getValue() on the given element. See Abstract.TimedObserver for general documentation on timed observers
Form.Element.Observer = Class.create(Abstract.TimedObserver, {
Function : getValue
Returns the current value of a form control.
getValue: function() { return Form.Element.getValue(this.element); }
});
Class : Form.Observer
Form observer implements the getValue() method using Form.serialize() on the element from the first argument. See Abstract.TimedObserver for general documentation on timed observers
Form.Observer = Class.create(Abstract.TimedObserver, {
Function : getValue
Serialize the form data to a string
getValue: function() { return Form.serialize(this.element); }
});
Class : Abstract.EventObserver
No info
Abstract.EventObserver = Class.create({
Function : initialize
Constructor function
initialize: function(element, callback) { this.element = $(element); this.callback = callback; this.lastValue = this.getValue(); if (this.element.tagName.toLowerCase() == 'form') this.registerFormCallbacks(); else this.registerCallback(this.element); },
Function : onElementEvent
Internal function
onElementEvent: function() { var value = this.getValue(); if (this.lastValue != value) { this.callback(this.element, value); this.lastValue = value; } },
Function : registerFormCallbacks
Internal function
registerFormCallbacks: function() { Form.getElements(this.element).each(this.registerCallback, this); },
Function : registerCallback
Internal function
registerCallback: function(element) { if (element.type) { switch (element.type.toLowerCase()) { case 'checkbox': case 'radio': Event.observe(element, 'click', this.onElementEvent.bind(this)); break; default: Event.observe(element, 'change', this.onElementEvent.bind(this)); break; } } }
});
Class : Form.Element.EventObserver
No info
Form.Element.EventObserver = Class.create(Abstract.EventObserver, { getValue: function() { return Form.Element.getValue(this.element); } });
Class : Form.EventObserver
No info
Form.EventObserver = Class.create(Abstract.EventObserver, { getValue: function() { return Form.serialize(this.element); } });
Section : Event
Code : Self-executing anonymous function
(function() {
object : Event
Event management class
var Event = { KEY_BACKSPACE: 8, KEY_TAB: 9, KEY_RETURN: 13, KEY_ESC: 27, KEY_LEFT: 37, KEY_UP: 38, KEY_RIGHT: 39, KEY_DOWN: 40, KEY_DELETE: 46, KEY_HOME: 36, KEY_END: 35, KEY_PAGEUP: 33, KEY_PAGEDOWN: 34, KEY_INSERT: 45, cache: {} };
Code : Browser-specific mouse event code
var docEl = document.documentElement; var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl && 'onmouseleave' in docEl; var _isButton; if (Prototype.Browser.IE) { var buttonMap = { 0: 1, 1: 4, 2: 2 }; _isButton = function(event, code) { return event.button === buttonMap[code]; }; } else if (Prototype.Browser.WebKit) { _isButton = function(event, code) { switch (code) { case 0: return event.which == 1 && !event.metaKey; case 1: return event.which == 1 && event.metaKey; default: return false; } }; } else { _isButton = function(event, code) { return event.which ? (event.which === code + 1) : (event.button === code); }; }
Function : isMiddleClick
Internal function
function isMiddleClick(event) { return _isButton(event, 1) }
Function : isRightClick
Internal function
function isRightClick(event) { return _isButton(event, 2) }
Function : element
Returns the DOM element on which the event occurred
function element(event) { event = Event.extend(event); var node = event.target, type = event.type, currentTarget = event.currentTarget; if (currentTarget && currentTarget.tagName) { if (type === 'load' || type === 'error' || (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' && currentTarget.type === 'radio')) node = currentTarget; } if (node.nodeType == Node.TEXT_NODE) node = node.parentNode; return Element.extend(node); }
Function : findElement
Returns the first DOM element with a given tag name, upwards from the one on which the event occurred
function findElement(event, expression) { var element = Event.element(event); if (!expression) return element; var elements = [element].concat(element.ancestors()); return Selector.findElement(elements, expression, 0); }
Function : pointer
Internal function
function pointer(event) { return { x: pointerX(event), y: pointerY(event) }; }
Function : pointerX
Returns the absolute horizontal position for a mouse event
function pointerX(event) { var docElement = document.documentElement, body = document.body || { scrollLeft: 0 }; return event.pageX || (event.clientX + (docElement.scrollLeft || body.scrollLeft) - (docElement.clientLeft || 0)); }
Function : pointerY
Returns the absolute vertical position for a mouse event
function pointerY(event) { var docElement = document.documentElement, body = document.body || { scrollTop: 0 }; return event.pageY || (event.clientY + (docElement.scrollTop || body.scrollTop) - (docElement.clientTop || 0)); }
Function : stop
Stops the event’s propagation and prevents its default action from being triggered eventually
function stop(event) { Event.extend(event); event.preventDefault(); event.stopPropagation(); event.stopped = true; }
Object : Event.Methods
Description
Event.Methods = { isLeftClick: isLeftClick, isMiddleClick: isMiddleClick, isRightClick: isRightClick, element: element, findElement: findElement, pointer: pointer, pointerX: pointerX, pointerY: pointerY, stop: stop };
Code : Assign event methods to Event object
var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { m[name] = Event.Methods[name].methodize(); return m; }); if (Prototype.Browser.IE) { function _relatedTarget(event) { var element; switch (event.type) { case 'mouseover': element = event.fromElement; break; case 'mouseout': element = event.toElement; break; default: return null; } return Element.extend(element); } Object.extend(methods, { stopPropagation: function() { this.cancelBubble = true }, preventDefault: function() { this.returnValue = false }, inspect: function() { return '[object Event]' } }); Event.extend = function(event, element) { if (!event) return false; if (event._extendedByPrototype) return event; event._extendedByPrototype = Prototype.emptyFunction; var pointer = Event.pointer(event); Object.extend(event, { target: event.srcElement || element, relatedTarget: _relatedTarget(event), pageX: pointer.x, pageY: pointer.y }); return Object.extend(event, methods); }; } else { Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; Object.extend(Event.prototype, methods); Event.extend = Prototype.K; }
Function : _createResponder
Internal function
function _createResponder(element, eventName, handler) { var registry = Element.retrieve(element, 'prototype_event_registry'); if (Object.isUndefined(registry)) { CACHE.push(element); registry = Element.retrieve(element, 'prototype_event_registry', $H()); } var respondersForEvent = registry.get(eventName); if (Object.isUndefined(respondersForEvent)) { respondersForEvent = []; registry.set(eventName, respondersForEvent); } if (respondersForEvent.pluck('handler').include(handler)) return false; var responder; if (eventName.include(":")) { responder = function(event) { if (Object.isUndefined(event.eventName)) return false; if (event.eventName !== eventName) return false; Event.extend(event, element); handler.call(element, event); }; } else { if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && (eventName === "mouseenter" || eventName === "mouseleave")) { if (eventName === "mouseenter" || eventName === "mouseleave") { responder = function(event) { Event.extend(event, element); var parent = event.relatedTarget; while (parent && parent !== element) { try { parent = parent.parentNode; } catch(e) { parent = element; } } if (parent === element) return; handler.call(element, event); }; } } else { responder = function(event) { Event.extend(event, element); handler.call(element, event); }; } } responder.handler = handler; respondersForEvent.push(responder); return responder; }
Function : _destroyCache
Internal function
function _destroyCache() { for (var i = 0, length = CACHE.length; i < length; i++) { Event.stopObserving(CACHE[i]); CACHE[i] = null; } }
Properties : cache
var CACHE = [];
Code : Browser-specific event code
if (Prototype.Browser.IE) window.attachEvent('onunload', _destroyCache); if (Prototype.Browser.WebKit) window.addEventListener('unload', Prototype.emptyFunction, false); var _getDOMEventName = Prototype.K; if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) { _getDOMEventName = function(eventName) { var translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; return eventName in translations ? translations[eventName] : eventName; }; }
Function : observe
Registers an event handler on a DOM element
function observe(element, eventName, handler) { element = $(element); var responder = _createResponder(element, eventName, handler); if (!responder) return element; if (eventName.include(':')) { if (element.addEventListener) element.addEventListener("dataavailable", responder, false); else { element.attachEvent("ondataavailable", responder); element.attachEvent("onfilterchange", responder); } } else { var actualEventName = _getDOMEventName(eventName); if (element.addEventListener) element.addEventListener(actualEventName, responder, false); else element.attachEvent("on" + actualEventName, responder); } return element; }
Function : stopObserving
Unregisters one or more event handlers
function stopObserving(element, eventName, handler) { element = $(element); var registry = Element.retrieve(element, 'prototype_event_registry'); if (Object.isUndefined(registry)) return element; if (eventName && !handler) { var responders = registry.get(eventName); if (Object.isUndefined(responders)) return element; responders.each( function(r) { Element.stopObserving(element, eventName, r.handler); }); return element; } else if (!eventName) { registry.each( function(pair) { var eventName = pair.key, responders = pair.value; responders.each( function(r) { Element.stopObserving(element, eventName, r.handler); }); }); return element; } var responders = registry.get(eventName); if (!responders) return; var responder = responders.find( function(r) { return r.handler === handler; }); if (!responder) return element; var actualEventName = _getDOMEventName(eventName); if (eventName.include(':')) { if (element.removeEventListener) element.removeEventListener("dataavailable", responder, false); else { element.detachEvent("ondataavailable", responder); element.detachEvent("onfilterchange", responder); } } else { if (element.removeEventListener) element.removeEventListener(actualEventName, responder, false); else element.detachEvent('on' + actualEventName, responder); } registry.set(eventName, responders.without(responder)); return element; }
Function : fire
Internal function
function fire(element, eventName, memo, bubble) { element = $(element); if (Object.isUndefined(bubble)) bubble = true; if (element == document && document.createEvent && !element.dispatchEvent) element = document.documentElement; var event; if (document.createEvent) { event = document.createEvent('HTMLEvents'); event.initEvent('dataavailable', true, true); } else { event = document.createEventObject(); event.eventType = bubble ? 'ondataavailable' : 'onfilterchange'; } event.eventName = eventName; event.memo = memo || { }; if (document.createEvent) element.dispatchEvent(event); else element.fireEvent(event.eventType, event); return Event.extend(event); }
Code : Assign events
Object.extend(Event, Event.Methods); Object.extend(Event, { fire: fire, observe: observe, stopObserving: stopObserving }); Element.addMethods({ fire: fire, observe: observe, stopObserving: stopObserving }); Object.extend(document, { fire: fire.methodize(), observe: observe.methodize(), stopObserving: stopObserving.methodize(), loaded: false }); if (window.Event) Object.extend(window.Event, Event); else window.Event = Event;
})();
Code : Browser-specific DOM content loaded code
(function() { /* Support for the DOMContentLoaded event is based on work by Dan Webb, Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ var timer; function fireContentLoadedEvent() { if (document.loaded) return; if (timer) window.clearTimeout(timer); document.loaded = true; document.fire('dom:loaded'); } function checkReadyState() { if (document.readyState === 'complete') { document.stopObserving('readystatechange', checkReadyState); fireContentLoadedEvent(); } } function pollDoScroll() { try { document.documentElement.doScroll('left'); } catch(e) { timer = pollDoScroll.defer(); return; } fireContentLoadedEvent(); } if (document.addEventListener) { document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); } else { document.observe('readystatechange', checkReadyState); if (window == top) timer = pollDoScroll.defer(); } Event.observe(window, 'load', fireContentLoadedEvent); })();