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);
223 Y.log('unable to add method: ' + name, 'warn', 'Node');
228 * Imports utility methods to be added as Y.Node methods.
229 * @method importMethod
232 * @param {Object} host The object that contains the method to import.
233 * @param {String} name The name of the method to import
234 * @param {String} altName An optional name to use in place of the host name
235 * @param {Object} context An optional context to call the method with
237 Y_Node.importMethod = function(host, name, altName) {
238 if (typeof name == 'string') {
239 altName = altName || name;
240 Y_Node.addMethod(altName, host[name], host);
242 Y.Array.each(name, function(n) {
243 Y_Node.importMethod(host, n);
249 * Retrieves a NodeList based on the given CSS selector.
252 * @param {string} selector The CSS selector to test against.
253 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
258 * Returns a single Node instance bound to the node or the
259 * first element matching the given selector. Returns null if no match found.
260 * <strong>Note:</strong> For chaining purposes you may want to
261 * use <code>Y.all</code>, which returns a NodeList when no match is found.
263 * @param {String | HTMLElement} node a node or Selector
264 * @return {Node | null} a Node instance or null if no match found.
269 * Returns a single Node instance bound to the node or the
270 * first element matching the given selector. Returns null if no match found.
271 * <strong>Note:</strong> For chaining purposes you may want to
272 * use <code>Y.all</code>, which returns a NodeList when no match is found.
275 * @param {String | HTMLElement} node a node or Selector
276 * @return {Node | null} a Node instance or null if no match found.
279 Y_Node.one = function(node) {
285 if (typeof node == 'string') {
286 node = Y_Node._fromString(node);
288 return null; // NOTE: return
290 } else if (node.getDOMNode) {
291 return node; // NOTE: return
294 if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc)
295 uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid;
296 instance = Y_Node._instances[uid]; // reuse exising instances
297 cachedNode = instance ? instance._node : null;
298 if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
299 instance = new Y_Node(node);
300 if (node.nodeType != 11) { // dont cache document fragment
301 Y_Node._instances[instance[UID]] = instance; // cache node
311 * The default setter for DOM properties
312 * Called with instance context (this === the Node instance)
313 * @method DEFAULT_SETTER
315 * @param {String} name The attribute/property being set
316 * @param {any} val The value to be set
317 * @return {any} The value
319 Y_Node.DEFAULT_SETTER = function(name, val) {
320 var node = this._stateProxy,
323 if (name.indexOf(DOT) > -1) {
325 name = name.split(DOT);
326 // only allow when defined on node
327 Y.Object.setValue(node, name, val);
328 } else if (typeof node[name] != 'undefined') { // pass thru DOM properties
336 * The default getter for DOM properties
337 * Called with instance context (this === the Node instance)
338 * @method DEFAULT_GETTER
340 * @param {String} name The attribute/property to look up
341 * @return {any} The current value
343 Y_Node.DEFAULT_GETTER = function(name) {
344 var node = this._stateProxy,
347 if (name.indexOf && name.indexOf(DOT) > -1) {
348 val = Y.Object.getValue(node, name.split(DOT));
349 } else if (typeof node[name] != 'undefined') { // pass thru from DOM
356 Y.mix(Y_Node.prototype, {
357 DATA_PREFIX: 'data-',
360 * The method called when outputting Node instances as strings
362 * @return {String} A string representation of the Node instance
364 toString: function() {
365 var str = this[UID] + ': not bound to a node',
367 attrs, id, className;
370 attrs = node.attributes;
371 id = (attrs && attrs.id) ? node.getAttribute('id') : null;
372 className = (attrs && attrs.className) ? node.getAttribute('className') : null;
373 str = node[NODE_NAME];
380 str += '.' + className.replace(' ', '.');
384 str += ' ' + this[UID];
390 * Returns an attribute value on the Node instance.
391 * Unless pre-configured (via `Node.ATTRS`), get hands
392 * off to the underlying DOM node. Only valid
393 * attributes/properties for the node will be queried.
395 * @param {String} attr The attribute
396 * @return {any} The current value of the attribute
398 get: function(attr) {
401 if (this._getAttr) { // use Attribute imple
402 val = this._getAttr(attr);
404 val = this._get(attr);
408 val = Y_Node.scrubVal(val, this);
409 } else if (val === null) {
410 val = null; // IE: DOM null is not true null (even though they ===)
416 * Helper method for get.
419 * @param {String} attr The attribute
420 * @return {any} The current value of the attribute
422 _get: function(attr) {
423 var attrConfig = Y_Node.ATTRS[attr],
426 if (attrConfig && attrConfig.getter) {
427 val = attrConfig.getter.call(this);
428 } else if (Y_Node.re_aria.test(attr)) {
429 val = this._node.getAttribute(attr, 2);
431 val = Y_Node.DEFAULT_GETTER.apply(this, arguments);
438 * Sets an attribute on the Node instance.
439 * Unless pre-configured (via Node.ATTRS), set hands
440 * off to the underlying DOM node. Only valid
441 * attributes/properties for the node will be set.
442 * To set custom attributes use setAttribute.
444 * @param {String} attr The attribute to be set.
445 * @param {any} val The value to set the attribute to.
448 set: function(attr, val) {
449 var attrConfig = Y_Node.ATTRS[attr];
451 if (this._setAttr) { // use Attribute imple
452 this._setAttr.apply(this, arguments);
453 } else { // use setters inline
454 if (attrConfig && attrConfig.setter) {
455 attrConfig.setter.call(this, val, attr);
456 } else if (Y_Node.re_aria.test(attr)) { // special case Aria
457 this._node.setAttribute(attr, val);
459 Y_Node.DEFAULT_SETTER.apply(this, arguments);
467 * Sets multiple attributes.
469 * @param {Object} attrMap an object of name/value pairs to set
472 setAttrs: function(attrMap) {
473 if (this._setAttrs) { // use Attribute imple
474 this._setAttrs(attrMap);
475 } else { // use setters inline
476 Y.Object.each(attrMap, function(v, n) {
485 * Returns an object containing the values for the requested attributes.
487 * @param {Array} attrs an array of attributes to get values
488 * @return {Object} An object with attribute name/value pairs.
490 getAttrs: function(attrs) {
492 if (this._getAttrs) { // use Attribute imple
493 this._getAttrs(attrs);
494 } else { // use setters inline
495 Y.Array.each(attrs, function(v, n) {
496 ret[v] = this.get(v);
504 * Compares nodes to determine if they match.
505 * Node instances can be compared to each other and/or HTMLElements.
507 * @param {HTMLElement | Node} refNode The reference node to compare to the node.
508 * @return {Boolean} True if the nodes match, false if they do not.
510 compareTo: function(refNode) {
511 var node = this._node;
513 if (refNode && refNode._node) {
514 refNode = refNode._node;
516 return node === refNode;
520 * Determines whether the node is appended to the document.
522 * @param {Node|HTMLElement} doc optional An optional document to check against.
523 * Defaults to current document.
524 * @return {Boolean} Whether or not this node is appended to the document.
526 inDoc: function(doc) {
527 var node = this._node;
528 doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
529 if (doc.documentElement) {
530 return Y_DOM.contains(doc.documentElement, node);
534 getById: function(id) {
535 var node = this._node,
536 ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]);
537 if (ret && Y_DOM.contains(node, ret)) {
546 * Returns the nearest ancestor that passes the test applied by supplied boolean method.
548 * @param {String | Function} fn A selector string or boolean method for testing elements.
549 * If a function is used, it receives the current node being tested as the only argument.
550 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
551 * @param {String | Function} stopFn optional A selector string or boolean
552 * method to indicate when the search should stop. The search bails when the function
553 * returns true or the selector matches.
554 * If a function is used, it receives the current node being tested as the only argument.
555 * @return {Node} The matching Node instance or null if not found
557 ancestor: function(fn, testSelf, stopFn) {
558 // testSelf is optional, check for stopFn as 2nd arg
559 if (arguments.length === 2 &&
560 (typeof testSelf == 'string' || typeof testSelf == 'function')) {
564 return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
568 * Returns the ancestors that pass the test applied by supplied boolean method.
570 * @param {String | Function} fn A selector string or boolean method for testing elements.
571 * @param {Boolean} testSelf optional Whether or not to include the element in the scan
572 * If a function is used, it receives the current node being tested as the only argument.
573 * @return {NodeList} A NodeList instance containing the matching elements
575 ancestors: function(fn, testSelf, stopFn) {
576 if (arguments.length === 2 &&
577 (typeof testSelf == 'string' || typeof testSelf == 'function')) {
580 return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
584 * Returns the previous matching sibling.
585 * Returns the nearest element node sibling if no method provided.
587 * @param {String | Function} fn A selector or boolean method for testing elements.
588 * If a function is used, it receives the current node being tested as the only argument.
589 * @return {Node} Node instance or null if not found
591 previous: function(fn, all) {
592 return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
596 * Returns the next matching sibling.
597 * Returns the nearest element node sibling if no method provided.
599 * @param {String | Function} fn A selector or boolean method for testing elements.
600 * If a function is used, it receives the current node being tested as the only argument.
601 * @return {Node} Node instance or null if not found
603 next: function(fn, all) {
604 return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
608 * Returns all matching siblings.
609 * Returns all siblings if no method provided.
611 * @param {String | Function} fn A selector or boolean method for testing elements.
612 * If a function is used, it receives the current node being tested as the only argument.
613 * @return {NodeList} NodeList instance bound to found siblings
615 siblings: function(fn) {
616 return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn)));
620 * Retrieves a Node instance of nodes based on the given CSS selector.
623 * @param {string} selector The CSS selector to test against.
624 * @return {Node} A Node instance for the matching HTMLElement.
626 one: function(selector) {
627 return Y.one(Y.Selector.query(selector, this._node, true));
631 * Retrieves a NodeList based on the given CSS selector.
634 * @param {string} selector The CSS selector to test against.
635 * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
637 all: function(selector) {
638 var nodelist = Y.all(Y.Selector.query(selector, this._node));
639 nodelist._query = selector;
640 nodelist._queryRoot = this._node;
644 // TODO: allow fn test
646 * Test if the supplied node matches the supplied selector.
649 * @param {string} selector The CSS selector to test against.
650 * @return {boolean} Whether or not the node matches the selector.
652 test: function(selector) {
653 return Y.Selector.test(this._node, selector);
657 * Removes the node from its parent.
658 * Shortcut for myNode.get('parentNode').removeChild(myNode);
660 * @param {Boolean} destroy whether or not to call destroy() on the node
665 remove: function(destroy) {
666 var node = this._node;
668 if (node && node.parentNode) {
669 node.parentNode.removeChild(node);
680 * Replace the node with the other node. This is a DOM update only
681 * and does not change the node bound to the Node instance.
682 * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
684 * @param {Node | HTMLNode} newNode Node to be inserted
688 replace: function(newNode) {
689 var node = this._node;
690 if (typeof newNode == 'string') {
691 newNode = Y_Node.create(newNode);
693 node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node);
698 * @method replaceChild
700 * @param {String | HTMLElement | Node} node Node to be inserted
701 * @param {HTMLElement | Node} refNode Node to be replaced
702 * @return {Node} The replaced node
704 replaceChild: function(node, refNode) {
705 if (typeof node == 'string') {
706 node = Y_DOM.create(node);
709 return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode)));
713 * Nulls internal node references, removes any plugins and event listeners
715 * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the
716 * node's subtree (default is false)
719 destroy: function(recursive) {
720 var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid',
723 this.purge(); // TODO: only remove events add via this Node
725 if (this.unplug) { // may not be a PluginHost
732 Y.NodeList.each(this.all('*'), function(node) {
733 instance = Y_Node._instances[node[UID]];
741 this._stateProxy = null;
743 delete Y_Node._instances[this._yuid];
747 * Invokes a method on the Node instance
749 * @param {String} method The name of the method to invoke
750 * @param {Any} a, b, c, etc. Arguments to invoke the method with.
751 * @return Whatever the underly method returns.
752 * DOM Nodes and Collections return values
753 * are converted to Node/NodeList instances.
756 invoke: function(method, a, b, c, d, e) {
757 var node = this._node,
768 ret = node[method](a, b, c, d, e);
769 return Y_Node.scrubVal(ret, this);
774 * @description Swap DOM locations with the given node.
775 * This does not change which DOM node each Node instance refers to.
776 * @param {Node} otherNode The node to swap with
779 swap: Y.config.doc.documentElement.swapNode ?
780 function(otherNode) {
781 this._node.swapNode(Y_Node.getDOMNode(otherNode));
783 function(otherNode) {
784 otherNode = Y_Node.getDOMNode(otherNode);
785 var node = this._node,
786 parent = otherNode.parentNode,
787 nextSibling = otherNode.nextSibling;
789 if (nextSibling === node) {
790 parent.insertBefore(node, otherNode);
791 } else if (otherNode === node.nextSibling) {
792 parent.insertBefore(otherNode, node);
794 node.parentNode.replaceChild(otherNode, node);
795 Y_DOM.addHTML(parent, node, nextSibling);
801 hasMethod: function(method) {
802 var node = this._node;
803 return !!(node && method in node &&
804 typeof node[method] != 'unknown' &&
805 (typeof node[method] == 'function' ||
806 String(node[method]).indexOf('function') === 1)); // IE reports as object, prepends space
809 isFragment: function() {
810 return (this.get('nodeType') === 11);
814 * Removes and destroys all of the nodes within the node.
819 this.get('childNodes').remove().destroy(true);
824 * Returns the DOM node bound to the Node instance
828 getDOMNode: function() {
836 * The NodeList module provides support for managing collections of Nodes.
838 * @submodule node-core
842 * The NodeList class provides a wrapper for manipulating DOM NodeLists.
843 * NodeList properties can be accessed via the set/get methods.
844 * Use Y.all() to retrieve NodeList instances.
850 var NodeList = function(nodes) {
854 if (typeof nodes === 'string') { // selector query
856 nodes = Y.Selector.query(nodes);
857 } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window
859 } else if (nodes._node) { // Y.Node
860 nodes = [nodes._node];
861 } else if (nodes[0] && nodes[0]._node) { // allow array of Y.Nodes
862 Y.Array.each(nodes, function(node) {
864 tmp.push(node._node);
868 } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes)
869 nodes = Y.Array(nodes, 0, true);
874 * The underlying array of DOM nodes bound to the Y.NodeList instance
878 this._nodes = nodes || [];
881 NodeList.NAME = 'NodeList';
884 * Retrieves the DOM nodes bound to a NodeList instance
885 * @method getDOMNodes
888 * @param {NodeList} nodelist The NodeList instance
889 * @return {Array} The array of DOM nodes bound to the NodeList
891 NodeList.getDOMNodes = function(nodelist) {
892 return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist;
895 NodeList.each = function(instance, fn, context) {
896 var nodes = instance._nodes;
897 if (nodes && nodes.length) {
898 Y.Array.each(nodes, fn, context || instance);
900 Y.log('no nodes bound to ' + this, 'warn', 'NodeList');
904 NodeList.addMethod = function(name, fn, context) {
906 NodeList.prototype[name] = function() {
910 Y.Array.each(this._nodes, function(node) {
911 var UID = (node.uniqueID && node.nodeType !== 9 ) ? 'uniqueID' : '_yuid',
912 instance = Y.Node._instances[node[UID]],
917 instance = NodeList._getTempNode(node);
919 ctx = context || instance;
920 result = fn.apply(ctx, args);
921 if (result !== undefined && result !== instance) {
922 ret[ret.length] = result;
926 // TODO: remove tmp pointer
927 return ret.length ? ret : this;
930 Y.log('unable to add method: ' + name + ' to NodeList', 'warn', 'node');
934 NodeList.importMethod = function(host, name, altName) {
935 if (typeof name === 'string') {
936 altName = altName || name;
937 NodeList.addMethod(name, host[name]);
939 Y.Array.each(name, function(n) {
940 NodeList.importMethod(host, n);
945 NodeList._getTempNode = function(node) {
946 var tmp = NodeList._tempNode;
948 tmp = Y.Node.create('<div></div>');
949 NodeList._tempNode = tmp;
953 tmp._stateProxy = node;
957 Y.mix(NodeList.prototype, {
958 _invoke: function(method, args, getter) {
959 var ret = (getter) ? [] : this;
961 this.each(function(node) {
962 var val = node[method].apply(node, args);
972 * Retrieves the Node instance at the given index.
975 * @param {Number} index The index of the target Node.
976 * @return {Node} The Node instance at the given index.
978 item: function(index) {
979 return Y.one((this._nodes || [])[index]);
983 * Applies the given function to each Node in the NodeList.
985 * @param {Function} fn The function to apply. It receives 3 arguments:
986 * the current node instance, the node's index, and the NodeList instance
987 * @param {Object} context optional An optional context to apply the function with
988 * Default context is the current Node instance
991 each: function(fn, context) {
993 Y.Array.each(this._nodes, function(node, index) {
995 return fn.call(context || node, node, index, instance);
1000 batch: function(fn, context) {
1001 var nodelist = this;
1003 Y.Array.each(this._nodes, function(node, index) {
1004 var instance = Y.Node._instances[node[UID]];
1006 instance = NodeList._getTempNode(node);
1009 return fn.call(context || instance, instance, index, nodelist);
1015 * Executes the function once for each node until a true value is returned.
1017 * @param {Function} fn The function to apply. It receives 3 arguments:
1018 * the current node instance, the node's index, and the NodeList instance
1019 * @param {Object} context optional An optional context to execute the function from.
1020 * Default context is the current Node instance
1021 * @return {Boolean} Whether or not the function returned true for any node.
1023 some: function(fn, context) {
1024 var instance = this;
1025 return Y.Array.some(this._nodes, function(node, index) {
1027 context = context || node;
1028 return fn.call(context, node, index, instance);
1033 * Creates a documenFragment from the nodes bound to the NodeList instance
1035 * @return {Node} a Node instance bound to the documentFragment
1037 toFrag: function() {
1038 return Y.one(Y.DOM._nl2frag(this._nodes));
1042 * Returns the index of the node in the NodeList instance
1043 * or -1 if the node isn't found.
1045 * @param {Node | DOMNode} node the node to search for
1046 * @return {Int} the index of the node value or -1 if not found
1048 indexOf: function(node) {
1049 return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
1053 * Filters the NodeList instance down to only nodes matching the given selector.
1055 * @param {String} selector The selector to filter against
1056 * @return {NodeList} NodeList containing the updated collection
1059 filter: function(selector) {
1060 return Y.all(Y.Selector.filter(this._nodes, selector));
1065 * Creates a new NodeList containing all nodes at every n indices, where
1066 * remainder n % index equals r.
1067 * (zero-based index).
1069 * @param {Int} n The offset to use (return every nth node)
1070 * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
1071 * @return {NodeList} NodeList containing the updated collection
1073 modulus: function(n, r) {
1076 NodeList.each(this, function(node, i) {
1082 return Y.all(nodes);
1086 * Creates a new NodeList containing all nodes at odd indices
1087 * (zero-based index).
1089 * @return {NodeList} NodeList containing the updated collection
1092 return this.modulus(2, 1);
1096 * Creates a new NodeList containing all nodes at even indices
1097 * (zero-based index), including zero.
1099 * @return {NodeList} NodeList containing the updated collection
1102 return this.modulus(2);
1105 destructor: function() {
1109 * Reruns the initial query, when created using a selector query
1113 refresh: function() {
1115 nodes = this._nodes,
1116 query = this._query,
1117 root = this._queryRoot;
1121 if (nodes && nodes[0] && nodes[0].ownerDocument) {
1122 root = nodes[0].ownerDocument;
1126 this._nodes = Y.Selector.query(query, root);
1133 * Returns the current number of items in the NodeList.
1135 * @return {Int} The number of items in the NodeList.
1138 return this._nodes.length;
1142 * Determines if the instance is bound to any nodes
1144 * @return {Boolean} Whether or not the NodeList is bound to any nodes
1146 isEmpty: function() {
1147 return this._nodes.length < 1;
1150 toString: function() {
1152 errorMsg = this[UID] + ': not bound to any nodes',
1153 nodes = this._nodes,
1156 if (nodes && nodes[0]) {
1158 str += node[NODE_NAME];
1160 str += '#' + node.id;
1163 if (node.className) {
1164 str += '.' + node.className.replace(' ', '.');
1167 if (nodes.length > 1) {
1168 str += '...[' + nodes.length + ' items]';
1171 return str || errorMsg;
1175 * Returns the DOM node bound to the Node instance
1176 * @method getDOMNodes
1179 getDOMNodes: function() {
1184 NodeList.importMethod(Y.Node.prototype, [
1185 /** Called on each Node instance
1191 /** Called on each Node instance
1197 /** Called on each Node instance
1203 /** Called on each Node instance
1210 // one-off implementation to convert array of Nodes to NodeList
1211 // e.g. Y.all('input').get('parentNode');
1213 /** Called on each Node instance
1217 NodeList.prototype.get = function(attr) {
1219 nodes = this._nodes,
1221 getTemp = NodeList._getTempNode,
1226 instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
1227 val = instance._get(attr);
1228 if (val && val.nodeType) {
1233 Y.Array.each(nodes, function(node) {
1234 instance = Y.Node._instances[node._yuid];
1237 instance = getTemp(node);
1240 val = instance._get(attr);
1241 if (!isNodeList) { // convert array of Nodes to NodeList
1242 val = Y.Node.scrubVal(val, instance);
1248 return (isNodeList) ? Y.all(ret) : ret;
1251 Y.NodeList = NodeList;
1253 Y.all = function(nodes) {
1254 return new NodeList(nodes);
1260 * @submodule node-core
1263 var Y_NodeList = Y.NodeList,
1264 ArrayProto = Array.prototype,
1266 /** Returns a new NodeList combining the given NodeList(s)
1269 * @param {NodeList | Array} valueN Arrays/NodeLists and/or values to
1270 * concatenate to the resulting NodeList
1271 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
1274 /** Removes the last from the NodeList and returns it.
1277 * @return {Node} The last item in the NodeList.
1280 /** Adds the given Node(s) to the end of the NodeList.
1283 * @param {Node | DOMNode} nodes One or more nodes to add to the end of the NodeList.
1286 /** Removes the first item from the NodeList and returns it.
1289 * @return {Node} The first item in the NodeList.
1292 /** Returns a new NodeList comprising the Nodes in the given range.
1295 * @param {Number} begin Zero-based index at which to begin extraction.
1296 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.
1297 * @param {Number} end Zero-based index at which to end extraction. slice extracts up to but not including end.
1298 slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3).
1299 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.
1300 If end is omitted, slice extracts to the end of the sequence.
1301 * @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
1304 /** Changes the content of the NodeList, adding new elements while removing old elements.
1307 * @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
1308 * @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.
1309 * {Node | DOMNode| element1, ..., elementN
1310 The elements to add to the array. If you don't specify any elements, splice simply removes elements from the array.
1311 * @return {NodeList} The element(s) removed.
1314 /** Adds the given Node(s) to the beginning of the NodeList.
1317 * @param {Node | DOMNode} nodes One or more nodes to add to the NodeList.
1323 Y.Object.each(ArrayMethods, function(returnNodeList, name) {
1324 Y_NodeList.prototype[name] = function() {
1330 while (typeof (arg = arguments[i++]) != 'undefined') { // use DOM nodes/nodeLists
1331 args.push(arg._node || arg._nodes || arg);
1334 ret = ArrayProto[name].apply(this._nodes, args);
1336 if (returnNodeList) {
1339 ret = Y.Node.scrubVal(ret);
1347 * @submodule node-core
1352 * Passes through to DOM method.
1354 * @method removeChild
1355 * @param {HTMLElement | Node} node Node to be removed
1356 * @return {Node} The removed node
1361 * Passes through to DOM method.
1362 * @method hasChildNodes
1363 * @return {Boolean} Whether or not the node has any childNodes
1368 * Passes through to DOM method.
1370 * @param {Boolean} deep Whether or not to perform a deep clone, which includes
1371 * subtree and attributes
1372 * @return {Node} The clone
1377 * Passes through to DOM method.
1378 * @method hasAttribute
1379 * @param {String} attribute The attribute to test for
1380 * @return {Boolean} Whether or not the attribute is present
1385 * Passes through to DOM method.
1386 * @method scrollIntoView
1392 * Passes through to DOM method.
1393 * @method getElementsByTagName
1394 * @param {String} tagName The tagName to collect
1395 * @return {NodeList} A NodeList representing the HTMLCollection
1397 'getElementsByTagName',
1400 * Passes through to DOM method.
1407 * Passes through to DOM method.
1414 * Passes through to DOM method.
1415 * Only valid on FORM elements
1422 * Passes through to DOM method.
1423 * Only valid on FORM elements
1430 * Passes through to DOM method.
1437 * Passes through to DOM method.
1438 * Only valid on TABLE elements
1439 * @method createCaption
1444 ], function(method) {
1445 Y.log('adding: ' + method, 'info', 'node');
1446 Y.Node.prototype[method] = function(arg1, arg2, arg3) {
1447 var ret = this.invoke(method, arg1, arg2, arg3);
1453 * Passes through to DOM method.
1454 * @method removeAttribute
1455 * @param {String} attribute The attribute to be removed
1458 // one-off implementation due to IE returning boolean, breaking chaining
1459 Y.Node.prototype.removeAttribute = function(attr) {
1460 var node = this._node;
1462 node.removeAttribute(attr);
1468 Y.Node.importMethod(Y.DOM, [
1470 * Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
1472 * @param {Node | HTMLElement} needle The possible node or descendent
1473 * @return {Boolean} Whether or not this node is the needle its ancestor
1477 * Allows setting attributes on DOM nodes, normalizing in some cases.
1478 * This passes through to the DOM node, allowing for custom attributes.
1479 * @method setAttribute
1482 * @param {string} name The attribute name
1483 * @param {string} value The value to set
1487 * Allows getting attributes on DOM nodes, normalizing in some cases.
1488 * This passes through to the DOM node, allowing for custom attributes.
1489 * @method getAttribute
1491 * @param {string} name The attribute name
1492 * @return {string} The attribute value
1497 * Wraps the given HTML around the node.
1499 * @param {String} html The markup to wrap around the node.
1506 * Removes the node's parent node.
1513 * Applies a unique ID to the node if none exists
1514 * @method generateID
1515 * @return {String} The existing or generated ID
1520 Y.NodeList.importMethod(Y.Node.prototype, [
1522 * Allows getting attributes on DOM nodes, normalizing in some cases.
1523 * This passes through to the DOM node, allowing for custom attributes.
1524 * @method getAttribute
1527 * @param {string} name The attribute name
1528 * @return {string} The attribute value
1533 * Allows setting attributes on DOM nodes, normalizing in some cases.
1534 * This passes through to the DOM node, allowing for custom attributes.
1535 * @method setAttribute
1539 * @param {string} name The attribute name
1540 * @param {string} value The value to set
1545 * Allows for removing attributes on DOM nodes.
1546 * This passes through to the DOM node, allowing for custom attributes.
1547 * @method removeAttribute
1550 * @param {string} name The attribute to remove
1554 * Removes the parent node from node in the list.
1560 * Wraps the given HTML around each node.
1562 * @param {String} html The markup to wrap around the node.
1568 * Applies a unique ID to each node if none exists
1569 * @method generateID
1570 * @return {String} The existing or generated ID
1576 }, '3.5.0' ,{requires:['dom-core', 'selector']});