From f4ea4c6ef7f48dca963e54fc6203350b2d233b97 Mon Sep 17 00:00:00 2001 From: Samuel Lebeau Date: Tue, 1 Sep 2009 15:59:47 +0200 Subject: [PATCH 01/24] Rewrite `String#camelize` using `String#replace` with a replacement function [#297 state:resolved] --- src/lang/string.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/lang/string.js b/src/lang/string.js index e141180..16935a7 100644 --- a/src/lang/string.js +++ b/src/lang/string.js @@ -311,17 +311,9 @@ Object.extend(String.prototype, (function() { * // -> 'MozBinding' **/ function camelize() { - var parts = this.split('-'), len = parts.length; - if (len == 1) return parts[0]; - - var camelized = this.charAt(0) == '-' - ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) - : parts[0]; - - for (var i = 1; i < len; i++) - camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); - - return camelized; + return this.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ''; + }); } /** From 3b525f194d0e573923c7d9ad1cbd0cf63a849954 Mon Sep 17 00:00:00 2001 From: Juriy Zaytsev Date: Mon, 28 Sep 2009 19:21:37 -0400 Subject: [PATCH 02/24] String#startsWith, String#endsWith performance optimization [#808 state:resolved] --- CHANGELOG | 4 ++++ src/lang/string.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index f5df87f..8934083 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +* 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) diff --git a/src/lang/string.js b/src/lang/string.js index 16935a7..3c7f23f 100644 --- a/src/lang/string.js +++ b/src/lang/string.js @@ -429,7 +429,7 @@ Object.extend(String.prototype, (function() { * Checks if the string starts with `substring`. **/ function startsWith(pattern) { - return this.indexOf(pattern) === 0; + return !this.lastIndexOf(pattern,0); } /** @@ -439,7 +439,7 @@ Object.extend(String.prototype, (function() { **/ function endsWith(pattern) { var d = this.length - pattern.length; - return d >= 0 && this.lastIndexOf(pattern) === d; + return d >= 0 && this.indexOf(pattern,d) === d; } /** From d2874c02940685b4257b88a7cb478d776f89bf70 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Thu, 8 Oct 2009 14:46:14 +0100 Subject: [PATCH 03/24] doc: Merged old docs for Element.addMethods. --- src/dom/dom.js | 120 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 117 insertions(+), 3 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 3fae168..ac787e4 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -1758,12 +1758,126 @@ Element.hasAttribute = function(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. * - * Takes a hash of methods and makes them available as methods of extended - * elements and of the `Element` object. + * `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`). * - * The second usage form is for adding methods only to specific tag names. + * You define the methods in a hash that you provide to `Element.addMethods`. + * Here's an example adding two methods: * + * 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 + *

Some content...

+ * + * ...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('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; From 7d073ad56a766c71fa6349d28db060ac82312d33 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 13:35:38 +0100 Subject: [PATCH 04/24] doc: Merged/updated old docs for Element overview --- src/dom/dom.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/dom/dom.js b/src/dom/dom.js index ac787e4..b5f7765 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -60,6 +60,32 @@ if (!Node.ELEMENT_NODE) { /** section: DOM * class Element + * + * The `Element` object provides a variety of powerful DOM methods for + * interacting with DOM elements — 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. **/ /** From d6d3ab1fefb30b03b8576864ce975401547eb4c3 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 13:36:14 +0100 Subject: [PATCH 05/24] doc: Merged/updated old docs for Element constructor --- src/dom/dom.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index b5f7765..4d44a83 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -90,11 +90,25 @@ if (!Node.ELEMENT_NODE) { /** * new Element(tagName[, attributes]) - * - tagName (String): The name of the HTML element to create. - * - attributes (Object): A list of attribute/value pairs to set on the - * element. + * - tagName (String): The name of the HTML element to create. + * - attributes (Object): An optional group of attribute/value pairs to set on + * the element. * - * Creates an HTML element with `tagName` as the tag name. + * 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"); **/ (function(global) { From 7df62ce864a1cb5e2fd1a5cd48f8e98a2a2bda45 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 13:56:39 +0100 Subject: [PATCH 06/24] doc: Merged/updated old docs for Element.extend --- src/dom/dom.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 4d44a83..bc047dc 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -1703,11 +1703,26 @@ Object.extend(Element, Element.Methods); /** * Element.extend(element) -> Element * - * 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`. + * 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`. **/ Element.extend = (function() { From 77832408bdd6a8ce6e82eef9199ee031c6fbfecc Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 14:14:14 +0100 Subject: [PATCH 07/24] doc: Merged/updated old docs for Element.addClassName --- src/dom/dom.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index bc047dc..9870c52 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -756,8 +756,24 @@ Element.Methods = { /** * Element.addClassName(@element, className) -> Element + * - className (String): The class name to add. * - * Adds a CSS class to `element`. + * Adds the given CSS class to `element`. + * + * ##### Example + * + * Assuming this HTML: + * + * language: html + *
+ * + * Then: + * + * $('mutsu').className; + * // -> 'apple fruit' + * $('mutsu').addClassName('food'); + * $('mutsu').className; + * // -> 'apple fruit food' **/ addClassName: function(element, className) { if (!(element = $(element))) return; From 402a2d408e8d7946f95e0652d9357b38e61ac2f8 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 14:31:02 +0100 Subject: [PATCH 08/24] doc: Merged/updated old docs for Element.adjacent --- src/dom/dom.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 9870c52..3b0f8d8 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -646,7 +646,30 @@ Element.Methods = { * - selector (String): A CSS selector. * * Finds all siblings of the current element that match the given - * selector(s). + * 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 + *
    + *
  • New York
  • + *
  • London
  • + *
  • Chicago
  • + * + *
  • Los Angeles
  • + *
  • Austin
  • + *
+ * + * Then: + * + * $('nyc').adjacent('li.us'); + * // -> [li#chi, li#la, li#aus] + * $('nyc').adjacent('li.uk', 'li.jp'); + * // -> [li#lon, li#tok] **/ adjacent: function(element) { var args = Array.prototype.slice.call(arguments, 1); From e3c89c08c650e69cce1e31da93f58a52b3568f03 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 14:48:47 +0100 Subject: [PATCH 09/24] doc: Merged/updated old docs for Element.ancestors. --- src/dom/dom.js | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 3b0f8d8..050562e 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -470,8 +470,34 @@ Element.Methods = { /** * Element.ancestors(@element) -> [Element...] * - * Collects all of `element`'s ancestors and returns them as an array of - * elements. + * 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 + * + * [...] + * + *
+ *
+ *
+ *
+ * + * + * + * Then: + * + * $('kid').ancestors(); + * // -> [div#father, body, html] **/ ancestors: function(element) { return Element.recursivelyCollect(element, 'parentNode'); From 892eb9d6b3b30a26c9ea4ad1d640c6b8792fa2a9 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 15:34:39 +0100 Subject: [PATCH 10/24] doc: Merged/updated old docs for Element.childElements / Element.immediateDescendants. Made immediateDescendants an alias of childElements rather than vice-versa as the latter is depreceated. --- src/dom/dom.js | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 050562e..37bc839 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -527,11 +527,10 @@ Element.Methods = { return $(element); }, - /** + /** deprecated, alias of: Element.childElements * Element.immediateDescendants(@element) -> [Element...] * - * Collects all of `element`'s immediate descendants (i.e., children) and - * returns them as an array of elements. + * **This method is deprecated, please see [[Element.childElements]]**. **/ immediateDescendants: function(element) { if (!(element = $(element).firstChild)) return []; @@ -1302,8 +1301,40 @@ 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 + *
+ * Some text in a text node + *
+ *
+ *
+ *
+ *
+ * + * Then: + * + * $('australopithecus').childElements(); + * // -> [div#homo-erectus] + * + * $('homo-erectus').childElements(); + * // -> [div#homo-neanderthalensis, div#homo-sapiens] + * + * $('homo-sapiens').childElements(); + * // -> [] **/ childElements: Element.Methods.immediateDescendants }); From 1e29e3c6c924db6d9c0b525e11a96c1f71bbab60 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 15:49:54 +0100 Subject: [PATCH 11/24] doc: Merged/updated old docs for Element.classNames, mostly by marking it deprecated. --- src/dom/dom.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 37bc839..813a5ae 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -780,11 +780,15 @@ 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); From 9ce1ea06b5e51dedbdb92f40b511f06e6fde131c Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 15:58:32 +0100 Subject: [PATCH 12/24] doc: Merged/updated old docs for Element.cleanWhitespace. --- src/dom/dom.js | 39 ++++++++++++++++++++++++++++++++++++++- vendor/pdoc | 2 +- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 813a5ae..48d255f 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -860,7 +860,44 @@ Element.Methods = { /** * Element.cleanWhitespace(@element) -> Element * - * Removes whitespace-only text node children from `element`. + * Removes all of `element`'s child text nodes that contain *only* + * whitespace. Returns `element`. + * + * This can be very useful when using standard methods 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 + *
    + *
  • Mutsu
  • + *
  • McIntosh
  • + *
  • Ida Red
  • + *
+ * + * 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' **/ cleanWhitespace: function(element) { element = $(element); diff --git a/vendor/pdoc b/vendor/pdoc index 147250b..fa11874 160000 --- a/vendor/pdoc +++ b/vendor/pdoc @@ -1 +1 @@ -Subproject commit 147250bd65eed627e32ca5a70b57fe4f7803ab4b +Subproject commit fa1187431a71d43c7b292feecd2dc5a61a6f630b From c448b38f7b955f26153e6925978678ca0db26057 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 16:55:42 +0100 Subject: [PATCH 13/24] doc: Merged/updated old docs for Element.clonePosition. --- src/dom/dom.js | 59 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 48d255f..ec3631c 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -1286,15 +1286,60 @@ 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 `element` as - * defined by `options`. + * 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. * - * 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`. + * 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 + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameDefaultDescription
setLefttrueClones source's left CSS property onto element.
setToptrueClones source's top CSS property onto element.
setWidthtrueClones source's width onto element.
setHeighttrueClones source's width onto element.
offsetLeft0Number by which to offset element's left CSS property.
offsetTop0Number by which to offset element's top CSS property.
**/ clonePosition: function(element, source) { var options = Object.extend({ From f1f6fca60b636339a0356ad00b34ef763d7eae55 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 17:01:40 +0100 Subject: [PATCH 14/24] doc: Clarified units in Element.cumulativeOffset and added example. --- src/dom/dom.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index ec3631c..6806c52 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -1124,10 +1124,24 @@ Element.Methods = { * Element.cumulativeOffset(@element) -> Array * * Returns the offsets of `element` from the top left corner of the - * document. + * document, in pixels. * * 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; From adf80ad1b34eb48d006410aa97ff838818e83146 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 17:10:13 +0100 Subject: [PATCH 15/24] doc: Merged/updated old docs for Element.cumulativeScrollOffset, clarified units, added example. --- src/dom/dom.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 6806c52..84c144d 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -1232,11 +1232,25 @@ Element.Methods = { /** * Element.cumulativeScrollOffset(@element) -> Array * - * Calculates the cumulative scroll offset of an element in nested - * scrolling containers. + * Calculates the cumulative scroll offset (in pixels) 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; From d10aad7bfad2699e87d9bac3a8df48e71ea4dd20 Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 17:15:15 +0100 Subject: [PATCH 16/24] doc: Merged/updated old docs for Element.descendantOf. --- src/dom/dom.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/dom/dom.js b/src/dom/dom.js index 84c144d..d68b232 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -922,8 +922,28 @@ 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 + *
+ *
+ *
+ *
+ *
+ * + * Then: + * + * $('homo-sapiens').descendantOf('australopithecus'); + * // -> true + * + * $('homo-erectus').descendantOf('homo-sapiens'); + * // -> false **/ descendantOf: function(element, ancestor) { element = $(element), ancestor = $(ancestor); From 859197ca8b65602a4d4766d0c78c6f45742794ce Mon Sep 17 00:00:00 2001 From: tjcrowder Date: Fri, 9 Oct 2009 17:20:39 +0100 Subject: [PATCH 17/24] doc: Merged/updated old docs for Element.descendants. --- src/dom/dom.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index d68b232..bbf72a7 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -506,8 +506,10 @@ Element.Methods = { /** * Element.descendants(@element) -> [Element...] * - * Collects all of element's descendants and returns them as an array of - * elements. + * 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. **/ descendants: function(element) { return Element.select(element, "*"); From 067a0ec364a8722d87ff7676f57c3a2a1883a1cc Mon Sep 17 00:00:00 2001 From: Tobie Langel Date: Wed, 21 Oct 2009 16:35:43 +0200 Subject: [PATCH 18/24] Update to latest PDoc release. --- vendor/pdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/pdoc b/vendor/pdoc index fa11874..147250b 160000 --- a/vendor/pdoc +++ b/vendor/pdoc @@ -1 +1 @@ -Subproject commit fa1187431a71d43c7b292feecd2dc5a61a6f630b +Subproject commit 147250bd65eed627e32ca5a70b57fe4f7803ab4b From 35ed99ba2e49a207afbb038afd67b20778b61995 Mon Sep 17 00:00:00 2001 From: Tobie Langel Date: Wed, 21 Oct 2009 17:00:17 +0200 Subject: [PATCH 19/24] doc: nitpicking. --- src/dom/dom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index bbf72a7..95fdd6a 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -865,7 +865,7 @@ Element.Methods = { * Removes all of `element`'s child text nodes that contain *only* * whitespace. Returns `element`. * - * This can be very useful when using standard methods like `nextSibling`, + * 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 From 8783065b8e1fc061dfa4cc39569d6fa82e5e6ff5 Mon Sep 17 00:00:00 2001 From: Tobie Langel Date: Wed, 21 Oct 2009 17:24:09 +0200 Subject: [PATCH 20/24] Cosmetic rewrite of String#startsWith and String#endsWith with performance-related comments. --- src/lang/string.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lang/string.js b/src/lang/string.js index 3c7f23f..c22885e 100644 --- a/src/lang/string.js +++ b/src/lang/string.js @@ -429,7 +429,9 @@ Object.extend(String.prototype, (function() { * Checks if the string starts with `substring`. **/ function startsWith(pattern) { - return !this.lastIndexOf(pattern,0); + // Using `lastIndexOf` instead of `indexOf` because execution time doesn't + // depend on string length. + return this.lastIndexOf(pattern, 0) === 0; } /** @@ -439,7 +441,9 @@ Object.extend(String.prototype, (function() { **/ function endsWith(pattern) { var d = this.length - pattern.length; - return d >= 0 && this.indexOf(pattern,d) === d; + // Using `indexOf` instead of `lastIndexOf` because execution time doesn't + // depend on string length. + return d >= 0 && this.indexOf(pattern, d) === d; } /** From f9c680a9bad9f127e1336c85aa9564ba2d1db159 Mon Sep 17 00:00:00 2001 From: Tobie Langel Date: Wed, 21 Oct 2009 17:58:09 +0200 Subject: [PATCH 21/24] More nitpicking. --- src/lang/string.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/string.js b/src/lang/string.js index c22885e..26c3cf2 100644 --- a/src/lang/string.js +++ b/src/lang/string.js @@ -429,8 +429,8 @@ Object.extend(String.prototype, (function() { * Checks if the string starts with `substring`. **/ function startsWith(pattern) { - // Using `lastIndexOf` instead of `indexOf` because execution time doesn't - // depend on string length. + // 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; } @@ -441,8 +441,8 @@ Object.extend(String.prototype, (function() { **/ function endsWith(pattern) { var d = this.length - pattern.length; - // Using `indexOf` instead of `lastIndexOf` because execution time doesn't - // depend on string 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; } From 2d3e4232303d81fc1b13e6347b299b43599200fa Mon Sep 17 00:00:00 2001 From: Yaffle Date: Tue, 20 Oct 2009 15:49:54 +0600 Subject: [PATCH 22/24] Add missing semicolons. [#837 state:resolved] --- src/dom/dom.js | 2 +- src/dom/selector.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dom/dom.js b/src/dom/dom.js index 95fdd6a..0f0e247 100644 --- a/src/dom/dom.js +++ b/src/dom/dom.js @@ -1930,7 +1930,7 @@ Object.extend(Element, Element.Methods); div = null; -})(document.createElement('div')) +})(document.createElement('div')); /** * Element.extend(element) -> Element diff --git a/src/dom/selector.js b/src/dom/selector.js index 947fd89..7692a62 100644 --- a/src/dom/selector.js +++ b/src/dom/selector.js @@ -309,7 +309,7 @@ Object.extend(Selector, { while (e && le != e && (/\S/).test(e)) { le = e; for (var i = 0; i Date: Thu, 22 Oct 2009 17:45:26 +0200 Subject: [PATCH 23/24] Make Event.stopObserving return element in all cases. [#810 state:resolved] --- CHANGELOG | 2 ++ src/dom/event.js | 43 ++++++++++++++++------------------------- test/unit/event_test.js | 2 ++ 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8934083..2d4e595 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* 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) diff --git a/src/dom/event.js b/src/dom/event.js index 7980c52..0f35d65 100644 --- a/src/dom/event.js +++ b/src/dom/event.js @@ -617,44 +617,34 @@ function stopObserving(element, eventName, handler) { element = $(element); - var registry = Element.retrieve(element, 'prototype_event_registry'); + var registry = Element.retrieve(element, 'prototype_event_registry') + if (!registry) return element; - 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. + if (!eventName) { + // We stop observing all events. + // e.g.: $(element).stopObserving(); registry.each( function(pair) { - var eventName = pair.key, responders = pair.value; - - responders.each( function(r) { - Element.stopObserving(element, eventName, r.handler); - }); + var eventName = pair.key; + stopObserving(element, eventName); }); return element; } var responders = registry.get(eventName); + if (!responders) return element; - // Fail gracefully if there are no responders assigned. - if (!responders) return; + 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; + } 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) @@ -665,6 +655,7 @@ } } else { // Ordinary event. + var actualEventName = _getDOMEventName(eventName); if (element.removeEventListener) element.removeEventListener(actualEventName, responder, false); else diff --git a/test/unit/event_test.js b/test/unit/event_test.js index fc6df25..3e3416c 100644 --- a/test/unit/event_test.js +++ b/test/unit/event_test.js @@ -184,6 +184,8 @@ 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 From af89847a4ff73715058fc453a20eae5358c3470b Mon Sep 17 00:00:00 2001 From: Tobie Langel Date: Thu, 22 Oct 2009 17:45:26 +0200 Subject: [PATCH 24/24] Make Event.stopObserving return element in all cases. [#810 state:resolved] --- src/dom/event.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dom/event.js b/src/dom/event.js index 0f35d65..0d86e11 100644 --- a/src/dom/event.js +++ b/src/dom/event.js @@ -617,7 +617,7 @@ function stopObserving(element, eventName, handler) { element = $(element); - var registry = Element.retrieve(element, 'prototype_event_registry') + var registry = Element.retrieve(element, 'prototype_event_registry'); if (!registry) return element; if (!eventName) {