Compare commits

..

No commits in common. "master" and "tobie_selector" have entirely different histories.

67 changed files with 9881 additions and 714 deletions

View File

@ -1,21 +1,3 @@
* Avoid object creation and an unnecessary function call in `Class#addMethods`, when working around JScript DontEnum bug. Replace with feature test and a simple boolean check at runtime. (kangax)
* Optimize Element#immediateDescendants. (kangax, Tobie Langel)
* Remove unnecessary function object creation and `Number#times` in `Element._getContentFromAnonymousElement`. (kangax)
* Eliminate runtime forking and long method lookup in `Element.hasAttribute`. (kangax)
* Remove redundant ternary. (kangax)
* Avoid repeating declaration statements where it makes sense, for slightly better runtime performance and minification. (kangax)
* Make `Event.stopObserving` return element in all cases. [#810 state:resolved] (Yaffle, Tobie Langel)
* String#startsWith, String#endsWith performance optimization (Yaffle, Tobie Langel, kangax)
* Rewrite String#camelize using String#replace with a replacement function (Phred, John-David Dalton, Samuel Lebeau, kangax)
*1.6.1* (August 24, 2009)
* Avoid triggering a warning when Java is disabled in IE8. [#668 state:resolved] (orv, kangax, Andrew Dupont, Tobie Langel)

View File

@ -13,7 +13,6 @@ module PrototypeHelper
TEST_UNIT_DIR = File.join(TEST_DIR, 'unit')
TMP_DIR = File.join(TEST_UNIT_DIR, 'tmp')
VERSION = YAML.load(IO.read(File.join(SRC_DIR, 'constants.yml')))['PROTOTYPE_VERSION']
DEFAULT_SELECTOR_ENGINE = 'sizzle'
%w[sprockets pdoc unittest_js caja_builder].each do |name|
$:.unshift File.join(PrototypeHelper::ROOT_DIR, 'vendor', name, 'lib')
@ -44,10 +43,11 @@ module PrototypeHelper
}.merge(options)
require_sprockets
load_path = [SRC_DIR]
load_path = [SRC_DIR, File.join(ROOT_DIR, 'ext', 'sizzle')]
if selector_path = get_selector_engine(options[:selector_engine])
load_path << selector_path
if selector = options[:selector_engine]
get_selector_engine(selector)
load_path << File.join(ROOT_DIR, 'vendor', selector)
end
secretary = Sprockets::Secretary.new(
@ -67,18 +67,16 @@ module PrototypeHelper
:path => 'src',
:source => file,
:destination => temp_path,
:selector_engine => ENV['SELECTOR_ENGINE'] || DEFAULT_SELECTOR_ENGINE,
:selector_engine => ENV['SELECTOR_ENGINE'] || 'sizzle',
:strip_comments => false
)
rm_rf DOC_DIR
PDoc.run({
:source_files => [temp_path],
:destination => DOC_DIR,
:index_page => 'README.markdown',
:syntax_highlighter => :pygments,
:markdown_parser => :bluecloth
})
PDoc::Runner.new(temp_path, {
:output => DOC_DIR,
:templates => File.join(TEMPLATES_DIR, "html"),
:index_page => 'README.markdown'
}).run
rm_rf temp_path
end
@ -100,14 +98,14 @@ module PrototypeHelper
end
def self.get_selector_engine(name)
return if name == DEFAULT_SELECTOR_ENGINE || !name
submodule_path = File.join(ROOT_DIR, "vendor", name)
return submodule_path if File.exist?(File.join(submodule_path, "repository", ".git"))
get_submodule('the required selector engine', "#{name}/repository")
unless File.exist?(submodule_path)
puts "The selector engine you required isn't available at vendor/#{name}.\n\n"
exit
submodule = File.join(ROOT_DIR, 'vendor', name, 'repository')
ext = File.join(ROOT_DIR, 'ext', name)
unless File.exist?(submodule) || File.exist?(ext)
get_submodule('the required selector engine', "#{name}/repository")
unless File.exist?(submodule)
puts "The selector engine you required isn't available at vendor/#{name}.\n\n"
exit
end
end
end
@ -153,7 +151,7 @@ task :dist do
PrototypeHelper.sprocketize(
:path => 'src',
:source => 'prototype.js',
:selector_engine => ENV['SELECTOR_ENGINE'] || PrototypeHelper::DEFAULT_SELECTOR_ENGINE
:selector_engine => ENV['SELECTOR_ENGINE'] || 'sizzle'
)
end

View File

@ -1,5 +1,5 @@
Prototype._original_property = window.Sizzle;
//= require "sizzle"
//= require "repository/sizzle"
Prototype.Selector = (function(engine) {
function extend(elements) {
@ -16,11 +16,16 @@ Prototype.Selector = (function(engine) {
function match(element, selector) {
return engine.matches(selector, [element]).length == 1;
}
function filter(elements, selector) {
return extend(engine.matches(selector, elements));
}
return {
engine: engine,
select: select,
match: match
match: match,
filter: filter
};
})(Sizzle);

View File

@ -64,14 +64,14 @@ Ajax.Response = Class.create({
var transport = this.transport = request.transport,
readyState = this.readyState = transport.readyState;
if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
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) {
if(readyState == 4) {
var xml = transport.responseXML;
this.responseXML = Object.isUndefined(xml) ? null : xml;
this.responseJSON = this._getResponseJSON();
@ -121,7 +121,7 @@ Ajax.Response = Class.create({
},
/**
* Ajax.Response#getResponseHeader(name) -> String
* Ajax.Response.getResponseHeader(name) -> String
*
* Returns the value of the requested header if present; throws an error
* otherwise. This is just a wrapper around the `XmlHttpRequest` method of
@ -132,7 +132,7 @@ Ajax.Response = Class.create({
},
/**
* Ajax.Response#getAllResponseHeaders() -> String
* Ajax.Response.getAllResponseHeaders() -> String
*
* Returns a string containing all headers separated by line breaks; throws
* an error if no headers exist. This is just a wrapper around the

View File

@ -1,6 +1,6 @@
/** section: DOM, related to: Element
* $(id) -> Element
* $(id...) -> [Element...]
* $(id...) -> [Element]...
* - id (String | Element): A DOM node or a string that references a node's
* ID.
*
@ -38,7 +38,7 @@ if (Prototype.BrowserFeatures.XPath) {
/*--------------------------------------------------------------------------*/
if (!Node) var Node = { };
if (!window.Node) var Node = { };
if (!Node.ELEMENT_NODE) {
// DOM level 2 ECMAScript Language Binding
@ -60,76 +60,41 @@ if (!Node.ELEMENT_NODE) {
/** section: DOM
* class Element
*
* The `Element` object provides a variety of powerful DOM methods for
* interacting with DOM elements&nbsp;&mdash; creating them, updating them,
* traversing them, etc. You can access these either as methods of `Element`
* itself, passing in the element to work with as the first argument, or as
* methods on extended element *instances*:
*
* // Using Element:
* Element.addClassName('target', 'highlighted');
*
* // Using an extended element instance:
* $('target').addClassName('highlighted');
*
* `Element` is also a constructor for building element instances from scratch,
* see [`new Element`](#new-constructor) for details.
*
* Most `Element` methods return the element instance, so that you can chain
* them easily:
*
* $('message').addClassName('read').update('I read this message!');
*
* ##### More Information
*
* For more information about extended elements, check out ["How Prototype
* extends the DOM"](http://prototypejs.org/learn/extensions), which will walk
* you through the inner workings of Prototype's DOM extension mechanism.
**/
/**
* new Element(tagName[, attributes])
* - tagName (String): The name of the HTML element to create.
* - attributes (Object): An optional group of attribute/value pairs to set on
* the element.
* - tagName (String): The name of the HTML element to create.
* - attributes (Object): A list of attribute/value pairs to set on the
* element.
*
* Creates an HTML element with `tagName` as the tag name, optionally with the
* given attributes. This can be markedly more concise than working directly
* with the DOM methods, and takes advantage of Prototype's workarounds for
* various browser issues with certain attributes:
*
* ##### Example
*
* // The old way:
* var a = document.createElement('a');
* a.setAttribute('class', 'foo');
* a.setAttribute('href', '/foo.html');
* a.appendChild(document.createTextNode("Next page"));
*
* // The new way:
* var a = new Element('a', {'class': 'foo', href: '/foo.html'}).update("Next page");
* Creates an HTML element with `tagName` as the tag name.
**/
(function(global) {
var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){
try {
var el = document.createElement('<input name="x">');
return el.tagName.toLowerCase() === 'input' && el.name === 'x';
}
catch(err) {
return false;
}
// setAttribute is broken in IE (particularly when setting name attribute)
// see: http://msdn.microsoft.com/en-us/library/ms536389.aspx
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 (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) {
if (SETATTRIBUTE_IGNORES_NAME && attributes.name) {
tagName = '<' + tagName + ' name="' + attributes.name + '">';
delete attributes.name;
return Element.writeAttribute(document.createElement(tagName), attributes);
@ -137,14 +102,12 @@ if (!Node.ELEMENT_NODE) {
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);
Element.idCounter = 1;
Element.cache = { };
Element.idCounter = 1;
Element.Methods = {
/**
@ -440,9 +403,8 @@ Element.Methods = {
element = $(element);
var result = '<' + element.tagName.toLowerCase();
$H({'id': 'id', 'className': 'class'}).each(function(pair) {
var property = pair.first(),
attribute = pair.last(),
value = (element[property] || '').toString();
var property = pair.first(), attribute = pair.last();
var value = (element[property] || '').toString();
if (value) result += ' ' + attribute + '=' + value.inspect(true);
});
return result + '>';
@ -474,34 +436,8 @@ Element.Methods = {
/**
* Element.ancestors(@element) -> [Element...]
*
* Collects all of `element`'s ancestor elements and returns them as an
* array of extended elements.
*
* The returned array's first element is `element`'s direct ancestor (its
* `parentNode`), the second one is its grandparent, and so on until the
* `html` element is reached. `html` will always be the last member of the
* array. Calling `ancestors` on the `html` element will return an empty
* array.
*
* ##### Example
*
* Assuming:
*
* language: html
* <html>
* [...]
* <body>
* <div id="father">
* <div id="kid">
* </div>
* </div>
* </body>
* </html>
*
* Then:
*
* $('kid').ancestors();
* // -> [div#father, body, html]
* Collects all of `element`'s ancestors and returns them as an array of
* elements.
**/
ancestors: function(element) {
return Element.recursivelyCollect(element, 'parentNode');
@ -510,10 +446,8 @@ Element.Methods = {
/**
* Element.descendants(@element) -> [Element...]
*
* Collects all of the element's descendants (its children, their children,
* etc.) and returns them as an array of extended elements. As with all of
* Prototype's DOM traversal methods, only Elements are returned, other
* nodes (text nodes, etc.) are skipped.
* Collects all of element's descendants and returns them as an array of
* elements.
**/
descendants: function(element) {
return Element.select(element, "*");
@ -533,20 +467,17 @@ Element.Methods = {
return $(element);
},
/** deprecated, alias of: Element.childElements
/**
* Element.immediateDescendants(@element) -> [Element...]
*
* **This method is deprecated, please see [[Element.childElements]]**.
* Collects all of `element`'s immediate descendants (i.e., children) and
* returns them as an array of elements.
**/
immediateDescendants: function(element) {
var results = [], child = $(element).firstChild;
while (child) {
if (child.nodeType === 1) {
results.push(Element.extend(child));
}
child = child.nextSibling;
}
return results;
if (!(element = $(element).firstChild)) return [];
while (element && element.nodeType != 1) element = element.nextSibling;
if (element) return [element].concat($(element).nextSiblings());
return [];
},
/**
@ -608,7 +539,7 @@ Element.Methods = {
if (arguments.length == 1) return $(element.parentNode);
var ancestors = Element.ancestors(element);
return Object.isNumber(expression) ? ancestors[expression] :
Prototype.Selector.find(ancestors, expression, index);
Prototype.Selector.filter(ancestors, expression)[index || 0];
},
/**
@ -644,7 +575,7 @@ Element.Methods = {
if (!Object.isNumber(index)) index = 0;
if (expression) {
return Prototype.Selector.find(element.previousSiblings(), expression, index);
return Prototype.Selector.filter(element.previousSiblings(), expression)[index];
} else {
return element.recursivelyCollect("previousSibling", index + 1)[index];
}
@ -666,7 +597,7 @@ Element.Methods = {
if (!Object.isNumber(index)) index = 0;
if (expression) {
return Prototype.Selector.find(element.nextSiblings(), expression, index);
return Prototype.Selector.filter(element.nextSiblings(), expression)[index];
} else {
var maximumLength = Object.isNumber(index) ? index + 1 : 1;
return element.recursivelyCollect("nextSibling", index + 1)[index];
@ -692,30 +623,7 @@ Element.Methods = {
* - selector (String): A CSS selector.
*
* Finds all siblings of the current element that match the given
* selector(s). If you provide multiple selectors, siblings matching *any*
* of the selectors are included. If a sibling matches multiple selectors,
* it is only included once. The order of the returned array is not defined.
*
* ##### Example
*
* Assuming this list:
*
* language: html
* <ul id="cities">
* <li class="us" id="nyc">New York</li>
* <li class="uk" id="lon">London</li>
* <li class="us" id="chi">Chicago</li>
* <li class="jp" id="tok">Tokyo</li>
* <li class="us" id="la">Los Angeles</li>
* <li class="us" id="aus">Austin</li>
* </ul>
*
* Then:
*
* $('nyc').adjacent('li.us');
* // -> [li#chi, li#la, li#aus]
* $('nyc').adjacent('li.uk', 'li.jp');
* // -> [li#lon, li#tok]
* selector(s).
**/
adjacent: function(element) {
element = $(element);
@ -802,15 +710,11 @@ Element.Methods = {
return Element.getDimensions(element).width;
},
/** deprecated
/**
* Element.classNames(@element) -> [String...]
*
* Returns a new instance of [[Element.ClassNames]], an [[Enumerable]]
* object used to read and write CSS class names of `element`.
*
* **Deprecated**, please see [[Element.addClassName]],
* [[Element.removeClassName]], and [[Element.hasClassName]]. If you want
* an array of classnames, you can use `$w(element.className)`.
**/
classNames: function(element) {
return new Element.ClassNames(element);
@ -830,24 +734,8 @@ Element.Methods = {
/**
* Element.addClassName(@element, className) -> Element
* - className (String): The class name to add.
*
* Adds the given CSS class to `element`.
*
* ##### Example
*
* Assuming this HTML:
*
* language: html
* <div id="mutsu" class="apple fruit"></div>
*
* Then:
*
* $('mutsu').className;
* // -> 'apple fruit'
* $('mutsu').addClassName('food');
* $('mutsu').className;
* // -> 'apple fruit food'
* Adds a CSS class to `element`.
**/
addClassName: function(element, className) {
if (!(element = $(element))) return;
@ -882,44 +770,7 @@ Element.Methods = {
/**
* Element.cleanWhitespace(@element) -> Element
*
* Removes all of `element`'s child text nodes that contain *only*
* whitespace. Returns `element`.
*
* This can be very useful when using standard properties like `nextSibling`,
* `previousSibling`, `firstChild` or `lastChild` to walk the DOM. Usually
* you'd only do that if you are interested in all of the DOM nodes, not
* just Elements (since if you just need to traverse the Elements in the
* DOM tree, you can use [[Element.up]], [[Element.down]],
* [[Element.next]], and [[Element.previous]] instead).
*
* #### Example
*
* Consider the following HTML snippet:
*
* language: html
* <ul id="apples">
* <li>Mutsu</li>
* <li>McIntosh</li>
* <li>Ida Red</li>
* </ul>
*
* Let's grab what we think is the first list item using the raw DOM
* method:
*
* var element = $('apples');
* element.firstChild.innerHTML;
* // -> undefined
*
* It's undefined because the `firstChild` of the `apples` element is a
* text node containing the whitespace after the end of the `ul` and before
* the first `li`.
*
* If we remove the useless whitespace, then `firstChild` works as expected:
*
* var element = $('apples');
* element.cleanWhitespace();
* element.firstChild.innerHTML;
* // -> 'Mutsu'
* Removes whitespace-only text node children from `element`.
**/
cleanWhitespace: function(element) {
element = $(element);
@ -944,28 +795,8 @@ Element.Methods = {
/**
* Element.descendantOf(@element, ancestor) -> Boolean
* - ancestor (Element | String): The element to check against (or its ID).
*
* Checks if `element` is a descendant of `ancestor`.
*
* ##### Example
*
* Assuming:
*
* language: html
* <div id="australopithecus">
* <div id="homo-erectus">
* <div id="homo-sapiens"></div>
* </div>
* </div>
*
* Then:
*
* $('homo-sapiens').descendantOf('australopithecus');
* // -> true
*
* $('homo-erectus').descendantOf('homo-sapiens');
* // -> false
**/
descendantOf: function(element, ancestor) {
element = $(element), ancestor = $(ancestor);
@ -1004,9 +835,6 @@ Element.Methods = {
**/
getStyle: function(element, style) {
element = $(element);
if (!element.style)
return null;
style = style == 'float' ? 'cssFloat' : style.camelize();
var value = element.style[style];
if (!value || value == 'auto') {
@ -1078,16 +906,16 @@ Element.Methods = {
// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style,
originalVisibility = els.visibility,
originalPosition = els.position,
originalDisplay = els.display;
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,
originalHeight = element.clientHeight;
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
@ -1169,34 +997,18 @@ Element.Methods = {
* Element.cumulativeOffset(@element) -> Array
*
* Returns the offsets of `element` from the top left corner of the
* document, in pixels.
* document.
*
* Returns an array in the form of `[leftValue, topValue]`. Also accessible
* as properties: `{ left: leftValue, top: topValue }`.
*
* ##### Example
*
* Assuming the div `foo` is at (25,40), then:
*
* var offset = $('foo').cumulativeOffset();
* offset[0];
* // -> 25
* offset[1];
* // -> 40
* offset.left;
* // -> 25
* offset.top;
* // -> 40
**/
cumulativeOffset: function(element) {
var valueT = 0, valueL = 0;
if (element.parentNode) {
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
}
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
return Element._returnOffset(valueL, valueT);
},
@ -1234,11 +1046,11 @@ Element.Methods = {
element = $(element);
if (Element.getStyle(element, 'position') == 'absolute') return element;
var offsets = Element.positionedOffset(element),
top = offsets[1],
left = offsets[0],
width = element.clientWidth,
height = element.clientHeight;
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);
@ -1266,8 +1078,8 @@ Element.Methods = {
if (Element.getStyle(element, 'position') == 'relative') return element;
element.style.position = 'relative';
var top = parseFloat(element.style.top || 0) - (element._originalTop || 0),
left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
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';
@ -1279,25 +1091,11 @@ Element.Methods = {
/**
* Element.cumulativeScrollOffset(@element) -> Array
*
* Calculates the cumulative scroll offset (in pixels) of an element in
* nested scrolling containers.
* Calculates the cumulative scroll offset of an element in nested
* scrolling containers.
*
* Returns an array in the form of `[leftValue, topValue]`. Also accessible
* as properties: `{ left: leftValue, top: topValue }`.
*
* ##### Example
*
* Assuming the div `foo` is at scroll offset (0,257), then:
*
* var offset = $('foo').cumulativeOffset();
* offset[0];
* // -> 0
* offset[1];
* // -> 257
* offset.left;
* // -> 0
* offset.top;
* // -> 257
**/
cumulativeScrollOffset: function(element) {
var valueT = 0, valueL = 0;
@ -1335,10 +1133,9 @@ Element.Methods = {
* as properties: `{ left: leftValue, top: topValue }`.
**/
viewportOffset: function(forElement) {
var valueT = 0,
valueL = 0,
element = forElement;
var valueT = 0, valueL = 0;
var element = forElement;
do {
valueT += element.offsetTop || 0;
valueL += element.offsetLeft || 0;
@ -1362,60 +1159,15 @@ Element.Methods = {
/**
* Element.clonePosition(@element, source[, options]) -> Element
* - source (Element | String): The source element (or its ID).
* - options (Object): The position fields to clone.
*
* Clones the position and/or dimensions of `source` onto the element as
* defined by `options`, with an optional offset for the `left` and `top`
* properties.
* Clones the position and/or dimensions of `source` onto `element` as
* defined by `options`.
*
* Note that the element will be positioned exactly like `source` whether or
* not it is part of the same [CSS containing
* block](http://www.w3.org/TR/CSS21/visudet.html#containing-block-details).
*
* ##### Options
*
* <table class='options'>
* <thead>
* <tr>
* <th style='text-align: left; padding-right: 1em'>Name</th>
* <th style='text-align: left; padding-right: 1em'>Default</th>
* <th style='text-align: left; padding-right: 1em'>Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <td><code>setLeft</code></td>
* <td><code>true</code></td>
* <td>Clones <code>source</code>'s <code>left</code> CSS property onto <code>element</code>.</td>
* </tr>
* <tr>
* <td><code>setTop</code></td>
* <td><code>true</code></td>
* <td>Clones <code>source</code>'s <code>top</code> CSS property onto <code>element</code>.</td>
* </tr>
* <tr>
* <td><code>setWidth</code></td>
* <td><code>true</code></td>
* <td>Clones <code>source</code>'s <code>width</code> onto <code>element</code>.</td>
* </tr>
* <tr>
* <td><code>setHeight</code></td>
* <td><code>true</code></td>
* <td>Clones <code>source</code>'s <code>width</code> onto <code>element</code>.</td>
* </tr>
* <tr>
* <td><code>offsetLeft</code></td>
* <td><code>0</code></td>
* <td>Number by which to offset <code>element</code>'s <code>left</code> CSS property.</td>
* </tr>
* <tr>
* <td><code>offsetTop</code></td>
* <td><code>0</code></td>
* <td>Number by which to offset <code>element</code>'s <code>top</code> CSS property.</td>
* </tr>
* </tbody>
* </table>
* Valid keys for `options` are: `setLeft`, `setTop`, `setWidth`, and
* `setHeight` (all booleans which default to `true`); and `offsetTop`
* and `offsetLeft` (numbers which default to `0`). Use these to control
* which aspects of `source`'s layout are cloned and how much to offset
* the resulting position of `element`.
**/
clonePosition: function(element, source) {
var options = Object.extend({
@ -1429,11 +1181,12 @@ Element.Methods = {
// find page position of source
source = $(source);
var p = Element.viewportOffset(source), delta = [0, 0], parent = null;
var p = Element.viewportOffset(source);
// find coordinate system to use
element = $(element);
var delta = [0, 0];
var parent = null;
// delta [0,0] will do fine with position: fixed elements,
// position:absolute needs offsetParent deltas
if (Element.getStyle(element, 'position') == 'absolute') {
@ -1462,40 +1215,8 @@ Object.extend(Element.Methods, {
**/
getElementsBySelector: Element.Methods.select,
/**
/** alias of: Element.immediateDescendants
* Element.childElements(@element) -> [Element...]
*
* Collects all of the element's children and returns them as an array of
* [extended](http://prototypejs.org/api/element/extend) elements, in
* document order. The first entry in the array is the topmost child of
* `element`, the next is the child after that, etc.
*
* Like all of Prototype's DOM traversal methods, `childElements` ignores
* text nodes and returns element nodes only.
*
* ##### Example
*
* Assuming:
*
* language: html
* <div id="australopithecus">
* Some text in a text node
* <div id="homo-erectus">
* <div id="homo-neanderthalensis"></div>
* <div id="homo-sapiens"></div>
* </div>
* </div>
*
* Then:
*
* $('australopithecus').childElements();
* // -> [div#homo-erectus]
*
* $('homo-erectus').childElements();
* // -> [div#homo-neanderthalensis, div#homo-sapiens]
*
* $('homo-sapiens').childElements();
* // -> []
**/
childElements: Element.Methods.immediateDescendants
});
@ -1560,7 +1281,8 @@ else if (Prototype.Browser.IE) {
function(proceed, element) {
element = $(element);
// IE throws an error if element is not in document
if (!element.parentNode) return $(document.body);
try { element.offsetParent }
catch(e) { return $(document.body) }
var position = element.getStyle('position');
if (position !== 'static') return proceed(element);
element.setStyle({ position: 'relative' });
@ -1574,15 +1296,15 @@ else if (Prototype.Browser.IE) {
Element.Methods[method] = Element.Methods[method].wrap(
function(proceed, element) {
element = $(element);
if (!element.parentNode) return Element._returnOffset(0, 0);
try { element.offsetParent }
catch(e) { return Element._returnOffset(0,0) }
var position = element.getStyle('position');
if (position !== 'static') return proceed(element);
// Trigger hasLayout on the offset parent so that IE6 reports
// accurate offsetTop and offsetLeft values for position: fixed.
var offsetParent = element.getOffsetParent();
if (offsetParent.getStyle)
if (offsetParent && offsetParent.getStyle('position') === 'fixed')
offsetParent.setStyle({ zoom: 1 });
if (offsetParent && offsetParent.getStyle('position') === 'fixed')
offsetParent.setStyle({ zoom: 1 });
element.setStyle({ position: 'relative' });
var value = proceed(element);
element.setStyle({ position: position });
@ -1591,11 +1313,16 @@ else if (Prototype.Browser.IE) {
);
});
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);
if (!element.style)
return null;
style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
var value = element.style[style];
if (!value && element.currentStyle) value = element.currentStyle[style];
@ -1637,9 +1364,10 @@ else if (Prototype.Browser.IE) {
Element._attributeTranslations = (function(){
var classProp = 'className',
forProp = 'for',
el = document.createElement('div');
var classProp = 'className';
var forProp = 'for';
var el = document.createElement('div');
// try "className" first (IE <8)
el.setAttribute(classProp, 'x');
@ -1684,9 +1412,10 @@ else if (Prototype.Browser.IE) {
},
_getEv: (function(){
var el = document.createElement('div'), f;
var el = document.createElement('div');
el.onclick = Prototype.emptyFunction;
var value = el.getAttribute('onclick');
var f;
// IE<8
if (String(value).indexOf('{') > -1) {
@ -1820,7 +1549,7 @@ else if (Prototype.Browser.WebKit) {
(value < 0.00001) ? 0 : value;
if (value == 1)
if (element.tagName.toUpperCase() == 'IMG' && element.width) {
if(element.tagName.toUpperCase() == 'IMG' && element.width) {
element.width++; element.width--;
} else try {
var n = document.createTextNode(' ');
@ -1863,8 +1592,8 @@ if ('outerHTML' in document.documentElement) {
var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
if (Element._insertionTranslations.tags[tagName]) {
var nextSibling = element.next(),
fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
var nextSibling = element.next();
var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
parent.removeChild(element);
if (nextSibling)
fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
@ -1886,17 +1615,11 @@ Element._returnOffset = function(l, t) {
};
Element._getContentFromAnonymousElement = function(tagName, html) {
var div = new Element('div'),
t = Element._insertionTranslations.tags[tagName];
var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
if (t) {
div.innerHTML = t[0] + html + t[1];
for (var i = t[2]; i--; ) {
div = div.firstChild;
}
}
else {
div.innerHTML = html;
}
t[2].times(function() { div = div.firstChild });
} else div.innerHTML = html;
return $A(div.childNodes);
};
@ -1953,31 +1676,16 @@ Object.extend(Element, Element.Methods);
div = null;
})(document.createElement('div'));
})(document.createElement('div'))
/**
* Element.extend(element) -> Element
*
* Extends the given element instance with all of the Prototype goodness and
* syntactic sugar, as well as any extensions added via [[Element.addMethods]].
* (If the element instance was already extended, this is a no-op.)
*
* You only need to use `Element.extend` on element instances you've acquired
* directly from the DOM; **all** Prototype methods that return element
* instances (such as [[$]], [[Element.down]], etc.) will pre-extend the
* element before returning it.
*
* Check out ["How Prototype extends the
* DOM"](http://prototypejs.org/learn/extensions) for more about element
* extensions.
*
* ##### Details
*
* Specifically, `Element.extend` extends the given instance with the methods
* contained in `Element.Methods` and `Element.Methods.Simulated`. If `element`
* is an `input`, `textarea`, or `select` element, it will also be extended
* with the methods from `Form.Element.Methods`. If it is a `form` element, it
* will also be extended with the methods from `Form.Methods`.
* 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 the methods from `Form.Methods`.
**/
Element.extend = (function() {
@ -1985,8 +1693,8 @@ Element.extend = (function() {
if (typeof window.Element != 'undefined') {
var proto = window.Element.prototype;
if (proto) {
var id = '_' + (Math.random()+'').slice(2),
el = document.createElement(tagName);
var id = '_' + (Math.random()+'').slice(2);
var el = document.createElement(tagName);
proto[id] = 'x';
var isBuggy = (el[id] !== 'x');
delete proto[id];
@ -2060,138 +1768,20 @@ Element.extend = (function() {
return extend;
})();
if (document.documentElement.hasAttribute) {
Element.hasAttribute = function(element, attribute) {
return element.hasAttribute(attribute);
};
}
else {
Element.hasAttribute = Element.Methods.Simulated.hasAttribute;
}
Element.hasAttribute = function(element, attribute) {
if (element.hasAttribute) return element.hasAttribute(attribute);
return Element.Methods.Simulated.hasAttribute(element, attribute);
};
/**
* Element.addMethods(methods) -> undefined
* Element.addMethods(tagName, methods) -> undefined
* - tagName (String): (Optional) The name of the HTML tag for which the
* methods should be available; if not given, all HTML elements will have
* the new methods.
* - methods (Object): A hash of methods to add.
*
* `Element.addMethods` makes it possible to mix your *own* methods into the
* `Element` object and extended element instances (all of them, or only ones
* with the given HTML tag if you specify `tagName`).
* Takes a hash of methods and makes them available as methods of extended
* elements and of the `Element` object.
*
* You define the methods in a hash that you provide to `Element.addMethods`.
* Here's an example adding two methods:
* The second usage form is for adding methods only to specific tag names.
*
* Element.addMethods({
*
* // myOwnMethod: Do something cool with the element
* myOwnMethod: function(element) {
* if (!(element = $(element))) return;
* // ...do smething with 'element'...
* return element;
* },
*
* // wrap: Wrap the element in a new element using the given tag
* wrap: function(element, tagName) {
* var wrapper;
* if (!(element = $(element))) return;
* wrapper = new Element(tagName);
* element.parentNode.replaceChild(wrapper, element);
* wrapper.appendChild(element);
* return wrapper;
* }
*
* });
*
* Once added, those can be used either via `Element`:
*
* // Wrap the element with the ID 'foo' in a div
* Element.wrap('foo', 'div');
*
* ...or as instance methods of extended elements:
*
* // Wrap the element with the ID 'foo' in a div
* $('foo').wrap('div');
*
* Note the following requirements and conventions for methods added to
* `Element`:
*
* - The first argument is *always* an element or ID, by convention this
* argument is called `element`.
* - The method passes the `element` argument through [[$]] and typically
* returns if the result is undefined.
* - Barring a good reason to return something else, the method returns the
* extended element to enable chaining.
*
* Our `myOwnMethod` method above returns the element because it doesn't have
* a good reason to return anything else. Our `wrap` method returns the
* wrapper, because that makes more sense for that method.
*
* ##### Extending only specific elements
*
* If you call `Element.addMethods` with *two* arguments, it will apply the
* methods only to elements with the given HTML tag:
*
* Element.addMethods('DIV', my_div_methods);
* // the given methods are now available on DIV elements, but not others
*
* You can also pass an *array* of tag names as the first argument:
*
* Element.addMethods(['DIV', 'SPAN'], my_additional_methods);
* // DIV and SPAN now both have the given methods
*
* (Tag names in the first argument are not case sensitive.)
*
* Note: `Element.addMethods` has built-in security which prevents you from
* overriding native element methods or properties (like `getAttribute` or
* `innerHTML`), but nothing prevents you from overriding one of Prototype's
* methods. Prototype uses a lot of its methods internally; overriding its
* methods is best avoided or at least done only with great care.
*
* ##### Example 1
*
* Our `wrap` method earlier was a complete example. For instance, given this
* paragraph:
*
* language: html
* <p id="first">Some content...</p>
*
* ...we might wrap it in a `div`:
*
* $('first').wrap('div');
*
* ...or perhaps wrap it and apply some style to the `div` as well:
*
* $('first').wrap('div').setStyle({
* backgroundImage: 'url(images/rounded-corner-top-left.png) top left'
* });
*
* ##### Example 2
*
* We can add a method to elements that makes it a bit easier to update them
* via [[Ajax.Updater]]:
*
* Element.addMethods({
* ajaxUpdate: function(element, url, options) {
* if (!(element = $(element))) return;
* element.update('<img src="/images/spinner.gif" alt="Loading...">');
* options = options || {};
* options.onFailure = options.onFailure || defaultFailureHandler.curry(element);
* new Ajax.Updater(element, url, options);
* return element;
* }
* });
*
* Now we can update an element via an Ajax call much more concisely than
* before:
*
* $('foo').ajaxUpdate('/new/content');
*
* That will use Ajax.Updater to load new content into the 'foo' element,
* showing a spinner while the call is in progress. It even applies a default
* failure handler (since we didn't supply one).
**/
Element.addMethods = function(methods) {
var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
@ -2255,9 +1845,8 @@ Element.addMethods = function(methods) {
klass = 'HTML' + tagName.capitalize() + 'Element';
if (window[klass]) return window[klass];
var element = document.createElement(tagName),
proto = element['__proto__'] || element.constructor.prototype;
var element = document.createElement(tagName);
var proto = element['__proto__'] || element.constructor.prototype;
element = null;
return proto;
}
@ -2285,8 +1874,8 @@ Element.addMethods = function(methods) {
Element.cache = { };
};
/**
* document.viewport
/** section: DOM
* document.viewport
*
* The `document.viewport` namespace contains methods that return information
* about the viewport &mdash; the rectangle that represents the portion of a web

View File

@ -25,10 +25,7 @@
* browsers.
*
* `Event` also provides a standardized list of key codes you can use with
* keyboard-related events, including `KEY_BACKSPACE`, `KEY_TAB`,
* `KEY_RETURN`, `KEY_ESC`, `KEY_LEFT`, `KEY_UP`, `KEY_RIGHT`, `KEY_DOWN`,
* `KEY_DELETE`, `KEY_HOME`, `KEY_END`, `KEY_PAGEUP`, `KEY_PAGEDOWN` and
* `KEY_INSERT`.
* keyboard-related events.
*
* The functions you're most likely to use a lot are [[Event.observe]],
* [[Event.element]] and [[Event.stop]]. If your web app uses custom events,
@ -156,10 +153,10 @@
if (Prototype.Selector.match(element, expression)) {
return Element.extend(element);
}
element = element.parentNode;
element = element.parentNode
}
}
/**
* Event.pointer(@event) -> Object
*
@ -386,12 +383,12 @@
window.addEventListener('unload', Prototype.emptyFunction, false);
var _getDOMEventName = Prototype.K,
translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
var _getDOMEventName = Prototype.K;
if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) {
_getDOMEventName = function(eventName) {
return (translations[eventName] || eventName);
var translations = { mouseenter: "mouseover", mouseleave: "mouseout" };
return eventName in translations ? translations[eventName] : eventName;
};
}
@ -415,7 +412,6 @@
* * Prototype handles cleaning up the handler when leaving the page (important for MSIE memory
* leak prevention).
* * `observe` makes it possible to stop observing the event easily via [[Event.stopObserving]].
* * Adds support for `mouseenter` / `mouseleave` in all browsers.
*
* Although you can use `Event.observe` directly and there are times when that's the most
* convenient or direct way, it's more common to use its alias [[Element#observe]]. These two
@ -621,39 +617,48 @@
* ...and then to remove:
*
* $('foo').stopObserving('click', this.boundHandlerMethod); // <== Right
*
**/
function stopObserving(element, eventName, handler) {
element = $(element);
var registry = Element.retrieve(element, 'prototype_event_registry');
if (!registry) return element;
if (!eventName) {
// We stop observing all events.
// e.g.: $(element).stopObserving();
if (Object.isUndefined(registry)) return element;
if (eventName && !handler) {
// If an event name is passed without a handler, we stop observing all
// handlers of that type.
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) {
// If both the event name and the handler are omitted, we stop observing
// _all_ handlers on the element.
registry.each( function(pair) {
var eventName = pair.key;
stopObserving(element, eventName);
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 element;
if (!handler) {
// We stop observing all handlers for the given eventName.
// e.g.: $(element).stopObserving('click');
responders.each(function(r) {
stopObserving(element, eventName, r.handler);
});
return element;
}
// Fail gracefully if there are no responders assigned.
if (!responders) return;
var responder = responders.find( function(r) { return r.handler === handler; });
if (!responder) return element;
var actualEventName = _getDOMEventName(eventName);
if (eventName.include(':')) {
// Custom event.
if (element.removeEventListener)
@ -664,7 +669,6 @@
}
} else {
// Ordinary event.
var actualEventName = _getDOMEventName(eventName);
if (element.removeEventListener)
element.removeEventListener(actualEventName, responder, false);
else
@ -745,7 +749,7 @@
});
/** section: DOM
* document
* document
*
* Prototype extends the built-in `document` object with several convenience
* methods related to events.

View File

@ -46,7 +46,7 @@ var Form = {
* fields are included in that object or string.
*
* If you do not supply an `options` object _at all_, the options
* `{ hash: false }` are used.
* `{hash: false}` are used.
*
* If you supply an `options` object, it may have the following options:
* - `hash` ([[Boolean]]): `true` to return a plain object with keys and values
@ -248,7 +248,7 @@ Form.Methods = {
/*--------------------------------------------------------------------------*/
/**
/** section: DOM
* Form.Element
*
* Utilities for dealing with form controls in the DOM.
@ -359,7 +359,7 @@ Form.Element.Methods = {
},
/**
* Form.Element.activate(@element) -> Element
* Form.Element#activate(element) -> Element
*
* Gives focus to a form control and selects its contents if it is a text
* input.
@ -479,7 +479,7 @@ Form.Element.Serializers = {
* Abstract
**/
/**
/** section: DOM
* class Abstract.TimedObserver
*
* An abstract DOM element observer class, subclasses of which can be used to periodically
@ -528,7 +528,7 @@ Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
}
});
/**
/** section: DOM
* class Form.Element.Observer < Abstract.TimedObserver
*
* An [[Abstract.TimedObserver]] subclass that watches for changes to a form field's value.
@ -551,7 +551,7 @@ Form.Element.Observer = Class.create(Abstract.TimedObserver, {
}
});
/**
/** section: DOM
* class Form.Observer < Abstract.TimedObserver
*
* An [[Abstract.TimedObserver]] subclass that watches for changes to a form.
@ -577,7 +577,7 @@ Form.Observer = Class.create(Abstract.TimedObserver, {
/*--------------------------------------------------------------------------*/
/**
/** section: DOM
* class Abstract.EventObserver
**/
Abstract.EventObserver = Class.create({
@ -619,7 +619,7 @@ Abstract.EventObserver = Class.create({
}
});
/**
/** section: DOM
* class Form.Element.EventObserver < Abstract.EventObserver
**/
Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
@ -628,8 +628,8 @@ Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
}
});
/**
* class Form.EventObserver < Abstract.EventObserver
/** section: DOM
* class Form.Element.EventObserver < Abstract.EventObserver
**/
Form.EventObserver = Class.create(Abstract.EventObserver, {
getValue: function() {

View File

@ -1,9 +1,8 @@
/** section: DOM, related to: Prototype.Selector
/** related to: Prototype.Selector
* $$(expression...) -> [Element...]
*
* Returns all elements in the document that match the provided CSS selectors.
**/
window.$$ = function() {
var expression = $A(arguments).join(', ');
return Prototype.Selector.select(expression, document);
@ -42,25 +41,15 @@ window.$$ = function() {
// Implementation provided by selector engine.
/**
* Prototype.Selector.find(elements, expression[, index = 0]) -> Element
* Prototype.Selector.filter(elements, expression) -> [Element...]
* - elements (Enumerable): a collection of DOM elements.
* - expression (String): A CSS selector.
* - index: Numeric index of the match to return, defaults to 0.
*
* Filters the given collection of elements with `expression` and returns the
* first matching element (or the `index`th matching element if `index` is
* specified).
* Filters the given collection of elements with `expression` and returns an
* array of extended [[Element]] objects.
*
* The only nodes returned will be those that match the given CSS selector.
**/
if (!Prototype.Selector.find) {
Prototype.Selector.find = function(elements, expression, index) {
if (Object.isUndefined(index)) index = 0;
var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i;
for (i = 0; i < length; i++) {
if (match(elements[i], expression) && index == matchIndex++) {
return Element.extend(elements[i]);
}
}
}
}
// Implementation provided by selector engine.

View File

@ -9,17 +9,6 @@
* inheritance](http://prototypejs.org/learn/class-inheritance).
**/
var Class = (function() {
// Some versions of JScript fail to enumerate over properties, names of which
// correspond to non-enumerable properties in the prototype chain
var IS_DONTENUM_BUGGY = (function(){
for (var p in { toString: 1 }) {
// check actual property name, so that it works with augmented Object.prototype
if (p === 'toString') return false;
}
return true;
})();
/**
* Class.create([superclass][, methods...]) -> Class
* - superclass (Class): The optional superclass to inherit methods from.
@ -70,7 +59,7 @@ var Class = (function() {
parent.subclasses.push(klass);
}
for (var i = 0, length = properties.length; i < length; i++)
for (var i = 0; i < properties.length; i++)
klass.addMethods(properties[i]);
if (!klass.prototype.initialize)
@ -146,13 +135,12 @@ var Class = (function() {
* //-> alerts "You should probably run. He looks really mad."
**/
function addMethods(source) {
var ancestor = this.superclass && this.superclass.prototype,
properties = Object.keys(source);
var ancestor = this.superclass && this.superclass.prototype;
var properties = Object.keys(source);
// IE6 doesn't enumerate `toString` and `valueOf` (among other built-in `Object.prototype`) properties,
// Force copy if they're not Object.prototype ones.
// Do not copy other Object.prototype.* for performance reasons
if (IS_DONTENUM_BUGGY) {
// IE6 doesn't enumerate toString and valueOf properties,
// Force copy if they're not coming from Object.prototype.
if (!Object.keys({ toString: true }).length) {
if (source.toString != Object.prototype.toString)
properties.push("toString");
if (source.valueOf != Object.prototype.valueOf)
@ -162,7 +150,7 @@ var Class = (function() {
for (var i = 0, length = properties.length; i < length; i++) {
var property = properties[i], value = source[property];
if (ancestor && Object.isFunction(value) &&
value.argumentNames()[0] == "$super") {
value.argumentNames().first() == "$super") {
var method = value;
value = (function(m) {
return function() { return ancestor[m].apply(this, arguments); };

View File

@ -217,13 +217,7 @@
function isArray(object) {
return _toString.call(object) == "[object Array]";
}
var hasNativeIsArray = (typeof Array.isArray == 'function')
&& Array.isArray([]) && !Array.isArray({});
if (hasNativeIsArray) {
isArray = Array.isArray;
}
/**
* Object.isHash(object) -> Boolean

View File

@ -42,11 +42,6 @@ Object.extend(String.prototype, (function() {
* Returns the string with every occurence of a given pattern replaced by either
* a regular string, the returned value of a function or a [[Template]] string.
* The pattern can be a string or a regular expression.
*
* <h5>Example</h5>
*
* ""hello".gsub(/([aeiou])/, '<#{1}>');
* // => "h<e>ll<o>"
**/
function gsub(pattern, replacement) {
var result = '', source = this, match;
@ -78,11 +73,6 @@ Object.extend(String.prototype, (function() {
* Returns a string with the first count occurrences of pattern replaced by either
* a regular string, the returned value of a function or a [[Template]] string.
* The pattern can be a string or a regular expression.
*
* <h5>Example</h5>
*
* "20091201".sub(/^(\d{4})(\d{2})(\d{2})$/, "#{1}-#{2}-#{3}");
* // => "2009-12-01"
**/
function sub(pattern, replacement, count) {
replacement = prepareReplacement(replacement);
@ -141,7 +131,7 @@ Object.extend(String.prototype, (function() {
*
* Note that the processing `stripTags` does is good enough for most purposes, but
* you cannot rely on it for security purposes. If you're processing end-user-supplied
* content, `stripTags` is _not_ sufficiently robust to ensure that the content
* content, `stripTags` is probably _not_ sufficiently robust to ensure that the content
* is completely devoid of HTML tags in the case of a user intentionally trying to circumvent
* tag restrictions. But then, you'll be running them through [[String#escapeHTML]] anyway,
* won't you?
@ -177,8 +167,8 @@ Object.extend(String.prototype, (function() {
* returns them as an array of strings.
**/
function extractScripts() {
var matchAll = new RegExp(Prototype.ScriptFragment, 'img'),
matchOne = new RegExp(Prototype.ScriptFragment, 'im');
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];
});
@ -261,9 +251,8 @@ Object.extend(String.prototype, (function() {
return match[1].split(separator || '&').inject({ }, function(hash, pair) {
if ((pair = pair.split('='))[0]) {
var key = decodeURIComponent(pair.shift()),
value = pair.length > 1 ? pair.join('=') : pair[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) {
@ -322,9 +311,17 @@ Object.extend(String.prototype, (function() {
* // -> 'MozBinding'
**/
function camelize() {
return this.replace(/-+(.)?/g, function(match, chr) {
return chr ? chr.toUpperCase() : '';
});
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;
}
/**
@ -440,9 +437,7 @@ Object.extend(String.prototype, (function() {
* Checks if the string starts with `substring`.
**/
function startsWith(pattern) {
// We use `lastIndexOf` instead of `indexOf` to avoid tying execution
// time to string length when string doesn't start with pattern.
return this.lastIndexOf(pattern, 0) === 0;
return this.indexOf(pattern) === 0;
}
/**
@ -452,9 +447,7 @@ Object.extend(String.prototype, (function() {
**/
function endsWith(pattern) {
var d = this.length - pattern.length;
// We use `indexOf` instead of `lastIndexOf` to avoid tying execution
// time to string length when string doesn't end with pattern.
return d >= 0 && this.indexOf(pattern, d) === d;
return d >= 0 && this.lastIndexOf(pattern) === d;
}
/**
@ -493,7 +486,7 @@ Object.extend(String.prototype, (function() {
truncate: truncate,
// Firefox 3.5+ supports String.prototype.trim
// (`trim` is ~ 5x faster than `strip` in FF3.5)
strip: String.prototype.trim || strip,
strip: String.prototype.trim ? String.prototype.trim : strip,
stripTags: stripTags,
stripScripts: stripScripts,
extractScripts: extractScripts,

View File

@ -82,14 +82,14 @@
* inadequate, there's a provision for customization. `Template`'s
* constructor accepts an optional second argument that is a regular expression
* object to match the replaceable symbols in the template string. Let's put
* together a template that uses a syntax similar to the now ubiquitous `{{ }}`
* together a template that uses a syntax similar to the ubiquitous `<&#38;= %>`
* constructs:
*
* // matches symbols like '{{ field }}'
* var syntax = /(^|.|\r|\n)(\{{\s*(\w+)\s*}})/;
* // matches symbols like '<&#38;= field %>'
* var syntax = /(^|.|\r|\n)(\<%=\s*(\w+)\s*%\>)/;
*
* var t = new Template(
* '<div>Name: <b>{{ name }}</b>, Age: <b>{{ age }}</b></div>',
* '<div>Name: <b><&#38;= name %></b>, Age: <b><&#38;=age%></b></div>',
* syntax);
* t.evaluate( {name: 'John Smith', age: 26} );
* // -> <div>Name: <b>John Smith</b>, Age: <b>26</b></div>
@ -133,9 +133,8 @@ var Template = Class.create({
var before = match[1] || '';
if (before == '\\') return match[2];
var ctx = object, expr = match[3],
pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
var ctx = object, expr = match[3];
var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
match = pattern.exec(expr);
if (match == null) return before;

6
src/prototype.js vendored
View File

@ -35,9 +35,9 @@ var Prototype = {
if (typeof window.HTMLDivElement !== 'undefined')
return true;
var div = document.createElement('div'),
form = document.createElement('form'),
isSupported = false;
var div = document.createElement('div');
var form = document.createElement('form');
var isSupported = false;
if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) {
isSupported = true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

View File

@ -0,0 +1,389 @@
if (typeof PDoc === "undefined") window.PDoc = {};
// Poor-man's history manager. Polls for changes to the hash.
(function() {
var PREVIOUS_HASH = null;
Event.observe(window, "load", function() {
var hash = window.location.hash;
if (hash && hash !== PREVIOUS_HASH) {
document.fire("hash:changed",
{ previous: PREVIOUS_HASH, current: hash });
PREVIOUS_HASH = hash;
}
window.setTimeout(arguments.callee, 100);
});
})();
// Place a "frame" around the element described by the hash.
// Update the frame when the hash changes.
PDoc.highlightSelected = function() {
if (!window.location.hash) return;
element = $(window.location.hash.substr(1));
if (element) PDoc.highlight(element.up('li, div'));
};
document.observe("hash:changed", PDoc.highlightSelected);
PDoc.highlight = function(element) {
var self = arguments.callee;
if (!self.frame) {
self.frame = new Element('div', { 'class': 'highlighter' });
document.body.appendChild(self.frame);
}
var frame = self.frame;
element.getOffsetParent().appendChild(frame);
var offset = element.positionedOffset();
var w = parseFloat(element.getStyle('width')),
h = parseFloat(element.getStyle('height'));
frame.setStyle({
position: 'absolute',
top: (offset.top - 15) + 'px',
left: (offset.left - 12) + 'px',
width: (w + 20) + 'px',
height: (h + 30) + 'px'
});
// Defer this call because Safari hasn't yet scrolled the viewport.
(function() {
var frameOffset = frame.viewportOffset(frame);
if (frameOffset.top < 0) {
window.scrollBy(0, frameOffset.top - 10);
}
}).defer();
};
// Live API search.
var Filterer = Class.create({
initialize: function(element, options) {
this.element = $(element);
this.options = Object.extend({
interval: 0.1,
resultsElement: '.search-results'
}, options || {});
this.element.writeAttribute("autocomplete", "off");
this.element.up('form').observe("submit", Event.stop);
// // The Safari-only "search" input type is prettier
// if (Prototype.Browser.WebKit)
// this.element.type = "search";
this.menu = this.options.menu;
this.links = this.menu.select('a');
this.resultsElement = this.options.resultsElement;
this.resultsElement.setStyle({
overflowX: 'hidden'
});
this.events = {
filter: this.filter.bind(this),
keydown: this.keydown.bind(this)
};
this.menu.setStyle({ opacity: 0.9 });
this.addObservers();
this.element.value = '';
},
addObservers: function() {
this.element.observe('keyup', this.events.filter);
},
filter: function(event) {
if (this._timer) window.clearTimeout(this._timer);
// Clear the text box on ESC
if (event.keyCode && event.keyCode === Event.KEY_ESC) {
this.element.value = '';
}
if ([Event.KEY_UP, Event.KEY_DOWN, Event.KEY_RETURN].include(event.keyCode))
return;
var value = $F(this.element).strip().toLowerCase();
if (value === "") {
this.onEmpty();
return;
}
var urls = this.findURLs(value);
this.buildResults(urls);
},
keydown: function(event) {
if (![Event.KEY_UP, Event.KEY_DOWN, Event.KEY_RETURN].include(event.keyCode))
return;
// ignore if any modifier keys are present
if (event.shiftKey || event.ctrlKey || event.altKey || event.metaKey)
return;
event.stop();
var highlighted = this.resultsElement.down('.highlighted');
if (event.keyCode === Event.KEY_RETURN) {
// Follow the highlighted item.
if (!highlighted) return;
window.location.href = highlighted.down('a').href;
} else {
var direction = (Event.KEY_DOWN === event.keyCode) ? 1 : -1;
highlighted = this.moveHighlight(direction);
}
if ([Event.KEY_UP, Event.KEY_DOWN].include(event.keyCode) &&
!Prototype.Browser.WebKit) {
// If up/down key is held down, list should keep scrolling.
// Safari does this automatically because it fires the keydown
// event over and over.
this._timer = window.setTimeout(this.scrollList.bind(this, direction), 1000);
}
},
moveHighlight: function(direction) {
var highlighted = this.resultsElement.down('.highlighted');
// move the focus
if (!highlighted) {
// if there is none, highlight the first result
var highlighted = this.resultsElement.down('li').addClassName('highlighted');
} else {
var method = (direction === 1) ? 'next' : 'previous';
highlighted.removeClassName('highlighted');
var adjacent = highlighted[method]('li');
if (!adjacent) {
adjacent = method == 'next' ? this.resultsElement.down('li') :
this.resultsElement.down('li:last-of-type');
}
adjacent.addClassName('highlighted');
highlighted = adjacent;
}
// Adjust the scroll offset of the container so that the highlighted
// item is always in view.
var distanceToBottom = highlighted.offsetTop + highlighted.offsetHeight;
if (distanceToBottom > this.resultsElement.offsetHeight + this.resultsElement.scrollTop) {
// item is too low
this.resultsElement.scrollTop = distanceToBottom - this.resultsElement.offsetHeight;
} else if (highlighted.offsetTop < this.resultsElement.scrollTop) {
// item is too high
this.resultsElement.scrollTop = highlighted.offsetTop;
}
return highlighted;
},
scrollList: function(direction) {
this.moveHighlight(direction);
this._timer = window.setTimeout(this.scrollList.bind(this, direction), 100);
},
// Given a path with any number of `../`s in front of it, remove them all.
// TODO: Fix this a better way.
_fixPath: function(path) {
return path.replace('../', '');
},
buildResults: function(urls) {
this.resultsElement.update();
var ul = this.resultsElement;
urls.each( function(url) {
var a = new Element('a', {
'class': url.type.gsub(/\s/, '_'),
href: PDoc.pathPrefix + this._fixPath(url.path)
}).update(url.name);
var li = new Element('li', { 'class': 'menu-item' });
li.appendChild(a);
ul.appendChild(li);
}, this);
this.showResults();
},
findURLs: function(str) {
var results = [];
for (var i in PDoc.elements) {
if (i.toLowerCase().include(str)) results.push(PDoc.elements[i]);
}
return results;
},
onEmpty: function() {
this.hideResults();
},
showResults: function() {
this.resultsElement.show();
document.stopObserving("keydown", this.events.keydown);
document.observe("keydown", this.events.keydown);
},
hideResults: function() {
this.resultsElement.hide();
document.stopObserving("keydown", this.events.keydown);
}
});
document.observe('dom:loaded', function() {
new Filterer($('search'), {
menu: $('api_menu'),
resultsElement: $('search_results')
});
});
Event.observe(window, 'load', function() {
var menu = $('menu');
var OFFSET = menu.viewportOffset().top;
Event.observe(window, 'scroll', function() {
var sOffset = document.viewport.getScrollOffsets();
if (sOffset.top > OFFSET) {
menu.addClassName('fixed');
} else menu.removeClassName('fixed');
});
});
(function() {
function menuButtonMouseOver(event) {
var menuButton = $('api_menu_button');
var target = event.element();
if (target === menuButton || target.descendantOf(menuButton)) {
$('api_menu').show();
}
}
function menuButtonMouseOut(event) {
var menuButton = $('api_menu_button');
var menu = $('api_menu');
var target = event.element(), related = event.relatedTarget || event.toElement;
if (related && (related === menu || related.descendantOf(menu))) return;
menu.hide();
}
function menuMouseOut(event) {
var menu = $('api_menu'), related = event.relatedTarget || event.toElement;
if (related && !related.descendantOf(menu)) {
arguments.callee.timer = Element.hide.delay(0.5, menu);
} else {
window.clearTimeout(arguments.callee.timer);
}
}
function menuItemMouseOver(event) {
var element = event.element();
if (element.tagName.toLowerCase() === 'a') {
element.addClassName('highlighted');
}
}
function menuItemMouseOut(event) {
var element = event.element();
if (element.tagName.toLowerCase() === 'a') {
element.removeClassName('highlighted');
}
}
var MENU_ITEMS;
document.observe('dom:loaded', function() {
MENU_ITEMS = $$('.api-box .menu-item a');
$('api_menu_button').observe('mouseenter', menuButtonMouseOver);
$('api_menu_button').observe('mouseleave', menuButtonMouseOut );
$('api_menu').observe('mouseleave', menuMouseOut);
if (Prototype.Browser.IE) {
$('api_menu').observe('mouseover', menuItemMouseOver);
$('api_menu').observe('mouseout', menuItemMouseOut);
}
});
})();
Form.GhostedField = Class.create({
initialize: function(element, title, options) {
this.element = $(element);
this.title = title;
options = options || {};
this.isGhosted = true;
if (options.cloak) {
// Wrap the native getValue function so that it never returns the
// ghosted value. This is optional because it presumes the ghosted
// value isn't valid input for the field.
this.element.getValue = this.element.getValue.wrap(this.wrappedGetValue.bind(this));
}
this.addObservers();
this.onBlur();
},
wrappedGetValue: function($proceed) {
var value = $proceed();
return value === this.title ? "" : value;
},
addObservers: function() {
this.element.observe('focus', this.onFocus.bind(this));
this.element.observe('blur', this.onBlur.bind(this));
var form = this.element.up('form');
if (form) {
form.observe('submit', this.onSubmit.bind(this));
}
// Firefox's bfcache means that form fields need to be re-initialized
// when you hit the "back" button to return to the page.
if (Prototype.Browser.Gecko) {
window.addEventListener('pageshow', this.onBlur.bind(this), false);
}
},
onFocus: function() {
if (this.isGhosted) {
this.element.setValue('');
this.setGhosted(false);
}
},
onBlur: function() {
var value = this.element.getValue();
if (value.blank() || value == this.title) {
this.setGhosted(true);
} else {
this.setGhosted(false);
}
},
setGhosted: function(isGhosted) {
this.isGhosted = isGhosted;
this.element[isGhosted ? 'addClassName' : 'removeClassName']('ghosted');
if (isGhosted) {
this.element.setValue(this.title);
}
},
// Hook into the enclosing form's `onsubmit` event so that we clear any
// ghosted text before the form is sent.
onSubmit: function() {
if (this.isGhosted) {
this.element.setValue('');
}
}
});
document.observe("dom:loaded", function() {
new Form.GhostedField($('search'), "Search");
});

View File

@ -0,0 +1,251 @@
/* Unobtrustive Code Highlighter By Dan Webb 11/2005
Version: 0.4
Usage:
Add a script tag for this script and any stylesets you need to use
to the page in question, add correct class names to CODE elements,
define CSS styles for elements. That's it!
Known to work on:
IE 5.5+ PC
Firefox/Mozilla PC/Mac
Opera 7.23 + PC
Safari 2
Known to degrade gracefully on:
IE5.0 PC
Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors
in older browsers use expressions that use lookahead in string format when defining stylesets.
This script is inspired by star-light by entirely cunning Dean Edwards
http://dean.edwards.name/star-light/.
*/
// replace callback support for safari.
if ("a".replace(/a/, function() {return "b"}) != "b") (function(){
var default_replace = String.prototype.replace;
String.prototype.replace = function(search,replace){
// replace is not function
if(typeof replace != "function"){
return default_replace.apply(this,arguments)
}
var str = "" + this;
var callback = replace;
// search string is not RegExp
if(!(search instanceof RegExp)){
var idx = str.indexOf(search);
return (
idx == -1 ? str :
default_replace.apply(str,[search,callback(search, idx, str)])
)
}
var reg = search;
var result = [];
var lastidx = reg.lastIndex;
var re;
while((re = reg.exec(str)) != null){
var idx = re.index;
var args = re.concat(idx, str);
result.push(
str.slice(lastidx,idx),
callback.apply(null,args).toString()
);
if(!reg.global){
lastidx += RegExp.lastMatch.length;
break
}else{
lastidx = reg.lastIndex;
}
}
result.push(str.slice(lastidx));
return result.join("")
}
})();
var CodeHighlighter = { styleSets : new Array };
CodeHighlighter.addStyle = function(name, rules) {
// using push test to disallow older browsers from adding styleSets
if ([].push) this.styleSets.push({
name : name,
rules : rules,
ignoreCase : arguments[2] || false
})
function setEvent() {
// set highlighter to run on load (use LowPro if present)
if (typeof Event != 'undefined' && typeof Event.onReady == 'function')
return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter));
var old = window.onload;
if (typeof window.onload != 'function') {
window.onload = function() { CodeHighlighter.init() };
} else {
window.onload = function() {
old();
CodeHighlighter.init();
}
}
}
// only set the event when the first style is added
if (this.styleSets.length==1) setEvent();
}
CodeHighlighter.init = function() {
if (!document.getElementsByTagName) return;
if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function
// throw out older browsers
var codeEls = document.getElementsByTagName("CODE");
// collect array of all pre elements
codeEls.filter = function(f) {
var a = new Array;
for (var i = 0; i < this.length; i++) if (f(this[i])) a[a.length] = this[i];
return a;
}
var rules = new Array;
rules.toString = function() {
// joins regexes into one big parallel regex
var exps = new Array;
for (var i = 0; i < this.length; i++) exps.push(this[i].exp);
return exps.join("|");
}
function addRule(className, rule) {
// add a replace rule
var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp;
// converts regex rules to strings and chops of the slashes
rules.push({
className : className,
exp : "(" + exp + ")",
length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule
replacement : rule.replacement || null
});
}
function parse(text, ignoreCase) {
// main text parsing and replacement
return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() {
var i = 0, j = 1, rule;
while (rule = rules[i++]) {
if (arguments[j]) {
// if no custom replacement defined do the simple replacement
if (!rule.replacement) return "<span class=\"" + rule.className + "\">" + arguments[0] + "</span>";
else {
// replace $0 with the className then do normal replaces
var str = rule.replacement.replace("$0", rule.className);
for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]);
return str;
}
} else j+= rule.length;
}
});
}
function highlightCode(styleSet) {
// clear rules array
var parsed;
rules.length = 0;
// get stylable elements by filtering out all code elements without the correct className
var stylableEls = codeEls.filter(function(item) {return (item.className.indexOf(styleSet.name)>=0)});
// add style rules to parser
for (var className in styleSet.rules) addRule(className, styleSet.rules[className]);
// replace for all elements
for (var i = 0; i < stylableEls.length; i++) {
// EVIL hack to fix IE whitespace badness if it's inside a <pre>
if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
stylableEls[i] = stylableEls[i].parentNode;
parsed = stylableEls[i].innerHTML.replace(/(<code[^>]*>)([^<]*)<\/code>/i, function() {
return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + "</code>"
});
parsed = parsed.replace(/\n( *)/g, function() {
var spaces = "";
for (var i = 0; i < arguments[1].length; i++) spaces+= "&nbsp;";
return "\n" + spaces;
});
parsed = parsed.replace(/\t/g, "&nbsp;&nbsp;&nbsp;&nbsp;");
parsed = parsed.replace(/\n(<\/\w+>)?/g, "<br />$1").replace(/<br \/>[\n\r\s]*<br \/>/g, "<p><br></p>");
} else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase);
stylableEls[i].innerHTML = parsed;
}
}
// run highlighter on all stylesets
for (var i=0; i < this.styleSets.length; i++) {
highlightCode(this.styleSets[i]);
}
};
CodeHighlighter.addStyle("css", {
comment : {
exp : /\/\*[^*]*\*+([^\/][^*]*\*+)*\//
},
keywords : {
exp : /@\w[\w\s]*/
},
selectors : {
exp : "([\\w-:\\[.#][^{};>]*)(?={)"
},
properties : {
exp : "([\\w-]+)(?=\\s*:)"
},
units : {
exp : /([0-9])(em|en|px|%|pt)\b/,
replacement : "$1<span class=\"$0\">$2</span>"
},
urls : {
exp : /url\([^\)]*\)/
}
});
CodeHighlighter.addStyle("html", {
comment : {
exp: /&lt;!\s*(--([^-]|[\r\n]|-[^-])*--\s*)&gt;/
},
tag : {
exp: /(&lt;\/?)([a-zA-Z]+\s?)/,
replacement: "$1<span class=\"$0\">$2</span>"
},
string : {
exp : /'[^']*'|"[^"]*"/
},
attribute : {
exp: /\b([a-zA-Z-:]+)(=)/,
replacement: "<span class=\"$0\">$1</span>$2"
},
doctype : {
exp: /&lt;!DOCTYPE([^&]|&[^g]|&g[^t])*&gt;/
}
});
CodeHighlighter.addStyle("javascript",{
comment : {
exp : /(\/\/[^\n]*(\n|$))|(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)/
},
brackets : {
exp : /\(|\)/
},
regex : {
exp : /\/(.*?)[g|s|m]?\/[;|\n]/
},
string : {
exp : /'(?:\.|(\\\')|[^\''])*'|"(?:\.|(\\\")|[^\""])*"/
},
keywords : {
exp : /\b(arguments|break|case|continue|default|delete|do|else|false|for|function|if|in|instanceof|new|null|return|switch|this|true|typeof|var|void|while|with)\b/
},
global : {
exp : /\b(toString|valueOf|window|element|prototype|constructor|document|escape|unescape|parseInt|parseFloat|setTimeout|clearTimeout|setInterval|clearInterval|NaN|isNaN|Infinity|alert|prompt|confirm)\b/
}
});

View File

@ -0,0 +1,963 @@
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
// Contributors:
// Richard Livsey
// Rahul Bhargava
// Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/
// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.
if(typeof Effect == 'undefined')
throw("controls.js requires including script.aculo.us' effects.js library");
var Autocompleter = { }
Autocompleter.Base = Class.create({
baseInitialize: function(element, update, options) {
element = $(element)
this.element = element;
this.update = $(update);
this.hasFocus = false;
this.changed = false;
this.active = false;
this.index = 0;
this.entryCount = 0;
this.oldElementValue = this.element.value;
if(this.setOptions)
this.setOptions(options);
else
this.options = options || { };
this.options.paramName = this.options.paramName || this.element.name;
this.options.tokens = this.options.tokens || [];
this.options.frequency = this.options.frequency || 0.4;
this.options.minChars = this.options.minChars || 1;
this.options.onShow = this.options.onShow ||
function(element, update){
if(!update.style.position || update.style.position=='absolute') {
update.style.position = 'absolute';
Position.clone(element, update, {
setHeight: false,
offsetTop: element.offsetHeight
});
}
Effect.Appear(update,{duration:0.15});
};
this.options.onHide = this.options.onHide ||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
if(typeof(this.options.tokens) == 'string')
this.options.tokens = new Array(this.options.tokens);
// Force carriage returns as token delimiters anyway
if (!this.options.tokens.include('\n'))
this.options.tokens.push('\n');
this.observer = null;
this.element.setAttribute('autocomplete','off');
Element.hide(this.update);
Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
},
show: function() {
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
if(!this.iefix &&
(Prototype.Browser.IE) &&
(Element.getStyle(this.update, 'position')=='absolute')) {
new Insertion.After(this.update,
'<iframe id="' + this.update.id + '_iefix" '+
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
this.iefix = $(this.update.id+'_iefix');
}
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
},
fixIEOverlapping: function() {
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
this.iefix.style.zIndex = 1;
this.update.style.zIndex = 2;
Element.show(this.iefix);
},
hide: function() {
this.stopIndicator();
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
if(this.iefix) Element.hide(this.iefix);
},
startIndicator: function() {
if(this.options.indicator) Element.show(this.options.indicator);
},
stopIndicator: function() {
if(this.options.indicator) Element.hide(this.options.indicator);
},
onKeyPress: function(event) {
if(this.active)
switch(event.keyCode) {
case Event.KEY_TAB:
case Event.KEY_RETURN:
this.selectEntry();
Event.stop(event);
case Event.KEY_ESC:
this.hide();
this.active = false;
Event.stop(event);
return;
case Event.KEY_LEFT:
case Event.KEY_RIGHT:
return;
case Event.KEY_UP:
this.markPrevious();
this.render();
Event.stop(event);
return;
case Event.KEY_DOWN:
this.markNext();
this.render();
Event.stop(event);
return;
}
else
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
this.changed = true;
this.hasFocus = true;
if(this.observer) clearTimeout(this.observer);
this.observer =
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
},
activate: function() {
this.changed = false;
this.hasFocus = true;
this.getUpdatedChoices();
},
onHover: function(event) {
var element = Event.findElement(event, 'LI');
if(this.index != element.autocompleteIndex)
{
this.index = element.autocompleteIndex;
this.render();
}
Event.stop(event);
},
onClick: function(event) {
var element = Event.findElement(event, 'LI');
this.index = element.autocompleteIndex;
this.selectEntry();
this.hide();
},
onBlur: function(event) {
// needed to make click events working
setTimeout(this.hide.bind(this), 250);
this.hasFocus = false;
this.active = false;
},
render: function() {
if(this.entryCount > 0) {
for (var i = 0; i < this.entryCount; i++)
this.index==i ?
Element.addClassName(this.getEntry(i),"selected") :
Element.removeClassName(this.getEntry(i),"selected");
if(this.hasFocus) {
this.show();
this.active = true;
}
} else {
this.active = false;
this.hide();
}
},
markPrevious: function() {
if(this.index > 0) this.index--
else this.index = this.entryCount-1;
this.getEntry(this.index).scrollIntoView(true);
},
markNext: function() {
if(this.index < this.entryCount-1) this.index++
else this.index = 0;
this.getEntry(this.index).scrollIntoView(false);
},
getEntry: function(index) {
return this.update.firstChild.childNodes[index];
},
getCurrentEntry: function() {
return this.getEntry(this.index);
},
selectEntry: function() {
this.active = false;
this.updateElement(this.getCurrentEntry());
},
updateElement: function(selectedElement) {
if (this.options.updateElement) {
this.options.updateElement(selectedElement);
return;
}
var value = '';
if (this.options.select) {
var nodes = $(selectedElement).select('.' + this.options.select) || [];
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
} else
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
var bounds = this.getTokenBounds();
if (bounds[0] != -1) {
var newValue = this.element.value.substr(0, bounds[0]);
var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
if (whitespace)
newValue += whitespace[0];
this.element.value = newValue + value + this.element.value.substr(bounds[1]);
} else {
this.element.value = value;
}
this.oldElementValue = this.element.value;
this.element.focus();
if (this.options.afterUpdateElement)
this.options.afterUpdateElement(this.element, selectedElement);
},
updateChoices: function(choices) {
if(!this.changed && this.hasFocus) {
this.update.innerHTML = choices;
Element.cleanWhitespace(this.update);
Element.cleanWhitespace(this.update.down());
if(this.update.firstChild && this.update.down().childNodes) {
this.entryCount =
this.update.down().childNodes.length;
for (var i = 0; i < this.entryCount; i++) {
var entry = this.getEntry(i);
entry.autocompleteIndex = i;
this.addObservers(entry);
}
} else {
this.entryCount = 0;
}
this.stopIndicator();
this.index = 0;
if(this.entryCount==1 && this.options.autoSelect) {
this.selectEntry();
this.hide();
} else {
this.render();
}
}
},
addObservers: function(element) {
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
},
onObserverEvent: function() {
this.changed = false;
this.tokenBounds = null;
if(this.getToken().length>=this.options.minChars) {
this.getUpdatedChoices();
} else {
this.active = false;
this.hide();
}
this.oldElementValue = this.element.value;
},
getToken: function() {
var bounds = this.getTokenBounds();
return this.element.value.substring(bounds[0], bounds[1]).strip();
},
getTokenBounds: function() {
if (null != this.tokenBounds) return this.tokenBounds;
var value = this.element.value;
if (value.strip().empty()) return [-1, 0];
var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
var offset = (diff == this.oldElementValue.length ? 1 : 0);
var prevTokenPos = -1, nextTokenPos = value.length;
var tp;
for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
if (tp > prevTokenPos) prevTokenPos = tp;
tp = value.indexOf(this.options.tokens[index], diff + offset);
if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
}
return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
}
});
Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
var boundary = Math.min(newS.length, oldS.length);
for (var index = 0; index < boundary; ++index)
if (newS[index] != oldS[index])
return index;
return boundary;
};
Ajax.Autocompleter = Class.create(Autocompleter.Base, {
initialize: function(element, update, url, options) {
this.baseInitialize(element, update, options);
this.options.asynchronous = true;
this.options.onComplete = this.onComplete.bind(this);
this.options.defaultParams = this.options.parameters || null;
this.url = url;
},
getUpdatedChoices: function() {
this.startIndicator();
var entry = encodeURIComponent(this.options.paramName) + '=' +
encodeURIComponent(this.getToken());
this.options.parameters = this.options.callback ?
this.options.callback(this.element, entry) : entry;
if(this.options.defaultParams)
this.options.parameters += '&' + this.options.defaultParams;
new Ajax.Request(this.url, this.options);
},
onComplete: function(request) {
this.updateChoices(request.responseText);
}
});
// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
// text only at the beginning of strings in the
// autocomplete array. Defaults to true, which will
// match text at the beginning of any *word* in the
// strings in the autocomplete array. If you want to
// search anywhere in the string, additionally set
// the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
// a partial match (unlike minChars, which defines
// how many characters are required to do any match
// at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
// Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.
Autocompleter.Local = Class.create(Autocompleter.Base, {
initialize: function(element, update, array, options) {
this.baseInitialize(element, update, options);
this.options.array = array;
},
getUpdatedChoices: function() {
this.updateChoices(this.options.selector(this));
},
setOptions: function(options) {
this.options = Object.extend({
choices: 10,
partialSearch: true,
partialChars: 2,
ignoreCase: true,
fullSearch: false,
selector: function(instance) {
var ret = []; // Beginning matches
var partial = []; // Inside matches
var entry = instance.getToken();
var count = 0;
for (var i = 0; i < instance.options.array.length &&
ret.length < instance.options.choices ; i++) {
var elem = instance.options.array[i];
var foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase()) :
elem.indexOf(entry);
while (foundPos != -1) {
if (foundPos == 0 && elem.length != entry.length) {
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
elem.substr(entry.length) + "</li>");
break;
} else if (entry.length >= instance.options.partialChars &&
instance.options.partialSearch && foundPos != -1) {
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
foundPos + entry.length) + "</li>");
break;
}
}
foundPos = instance.options.ignoreCase ?
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
elem.indexOf(entry, foundPos + 1);
}
}
if (partial.length)
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
return "<ul>" + ret.join('') + "</ul>";
}
}, options || { });
}
});
// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
Field.scrollFreeActivate = function(field) {
setTimeout(function() {
Field.activate(field);
}, 1);
}
Ajax.InPlaceEditor = Class.create({
initialize: function(element, url, options) {
this.url = url;
this.element = element = $(element);
this.prepareOptions();
this._controls = { };
arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
Object.extend(this.options, options || { });
if (!this.options.formId && this.element.id) {
this.options.formId = this.element.id + '-inplaceeditor';
if ($(this.options.formId))
this.options.formId = '';
}
if (this.options.externalControl)
this.options.externalControl = $(this.options.externalControl);
if (!this.options.externalControl)
this.options.externalControlOnly = false;
this._originalBackground = this.element.getStyle('background-color') || 'transparent';
this.element.title = this.options.clickToEditText;
this._boundCancelHandler = this.handleFormCancellation.bind(this);
this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
this._boundFailureHandler = this.handleAJAXFailure.bind(this);
this._boundSubmitHandler = this.handleFormSubmission.bind(this);
this._boundWrapperHandler = this.wrapUp.bind(this);
this.registerListeners();
},
checkForEscapeOrReturn: function(e) {
if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
if (Event.KEY_ESC == e.keyCode)
this.handleFormCancellation(e);
else if (Event.KEY_RETURN == e.keyCode)
this.handleFormSubmission(e);
},
createControl: function(mode, handler, extraClasses) {
var control = this.options[mode + 'Control'];
var text = this.options[mode + 'Text'];
if ('button' == control) {
var btn = document.createElement('input');
btn.type = 'submit';
btn.value = text;
btn.className = 'editor_' + mode + '_button';
if ('cancel' == mode)
btn.onclick = this._boundCancelHandler;
this._form.appendChild(btn);
this._controls[mode] = btn;
} else if ('link' == control) {
var link = document.createElement('a');
link.href = '#';
link.appendChild(document.createTextNode(text));
link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
link.className = 'editor_' + mode + '_link';
if (extraClasses)
link.className += ' ' + extraClasses;
this._form.appendChild(link);
this._controls[mode] = link;
}
},
createEditField: function() {
var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
var fld;
if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
fld = document.createElement('input');
fld.type = 'text';
var size = this.options.size || this.options.cols || 0;
if (0 < size) fld.size = size;
} else {
fld = document.createElement('textarea');
fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
fld.cols = this.options.cols || 40;
}
fld.name = this.options.paramName;
fld.value = text; // No HTML breaks conversion anymore
fld.className = 'editor_field';
if (this.options.submitOnBlur)
fld.onblur = this._boundSubmitHandler;
this._controls.editor = fld;
if (this.options.loadTextURL)
this.loadExternalText();
this._form.appendChild(this._controls.editor);
},
createForm: function() {
var ipe = this;
function addText(mode, condition) {
var text = ipe.options['text' + mode + 'Controls'];
if (!text || condition === false) return;
ipe._form.appendChild(document.createTextNode(text));
};
this._form = $(document.createElement('form'));
this._form.id = this.options.formId;
this._form.addClassName(this.options.formClassName);
this._form.onsubmit = this._boundSubmitHandler;
this.createEditField();
if ('textarea' == this._controls.editor.tagName.toLowerCase())
this._form.appendChild(document.createElement('br'));
if (this.options.onFormCustomization)
this.options.onFormCustomization(this, this._form);
addText('Before', this.options.okControl || this.options.cancelControl);
this.createControl('ok', this._boundSubmitHandler);
addText('Between', this.options.okControl && this.options.cancelControl);
this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
addText('After', this.options.okControl || this.options.cancelControl);
},
destroy: function() {
if (this._oldInnerHTML)
this.element.innerHTML = this._oldInnerHTML;
this.leaveEditMode();
this.unregisterListeners();
},
enterEditMode: function(e) {
if (this._saving || this._editing) return;
this._editing = true;
this.triggerCallback('onEnterEditMode');
if (this.options.externalControl)
this.options.externalControl.hide();
this.element.hide();
this.createForm();
this.element.parentNode.insertBefore(this._form, this.element);
if (!this.options.loadTextURL)
this.postProcessEditField();
if (e) Event.stop(e);
},
enterHover: function(e) {
if (this.options.hoverClassName)
this.element.addClassName(this.options.hoverClassName);
if (this._saving) return;
this.triggerCallback('onEnterHover');
},
getText: function() {
return this.element.innerHTML;
},
handleAJAXFailure: function(transport) {
this.triggerCallback('onFailure', transport);
if (this._oldInnerHTML) {
this.element.innerHTML = this._oldInnerHTML;
this._oldInnerHTML = null;
}
},
handleFormCancellation: function(e) {
this.wrapUp();
if (e) Event.stop(e);
},
handleFormSubmission: function(e) {
var form = this._form;
var value = $F(this._controls.editor);
this.prepareSubmission();
var params = this.options.callback(form, value) || '';
if (Object.isString(params))
params = params.toQueryParams();
params.editorId = this.element.id;
if (this.options.htmlResponse) {
var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
Object.extend(options, {
parameters: params,
onComplete: this._boundWrapperHandler,
onFailure: this._boundFailureHandler
});
new Ajax.Updater({ success: this.element }, this.url, options);
} else {
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: params,
onComplete: this._boundWrapperHandler,
onFailure: this._boundFailureHandler
});
new Ajax.Request(this.url, options);
}
if (e) Event.stop(e);
},
leaveEditMode: function() {
this.element.removeClassName(this.options.savingClassName);
this.removeForm();
this.leaveHover();
this.element.style.backgroundColor = this._originalBackground;
this.element.show();
if (this.options.externalControl)
this.options.externalControl.show();
this._saving = false;
this._editing = false;
this._oldInnerHTML = null;
this.triggerCallback('onLeaveEditMode');
},
leaveHover: function(e) {
if (this.options.hoverClassName)
this.element.removeClassName(this.options.hoverClassName);
if (this._saving) return;
this.triggerCallback('onLeaveHover');
},
loadExternalText: function() {
this._form.addClassName(this.options.loadingClassName);
this._controls.editor.disabled = true;
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: 'editorId=' + encodeURIComponent(this.element.id),
onComplete: Prototype.emptyFunction,
onSuccess: function(transport) {
this._form.removeClassName(this.options.loadingClassName);
var text = transport.responseText;
if (this.options.stripLoadedTextTags)
text = text.stripTags();
this._controls.editor.value = text;
this._controls.editor.disabled = false;
this.postProcessEditField();
}.bind(this),
onFailure: this._boundFailureHandler
});
new Ajax.Request(this.options.loadTextURL, options);
},
postProcessEditField: function() {
var fpc = this.options.fieldPostCreation;
if (fpc)
$(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
},
prepareOptions: function() {
this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
[this._extraDefaultOptions].flatten().compact().each(function(defs) {
Object.extend(this.options, defs);
}.bind(this));
},
prepareSubmission: function() {
this._saving = true;
this.removeForm();
this.leaveHover();
this.showSaving();
},
registerListeners: function() {
this._listeners = { };
var listener;
$H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
listener = this[pair.value].bind(this);
this._listeners[pair.key] = listener;
if (!this.options.externalControlOnly)
this.element.observe(pair.key, listener);
if (this.options.externalControl)
this.options.externalControl.observe(pair.key, listener);
}.bind(this));
},
removeForm: function() {
if (!this._form) return;
this._form.remove();
this._form = null;
this._controls = { };
},
showSaving: function() {
this._oldInnerHTML = this.element.innerHTML;
this.element.innerHTML = this.options.savingText;
this.element.addClassName(this.options.savingClassName);
this.element.style.backgroundColor = this._originalBackground;
this.element.show();
},
triggerCallback: function(cbName, arg) {
if ('function' == typeof this.options[cbName]) {
this.options[cbName](this, arg);
}
},
unregisterListeners: function() {
$H(this._listeners).each(function(pair) {
if (!this.options.externalControlOnly)
this.element.stopObserving(pair.key, pair.value);
if (this.options.externalControl)
this.options.externalControl.stopObserving(pair.key, pair.value);
}.bind(this));
},
wrapUp: function(transport) {
this.leaveEditMode();
// Can't use triggerCallback due to backward compatibility: requires
// binding + direct element
this._boundComplete(transport, this.element);
}
});
Object.extend(Ajax.InPlaceEditor.prototype, {
dispose: Ajax.InPlaceEditor.prototype.destroy
});
Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
initialize: function($super, element, url, options) {
this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
$super(element, url, options);
},
createEditField: function() {
var list = document.createElement('select');
list.name = this.options.paramName;
list.size = 1;
this._controls.editor = list;
this._collection = this.options.collection || [];
if (this.options.loadCollectionURL)
this.loadCollection();
else
this.checkForExternalText();
this._form.appendChild(this._controls.editor);
},
loadCollection: function() {
this._form.addClassName(this.options.loadingClassName);
this.showLoadingText(this.options.loadingCollectionText);
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: 'editorId=' + encodeURIComponent(this.element.id),
onComplete: Prototype.emptyFunction,
onSuccess: function(transport) {
var js = transport.responseText.strip();
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
throw 'Server returned an invalid collection representation.';
this._collection = eval(js);
this.checkForExternalText();
}.bind(this),
onFailure: this.onFailure
});
new Ajax.Request(this.options.loadCollectionURL, options);
},
showLoadingText: function(text) {
this._controls.editor.disabled = true;
var tempOption = this._controls.editor.firstChild;
if (!tempOption) {
tempOption = document.createElement('option');
tempOption.value = '';
this._controls.editor.appendChild(tempOption);
tempOption.selected = true;
}
tempOption.update((text || '').stripScripts().stripTags());
},
checkForExternalText: function() {
this._text = this.getText();
if (this.options.loadTextURL)
this.loadExternalText();
else
this.buildOptionList();
},
loadExternalText: function() {
this.showLoadingText(this.options.loadingText);
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
Object.extend(options, {
parameters: 'editorId=' + encodeURIComponent(this.element.id),
onComplete: Prototype.emptyFunction,
onSuccess: function(transport) {
this._text = transport.responseText.strip();
this.buildOptionList();
}.bind(this),
onFailure: this.onFailure
});
new Ajax.Request(this.options.loadTextURL, options);
},
buildOptionList: function() {
this._form.removeClassName(this.options.loadingClassName);
this._collection = this._collection.map(function(entry) {
return 2 === entry.length ? entry : [entry, entry].flatten();
});
var marker = ('value' in this.options) ? this.options.value : this._text;
var textFound = this._collection.any(function(entry) {
return entry[0] == marker;
}.bind(this));
this._controls.editor.update('');
var option;
this._collection.each(function(entry, index) {
option = document.createElement('option');
option.value = entry[0];
option.selected = textFound ? entry[0] == marker : 0 == index;
option.appendChild(document.createTextNode(entry[1]));
this._controls.editor.appendChild(option);
}.bind(this));
this._controls.editor.disabled = false;
Field.scrollFreeActivate(this._controls.editor);
}
});
//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only exists for a while, in order to let ****
//**** users adapt to the new API. Read up on the new ****
//**** API and convert your code to it ASAP! ****
Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
if (!options) return;
function fallback(name, expr) {
if (name in options || expr === undefined) return;
options[name] = expr;
};
fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
options.cancelLink == options.cancelButton == false ? false : undefined)));
fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
options.okLink == options.okButton == false ? false : undefined)));
fallback('highlightColor', options.highlightcolor);
fallback('highlightEndColor', options.highlightendcolor);
};
Object.extend(Ajax.InPlaceEditor, {
DefaultOptions: {
ajaxOptions: { },
autoRows: 3, // Use when multi-line w/ rows == 1
cancelControl: 'link', // 'link'|'button'|false
cancelText: 'cancel',
clickToEditText: 'Click to edit',
externalControl: null, // id|elt
externalControlOnly: false,
fieldPostCreation: 'activate', // 'activate'|'focus'|false
formClassName: 'inplaceeditor-form',
formId: null, // id|elt
highlightColor: '#ffff99',
highlightEndColor: '#ffffff',
hoverClassName: '',
htmlResponse: true,
loadingClassName: 'inplaceeditor-loading',
loadingText: 'Loading...',
okControl: 'button', // 'link'|'button'|false
okText: 'ok',
paramName: 'value',
rows: 1, // If 1 and multi-line, uses autoRows
savingClassName: 'inplaceeditor-saving',
savingText: 'Saving...',
size: 0,
stripLoadedTextTags: false,
submitOnBlur: false,
textAfterControls: '',
textBeforeControls: '',
textBetweenControls: ''
},
DefaultCallbacks: {
callback: function(form) {
return Form.serialize(form);
},
onComplete: function(transport, element) {
// For backward compatibility, this one is bound to the IPE, and passes
// the element directly. It was too often customized, so we don't break it.
new Effect.Highlight(element, {
startcolor: this.options.highlightColor, keepBackgroundImage: true });
},
onEnterEditMode: null,
onEnterHover: function(ipe) {
ipe.element.style.backgroundColor = ipe.options.highlightColor;
if (ipe._effect)
ipe._effect.cancel();
},
onFailure: function(transport, ipe) {
alert('Error communication with the server: ' + transport.responseText.stripTags());
},
onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
onLeaveEditMode: null,
onLeaveHover: function(ipe) {
ipe._effect = new Effect.Highlight(ipe.element, {
startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
restorecolor: ipe._originalBackground, keepBackgroundImage: true
});
}
},
Listeners: {
click: 'enterEditMode',
keydown: 'checkForEscapeOrReturn',
mouseover: 'enterHover',
mouseout: 'leaveHover'
}
});
Ajax.InPlaceCollectionEditor.DefaultOptions = {
loadingCollectionText: 'Loading options...'
};
// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields
Form.Element.DelayedObserver = Class.create({
initialize: function(element, delay, callback) {
this.delay = delay || 0.5;
this.element = $(element);
this.callback = callback;
this.timer = null;
this.lastValue = $F(this.element);
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
},
delayedListener: function(event) {
if(this.lastValue == $F(this.element)) return;
if(this.timer) clearTimeout(this.timer);
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
this.lastValue = $F(this.element);
},
onTimerEvent: function() {
this.timer = null;
this.callback(this.element, $F(this.element));
}
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,487 @@
/*
* API STYLES
*/
/* tag styles */
pre {
padding: 0;
}
code {
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", monospace;
font-size: 12px;
}
/* masthead */
div#masthead {
background: url(../images/header-stripe-small.png) repeat-x top left;
height: 76px;
}
div#masthead div#masthead_content {
margin: 0 auto;
width: 835px;
position: relative;
}
div#masthead h1#logo {
background: url(../images/header-logo-small.png) no-repeat 0 1px;
width: 118px;
height: 76px;
position: relative;
left: 60px;
}
/* footer */
div#footer {
width: 960px;
margin: 0 auto;
}
div.about {
margin-top: 20px;
padding-top: 20px;
width: 835px;
margin-left: 120px;
border-top: 1px solid #aaa;
color: #aaa;
}
div.about a,
div.about a:link {
color: #aaa;
text-decoration: underline;
border: 0;
}
div.copyright,
div.credits {
width: 360px;
float: left;
}
div.copyright .cc {
width: 115px;
margin-right: 5px;
text-align: center;
float: left;
}
div.copyright .copyright-about {
width: 235px;
float: left;
}
div.credits {
margin-left: 115px;
}
.page-title span.type {
display: block;
text-transform: uppercase;
font-size: 14px;
color: #aaa;
letter-spacing: 0;
}
h2.page-title {
margin-top: 0;
line-height: 100%;
}
ul.breadcrumbs {
margin-left: 120px;
}
ul.breadcrumbs li {
float: left;
list-style-type: none;
margin-right: 10px;
margin-left: 0;
}
ul.method-list {
margin-top: 0;
}
ul.method-list li {
margin-top: 0;
float: left;
margin-right: 5px;
margin-left: 0;
list-style-type: none;
}
ul.method-list li a,
ul.method-list li a:link {
text-decoration: none;
border: 0;
}
ul.method-details-list {
margin-top: 0;
}
ul.method-details-list li.method-description {
margin-top: 0;
margin-bottom: 3.0em;
margin-left: 0;
list-style-type: none;
}
ul.method-details-list li h4 {
margin: 0 0 0.6em;
line-height: 90%;
}
ul.method-details-list li pre {
margin-top: 0;
}
ul.method-details-list li pre code {
font-size: 13px;
}
ul.method-details-list li code {
font-size: 12px;
}
h4.inherited {
padding-top: 5px;
clear: left;
font-style: italic;
font-weight: bold;
font-size: 14px;
}
.method-description h4 .method-permalink a {
color: #aaa;
text-decoration: none;
border: 0;
font-size: 14px;
}
ul.argument-list {
margin-top: -5px;
list-style-type: disc;
margin-left: 20px;
}
ul.argument-list li {
list-style-type: disc;
font-size: 90%;
margin-bottom: 0;
}
ul.argument-list li code {
font-size: 11px;
}
ul.section-list {
margin-top: 0;
}
ul.section-list li {
margin-top: 0;
margin-left: 0;
list-style-type: none;
}
ul.section-list li h4 {
margin-top: 0;
}
/* Aliases */
.alias,
.related-to {
font-style: italic;
}
.alias code,
.related-to code {
font-style: normal;
}
/* Section icons */
.page-content .section .section-title h3 {
padding-right: 24px;
background-position: right 0;
background-repeat: no-repeat;
}
.section-constructor .section-title h3 {
background-image: url(../images/constructor.png);
}
.section-method-list .section-title h3 {
background-image: url(../images/method.png);
}
.section-klass_methods .section-title h3 {
background-image: url(../images/class_method.png);
}
.section-klass_properties .section-title h3 {
background-image: url(../images/class_property.png);
}
.section-instance_methods .section-title h3 {
background-image: url(../images/instance_method.png);
}
.section-instance_properties .section-title h3 {
background-image: url(../images/instance_property.png);
}
.section-mixins .section-title h3 {
background-image: url(../images/mixin.png);
}
.section-classes .section-title h3 {
background-image: url(../images/class.png);
}
.section-namespaces .section-title h3 {
background-image: url(../images/namespace.png);
}
.section-sections .section-title h3 {
background-image: url(../images/section.png);
}
.section-utilities .section-title h3 {
background-image: url(../images/utility.png);
}
.section-description .section-title h3 {
background-image: url(../images/description.png);
}
.section-subclasses .section-title h3 {
background-image: url(../images/subclass.png);
}
.section-superclass .section-title h3 {
background-image: url(../images/superclass.png);
}
/* search box */
.search-results {
position: absolute;
background-color: #fff;
height: 200px;
width: 233px;
overflow: auto;
overflow-y: scroll;
overflow-x: hidden;
margin: 7px -11px 0;
border: 1px solid #999;
top: 28px;
}
* html .search-results {
left: 486px;
top: 30px;
}
/* search result types */
.menu-item a,
.menu-item a:link {
display: block;
padding: 3px 10px 3px 28px;
background-position: 6px 50%;
background-repeat: no-repeat;
text-align: left;
text-decoration: none;
color: #333;
border-top: 1px solid #fff;
border-bottom: 1px solid #fff;
white-space: nowrap;
}
.menu-item a:hover,
.menu-item a.highlighted,
#menu .highlighted a {
border-top: 1px solid #69f;
border-bottom: 1px solid #69f;
background-color: #f0f0ff;
}
.menu-item a.section {
font-weight: bold;
background-image: url(../images/section.png);
}
.menu-item a.class_method,
.menu-item a.instance_method {
background-image: url(../images/method.png);
}
.menu-item a.class {
background-image: url(../images/class.png);
}
.menu-item a.constructor {
background-image: url(../images/constructor.png);
}
.menu-item a.class_property {
background-image: url(../images/class_property.png);
}
.menu-item a.instance_property {
background-image: url(../images/instance_property.png);
}
.menu-item a.namespace {
background-image: url(../images/namespace.png);
}
.menu-item a.utility {
background-image: url(../images/utility.png);
}
/* halo around selected method */
.highlighter {
border: 3px solid #69f;
z-index: -1;
-webkit-border-radius: 15px;
-moz-border-radius: 15px;
border-radius: 15px;
}
/* MENU */
div#menu {
width: 960px;
margin: 0 auto;
position: relative;
}
#menu .api-box h2 a,
#menu .search-box {
width: 213px;
height: 25px;
line-height: 25px;
padding: 5px 10px;
margin-right: 5px;
text-align: center;
float: right;
}
* html #menu .api-box h2 a,
* html #menu .search-box {
height: 30px;
line-height: 30px;
}
#menu .api-box {
}
#menu .api-box h2 a {
font-size: 14px;
font-weight: normal;
font-family: Verdana, sans-serif;
display: block;
background-color: #cde0fb;
border: 1px solid #669;
border-top: 0;
text-decoration: none;
color: #222;
}
#menu .api-box .menu-items {
position: absolute;
background-color: #fff;
height: 200px;
width: 233px;
overflow: auto;
overflow-y: scroll;
overflow-x: hidden;
top: 35px;
border: 1px solid #999;
right: 5px;
}
* html #menu .api-box .menu-items {
right: 10px;
top: 37px;
}
#menu .api-box ul,
#menu .api-box li {
margin: 0;
padding: 0;
}
#menu .api-box .menu-item a {
}
#menu .search-box {
background-color: #cee8c3;
border: 1px solid #696;
border-top: 0;
}
#menu .search-box input {
width: 150px;
padding: 3px 10px;
margin-top: 2px;
border: 1px solid #999;
border-radius: 10px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
}
#menu #search.ghosted {
color: #aaa;
text-align: left;
}
/* notes */
p.note,
p.alias,
p.related-to {
font-size: 11px;
line-height: 14px;
padding: 5px 5px 5px 60px;
background-repeat: no-repeat;
background-position: 20px 50%;
border: 1px solid #000;
}
p.note {
background-color: #f0f0f4;
background-image: url(../images/information.png);
border-color: #69c;
}
p.alias {
background-color: #fff6de;
background-image: url(../images/alias.png);
border-color: #cc9;
}
p.related-to {
background-color: #f4f0f4;
background-image: url(../images/related_to.png);
border-color: #c9c;
}

View File

@ -0,0 +1,415 @@
/* The "section" class implicitly needs a clearfix; adding it here for convenience. */
.clearfix:after,
.section:after {
content: ".";
display: block;
clear: both;
visibility: hidden;
line-height: 0;
height: 0;
}
.clearfix, .section {
display: inline-block;
}
html[xmlns] .clearfix,
html[xmlns] .section {
display: block;
}
* html .clearfix,
* html .section {
height: 1%;
}
span.replaced { visibility: hidden; }
span.hidden { display: none; }
body {
font-family: Verdana, sans-serif;
font-size: 82.5%;
line-height: 1.5em;
margin: 0;
padding: 0;
}
body * {
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6 {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
}
h4 {
font-size: 17px;
color: #555;
margin: 1.0em 0 0.6em;
}
h5 {
font-size: 15px;
color: #2a2a2a;
margin: 1.0em 0 0.6em;
}
h6 {
font-size: 14px;
color: #000;
margin: 1.0em 0 0.6em;
}
a img {
border: 0;
}
ul, li {
list-style-type: none;
}
ol, ul {
margin: 0 0 15px;
}
#page a.img,
#page a.img:link,
#page a.img:visited {
border: none;
}
/* Link bar */
div#links {
margin: 0 auto;
width: 835px;
padding: 16px 0;
height: 16px;
overflow: hidden;
}
div#links_wrapper {
background: #fff;
}
div#links li {
display: inline;
}
div#links li a {
color: #666;
}
div#links li.selected a {
color: #000;
font-weight: bold;
text-decoration: none;
}
ul#internal_links {
float: left;
}
ul#internal_links li {
margin-right: 25px;
}
ul#external_links {
float: right;
}
ul#external_links li {
margin-left: 25px;
padding-left: 21px;
}
li#scripty_link {
background: url(http://prototype.conio.net/new/images/link-logo-scripty.png) no-repeat center left;
}
li#rails_link {
background: url(http://prototype.conio.net/new/images/link-logo-rails.png) no-repeat center left;
}
p a, p a:link,
h1 a, h1 a:link,
h2 a, h2 a:link,
h3 a, h3 a:link,
h4 a, h4 a:link,
h5 a, h5 a:link,
h6 a, h6 a:link {
color: #036;
border-bottom: 1px solid #036;
text-decoration: none;
}
p a:visited {
border-bottom: 1px solid #666;
}
code {
font-family: "Panic Sans", "Bitstream Vera Sans Mono", Monaco, Consolas, Andale Mono, monospace;
font-size: 13px;
}
p code,
li code {
background-color: #f0f0f0;
border: 1px solid #ccc;
border-radius: 3px;
-webkit-border-radius: 3px;
padding: 0 3px;
}
pre code {
background-color: transparent;
border: 0;
padding: 0;
}
#page {
margin: 0 auto;
padding-bottom: 100px; /* FIXME: Temporary as pages are built */
}
/* top */
.related-links {
width: 835px;
font-size: 0.9em;
margin: 0 auto 10px;
padding: 10px 0 0;
}
.related-links a {
color: #000;
text-decoration: none;
}
.related-links ul {
list-style-type: none;
}
.related-links ul.internal {
float: left;
width: 355px;
}
.related-links ul.internal li {
text-align: center;
}
.related-links ul.external {
float: right;
width: 295px;
}
.related-links li {
float: left;
padding: 0 15px;
width: 85px;
margin-right: 5px;
}
.related-links li.last {
margin-right: 0;
}
.related-links ul.external li.scripty {
padding: 0 8px 0 22px;
background: url(../images/icon-scripty.png) no-repeat;
margin-right: 65px;
}
.related-links ul.external li.rails {
padding: 0 8px 0 22px;
background: url(../images/icon-rails.png) no-repeat;
}
.banner {
height: 152px;
padding: 1px 0;
background: url(../images/header-stripe.png) repeat-x;
}
.banner-content {
width: 835px;
margin: 0 auto;
}
.banner h1 {
width: 236px;
height: 150px;
background: url(../images/header-logo.png) no-repeat;
}
.banner h1 span {
display: none;
}
.banner-small {
height: 75px;
padding: 1px 0;
background: url(../images/header-stripe-small.png) repeat-x;
}
.banner-small h1 {
width: 118px;
height: 75px;
background: url(../images/header-logo-small.png) no-repeat;
margin-left: 60px;
}
.banner-small h1 span {
display: none;
}
/* PAGE CONTENT */
.page-content {
width: 955px;
margin: 30px auto 0;
}
.page-content .page-title {
margin-left: 120px;
margin-bottom: 15px;
font-size: 27px;
letter-spacing: -1px;
color: #444;
}
.page-content .page-introduction {
margin-left: 120px;
margin-bottom: 25px;
}
.page-content .section {
width: 955px;
margin: 10px 0 20px;
}
.page-content .section .section-title {
width: 110px;
margin-right: 10px;
padding-right: 0;
float: left;
text-align: right;
overflow: hidden;
}
.page-content .section .section-title h3 {
color: #999;
font-size: 14px;
line-height: 110%;
padding-right: 10px;
padding-bottom: 5px;
}
.page-content .section .section-content {
width: 835px;
float: left;
}
.page-content a,
.page-content a:link {
color: #036;
border-bottom: 1px solid #036;
text-decoration: none;
}
.page-content a:visited {
border-bottom: 1px solid #bbb;
}
.page-content ul,
.page-content ol {
margin: 10px 0;
}
.page-content li {
margin: 5px 0 8px 20px;
list-style-type: disc;
}
.page-content p {
margin: 0 0 0.8em;
}
.page-content pre {
color: #333;
background-color: #f0f0ff;
border: 1px solid #dde;
padding: 3px 5px;
margin: 0 0 1em;
}
.page-content .two-column {
}
.page-content .two-column-left,
.page-content .two-column-right {
width: 475px;
margin-right: 5px;
float: left;
}
.page-content .two-column-right {
margin-right: 0;
}
.page-content .two-column .section {
width: 475px;
}
.page-content .two-column .section-content {
width: 345px;
padding-right: 10px;
}
.smallcaps {
font-size: 0.85em;
text-transform: uppercase;
letter-spacing: 1px;
}
/* MASTHEAD */
div#masthead {
margin-top: 50px;
background: url(../images/header-stripe-small.png) repeat-x top left;
height: 76px;
}
div#masthead div#masthead_content {
margin: 0 auto;
width: 835px;
position: relative;
}
div#masthead h1#logo {
background: url(../images/header-logo-small.png) no-repeat 0 1px;
width: 118px;
height: 76px;
position: relative;
left: 60px;
}
div#masthead a {
text-decoration: none;
}

View File

@ -0,0 +1,13 @@
body.grid {
width: 955px;
margin: auto;
}
body.grid div#page {
overflow: hidden;
background: url(../images/grid.png);
}
body.grid div#page * {
opacity: .9;
}

View File

@ -0,0 +1,116 @@
/* See license.txt for terms of usage */
.firebugHighlight {
z-index: 2147483647;
position: absolute;
background-color: #3875d7;
}
.firebugLayoutBoxParent {
z-index: 2147483647;
position: absolute;
border-right: 1px dashed #BBBBBB;
border-bottom: 1px dashed #BBBBBB;
}
.firebugRulerH {
position: absolute;
top: -15px;
left: 0;
width: 100%;
height: 14px;
background: url(chrome://firebug/skin/rulerH.png) repeat-x;
border-top: 1px solid #BBBBBB;
border-right: 1px dashed #BBBBBB;
border-bottom: 1px solid #000000;
}
.firebugRulerV {
position: absolute;
top: 0;
left: -15px;
width: 14px;
height: 100%;
background: url(chrome://firebug/skin/rulerV.png) repeat-y;
border-left: 1px solid #BBBBBB;
border-right: 1px solid #000000;
border-bottom: 1px dashed #BBBBBB;
}
.overflowRulerX > .firebugRulerV {
left: 0;
}
.overflowRulerY > .firebugRulerH {
top: 0;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
.firebugLayoutBoxOffset {
z-index: 2147483647;
position: absolute;
opacity: 0.8;
}
.firebugLayoutBoxMargin {
background-color: #EDFF64;
}
.firebugLayoutBoxBorder {
background-color: #666666;
}
.firebugLayoutBoxPadding {
background-color: SlateBlue;
}
.firebugLayoutBoxContent {
background-color: SkyBlue;
}
/*.firebugHighlightGroup .firebugLayoutBox {
background-color: transparent;
}
.firebugHighlightBox {
background-color: Blue !important;
}*/
.firebugLayoutLine {
z-index: 2147483647;
background-color: #000000;
opacity: 0.4;
}
.firebugLayoutLineLeft,
.firebugLayoutLineRight {
position: fixed;
width: 1px;
height: 100%;
}
.firebugLayoutLineTop,
.firebugLayoutLineBottom {
position: absolute;
width: 100%;
height: 1px;
}
.firebugLayoutLineTop {
margin-top: -1px;
border-top: 1px solid #999999;
}
.firebugLayoutLineRight {
border-right: 1px solid #999999;
}
.firebugLayoutLineBottom {
border-bottom: 1px solid #999999;
}
.firebugLayoutLineLeft {
margin-left: -1px;
border-left: 1px solid #999999;
}

View File

@ -0,0 +1,443 @@
/* @group Tags */
body {
font-family: Verdana, sans-serif;
}
form {
margin: 0;
padding: 0;
}
a {
color: #036;
text-decoration: none;
}
p a {
border-bottom: 1px solid #999;
padding: 0 1px;
}
p a:hover {
background-color: #036;
color: #fff;
border-bottom: 1px solid #036;
}
h1, h2, h3, h4, h5, h6 {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
}
pre {
padding: 0;
}
code {
font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", monospace;
font-size: 11px;
}
/* @end */
/* @group Masthead */
div#masthead {
background: url(../images/header-stripe-small.png) repeat-x top left;
height: 76px;
}
div#masthead div#masthead_content {
margin: 0 auto;
width: 835px;
position: relative;
}
div#masthead h1#logo {
background: url(../images/header-logo-small.png) no-repeat 0 1px;
width: 118px;
height: 76px;
position: relative;
left: 60px;
}
/* @end */
/* @group Main */
#main {
width: 840px;
margin: 20px auto;
position: relative;
}
#main h2 {
color: #444;
font-size: 22px;
padding-left: 69px;
margin-top: 0;
}
#main h2 span {
color: #ccc;
display: block;
text-transform: uppercase;
font-size: 13px;
margin-bottom: -10px;
padding-left: 2px;
}
#main h2 a {
color: #444;
text-decoration: none;
}
#main h2 a:hover {
color: #222;
border-bottom: 1px solid #999;
}
#main h4, h5, h6 {
padding-left: 4px;
}
#main li h4,
#main li h5,
#main li h6 {
padding-left: 0;
}
#main h4.inherited {
color: #888 !important;
}
#main .section {
overflow: hidden;
padding-left: 65px;
padding-bottom: 0;
margin: 5px 0 0;
}
#main .section h3 {
position: absolute;
left: -85px;
width: 110px;
text-align: right;
font-size: 13px;
padding-top: 3px;
color: #999;
line-height: 100%;
min-height: 30px;
}
#main .section h4 {
font-size: 15px;
color: #444;
margin: 0 0 0.3em;
}
#main .section h4.inherited {
color: #888;
}
#main .section h4.inherited a {
color: #49B;
}
#main p {
margin: 0 0 1.1em;
}
#main pre {
background-color: #000;
color: #fff;
padding: 5px 10px;
margin: 0 0 5px;
}
#main pre.syntax {
background-color: #f5f5f5;
color: #000;
padding: 3px 5px;
}
#main .section p.purpose {
background-color: #CDE0FB;
padding: 3px 5px;
}
#main .section p {
padding: 0 5px;
}
#main ul, #main ol {
padding-left: 5px;
margin: 0 0 10px;
}
#main ul, #main ul li {
list-style-type: none;
}
#main #excerpt p {
color: #000;
border-left: 3px solid #bbb;
padding: 0;
margin-left: -10px;
font-size: 94%;
padding-left: 10px;
}
#main .meta {
position: absolute;
width: 108px;
left: -60px;
text-align: right;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: bold;
color: #888;
}
/* @end */
/* @group Method List */
ul.method-list li {
display: inline;
border-top: 1px solid #ccc;
}
ul.method-list li a {
background-color: #eee;
padding: 2px 3px;
border: 1px solid #ccc;
}
ul.method-list li a:hover {
background-color: #ddd;
}
/* @end */
/* @group Menu */
#menu {
width: 840px;
margin: 0 auto;
position: relative;
height: 25px;
}
#menu div {
width: 234px;
float: right;
margin: 0 3px;
}
#menu div.search-box {
width: 222px;
background-color: #cee8c3;
font-size: 13px;
height: 25px;
line-height: 25px;
padding: 2px 6px;
}
#menu div.search-box label {
color: #555;
}
#menu div.search-box input {
border: 1px solid #bbb;
padding: 2px;
width: 163px;
}
#menu h2 {
font-size: 13px;
font-weight: normal;
}
#menu h2 a {
height: 25px;
line-height: 25px;
display: block;
text-align: center;
background-color: #CDE0FB;
font-family: Verdana, sans-serif;
padding: 2px 6px;
}
#menu h2 a:hover {
background-color: #a4c8fb;
}
#menu #api_menu,
#menu #search_results {
border: 1px solid #ddd;
background: #fff;
position: absolute;
width: 232px;
top: 26px;
z-index: 1500;
max-height: 200px;
overflow: auto;
}
#menu #api_menu {
right: 3px;
}
#menu #search_results {
right: 243px;
}
#menu .menu-items li {
background-color: #fff;
}
#menu .menu-items li a {
display: block;
color: #333;
background-color: #fff;
padding: 2px 6px;
border-top: 1px solid #fff;
border-bottom: 1px solid #fff;
}
#menu .menu-items li a:hover,
#menu .menu-items li.highlighted a {
background-color: #CDE0FB;
border-top-color: #a4c8fb;
border-bottom-color: #a4c8fb;
}
#menu .menu-items ul {
margin: 0;
padding: 0;
}
#menu .menu-items li a {
background-repeat: no-repeat;
background-position: 6px 3px;
padding: 3px 5px 3px 30px;
font-size: 12px;
}
#menu .menu-items li a.class {
background-image: url(../images/class.png);
}
#menu .menu-items li a.namespace {
background-image: url(../images/namespace.png);
}
#menu .menu-items li a.mixin {
background-image: url(../images/mixin.png);
}
#menu .menu-items li a.section {
text-transform: uppercase;
color: #777;
background-image: url(../images/section.png);
}
#menu .menu-items li a.class_method,
#menu .menu-items li a.instance_method,
#menu .menu-items li a.utility {
background-image: url(../images/method.png);
}
#menu .menu-items li a.class_property,
#menu .menu-items li a.instance_property {
background-image: url(../images/property.png);
}
#menu .menu-items li a.constructor {
background-image: url(../images/constructor.png);
}
#menu .menu-items li a.constant {
background-image: url(../images/constant.png);
}
/* @end */
/* @group Section Headings (H3s) */
.section h3 {
padding-right: 25px;
background-repeat: no-repeat;
background-position: right 4px;
}
.section-superclass h3 {
background-image: url(../images/class.png);
}
.section-method-list h3,
.section-instance_methods h3,
.section-klass_methods h3 {
background-image: url(../images/method.png);
}
.section-klass_properties h3,
.section-instance_properties h3 {
background-image: url(../images/property.png);
}
.section-constructor h3 {
background-image: url(../images/constructor.png);
}
.section-constants h3 {
background-image: url(../images/constant.png);
}
.section-mixins h3 {
background-image: url(../images/mixin.png);
}
/* @end */
/* @group Method Details List */
ul.method-details-list li {
border: 2px solid #fff;
}
ul.method-details-list li.highlighted {
border: 2px solid #CDE0FB;
}
/* @end */
/* floating menu */
#menu.fixed {
position: fixed;
top: 0;
z-index: 1000;
left: 232px;
}

View File

@ -0,0 +1,244 @@
/* Masthead */
div#masthead {
background: url(http://prototype.conio.net/new/images/header-stripe.png) repeat-x top left;
height: 152px;
}
div#masthead div#masthead_content {
margin: 0 auto;
width: 835px;
position: relative;
}
div#masthead h1#logo {
background: url(http://prototype.conio.net/new/images/header-logo.png) no-repeat 0 1px;
width: 236px;
height: 152px;
}
div#masthead div#pitch {
position: absolute;
background: url(http://prototype.conio.net/new/images/header-copy.png) no-repeat 0 1px;
top: 0;
left: 300px;
width: 535px;
height: 152px;
}
div#masthead h1#logo *,
div#masthead div#pitch * {
display: none;
}
/* Buttons */
div#buttons, div#more {
overflow: hidden;
width: 840px;
margin: -1px auto 0 auto;
padding: 0 0 0 5px;
}
div#more {
margin-top: 35px;
}
div#buttons li {
display: block;
float: left;
border-top: 1px solid #929fb3;
margin: 0 5px 0 0;
width: 175px;
height: 100px;
}
div#more li {
height: 50px;
}
div#buttons li a {
display: block;
position: relative;
width: 175px;
height: 100px;
text-decoration: none;
background: #cde0fb no-repeat center 30px;
}
div#buttons li a:hover {
background-color: #a4c8fb;
}
div#buttons li a span.title {
display: none;
}
div#buttons li a span.description {
position: absolute;
display: block;
width: 175px;
top: 55px;
text-align: center;
color: #666;
}
div#more li a span.title {
position: absolute;
display: block;
width: 175px;
top: 15px;
text-align: center;
color: #666;
}
div#buttons li a:hover span.description {
color: #333;
}
div#buttons li#download_button {
width: 295px;
}
div#buttons li#download_button a,
div#buttons li#download_button a span.description {
width: 295px;
}
div#buttons li#download_button a {
background-color: #cee8c3;
background-image: url(http://prototype.conio.net/new/images/button-download.png);
}
div#buttons li#download_button a:hover {
background-color: #a4e289;
}
div#buttons li#learn_button a {
background-image: url(http://prototype.conio.net/new/images/button-learn.png);
}
div#buttons li#discuss_button a {
background-image: url(http://prototype.conio.net/new/images/button-discuss.png);
}
div#buttons li#contribute_button a {
background-image: url(http://prototype.conio.net/new/images/button-contribute.png);
}
div#more {
position: relative;
height: 400px;
margin: 15px 60px;
color: #444;
}
div#more .column {
display: block;
float: left;
margin: 0 5px 0 0;
padding-left: 5px;
min-height: 300px;
}
div#more h3 {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
letter-spacing: -1px;
font-weight: bold;
color: #444;
font-size: 18px;
margin: 0 0 5px;
}
.column {
width: 175px;
}
.wide-column {
width: 295px;
}
.medium-column {
width: 235px;
}
.main-column {
width: 530px;
}
.sidebar-column {
width: 290px;
}
div#more {
width: 840px;
margin: 25px auto 0;
}
/*#buttons_wrapper {
background-color: #0E4FAF;
}
#page {
background-color: #292929;
}
*/
div#more div.main-column p {
font-size: 16px;
margin-right: 10px;
}
div#more h3.tagline {
height: 23px;
background: url(../images/tagline.png) no-repeat top left;
width: 530px;
}
div#more h3.tagline span { display: none; }
.view-master {
text-align: center;
font-family: Verdana;
color: #ccc;
font-size: 18px;
padding: 35px 0;
margin: 10px 0;
}
.using-prototype {
border-top: 2px solid #ddd;
border-bottom: 2px solid #ddd;
margin: 25px 10px 10px 0;
padding: 5px 0;
}
.sidebar-column h4 {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
font-weight: bold;
margin: 0;
}
.sidebar-column p {
margin-bottom: 15px;
}
ol, ol li {
list-style-type: decimal;
line-height: 160%;
}
ol li {
margin: 0 0 2px;
}
ol, ul {
margin: 0 0 15px;
padding-left: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}

19
templates/html/helpers.rb Normal file
View File

@ -0,0 +1,19 @@
module PDoc
module Generators
module Html
module Helpers
module BaseHelper
end
module LinkHelper
end
module CodeHelper
end
module MenuHelper
end
end
end
end
end

24
templates/html/index.erb Normal file
View File

@ -0,0 +1,24 @@
<% @title = "Home" %>
<div class="page-introduction">
<%= @index_page_content %>
</div> <!-- .section -->
<div class="section section-sections">
<div class="section-title">
<h3>Sections</h3>
</div> <!-- .section-title -->
<div class="section-content">
<ul class="section-list">
<% @root.sections.each do |section| %>
<li>
<h4>
<a href="<%= path_to_section(section) %>"><%= section.name %></a>
</h4>
<p><%= htmlize(section.short_description) %></p>
</li>
<% end %>
</ul>
</div> <!-- .section-content -->
</div> <!-- .section clearfix -->

View File

@ -0,0 +1,6 @@
if (!window.PDoc) window.PDoc = {};
PDoc.elements = {
<%= @root.map { |e, i|
"'#{e.full_name}': { 'name': '#{e.full_name}', 'type': '#{e.type}', 'path': '#{path_to(e)}' }" }.join(",\n")
%>
};

79
templates/html/layout.erb Normal file
View File

@ -0,0 +1,79 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Prototype API documentation | <%= @title %></title>
<meta name="generator" content="PDoc" />
<%= javascript_include_tag "prototype", "effects", "controls" %>
<%= javascript_include_tag "application", "code_highlighter" %>
<%= javascript_include_tag "item_index" %>
<%= stylesheet_link_tag "core", "api" %>
<script type="text/javascript">PDoc.pathPrefix = '<%= path_prefix %>';</script>
</head>
<body class="">
<div id="page">
<div id="masthead">
<div id="masthead_content">
<a href="http://prototypejs.org">
<h1 id="logo"><span class="replaced">Prototype JavaScript framework</span></h1>
</a>
</div> <!-- #masthead_content -->
</div> <!-- #masthead -->
<div id="menu" class="clearfix">
<div class="api-box">
<h2><a href="#" id="api_menu_button">Menu &darr;</a></h2>
<ul id="api_menu" class="menu-items" style="display: none">
<% @root.sections.each do |section| %>
<%= menu(section) %>
<% end %>
</ul>
</div> <!-- .api-box -->
<div class="search-box">
<form>
<label><span class="hidden">Search </span><input type="text" id="search" size="20" /></label>
</form>
<ul id="search_results" class="search-results menu-items" style="display:none"></ul>
</div> <!-- .search-box -->
</div> <!-- #menu -->
<div id="main" class="page-content">
<%= @content_for_layout %>
</div> <!-- #main -->
<div id="footer">
<div class="about clearfix">
<div class="copyright clearfix">
<div class="cc">
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://creativecommons.org/images/public/somerights20.png" /></a>
</div> <!-- .cc -->
<div class="copyright-about">
This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Creative Commons Attribution-Share Alike 3.0 Unported License</a>.
</div> <!-- .copyright-about -->
</div> <!-- .copyright -->
<div class="credits">
<p>Generated by <a href="http://pdoc.org">PDoc</a>. Uses <a href="http://famfamfam.com/lab/icons/silk/" title="famfamfam.com: Silk Icons">Silk Icons</a>.</p>
</div> <!-- .credits -->
</div> <!-- .about -->
</div> <!-- #footer -->
</div> <!-- #page -->
</body>
</html>

View File

@ -0,0 +1,171 @@
<% d = @doc_instance %>
<% @title = "#{d.full_name} #{d.type}" %>
<%= include "partials/breadcrumbs", :object => d %>
<h2 class="page-title <%= class_names_for(d) %>">
<span class="type"><%= d.type %> </span><%= d.full_name %>
</h2>
<% # Does it have a description? %>
<% if d.description && !d.description.empty? %>
<div class="section section-description">
<div class="section-title">
<h3>Description</h3>
</div> <!-- .section-title -->
<div class="section-content">
<%= htmlize(d.description) %>
</div> <!-- .section-content -->
</div> <!--.section -->
<% end %>
<% # Is it a CLASS? %>
<% if d.is_a?(Documentation::Klass) %>
<% if d.superklass %>
<div class="section section-superclass">
<div class="section-title">
<h3>Superclass</h3>
</div> <!-- .section-title -->
<div class="section-content">
<p><%= auto_link_code(d.superklass, false) %></p>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% unless d.subklasses.empty? %>
<div class="section section-subclasses">
<div class="section-title">
<h3>Subclasses</h3>
</div> <!-- .section-title -->
<div class="section-content">
<p><%= d.subklasses.map { |s| auto_link_code(s, false) }.join(', ') %></p>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% end %>
<% # Does it have MIXINS? %>
<% unless d.mixins.compact.empty? %>
<div class="section section-mixins">
<div class="section-title">
<h3>Includes</h3>
</div> <!-- .section-title -->
<div class="section-content">
<p><%= d.mixins.map { |m| auto_link_code(m, false) }.join(', ') %></p>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% # Are there related utilities? %>
<% unless d.related_utilities.empty? %>
<div class="section section-utilities">
<div class="section-title">
<h3>Related utilities</h3>
</div> <!-- .section-title -->
<div class="section-content">
<p><%= d.related_utilities.map { |u| auto_link_code(u, false) }.join(', ') %></p>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% # List its methods. %>
<% unless d.all_methods.empty? && d.mixins.empty? %>
<div class="section section-method-list">
<div class="section-title">
<h3>Methods</h3>
</div> <!-- .section-title -->
<div class="section-content">
<ul class="method-list">
<% d.all_methods.each do |method| %>
<li><%= auto_link_code(method, true, :class => class_names_for(method)) %></li>
<% end %>
</ul>
<% unless d.mixins.compact.empty? %>
<% d.mixins.each do |mixin| %>
<h4 class="inherited">Inherited from <%= auto_link(mixin) %></h4>
<ul class="method-list">
<% mixin.all_methods.each do |method| %>
<li><%= auto_link_code(method, true, :class => class_names_for(method)) %></li>
<% end %>
</ul>
<% end %>
<% end %>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% if d.is_a?(Documentation::Klass) && d.constructor %>
<div class="section section-constructor">
<div class="section-title">
<h3>Constructor</h3>
</div> <!-- .section-title -->
<div class="section-content">
<pre id="<%= dom_id(d.constructor) %>" class="syntax"><code><%= d.constructor.ebnf_expressions.join("\n").strip %></code></pre>
<% unless d.constructor.arguments.empty? %>
<ul class="argument-list">
<% d.constructor.arguments.each do |arg| %>
<li>
<code><%= arg.name %></code>
<% unless arg.types.empty? %>
(<%= arg.types.map { |t| auto_link_code(t, false) }.join(' | ') %>)
<% end %>
<%= ' &ndash; ' + inline_htmlize(arg.description) unless arg.description.empty? %>
</li>
<% end %>
</ul> <!-- .argument-list -->
<% end %>
<p><%= htmlize(d.constructor.description) %></p>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<%
types = {
:constants => "Constants",
:klass_methods => "Class methods",
:klass_properties => "Class properties",
:instance_methods => "Instance methods",
:instance_properties => "Instance properties"
}
%>
<% types.each do |method, title| %>
<% methods = d.send(method) %>
<% unless methods.empty? %>
<div class="section section-<%= method.to_s %>">
<div class="section-title">
<h3><%= title %></h3>
</div> <!-- .section-title -->
<div class="section-content">
<ul class="method-details-list">
<%= include "partials/short_description", :collection => methods %>
</ul>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% end %>

View File

@ -0,0 +1,6 @@
<ul class="breadcrumbs clearfix">
<li><a href="<%= path_prefix %>index.html">Home</a> &rarr;</li>
<% unless object.is_a?(Documentation::Section) %>
<li><a href="<%= path_to(object.section) %>"><%= object.section.name %></a> &rarr;</li>
<% end %>
</ul>

View File

@ -0,0 +1,48 @@
<li class="method-description">
<h4 id="<%= dom_id(object) %>"><%= object.name %>
<span class="method-permalink"><a href="<%= path_to(object) %>">#</a></span></h4>
<% if object.signature %>
<%= method_synopsis(object) %>
<% end %>
<% if object.is_a?(Documentation::Method) %>
<% unless object.arguments.empty? %>
<ul class="argument-list">
<% object.arguments.each do |arg| %>
<li>
<code><%= arg.name %></code>
<% unless arg.types.empty? %>
(<%= arg.types.map { |t| auto_link_code(t, false) }.join(' | ') %>)
<% end %>
<%= ' &ndash; ' + inline_htmlize(arg.description) unless arg.description.empty? %>
</li>
<% end %>
</ul> <!-- .argument-list -->
<% end %>
<% end %>
<%= htmlize(object.description) %>
<% # Does it have an alias? %>
<% unless object.aliases.empty? %>
<p class="alias aliases">Aliased as: <%= object.aliases.map { |a| auto_link_code(a, false) }.join(', ') %></p>
<% end %>
<% # Is it an alias of something else? %>
<% if object.alias? %>
<p class="alias alias-of">Alias of: <%= auto_link_code(object.alias_of, false) %></p>
<% end %>
<% # Is it related to something? %>
<% if object.is_a?(Documentation::Utility) && object.related_to %>
<p class="related-to">Related to:
<%= auto_link_code(object.related_to, false) %></p>
<% end %>
<% # Is it methodized? %>
<% if object.is_a?(Documentation::Method) && object.methodized? %>
<p class="note">This method can be called <em>either</em> as an instance method <em>or</em> as a generic method. If calling as a generic, pass the instance in as the first argument.</p>
<% end %>
</li>

View File

@ -0,0 +1,99 @@
<% @title = "#{@doc_instance.full_name}" %>
<% section = @doc_instance %>
<%= include "partials/breadcrumbs", :object => section %>
<h2 class="page-title">
<span class="type">Section </span><%= section.name %>
</h2>
<div class="section section-description">
<div class="section-title">
<h3>Description</h3>
</div> <!-- .section-title -->
<div class="section-content">
<%= htmlize(section.description) %>
</div> <!-- .section-content -->
</div> <!--.section -->
<% # Iterate over the items in this section. %>
<% utilities = section.utilities %>
<% unless utilities.empty? %>
<div class="section section-utilities">
<div class="section-title">
<h3>Utilities</h3>
</div> <!-- .section-title -->
<div class="section-content">
<ul class="method-list">
<% utilities.each do |utility| %>
<li><%= auto_link_code(utility) %></li>
<% end %>
</ul>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% namespaces = section.namespaces %>
<% unless namespaces.empty? %>
<div class="section section-namespaces">
<div class="section-title">
<h3>Namespaces</h3>
</div> <!-- .section-title -->
<div class="section-content">
<ul class="method-details-list">
<% namespaces.each do |namespace| %>
<li class="method-description">
<h4>
<a href="<%= path_to(namespace) %>"><%= namespace.full_name %></a>
</h4>
<% unless namespace.short_description.nil? %>
<p><%= htmlize(namespace.short_description) %></p>
<% end %>
</li>
<% end %>
</ul>
</div> <!-- .section-content -->
</div> <!-- .section -->
<% end %>
<% klasses = section.klasses %>
<% unless klasses.empty? %>
<div class="section section-classes">
<div class="section-title">
<h3>Classes</h3>
</div> <!-- .section-title -->
<div class="section-content">
<ul class="method-details-list">
<% klasses.each do |klass| %>
<li class="method-description">
<h4>
<a href="<%= path_to(klass) %>"><%= klass.full_name %></a>
</h4>
<% unless klass.short_description.nil? %>
<p><%= htmlize(klass.short_description) %></p>
<% end %>
</li>
<% end %>
</ul>
</div> <!-- .section-content -->
</div> <!--.section -->
<% end %>

View File

@ -0,0 +1,21 @@
<% d = @doc_instance %>
<% @title = "#{@doc_instance.full_name} utility" %>
<%= include "partials/breadcrumbs", :object => d %>
<h2 class="page-title">
<span class="type">utility </span><%= @doc_instance.name %>
</h2>
<div class="section">
<div class="section-title">
<h3>Description</h3>
</div> <!-- .section-title -->
<div class="section-content">
<ul class="method-details-list">
<%= include "partials/short_description", :collection => [@doc_instance] %>
</ul>
</div> <!-- .section-content -->
</div> <!-- .section -->

View File

@ -951,9 +951,6 @@ new Test.Unit.Runner({
// height/width could always be calculated if it's set to "auto" (Firefox)
this.assertNotNull($('auto_dimensions').getStyle('height'));
this.assertNotNull($('auto_dimensions').getStyle('width'));
// assert that using getStyle on an object without a style property returns null
this.assertNull(Element.Methods.getStyle({}, 'width'));
},
testElementGetOpacity: function() {
@ -1325,10 +1322,6 @@ new Test.Unit.Runner({
this.assertEnumEqual([0,0], offset);
this.assertIdentical(0, offset.top);
this.assertIdentical(0, offset.left);
var innerEl = new Element('div'), outerEl = new Element('div');
outerEl.appendChild(innerEl);
this.assertEnumEqual([0,0], innerEl.cumulativeOffset());
},
testViewportOffset: function() {

View File

@ -184,8 +184,6 @@ new Test.Unit.Runner({
span.observe("test:somethingHappened", observer);
this.assertEqual(span, span.stopObserving("test:somethingHappened"));
this.assertEqual(span, span.stopObserving("test:somethingOtherHappened", observer));
span.observe("test:somethingHappened", observer);
this.assertEqual(span, span.stopObserving());
this.assertEqual(span, span.stopObserving()); // assert it again, after there are no observers

View File

@ -41,10 +41,13 @@ new Test.Unit.Runner({
this.assertEqual(false, Prototype.Selector.match(element, '.non_existent'));
},
testFind: function() {
testFilter: function() {
var elements = document.getElementsByTagName('*'),
expression = '.test_class';
this.assertEqual('test_div_parent', Prototype.Selector.find(elements, expression).id);
this.assertEqual('test_div_child', Prototype.Selector.find(elements, expression, 1).id);
filtered = Prototype.Selector.filter(elements, '.test_class');
this.assert(Object.isArray(filtered));
this.assertEqual(2, filtered.length);
this.assertEqual('test_div_parent', filtered[0].id);
this.assertEqual('test_div_child', filtered[1].id);
}
});

View File

@ -6,10 +6,22 @@ Prototype.Selector = (function(engine) {
return engine.select(selector, scope || document, null, Element.extend);
}
function filter(elements, selector) {
var results = [], element, i = 0;
while (element = elements[i++]) {
if (engine.match(element, selector)) {
Element.extend(element);
results.push(element);
}
}
return results;
}
return {
engine: engine,
select: select,
match: engine.match
match: engine.match,
filter: filter
};
})(NW.Dom);

2
vendor/pdoc vendored

@ -1 +1 @@
Subproject commit 472a55dd0019acf034d4f72522915a5e9efd0a1a
Subproject commit 147250bd65eed627e32ca5a70b57fe4f7803ab4b