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, [
1186
* Called on each Node instance. Nulls internal node references,
1187
* removes any plugins and event listeners
1189
* @param {Boolean} recursivePurge (optional) Whether or not to
1190
* remove listeners from the node's subtree (default is false)
1196
* Called on each Node instance. Removes and destroys all of the nodes
1205
* Called on each Node instance. Removes the node from its parent.
1206
* Shortcut for myNode.get('parentNode').removeChild(myNode);
1208
* @param {Boolean} destroy whether or not to call destroy() on the node
1216
* Called on each Node instance. Sets an attribute on the Node instance.
1217
* Unless pre-configured (via Node.ATTRS), set hands
1218
* off to the underlying DOM node. Only valid
1219
* attributes/properties for the node will be set.
1220
* To set custom attributes use setAttribute.
1222
* @param {String} attr The attribute to be set.
1223
* @param {any} val The value to set the attribute to.
1230
// one-off implementation to convert array of Nodes to NodeList
1231
// e.g. Y.all('input').get('parentNode');
1233
/** Called on each Node instance
1237
NodeList.prototype.get = function(attr) {
1239
nodes = this._nodes,
1241
getTemp = NodeList._getTempNode,
1246
instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
1247
val = instance._get(attr);
1248
if (val && val.nodeType) {
1253
Y.Array.each(nodes, function(node) {
1254
instance = Y.Node._instances[node._yuid];
1257
instance = getTemp(node);
1260
val = instance._get(attr);
1261
if (!isNodeList) { // convert array of Nodes to NodeList
1262
val = Y.Node.scrubVal(val, instance);
1268
return (isNodeList) ? Y.all(ret) : ret;
1271
Y.NodeList = NodeList;
1273
Y.all = function(nodes) {
1274
return new NodeList(nodes);
1280
* @submodule node-core
1283
var Y_NodeList = Y.NodeList,
1284
ArrayProto = Array.prototype,
1286
/** Returns a new NodeList combining the given NodeList(s)
1289
* @param {NodeList | Array} valueN Arrays/NodeLists and/or values to
1290
* concatenate to the resulting NodeList
1291
* @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
1294
/** Removes the last from the NodeList and returns it.
1297
* @return {Node} The last item in the NodeList.
1300
/** Adds the given Node(s) to the end of the NodeList.
1303
* @param {Node | DOMNode} nodes One or more nodes to add to the end of the NodeList.
1306
/** Removes the first item from the NodeList and returns it.
1309
* @return {Node} The first item in the NodeList.
1312
/** Returns a new NodeList comprising the Nodes in the given range.
1315
* @param {Number} begin Zero-based index at which to begin extraction.
1316
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.
1317
* @param {Number} end Zero-based index at which to end extraction. slice extracts up to but not including end.
1318
slice(1,4) extracts the second element through the fourth element (elements indexed 1, 2, and 3).
1319
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.
1320
If end is omitted, slice extracts to the end of the sequence.
1321
* @return {NodeList} A new NodeList comprised of this NodeList joined with the input.
1324
/** Changes the content of the NodeList, adding new elements while removing old elements.
1327
* @param {Number} index Index at which to start changing the array. If negative, will begin that many elements from the end.
1328
* @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.
1329
* {Node | DOMNode| element1, ..., elementN
1330
The elements to add to the array. If you don't specify any elements, splice simply removes elements from the array.
1331
* @return {NodeList} The element(s) removed.
1334
/** Adds the given Node(s) to the beginning of the NodeList.
1337
* @param {Node | DOMNode} nodes One or more nodes to add to the NodeList.
1343
Y.Object.each(ArrayMethods, function(returnNodeList, name) {
1344
Y_NodeList.prototype[name] = function() {
1350
while (typeof (arg = arguments[i++]) != 'undefined') { // use DOM nodes/nodeLists
1351
args.push(arg._node || arg._nodes || arg);
1354
ret = ArrayProto[name].apply(this._nodes, args);
1356
if (returnNodeList) {
1359
ret = Y.Node.scrubVal(ret);
1367
* @submodule node-core
1372
* Passes through to DOM method.
1374
* @method removeChild
1375
* @param {HTMLElement | Node} node Node to be removed
1376
* @return {Node} The removed node
1381
* Passes through to DOM method.
1382
* @method hasChildNodes
1383
* @return {Boolean} Whether or not the node has any childNodes
1388
* Passes through to DOM method.
1390
* @param {Boolean} deep Whether or not to perform a deep clone, which includes
1391
* subtree and attributes
1392
* @return {Node} The clone
1397
* Passes through to DOM method.
1398
* @method hasAttribute
1399
* @param {String} attribute The attribute to test for
1400
* @return {Boolean} Whether or not the attribute is present
1405
* Passes through to DOM method.
1406
* @method scrollIntoView
1412
* Passes through to DOM method.
1413
* @method getElementsByTagName
1414
* @param {String} tagName The tagName to collect
1415
* @return {NodeList} A NodeList representing the HTMLCollection
1417
'getElementsByTagName',
1420
* Passes through to DOM method.
1427
* Passes through to DOM method.
1434
* Passes through to DOM method.
1435
* Only valid on FORM elements
1442
* Passes through to DOM method.
1443
* Only valid on FORM elements
1450
* Passes through to DOM method.
1457
* Passes through to DOM method.
1458
* Only valid on TABLE elements
1459
* @method createCaption
1464
], function(method) {
1465
Y.log('adding: ' + method, 'info', 'node');
1466
Y.Node.prototype[method] = function(arg1, arg2, arg3) {
1467
var ret = this.invoke(method, arg1, arg2, arg3);
1473
* Passes through to DOM method.
1474
* @method removeAttribute
1475
* @param {String} attribute The attribute to be removed
1478
// one-off implementation due to IE returning boolean, breaking chaining
1479
Y.Node.prototype.removeAttribute = function(attr) {
1480
var node = this._node;
1482
node.removeAttribute(attr, 0); // comma zero for IE < 8 to force case-insensitive
1488
Y.Node.importMethod(Y.DOM, [
1490
* Determines whether the node is an ancestor of another HTML element in the DOM hierarchy.
1492
* @param {Node | HTMLElement} needle The possible node or descendent
1493
* @return {Boolean} Whether or not this node is the needle its ancestor
1497
* Allows setting attributes on DOM nodes, normalizing in some cases.
1498
* This passes through to the DOM node, allowing for custom attributes.
1499
* @method setAttribute
1502
* @param {string} name The attribute name
1503
* @param {string} value The value to set
1507
* Allows getting attributes on DOM nodes, normalizing in some cases.
1508
* This passes through to the DOM node, allowing for custom attributes.
1509
* @method getAttribute
1511
* @param {string} name The attribute name
1512
* @return {string} The attribute value
1517
* Wraps the given HTML around the node.
1519
* @param {String} html The markup to wrap around the node.
1526
* Removes the node's parent node.
1533
* Applies a unique ID to the node if none exists
1534
* @method generateID
1535
* @return {String} The existing or generated ID
1540
Y.NodeList.importMethod(Y.Node.prototype, [
1542
* Allows getting attributes on DOM nodes, normalizing in some cases.
1543
* This passes through to the DOM node, allowing for custom attributes.
1544
* @method getAttribute
1547
* @param {string} name The attribute name
1548
* @return {string} The attribute value
1553
* Allows setting attributes on DOM nodes, normalizing in some cases.
1554
* This passes through to the DOM node, allowing for custom attributes.
1555
* @method setAttribute
1559
* @param {string} name The attribute name
1560
* @param {string} value The value to set
1565
* Allows for removing attributes on DOM nodes.
1566
* This passes through to the DOM node, allowing for custom attributes.
1567
* @method removeAttribute
1570
* @param {string} name The attribute to remove
1574
* Removes the parent node from node in the list.
1580
* Wraps the given HTML around each node.
1582
* @param {String} html The markup to wrap around the node.
1588
* Applies a unique ID to each node if none exists
1589
* @method generateID
1590
* @return {String} The existing or generated ID
1596
}, '3.5.1' ,{requires:['dom-core', 'selector']});