API Docs for: 3.10.3
Show:

File: node/js/nodelist.js

  1. /**
  2. * The NodeList module provides support for managing collections of Nodes.
  3. * @module node
  4. * @submodule node-core
  5. */
  6.  
  7. /**
  8. * The NodeList class provides a wrapper for manipulating DOM NodeLists.
  9. * NodeList properties can be accessed via the set/get methods.
  10. * Use Y.all() to retrieve NodeList instances.
  11. *
  12. * @class NodeList
  13. * @constructor
  14. * @param nodes {String|element|Node|Array} A selector, DOM element, Node, list of DOM elements, or list of Nodes with which to populate this NodeList.
  15. */
  16.  
  17. var NodeList = function(nodes) {
  18. var tmp = [];
  19.  
  20. if (nodes) {
  21. if (typeof nodes === 'string') { // selector query
  22. this._query = nodes;
  23. nodes = Y.Selector.query(nodes);
  24. } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window
  25. nodes = [nodes];
  26. } else if (nodes._node) { // Y.Node
  27. nodes = [nodes._node];
  28. } else if (nodes[0] && nodes[0]._node) { // allow array of Y.Nodes
  29. Y.Array.each(nodes, function(node) {
  30. if (node._node) {
  31. tmp.push(node._node);
  32. }
  33. });
  34. nodes = tmp;
  35. } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes)
  36. nodes = Y.Array(nodes, 0, true);
  37. }
  38. }
  39.  
  40. /**
  41. * The underlying array of DOM nodes bound to the Y.NodeList instance
  42. * @property _nodes
  43. * @private
  44. */
  45. this._nodes = nodes || [];
  46. };
  47.  
  48. NodeList.NAME = 'NodeList';
  49.  
  50. /**
  51. * Retrieves the DOM nodes bound to a NodeList instance
  52. * @method getDOMNodes
  53. * @static
  54. *
  55. * @param {NodeList} nodelist The NodeList instance
  56. * @return {Array} The array of DOM nodes bound to the NodeList
  57. */
  58. NodeList.getDOMNodes = function(nodelist) {
  59. return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist;
  60. };
  61.  
  62. NodeList.each = function(instance, fn, context) {
  63. var nodes = instance._nodes;
  64. if (nodes && nodes.length) {
  65. Y.Array.each(nodes, fn, context || instance);
  66. } else {
  67. Y.log('no nodes bound to ' + this, 'warn', 'NodeList');
  68. }
  69. };
  70.  
  71. NodeList.addMethod = function(name, fn, context) {
  72. if (name && fn) {
  73. NodeList.prototype[name] = function() {
  74. var ret = [],
  75. args = arguments;
  76.  
  77. Y.Array.each(this._nodes, function(node) {
  78. var UID = (node.uniqueID && node.nodeType !== 9 ) ? 'uniqueID' : '_yuid',
  79. instance = Y.Node._instances[node[UID]],
  80. ctx,
  81. result;
  82.  
  83. if (!instance) {
  84. instance = NodeList._getTempNode(node);
  85. }
  86. ctx = context || instance;
  87. result = fn.apply(ctx, args);
  88. if (result !== undefined && result !== instance) {
  89. ret[ret.length] = result;
  90. }
  91. });
  92.  
  93. // TODO: remove tmp pointer
  94. return ret.length ? ret : this;
  95. };
  96. } else {
  97. Y.log('unable to add method: ' + name + ' to NodeList', 'warn', 'node');
  98. }
  99. };
  100.  
  101. NodeList.importMethod = function(host, name, altName) {
  102. if (typeof name === 'string') {
  103. altName = altName || name;
  104. NodeList.addMethod(name, host[name]);
  105. } else {
  106. Y.Array.each(name, function(n) {
  107. NodeList.importMethod(host, n);
  108. });
  109. }
  110. };
  111.  
  112. NodeList._getTempNode = function(node) {
  113. var tmp = NodeList._tempNode;
  114. if (!tmp) {
  115. tmp = Y.Node.create('<div></div>');
  116. NodeList._tempNode = tmp;
  117. }
  118.  
  119. tmp._node = node;
  120. tmp._stateProxy = node;
  121. return tmp;
  122. };
  123.  
  124. Y.mix(NodeList.prototype, {
  125. _invoke: function(method, args, getter) {
  126. var ret = (getter) ? [] : this;
  127.  
  128. this.each(function(node) {
  129. var val = node[method].apply(node, args);
  130. if (getter) {
  131. ret.push(val);
  132. }
  133. });
  134.  
  135. return ret;
  136. },
  137.  
  138. /**
  139. * Retrieves the Node instance at the given index.
  140. * @method item
  141. *
  142. * @param {Number} index The index of the target Node.
  143. * @return {Node} The Node instance at the given index.
  144. */
  145. item: function(index) {
  146. return Y.one((this._nodes || [])[index]);
  147. },
  148.  
  149. /**
  150. * Applies the given function to each Node in the NodeList.
  151. * @method each
  152. * @param {Function} fn The function to apply. It receives 3 arguments:
  153. * the current node instance, the node's index, and the NodeList instance
  154. * @param {Object} context optional An optional context to apply the function with
  155. * Default context is the current Node instance
  156. * @chainable
  157. */
  158. each: function(fn, context) {
  159. var instance = this;
  160. Y.Array.each(this._nodes, function(node, index) {
  161. node = Y.one(node);
  162. return fn.call(context || node, node, index, instance);
  163. });
  164. return instance;
  165. },
  166.  
  167. batch: function(fn, context) {
  168. var nodelist = this;
  169.  
  170. Y.Array.each(this._nodes, function(node, index) {
  171. var instance = Y.Node._instances[node[UID]];
  172. if (!instance) {
  173. instance = NodeList._getTempNode(node);
  174. }
  175.  
  176. return fn.call(context || instance, instance, index, nodelist);
  177. });
  178. return nodelist;
  179. },
  180.  
  181. /**
  182. * Executes the function once for each node until a true value is returned.
  183. * @method some
  184. * @param {Function} fn The function to apply. It receives 3 arguments:
  185. * the current node instance, the node's index, and the NodeList instance
  186. * @param {Object} context optional An optional context to execute the function from.
  187. * Default context is the current Node instance
  188. * @return {Boolean} Whether or not the function returned true for any node.
  189. */
  190. some: function(fn, context) {
  191. var instance = this;
  192. return Y.Array.some(this._nodes, function(node, index) {
  193. node = Y.one(node);
  194. context = context || node;
  195. return fn.call(context, node, index, instance);
  196. });
  197. },
  198.  
  199. /**
  200. * Creates a documenFragment from the nodes bound to the NodeList instance
  201. * @method toFrag
  202. * @return {Node} a Node instance bound to the documentFragment
  203. */
  204. toFrag: function() {
  205. return Y.one(Y.DOM._nl2frag(this._nodes));
  206. },
  207.  
  208. /**
  209. * Returns the index of the node in the NodeList instance
  210. * or -1 if the node isn't found.
  211. * @method indexOf
  212. * @param {Node | DOMNode} node the node to search for
  213. * @return {Int} the index of the node value or -1 if not found
  214. */
  215. indexOf: function(node) {
  216. return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
  217. },
  218.  
  219. /**
  220. * Filters the NodeList instance down to only nodes matching the given selector.
  221. * @method filter
  222. * @param {String} selector The selector to filter against
  223. * @return {NodeList} NodeList containing the updated collection
  224. * @see Selector
  225. */
  226. filter: function(selector) {
  227. return Y.all(Y.Selector.filter(this._nodes, selector));
  228. },
  229.  
  230.  
  231. /**
  232. * Creates a new NodeList containing all nodes at every n indices, where
  233. * remainder n % index equals r.
  234. * (zero-based index).
  235. * @method modulus
  236. * @param {Int} n The offset to use (return every nth node)
  237. * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
  238. * @return {NodeList} NodeList containing the updated collection
  239. */
  240. modulus: function(n, r) {
  241. r = r || 0;
  242. var nodes = [];
  243. NodeList.each(this, function(node, i) {
  244. if (i % n === r) {
  245. nodes.push(node);
  246. }
  247. });
  248.  
  249. return Y.all(nodes);
  250. },
  251.  
  252. /**
  253. * Creates a new NodeList containing all nodes at odd indices
  254. * (zero-based index).
  255. * @method odd
  256. * @return {NodeList} NodeList containing the updated collection
  257. */
  258. odd: function() {
  259. return this.modulus(2, 1);
  260. },
  261.  
  262. /**
  263. * Creates a new NodeList containing all nodes at even indices
  264. * (zero-based index), including zero.
  265. * @method even
  266. * @return {NodeList} NodeList containing the updated collection
  267. */
  268. even: function() {
  269. return this.modulus(2);
  270. },
  271.  
  272. destructor: function() {
  273. },
  274.  
  275. /**
  276. * Reruns the initial query, when created using a selector query
  277. * @method refresh
  278. * @chainable
  279. */
  280. refresh: function() {
  281. var doc,
  282. nodes = this._nodes,
  283. query = this._query,
  284. root = this._queryRoot;
  285.  
  286. if (query) {
  287. if (!root) {
  288. if (nodes && nodes[0] && nodes[0].ownerDocument) {
  289. root = nodes[0].ownerDocument;
  290. }
  291. }
  292.  
  293. this._nodes = Y.Selector.query(query, root);
  294. }
  295.  
  296. return this;
  297. },
  298.  
  299. /**
  300. * Returns the current number of items in the NodeList.
  301. * @method size
  302. * @return {Int} The number of items in the NodeList.
  303. */
  304. size: function() {
  305. return this._nodes.length;
  306. },
  307.  
  308. /**
  309. * Determines if the instance is bound to any nodes
  310. * @method isEmpty
  311. * @return {Boolean} Whether or not the NodeList is bound to any nodes
  312. */
  313. isEmpty: function() {
  314. return this._nodes.length < 1;
  315. },
  316.  
  317. toString: function() {
  318. var str = '',
  319. errorMsg = this[UID] + ': not bound to any nodes',
  320. nodes = this._nodes,
  321. node;
  322.  
  323. if (nodes && nodes[0]) {
  324. node = nodes[0];
  325. str += node[NODE_NAME];
  326. if (node.id) {
  327. str += '#' + node.id;
  328. }
  329.  
  330. if (node.className) {
  331. str += '.' + node.className.replace(' ', '.');
  332. }
  333.  
  334. if (nodes.length > 1) {
  335. str += '...[' + nodes.length + ' items]';
  336. }
  337. }
  338. return str || errorMsg;
  339. },
  340.  
  341. /**
  342. * Returns the DOM node bound to the Node instance
  343. * @method getDOMNodes
  344. * @return {Array}
  345. */
  346. getDOMNodes: function() {
  347. return this._nodes;
  348. }
  349. }, true);
  350.  
  351. NodeList.importMethod(Y.Node.prototype, [
  352. /**
  353. * Called on each Node instance. Nulls internal node references,
  354. * removes any plugins and event listeners
  355. * @method destroy
  356. * @param {Boolean} recursivePurge (optional) Whether or not to
  357. * remove listeners from the node's subtree (default is false)
  358. * @see Node.destroy
  359. */
  360. 'destroy',
  361.  
  362. /**
  363. * Called on each Node instance. Removes and destroys all of the nodes
  364. * within the node
  365. * @method empty
  366. * @chainable
  367. * @see Node.empty
  368. */
  369. 'empty',
  370.  
  371. /**
  372. * Called on each Node instance. Removes the node from its parent.
  373. * Shortcut for myNode.get('parentNode').removeChild(myNode);
  374. * @method remove
  375. * @param {Boolean} destroy whether or not to call destroy() on the node
  376. * after removal.
  377. * @chainable
  378. * @see Node.remove
  379. */
  380. 'remove',
  381.  
  382. /**
  383. * Called on each Node instance. Sets an attribute on the Node instance.
  384. * Unless pre-configured (via Node.ATTRS), set hands
  385. * off to the underlying DOM node. Only valid
  386. * attributes/properties for the node will be set.
  387. * To set custom attributes use setAttribute.
  388. * @method set
  389. * @param {String} attr The attribute to be set.
  390. * @param {any} val The value to set the attribute to.
  391. * @chainable
  392. * @see Node.set
  393. */
  394. 'set'
  395. ]);
  396.  
  397. // one-off implementation to convert array of Nodes to NodeList
  398. // e.g. Y.all('input').get('parentNode');
  399.  
  400. /** Called on each Node instance
  401. * @method get
  402. * @see Node
  403. */
  404. NodeList.prototype.get = function(attr) {
  405. var ret = [],
  406. nodes = this._nodes,
  407. isNodeList = false,
  408. getTemp = NodeList._getTempNode,
  409. instance,
  410. val;
  411.  
  412. if (nodes[0]) {
  413. instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
  414. val = instance._get(attr);
  415. if (val && val.nodeType) {
  416. isNodeList = true;
  417. }
  418. }
  419.  
  420. Y.Array.each(nodes, function(node) {
  421. instance = Y.Node._instances[node._yuid];
  422.  
  423. if (!instance) {
  424. instance = getTemp(node);
  425. }
  426.  
  427. val = instance._get(attr);
  428. if (!isNodeList) { // convert array of Nodes to NodeList
  429. val = Y.Node.scrubVal(val, instance);
  430. }
  431.  
  432. ret.push(val);
  433. });
  434.  
  435. return (isNodeList) ? Y.all(ret) : ret;
  436. };
  437.  
  438. Y.NodeList = NodeList;
  439.  
  440. Y.all = function(nodes) {
  441. return new NodeList(nodes);
  442. };
  443.  
  444. Y.Node.all = Y.all;
  445.