API Docs for: 3.8.0
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.  * The NodeList class provides a wrapper for manipulating DOM NodeLists.
  8.  * NodeList properties can be accessed via the set/get methods.
  9.  * Use Y.all() to retrieve NodeList instances.
  10.  *
  11.  * @class NodeList
  12.  * @constructor
  13.  * @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.
  14.  */

  15. var NodeList = function(nodes) {
  16.     var tmp = [];

  17.     if (nodes) {
  18.         if (typeof nodes === 'string') { // selector query
  19.             this._query = nodes;
  20.             nodes = Y.Selector.query(nodes);
  21.         } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window
  22.             nodes = [nodes];
  23.         } else if (nodes._node) { // Y.Node
  24.             nodes = [nodes._node];
  25.         } else if (nodes[0] && nodes[0]._node) { // allow array of Y.Nodes
  26.             Y.Array.each(nodes, function(node) {
  27.                 if (node._node) {
  28.                     tmp.push(node._node);
  29.                 }
  30.             });
  31.             nodes = tmp;
  32.         } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes)
  33.             nodes = Y.Array(nodes, 0, true);
  34.         }
  35.     }

  36.     /**
  37.      * The underlying array of DOM nodes bound to the Y.NodeList instance
  38.      * @property _nodes
  39.      * @private
  40.      */
  41.     this._nodes = nodes || [];
  42. };

  43. NodeList.NAME = 'NodeList';

  44. /**
  45.  * Retrieves the DOM nodes bound to a NodeList instance
  46.  * @method getDOMNodes
  47.  * @static
  48.  *
  49.  * @param {NodeList} nodelist The NodeList instance
  50.  * @return {Array} The array of DOM nodes bound to the NodeList
  51.  */
  52. NodeList.getDOMNodes = function(nodelist) {
  53.     return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist;
  54. };

  55. NodeList.each = function(instance, fn, context) {
  56.     var nodes = instance._nodes;
  57.     if (nodes && nodes.length) {
  58.         Y.Array.each(nodes, fn, context || instance);
  59.     } else {
  60.         Y.log('no nodes bound to ' + this, 'warn', 'NodeList');
  61.     }
  62. };

  63. NodeList.addMethod = function(name, fn, context) {
  64.     if (name && fn) {
  65.         NodeList.prototype[name] = function() {
  66.             var ret = [],
  67.                 args = arguments;

  68.             Y.Array.each(this._nodes, function(node) {
  69.                 var UID = (node.uniqueID && node.nodeType !== 9 ) ? 'uniqueID' : '_yuid',
  70.                     instance = Y.Node._instances[node[UID]],
  71.                     ctx,
  72.                     result;

  73.                 if (!instance) {
  74.                     instance = NodeList._getTempNode(node);
  75.                 }
  76.                 ctx = context || instance;
  77.                 result = fn.apply(ctx, args);
  78.                 if (result !== undefined && result !== instance) {
  79.                     ret[ret.length] = result;
  80.                 }
  81.             });

  82.             // TODO: remove tmp pointer
  83.             return ret.length ? ret : this;
  84.         };
  85.     } else {
  86.         Y.log('unable to add method: ' + name + ' to NodeList', 'warn', 'node');
  87.     }
  88. };

  89. NodeList.importMethod = function(host, name, altName) {
  90.     if (typeof name === 'string') {
  91.         altName = altName || name;
  92.         NodeList.addMethod(name, host[name]);
  93.     } else {
  94.         Y.Array.each(name, function(n) {
  95.             NodeList.importMethod(host, n);
  96.         });
  97.     }
  98. };

  99. NodeList._getTempNode = function(node) {
  100.     var tmp = NodeList._tempNode;
  101.     if (!tmp) {
  102.         tmp = Y.Node.create('<div></div>');
  103.         NodeList._tempNode = tmp;
  104.     }

  105.     tmp._node = node;
  106.     tmp._stateProxy = node;
  107.     return tmp;
  108. };

  109. Y.mix(NodeList.prototype, {
  110.     _invoke: function(method, args, getter) {
  111.         var ret = (getter) ? [] : this;

  112.         this.each(function(node) {
  113.             var val = node[method].apply(node, args);
  114.             if (getter) {
  115.                 ret.push(val);
  116.             }
  117.         });

  118.         return ret;
  119.     },

  120.     /**
  121.      * Retrieves the Node instance at the given index.
  122.      * @method item
  123.      *
  124.      * @param {Number} index The index of the target Node.
  125.      * @return {Node} The Node instance at the given index.
  126.      */
  127.     item: function(index) {
  128.         return Y.one((this._nodes || [])[index]);
  129.     },

  130.     /**
  131.      * Applies the given function to each Node in the NodeList.
  132.      * @method each
  133.      * @param {Function} fn The function to apply. It receives 3 arguments:
  134.      * the current node instance, the node's index, and the NodeList instance
  135.      * @param {Object} context optional An optional context to apply the function with
  136.      * Default context is the current Node instance
  137.      * @chainable
  138.      */
  139.     each: function(fn, context) {
  140.         var instance = this;
  141.         Y.Array.each(this._nodes, function(node, index) {
  142.             node = Y.one(node);
  143.             return fn.call(context || node, node, index, instance);
  144.         });
  145.         return instance;
  146.     },

  147.     batch: function(fn, context) {
  148.         var nodelist = this;

  149.         Y.Array.each(this._nodes, function(node, index) {
  150.             var instance = Y.Node._instances[node[UID]];
  151.             if (!instance) {
  152.                 instance = NodeList._getTempNode(node);
  153.             }

  154.             return fn.call(context || instance, instance, index, nodelist);
  155.         });
  156.         return nodelist;
  157.     },

  158.     /**
  159.      * Executes the function once for each node until a true value is returned.
  160.      * @method some
  161.      * @param {Function} fn The function to apply. It receives 3 arguments:
  162.      * the current node instance, the node's index, and the NodeList instance
  163.      * @param {Object} context optional An optional context to execute the function from.
  164.      * Default context is the current Node instance
  165.      * @return {Boolean} Whether or not the function returned true for any node.
  166.      */
  167.     some: function(fn, context) {
  168.         var instance = this;
  169.         return Y.Array.some(this._nodes, function(node, index) {
  170.             node = Y.one(node);
  171.             context = context || node;
  172.             return fn.call(context, node, index, instance);
  173.         });
  174.     },

  175.     /**
  176.      * Creates a documenFragment from the nodes bound to the NodeList instance
  177.      * @method toFrag
  178.      * @return {Node} a Node instance bound to the documentFragment
  179.      */
  180.     toFrag: function() {
  181.         return Y.one(Y.DOM._nl2frag(this._nodes));
  182.     },

  183.     /**
  184.      * Returns the index of the node in the NodeList instance
  185.      * or -1 if the node isn't found.
  186.      * @method indexOf
  187.      * @param {Node | DOMNode} node the node to search for
  188.      * @return {Int} the index of the node value or -1 if not found
  189.      */
  190.     indexOf: function(node) {
  191.         return Y.Array.indexOf(this._nodes, Y.Node.getDOMNode(node));
  192.     },

  193.     /**
  194.      * Filters the NodeList instance down to only nodes matching the given selector.
  195.      * @method filter
  196.      * @param {String} selector The selector to filter against
  197.      * @return {NodeList} NodeList containing the updated collection
  198.      * @see Selector
  199.      */
  200.     filter: function(selector) {
  201.         return Y.all(Y.Selector.filter(this._nodes, selector));
  202.     },


  203.     /**
  204.      * Creates a new NodeList containing all nodes at every n indices, where
  205.      * remainder n % index equals r.
  206.      * (zero-based index).
  207.      * @method modulus
  208.      * @param {Int} n The offset to use (return every nth node)
  209.      * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
  210.      * @return {NodeList} NodeList containing the updated collection
  211.      */
  212.     modulus: function(n, r) {
  213.         r = r || 0;
  214.         var nodes = [];
  215.         NodeList.each(this, function(node, i) {
  216.             if (i % n === r) {
  217.                 nodes.push(node);
  218.             }
  219.         });

  220.         return Y.all(nodes);
  221.     },

  222.     /**
  223.      * Creates a new NodeList containing all nodes at odd indices
  224.      * (zero-based index).
  225.      * @method odd
  226.      * @return {NodeList} NodeList containing the updated collection
  227.      */
  228.     odd: function() {
  229.         return this.modulus(2, 1);
  230.     },

  231.     /**
  232.      * Creates a new NodeList containing all nodes at even indices
  233.      * (zero-based index), including zero.
  234.      * @method even
  235.      * @return {NodeList} NodeList containing the updated collection
  236.      */
  237.     even: function() {
  238.         return this.modulus(2);
  239.     },

  240.     destructor: function() {
  241.     },

  242.     /**
  243.      * Reruns the initial query, when created using a selector query
  244.      * @method refresh
  245.      * @chainable
  246.      */
  247.     refresh: function() {
  248.         var doc,
  249.             nodes = this._nodes,
  250.             query = this._query,
  251.             root = this._queryRoot;

  252.         if (query) {
  253.             if (!root) {
  254.                 if (nodes && nodes[0] && nodes[0].ownerDocument) {
  255.                     root = nodes[0].ownerDocument;
  256.                 }
  257.             }

  258.             this._nodes = Y.Selector.query(query, root);
  259.         }

  260.         return this;
  261.     },

  262.     /**
  263.      * Returns the current number of items in the NodeList.
  264.      * @method size
  265.      * @return {Int} The number of items in the NodeList.
  266.      */
  267.     size: function() {
  268.         return this._nodes.length;
  269.     },

  270.     /**
  271.      * Determines if the instance is bound to any nodes
  272.      * @method isEmpty
  273.      * @return {Boolean} Whether or not the NodeList is bound to any nodes
  274.      */
  275.     isEmpty: function() {
  276.         return this._nodes.length < 1;
  277.     },

  278.     toString: function() {
  279.         var str = '',
  280.             errorMsg = this[UID] + ': not bound to any nodes',
  281.             nodes = this._nodes,
  282.             node;

  283.         if (nodes && nodes[0]) {
  284.             node = nodes[0];
  285.             str += node[NODE_NAME];
  286.             if (node.id) {
  287.                 str += '#' + node.id;
  288.             }

  289.             if (node.className) {
  290.                 str += '.' + node.className.replace(' ', '.');
  291.             }

  292.             if (nodes.length > 1) {
  293.                 str += '...[' + nodes.length + ' items]';
  294.             }
  295.         }
  296.         return str || errorMsg;
  297.     },

  298.     /**
  299.      * Returns the DOM node bound to the Node instance
  300.      * @method getDOMNodes
  301.      * @return {Array}
  302.      */
  303.     getDOMNodes: function() {
  304.         return this._nodes;
  305.     }
  306. }, true);

  307. NodeList.importMethod(Y.Node.prototype, [
  308.      /**
  309.       * Called on each Node instance. Nulls internal node references,
  310.       * removes any plugins and event listeners
  311.       * @method destroy
  312.       * @param {Boolean} recursivePurge (optional) Whether or not to
  313.       * remove listeners from the node's subtree (default is false)
  314.       * @see Node.destroy
  315.       */
  316.     'destroy',

  317.      /**
  318.       * Called on each Node instance. Removes and destroys all of the nodes
  319.       * within the node
  320.       * @method empty
  321.       * @chainable
  322.       * @see Node.empty
  323.       */
  324.     'empty',

  325.      /**
  326.       * Called on each Node instance. Removes the node from its parent.
  327.       * Shortcut for myNode.get('parentNode').removeChild(myNode);
  328.       * @method remove
  329.       * @param {Boolean} destroy whether or not to call destroy() on the node
  330.       * after removal.
  331.       * @chainable
  332.       * @see Node.remove
  333.       */
  334.     'remove',

  335.      /**
  336.       * Called on each Node instance. Sets an attribute on the Node instance.
  337.       * Unless pre-configured (via Node.ATTRS), set hands
  338.       * off to the underlying DOM node.  Only valid
  339.       * attributes/properties for the node will be set.
  340.       * To set custom attributes use setAttribute.
  341.       * @method set
  342.       * @param {String} attr The attribute to be set.
  343.       * @param {any} val The value to set the attribute to.
  344.       * @chainable
  345.       * @see Node.set
  346.       */
  347.     'set'
  348. ]);

  349. // one-off implementation to convert array of Nodes to NodeList
  350. // e.g. Y.all('input').get('parentNode');

  351. /** Called on each Node instance
  352.   * @method get
  353.   * @see Node
  354.   */
  355. NodeList.prototype.get = function(attr) {
  356.     var ret = [],
  357.         nodes = this._nodes,
  358.         isNodeList = false,
  359.         getTemp = NodeList._getTempNode,
  360.         instance,
  361.         val;

  362.     if (nodes[0]) {
  363.         instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
  364.         val = instance._get(attr);
  365.         if (val && val.nodeType) {
  366.             isNodeList = true;
  367.         }
  368.     }

  369.     Y.Array.each(nodes, function(node) {
  370.         instance = Y.Node._instances[node._yuid];

  371.         if (!instance) {
  372.             instance = getTemp(node);
  373.         }

  374.         val = instance._get(attr);
  375.         if (!isNodeList) { // convert array of Nodes to NodeList
  376.             val = Y.Node.scrubVal(val, instance);
  377.         }

  378.         ret.push(val);
  379.     });

  380.     return (isNodeList) ? Y.all(ret) : ret;
  381. };

  382. Y.NodeList = NodeList;

  383. Y.all = function(nodes) {
  384.     return new NodeList(nodes);
  385. };

  386. Y.Node.all = Y.all;

  387.