3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('node-core', function(Y) {
10 * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
13 * @submodule node-core
17 * The Node class provides a wrapper for manipulating DOM Nodes.
18 * Node properties can be accessed via the set/get methods.
19 * Use `Y.one()` to retrieve Node instances.
21 * <strong>NOTE:</strong> Node properties are accessed using
22 * the <code>set</code> and <code>get</code> methods.
26 * @param {DOMNode} node the DOM node to be mapped to the Node instance.
32 NODE_NAME = 'nodeName',
33 NODE_TYPE = 'nodeType',
34 OWNER_DOCUMENT = 'ownerDocument',
39 _slice = Array.prototype.slice,
43 Y_Node = function(node) {
44 if (!this.getDOMNode) { // support optional "new"
45 return new Y_Node(node);
48 if (typeof node == 'string') {
49 node = Y_Node._fromString(node);
51 return null; // NOTE: return
55 var uid = (node.nodeType !== 9) ? node.uniqueID : node[UID];
57 if (uid && Y_Node._instances[uid] && Y_Node._instances[uid]._node !== node) {
58 node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
61 uid = uid || Y.stamp(node);
62 if (!uid) { // stamp failed; likely IE non-HTMLElement
69 * The underlying DOM node bound to the Y.Node instance
75 this._stateProxy = node; // when augmented with Attribute
77 if (this._initPlugins) { // when augmented with Plugin.Host
82 // used with previous/next/ancestor tests
83 _wrapFn = function(fn) {
86 ret = (typeof fn == 'string') ?
88 return Y.Selector.test(n, fn);
100 Y_Node.DOM_EVENTS = {};
102 Y_Node._fromString = function(node) {
104 if (node.indexOf('doc') === 0) { // doc OR document
106 } else if (node.indexOf('win') === 0) { // win OR window
109 node = Y.Selector.query(node, null, true);
117 * The name of the component
121 Y_Node.NAME = 'node';
124 * The pattern used to identify ARIA attributes
126 Y_Node.re_aria = /^(?:role$|aria-)/;
128 Y_Node.SHOW_TRANSITION = 'fadeIn';
129 Y_Node.HIDE_TRANSITION = 'fadeOut';
132 * A list of Node instances that have been created
134 * @property _instances
138 Y_Node._instances = {};
141 * Retrieves the DOM node bound to a Node instance
145 * @param {Node | HTMLNode} node The Node instance or an HTMLNode
146 * @return {HTMLNode} The DOM node bound to the Node instance. If a DOM node is passed
147 * as the node argument, it is simply returned.
149 Y_Node.getDOMNode = function(node) {
151 return (node.nodeType) ? node : node._node || null;
157 * Checks Node return values and wraps DOM Nodes as Y.Node instances
158 * and DOM Collections / Arrays as Y.NodeList instances.
159 * Other return values just pass thru. If undefined is returned (e.g. no return)
160 * then the Node instance is returned for chainability.
164 * @param {any} node The Node instance or an HTMLNode
165 * @return {Node | NodeList | Any} Depends on what is returned from the DOM node.
167 Y_Node.scrubVal = function(val, node) {
168 if (val) { // only truthy values are risky
169 if (typeof val == 'object' || typeof val == 'function') { // safari nodeList === function
170 if (NODE_TYPE in val || Y_DOM.isWindow(val)) {// node || window
172 } else if ((val.item && !val._nodes) || // dom collection or Node instance
173 (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
177 } else if (typeof val === 'undefined') {
178 val = node; // for chaining
179 } else if (val === null) {
180 val = null; // IE: DOM null not the same as null
187 * Adds methods to the Y.Node prototype, routing through scrubVal.
191 * @param {String} name The name of the method to add
192 * @param {Function} fn The function that becomes the method
193 * @param {Object} context An optional context to call the method with
194 * (defaults to the Node instance)
195 * @return {any} Depends on what is returned from the DOM node.
197 Y_Node.addMethod = function(name, fn, context) {
198 if (name && fn && typeof fn == 'function') {
199 Y_Node.prototype[name] = function() {
200 var args = _slice.call(arguments),
204 if (args[0] && args[0]._node) {
205 args[0] = args[0]._node;
208 if (args[1] && args[1]._node) {
209 args[1] = args[1]._node;
211 args.unshift(node._node);
213 ret = fn.apply(node, args);
215 if (ret) { // scrub truthy
216 ret = Y_Node.scrubVal(ret, node);
219 (typeof ret != 'undefined') || (ret = node);
227 * Imports utility methods to be added as Y.Node methods.
228 * @method importMethod
231 * @param {Object} host The object that contains the method to import.
232 * @param {String} name The name of the method to import
233 * @param {String} altName An optional name to use in place of the host name
234 * @param {Object} context An optional context to call the method with
236 Y_Node.importMethod = function(host, name, altName) {
237 if (typeof name == 'string') {
238 altName = altName || name;
239 Y_Node.addMethod(altName, host[name], host);
241 Y.Array.each(name, function(n) {
242 Y_Node.importMethod(host, n);
248 * Retrieves a NodeList based on the given CSS selector.
251 * @param {string} selector The CSS selector to test against.
252 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
257 * Returns a single Node instance bound to the node or the
258 * first element matching the given selector. Returns null if no match found.
259 * <strong>Note:</strong> For chaining purposes you may want to
260 * use <code>Y.all</code>, which returns a NodeList when no match is found.
262 * @param {String | HTMLElement} node a node or Selector
263 * @return {Node | null} a Node instance or null if no match found.
268 * Returns a single Node instance bound to the node or the
269 * first element matching the given selector. Returns null if no match found.
270 * <strong>Note:</strong> For chaining purposes you may want to
271 * use <code>Y.all</code>, which returns a NodeList when no match is found.
274 * @param {String | HTMLElement} node a node or Selector
275 * @return {Node | null} a Node instance or null if no match found.
278 Y_Node.one = function(node) {
284 if (typeof node == 'string') {
285 node = Y_Node._fromString(node);
287 return null; // NOTE: return
289 } else if (node.getDOMNode) {
290 return node; // NOTE: return
293 if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc)
294 uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid;
295 instance = Y_Node._instances[uid]; // reuse exising instances
296 cachedNode = instance ? instance._node : null;
297 if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
298 instance = new Y_Node(node);
299 if (node.nodeType != 11) { // dont cache document fragment
300 Y_Node._instances[instance[UID]] = instance; // cache node
310 * The default setter for DOM properties
311 * Called with instance context (this === the Node instance)
312 * @method DEFAULT_SETTER
314 * @param {String} name The attribute/property being set
315 * @param {any} val The value to be set
316 * @return {any} The value
318 Y_Node.DEFAULT_SETTER = function(name, val) {
319 var node = this._stateProxy,
322 if (name.indexOf(DOT) > -1) {
324 name = name.split(DOT);
325 // only allow when defined on node
326 Y.Object.setValue(node, name, val);
327 } else if (typeof node[name] != 'undefined') { // pass thru DOM properties
335 * The default getter for DOM properties
336 * Called with instance context (this === the Node instance)
337 * @method DEFAULT_GETTER
339 * @param {String} name The attribute/property to look up
340 * @return {any} The current value
342 Y_Node.DEFAULT_GETTER = function(name) {
343 var node = this._stateProxy,
346 if (name.indexOf && name.indexOf(DOT) > -1) {
347 val = Y.Object.getValue(node, name.split(DOT));
348 } else if (typeof node[name] != 'undefined') { // pass thru from DOM
355 Y.mix(Y_Node.prototype, {
356 DATA_PREFIX: 'data-',
359 * The method called when outputting Node instances as strings
361 * @return {String} A string representation of the Node instance
363 toString: function() {
364 var str = this[UID] + ': not bound to a node',
366 attrs, id, className;
369 attrs = node.attributes;
370 id = (attrs && attrs.id) ? node.getAttribute('id') : null;
371 className = (attrs && attrs.className) ? node.getAttribute('className') : null;
372 str = node[NODE_NAME];
379 str += '.' + className.replace(' ', '.');
383 str += ' ' + this[UID];
389 * Returns an attribute value on the Node instance.
390 * Unless pre-configured (via `Node.ATTRS`), get hands
391 * off to the underlying DOM node. Only valid
392 * attributes/properties for the node will be queried.
394 * @param {String} attr The attribute
395 * @return {any} The current value of the attribute
397 get: function(attr) {
400 if (this._getAttr) { // use Attribute imple
401 val = this._getAttr(attr);
403 val = this._get(attr);
407 val = Y_Node.scrubVal(val, this);
408 } else if (val === null) {
409 val = null; // IE: DOM null is not true null (even though they ===)
415 * Helper method for get.
418 * @param {String} attr The attribute
419 * @return {any} The current value of the attribute
421 _get: function(attr) {
422 var attrConfig = Y_Node.ATTRS[attr],
425 if (attrConfig && attrConfig.getter) {
426 val = attrConfig.getter.call(this);
427 } else if (Y_Node.re_aria.test(attr)) {
428 val = this._node.getAttribute(attr, 2);
430 val = Y_Node.DEFAULT_GETTER.apply(this, arguments);
437 * Sets an attribute on the Node instance.
438 * Unless pre-configured (via Node.ATTRS), set hands
439 * off to the underlying DOM node. Only valid
440 * attributes/properties for the node will be set.
441 * To set custom attributes use setAttribute.
443 * @param {String} attr The attribute to be set.
444 * @param {any} val The value to set the attribute to.
447 set: function(attr, val) {
448 var attrConfig = Y_Node.ATTRS[attr];
450 if (this._setAttr) { // use Attribute imple
451 this._setAttr.apply(this, arguments);
452 } else { // use setters inline
453 if (attrConfig && attrConfig.setter) {
454 attrConfig.setter.call(this, val, attr);
455 } else if (Y_Node.re_aria.test(attr)) { // special case Aria
456 this._node.setAttribute(attr, val);
458 Y_Node.DEFAULT_SETTER.apply(this, arguments);
466 * Sets multiple attributes.
468 * @param {Object} attrMap an object of name/value pairs to set
471 setAttrs: function(attrMap) {
472 if (this._setAttrs) { // use Attribute imple
473 this._setAttrs(attrMap);
474 } else { // use setters inline
475 Y.Object.each(attrMap, function(v, n) {
484 * Returns an object containing the values for the requested attributes.
486 * @param {Array} attrs an array of attributes to get values
487 * @return {Object} An object with attribute name/value pairs.
489 getAttrs: function(attrs) {
491 if (this._getAttrs) { // use Attribute imple
492 this._getAttrs(attrs);
493 } else { // use setters inline
494 Y.Array.each(attrs, function(v, n) {
495 ret[v] = this.get(v);
503 * Compares nodes to determine if they match.
504 * Node instances can be compared to each other and/or HTMLElements.
506 * @param {HTMLElement | Node} refNode The reference node to compare to the node.
507 * @return {Boolean} True if the nodes match, false if they do not.
509 compareTo: function(refNode) {
510 var node = this._node;
512 if (refNode && refNode._node) {
513 refNode = refNode._node;
515 return node === refNode;
519 * Determines whether the node is appended to the document.
521 * @param {Node|HTMLElement} doc optional An optional document to check against.
522 * Defaults to current document.
523 * @return {Boolean} Whether or not this node is appended to the document.
525 inDoc: function(doc) {
526 var node = this._node;
527 doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
528 if (doc.documentElement) {
529 return Y_DOM.contains(doc.documentElement, node);
533 getById: function(id) {
534 var node = this._node,
535 ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]);
536 if (ret && Y_DOM.contains(node, ret)) {
545 * Returns the nearest ancestor that passes the test applied by supplied boolean method.
547 * @param {String | Function} fn A selector string or boolean method for testing elements.
548 * If a function is used, it receives the current node being tested as the only argument.
549 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
550 * @param {String | Function} stopFn optional A selector string or boolean
551 * method to indicate when the search should stop. The search bails when the function
552 * returns true or the selector matches.
553 * If a function is used, it receives the current node being tested as the only argument.
554 * @return {Node} The matching Node instance or null if not found
556 ancestor: function(fn, testSelf, stopFn) {
557 // testSelf is optional, check for stopFn as 2nd arg
558 if (arguments.length === 2 &&
559 (typeof testSelf == 'string' || typeof testSelf == 'function')) {
563 return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
567 * Returns the ancestors that pass the test applied by supplied boolean method.
569 * @param {String | Function} fn A selector string or boolean method for testing elements.
570 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
571 * If a function is used, it receives the current node being tested as the only argument.
572 * @return {NodeList} A NodeList instance containing the matching elements
574 ancestors: function(fn, testSelf, stopFn) {
575 if (arguments.length === 2 &&
576 (typeof testSelf == 'string' || typeof testSelf == 'function')) {
579 return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
583 * Returns the previous matching sibling.
584 * Returns the nearest element node sibling if no method provided.
586 * @param {String | Function} fn A selector or boolean method for testing elements.
587 * If a function is used, it receives the current node being tested as the only argument.
588 * @return {Node} Node instance or null if not found
590 previous: function(fn, all) {
591 return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
595 * Returns the next matching sibling.
596 * Returns the nearest element node sibling if no method provided.
598 * @param {String | Function} fn A selector or boolean method for testing elements.
599 * If a function is used, it receives the current node being tested as the only argument.
600 * @return {Node} Node instance or null if not found
602 next: function(fn, all) {
603 return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
607 * Returns all matching siblings.
608 * Returns all siblings if no method provided.
610 * @param {String | Function} fn A selector or boolean method for testing elements.
611 * If a function is used, it receives the current node being tested as the only argument.
612 * @return {NodeList} NodeList instance bound to found siblings
614 siblings: function(fn) {
615 return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn)));
619 * Retrieves a Node instance of nodes based on the given CSS selector.
622 * @param {string} selector The CSS selector to test against.
623 * @return {Node} A Node instance for the matching HTMLElement.
625 one: function(selector) {
626 return Y.one(Y.Selector.query(selector, this._node, true));
630 * Retrieves a NodeList based on the given CSS selector.
633 * @param {string} selector The CSS selector to test against.
634 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
636 all: function(selector) {
637 var nodelist = Y.all(Y.Selector.query(selector, this._node));
638 nodelist._query = selector;
639 nodelist._queryRoot = this._node;
643 // TODO: allow fn test
645 * Test if the supplied node matches the supplied selector.
648 * @param {string} selector The CSS selector to test against.
649 * @return {boolean} Whether or not the node matches the selector.
651 test: function(selector) {
652 return Y.Selector.test(this._node, selector);
656 * Removes the node from its parent.
657 * Shortcut for myNode.get('parentNode').removeChild(myNode);
659 * @param {Boolean} destroy whether or not to call destroy() on the node
664 remove: function(destroy) {
665 var node = this._node;
667 if (node && node.parentNode) {
668 node.parentNode.removeChild(node);
679 * Replace the node with the other node. This is a DOM update only
680 * and does not change the node bound to the Node instance.
681 * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
683 * @param {Node | HTMLNode} newNode Node to be inserted
687 replace: function(newNode) {
688 var node = this._node;
689 if (typeof newNode == 'string') {
690 newNode = Y_Node.create(newNode);
692 node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node);
697 * @method replaceChild
699 * @param {String | HTMLElement | Node} node Node to be inserted
700 * @param {HTMLElement | Node} refNode Node to be replaced
701 * @return {Node} The replaced node
703 replaceChild: function(node, refNode) {
704 if (typeof node == 'string') {
705 node = Y_DOM.create(node);
708 return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode)));
712 * Nulls internal node references, removes any plugins and event listeners
714 * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the
715 * node's subtree (default is false)
718 destroy: function(recursive) {
719 var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid',
722 this.purge(); // TODO: only remove events add via this Node
724 if (this.unplug) { // may not be a PluginHost
731 Y.NodeList.each(this.all('*'), function(node) {
732 instance = Y_Node._instances[node[UID]];
740 this._stateProxy = null;
742 delete Y_Node._instances[this._yuid];
746 * Invokes a method on the Node instance
748 * @param {String} method The name of the method to invoke
749 * @param {Any} a, b, c, etc. Arguments to invoke the method with.
750 * @return Whatever the underly method returns.
751 * DOM Nodes and Collections return values
752 * are converted to Node/NodeList instances.
755 invoke: function(method, a, b, c, d, e) {
756 var node = this._node,
767 ret = node[method](a, b, c, d, e);
768 return Y_Node.scrubVal(ret, this);
773 * @description Swap DOM locations with the given node.
774 * This does not change which DOM node each Node instance refers to.
775 * @param {Node} otherNode The node to swap with
778 swap: Y.config.doc.documentElement.swapNode ?
779 function(otherNode) {
780 this._node.swapNode(Y_Node.getDOMNode(otherNode));
782 function(otherNode) {
783 otherNode = Y_Node.getDOMNode(otherNode);
784 var node = this._node,
785 parent = otherNode.parentNode,
786 nextSibling = otherNode.nextSibling;
788 if (nextSibling === node) {
789 parent.insertBefore(node, otherNode);
790 } else if (otherNode === node.nextSibling) {
791 parent.insertBefore(otherNode, node);
793 node.parentNode.replaceChild(otherNode, node);
794 Y_DOM.addHTML(parent, node, nextSibling);
800 hasMethod: function(method) {
801 var node = this._node;
802 return !!(node && method in node &&
803 typeof node[method] != 'unknown' &&
804 (typeof node[method] == 'function' ||
805 String(node[method]).indexOf('function') === 1)); // IE reports as object, prepends space
808 isFragment: function() {
809 return (this.get('nodeType') === 11);
813 * Removes and destroys all of the nodes within the node.
818 this.get('childNodes').remove().destroy(true);
823 * Returns the DOM node bound to the Node instance
827 getDOMNode: function() {
835 * The NodeList module provides support for managing collections of Nodes.
837 * @submodule node-core
841 * The NodeList class provides a wrapper for manipulating DOM NodeLists.
842 * NodeList properties can be accessed via the set/get methods.
843 * Use Y.all() to retrieve NodeList instances.
849 var NodeList = function(nodes) {
853 if (typeof nodes === 'string') { // selector query
855 nodes = Y.Selector.query(nodes);
856 } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window
858 } else if (nodes._node) { // Y.Node
859 nodes = [nodes._node];
860 } else if (nodes[0] && nodes[0]._node) { // allow array of Y.Nodes
861 Y.Array.each(nodes, function(node) {
863 tmp.push(node._node);
867 } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes)
868 nodes = Y.Array(nodes, 0, true);
873 * The underlying array of DOM nodes bound to the Y.NodeList instance
877 this._nodes = nodes || [];
880 NodeList.NAME = 'NodeList';
883 * Retrieves the DOM nodes bound to a NodeList instance
884 * @method getDOMNodes
887 * @param {NodeList} nodelist The NodeList instance
888 * @return {Array} The array of DOM nodes bound to the NodeList
890 NodeList.getDOMNodes = function(nodelist) {
891 return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist;
894 NodeList.each = function(instance, fn, context) {
895 var nodes = instance._nodes;
896 if (nodes && nodes.length) {
897 Y.Array.each(nodes, fn, context || instance);
902 NodeList.addMethod = function(name, fn, context) {
904 NodeList.prototype[name] = function() {
908 Y.Array.each(this._nodes, function(node) {
909 var UID = (node.uniqueID && node.nodeType !== 9 ) ? 'uniqueID' : '_yuid',
910 instance = Y.Node._instances[node[UID]],
915 instance = NodeList._getTempNode(node);
917 ctx = context || instance;
918 result = fn.apply(ctx, args);
919 if (result !== undefined && result !== instance) {
920 ret[ret.length] = result;
924 // TODO: remove tmp pointer
925 return ret.length ? ret : this;
931 NodeList.importMethod = function(host, name, altName) {
932 if (typeof name === 'string') {
933 altName = altName || name;
934 NodeList.addMethod(name, host[name]);
936 Y.Array.each(name, function(n) {
937 NodeList.importMethod(host, n);
942 NodeList._getTempNode = function(node) {
943 var tmp = NodeList._tempNode;
945 tmp = Y.Node.create('<div></div>');
946 NodeList._tempNode = tmp;
950 tmp._stateProxy = node;
954 Y.mix(NodeList.prototype, {
955 _invoke: function(method, args, getter) {
956 var ret = (getter) ? [] : this;
958 this.each(function(node) {
959 var val = node[method].apply(node, args);
969 * Retrieves the Node instance at the given index.
972 * @param {Number} index The index of the target Node.
973 * @return {Node} The Node instance at the given index.
975 item: function(index) {
976 return Y.one((this._nodes || [])[index]);
980 * Applies the given function to each Node in the NodeList.
982 * @param {Function} fn The function to apply. It receives 3 arguments:
983 * the current node instance, the node's index, and the NodeList instance
984 * @param {Object} context optional An optional context to apply the function with
985 * Default context is the current Node instance
988 each: function(fn, context) {
990 Y.Array.each(this._nodes, function(node, index) {
992 return fn.call(context || node, node, index, instance);
997 batch: function(fn, context) {
1000 Y.Array.each(this._nodes, function(node, index) {
1001 var instance = Y.Node._instances[node[UID]];
1003 instance = NodeList._getTempNode(node);
1006 return fn.call(context || instance, instance, index, nodelist);
1012 * Executes the function once for each node until a true value is returned.
1014 * @param {Function} fn The function to apply. It receives 3 arguments:
1015 * the current node instance, the node's index, and the NodeList instance
1016 * @param {Object} context optional An optional context to execute the function from.
1017 * Default context is the current Node instance
1018 * @return {Boolean} Whether or not the function returned true for any node.
1020 some: function(fn, context) {
1021 var instance = this;
1022 return Y.Array.some(this._nodes, function(node, index) {
1024 context = context || node;
1025 return fn.call(context, node, index, instance);
1030 * Creates a documenFragment from the nodes bound to the NodeList instance
1032 * @return {Node} a Node instance bound to the documentFragment
1034 toFrag: function() {
1035 return Y.one(Y.DOM._nl2frag(this._nodes));
1039 * Returns the index of the node in the NodeList instance
1040 * or -1 if the node isn't found.
1042 * @param {Node | DOMNode} node the node to search for
1043 * @return {Int} the index of the node value or -1 if not found
1045 indexOf: function(node) {
1046 return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
1050 * Filters the NodeList instance down to only nodes matching the given selector.
1052 * @param {String} selector The selector to filter against
1053 * @return {NodeList} NodeList containing the updated collection
1056 filter: function(selector) {
1057 return Y.all(Y.Selector.filter(this._nodes, selector));
1062 * Creates a new NodeList containing all nodes at every n indices, where
1063 * remainder n % index equals r.
1064 * (zero-based index).
1066 * @param {Int} n The offset to use (return every nth node)
1067 * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
1068 * @return {NodeList} NodeList containing the updated collection
1070 modulus: function(n, r) {
1073 NodeList.each(this, function(node, i) {
1079 return Y.all(nodes);
1083 * Creates a new NodeList containing all nodes at odd indices
1084 * (zero-based index).
1086 * @return {NodeList} NodeList containing the updated collection
1089 return this.modulus(2, 1);
1093 * Creates a new NodeList containing all nodes at even indices
1094 * (zero-based index), including zero.
1096 * @return {NodeList} NodeList containing the updated collection
1099 return this.modulus(2);
1102 destructor: function() {
1106 * Reruns the initial query, when created using a selector query
1110 refresh: function() {
1112 nodes = this._nodes,
1113 query = this._query,
1114 root = this._queryRoot;
1118 if (nodes && nodes[0] && nodes[0].ownerDocument) {
1119 root = nodes[0].ownerDocument;
1123 this._nodes = Y.Selector.query(query, root);
1130 * Returns the current number of items in the NodeList.
1132 * @return {Int} The number of items in the NodeList.
1135 return this._nodes.length;
1139 * Determines if the instance is bound to any nodes
1141 * @return {Boolean} Whether or not the NodeList is bound to any nodes
1143 isEmpty: function() {
1144 return this._nodes.length < 1;
1147 toString: function() {
1149 errorMsg = this[UID] + ': not bound to any nodes',
1150 nodes = this._nodes,
1153 if (nodes && nodes[0]) {
1155 str += node[NODE_NAME];
1157 str += '#' + node.id;
1160 if (node.className) {
1161 str += '.' + node.className.replace(' ', '.');
1164 if (nodes.length > 1) {
1165 str += '...[' + nodes.length + ' items]';
1168 return str || errorMsg;
1172 * Returns the DOM node bound to the Node instance
1173 * @method getDOMNodes
1176 getDOMNodes: function() {
1181 NodeList.importMethod(Y.Node.prototype, [
1183 * Called on each Node instance. Nulls internal node references,
1184 * removes any plugins and event listeners
1186 * @param {Boolean} recursivePurge (optional) Whether or not to
1187 * remove listeners from the node's subtree (default is false)
1193 * Called on each Node instance. Removes and destroys all of the nodes
1202 * Called on each Node instance. Removes the node from its parent.
1203 * Shortcut for myNode.get('parentNode').removeChild(myNode);
1205 * @param {Boolean} destroy whether or not to call destroy() on the node
1213 * Called on each Node instance. Sets an attribute on the Node instance.
1214 * Unless pre-configured (via Node.ATTRS), set hands
1215 * off to the underlying DOM node. Only valid
1216 * attributes/properties for the node will be set.
1217 * To set custom attributes use setAttribute.
1219 * @param {String} attr The attribute to be set.
1220 * @param {any} val The value to set the attribute to.
1227 // one-off implementation to convert array of Nodes to NodeList
1228 // e.g. Y.all('input').get('parentNode');
1230 /** Called on each Node instance
1234 NodeList.prototype.get = function(attr) {
1236 nodes = this._nodes,
1238 getTemp = NodeList._getTempNode,
1243 instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
1244 val = instance._get(attr);
1245 if (val && val.nodeType) {
1250 Y.Array.each(nodes, function(node) {
1251 instance = Y.Node._instances[node._yuid];
1254 instance = getTemp(node);
1257 val = instance._get(attr);
1258 if (!isNodeList) { // convert array of Nodes to NodeList
1259 val = Y.Node.scrubVal(val, instance);
1265 return (isNodeList) ? Y.all(ret) : ret;
1268 Y.NodeList = NodeList;
1270 Y.all = function(nodes) {
1271 return new NodeList(nodes);
1277 * @submodule node-core
1280 var Y_NodeList = Y.NodeList,
1281 ArrayProto = Array.prototype,
1283 /** Returns a new NodeList combining the given NodeList(s)
1286 * @param {NodeList | Array} valueN Arrays/NodeLists and/or values to
1287 * concatenate to the resulting NodeList
1288 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
1291 /** Removes the last from the NodeList and returns it.
1294 * @return {Node} The last item in the NodeList.
1297 /** Adds the given Node(s) to the end of the NodeList.
1300 * @param {Node | DOMNode} nodes One or more nodes to add to the end of the NodeList.
1303 /** Removes the first item from the NodeList and returns it.
1306 * @return {Node} The first item in the NodeList.
1309 /** Returns a new NodeList comprising the Nodes in the given range.
1312 * @param {Number} begin Zero-based index at which to begin extraction.
1313 As a negative index, start indicates an offset from the end of the sequence. slice(-2) extracts the second-to-last element and the last element in the sequence.
1314 * @param {Number} end Zero-based index at which to end extraction. slice extracts up to but not including end.
1315 slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3).
1316 As a negative index, end indicates an offset from the end of the sequence. slice(2,-1) extracts the third element through the second-to-last element in the sequence.
1317 If end is omitted, slice extracts to the end of the sequence.
1318 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
1321 /** Changes the content of the NodeList, adding new elements while removing old elements.
1324 * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
1325 * @param {Number} howMany An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed. In this case, you should specify at least one new element. If no howMany parameter is specified (second syntax above, which is a SpiderMonkey extension), all elements after index are removed.
1326 * {Node | DOMNode| element1, ..., elementN
1327 The elements to add to the array. If you don't specify any elements, splice simply removes elements from the array.
1328 * @return {NodeList} The element(s) removed.
1331 /** Adds the given Node(s) to the beginning of the NodeList.
1334 * @param {Node | DOMNode} nodes One or more nodes to add to the NodeList.
1340 Y.Object.each(ArrayMethods, function(returnNodeList, name) {
1341 Y_NodeList.prototype[name] = function() {
1347 while (typeof (arg = arguments[i++]) != 'undefined') { // use DOM nodes/nodeLists
1348 args.push(arg._node || arg._nodes || arg);
1351 ret = ArrayProto[name].apply(this._nodes, args);
1353 if (returnNodeList) {
1356 ret = Y.Node.scrubVal(ret);
1364 * @submodule node-core
1369 * Passes through to DOM method.
1371 * @method removeChild
1372 * @param {HTMLElement | Node} node Node to be removed
1373 * @return {Node} The removed node
1378 * Passes through to DOM method.
1379 * @method hasChildNodes
1380 * @return {Boolean} Whether or not the node has any childNodes
1385 * Passes through to DOM method.
1387 * @param {Boolean} deep Whether or not to perform a deep clone, which includes
1388 * subtree and attributes
1389 * @return {Node} The clone
1394 * Passes through to DOM method.
1395 * @method hasAttribute
1396 * @param {String} attribute The attribute to test for
1397 * @return {Boolean} Whether or not the attribute is present
1402 * Passes through to DOM method.
1403 * @method scrollIntoView
1409 * Passes through to DOM method.
1410 * @method getElementsByTagName
1411 * @param {String} tagName The tagName to collect
1412 * @return {NodeList} A NodeList representing the HTMLCollection
1414 'getElementsByTagName',
1417 * Passes through to DOM method.
1424 * Passes through to DOM method.
1431 * Passes through to DOM method.
1432 * Only valid on FORM elements
1439 * Passes through to DOM method.
1440 * Only valid on FORM elements
1447 * Passes through to DOM method.
1454 * Passes through to DOM method.
1455 * Only valid on TABLE elements
1456 * @method createCaption
1461 ], function(method) {
1462 Y.Node.prototype[method] = function(arg1, arg2, arg3) {
1463 var ret = this.invoke(method, arg1, arg2, arg3);
1469 * Passes through to DOM method.
1470 * @method removeAttribute
1471 * @param {String} attribute The attribute to be removed
1474 // one-off implementation due to IE returning boolean, breaking chaining
1475 Y.Node.prototype.removeAttribute = function(attr) {
1476 var node = this._node;
1478 node.removeAttribute(attr, 0); // comma zero for IE < 8 to force case-insensitive
1484 Y.Node.importMethod(Y.DOM, [
1486 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
1488 * @param {Node | HTMLElement} needle The possible node or descendent
1489 * @return {Boolean} Whether or not this node is the needle its ancestor
1493 * Allows setting attributes on DOM nodes, normalizing in some cases.
1494 * This passes through to the DOM node, allowing for custom attributes.
1495 * @method setAttribute
1498 * @param {string} name The attribute name
1499 * @param {string} value The value to set
1503 * Allows getting attributes on DOM nodes, normalizing in some cases.
1504 * This passes through to the DOM node, allowing for custom attributes.
1505 * @method getAttribute
1507 * @param {string} name The attribute name
1508 * @return {string} The attribute value
1513 * Wraps the given HTML around the node.
1515 * @param {String} html The markup to wrap around the node.
1522 * Removes the node's parent node.
1529 * Applies a unique ID to the node if none exists
1530 * @method generateID
1531 * @return {String} The existing or generated ID
1536 Y.NodeList.importMethod(Y.Node.prototype, [
1538 * Allows getting attributes on DOM nodes, normalizing in some cases.
1539 * This passes through to the DOM node, allowing for custom attributes.
1540 * @method getAttribute
1543 * @param {string} name The attribute name
1544 * @return {string} The attribute value
1549 * Allows setting attributes on DOM nodes, normalizing in some cases.
1550 * This passes through to the DOM node, allowing for custom attributes.
1551 * @method setAttribute
1555 * @param {string} name The attribute name
1556 * @param {string} value The value to set
1561 * Allows for removing attributes on DOM nodes.
1562 * This passes through to the DOM node, allowing for custom attributes.
1563 * @method removeAttribute
1566 * @param {string} name The attribute to remove
1570 * Removes the parent node from node in the list.
1576 * Wraps the given HTML around each node.
1578 * @param {String} html The markup to wrap around the node.
1584 * Applies a unique ID to each node if none exists
1585 * @method generateID
1586 * @return {String} The existing or generated ID
1592 }, '3.5.1' ,{requires:['dom-core', 'selector']});