#

API Docs for: 3.10.3
Show: Inherited Protected Private Deprecated

# File: node/js/nodelist.js

    /**
     * The NodeList module provides support for managing collections of Nodes.
     * @module node
     * @submodule node-core
     */

    /**
     * The NodeList class provides a wrapper for manipulating DOM NodeLists.
     * NodeList properties can be accessed via the set/get methods.
     * Use Y.all() to retrieve NodeList instances.
     *
     * @class NodeList
     * @constructor
     * @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.
     */

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

        if (nodes) {
            if (typeof nodes === 'string') { // selector query
                this._query = nodes;
                nodes = Y.Selector.query(nodes);
            } else if (nodes.nodeType || Y_DOM.isWindow(nodes)) { // domNode || window
                nodes = [nodes];
            } else if (nodes._node) { // Y.Node
                nodes = [nodes._node];
            } else if (nodes[0] && nodes[0]._node) { // allow array of Y.Nodes
                Y.Array.each(nodes, function(node) {
                    if (node._node) {
                        tmp.push(node._node);
                    }
                });
                nodes = tmp;
            } else { // array of domNodes or domNodeList (no mixed array of Y.Node/domNodes)
                nodes = Y.Array(nodes, 0, true);
            }
        }

        /**
         * The underlying array of DOM nodes bound to the Y.NodeList instance
         * @property _nodes
         * @private
         */
        this._nodes = nodes || [];
    };

    NodeList.NAME = 'NodeList';

    /**
     * Retrieves the DOM nodes bound to a NodeList instance
     * @method getDOMNodes
     * @static
     *
     * @param {NodeList} nodelist The NodeList instance
     * @return {Array} The array of DOM nodes bound to the NodeList
     */
    NodeList.getDOMNodes = function(nodelist) {
        return (nodelist && nodelist._nodes) ? nodelist._nodes : nodelist;
    };

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

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

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

                    if (!instance) {
                        instance = NodeList._getTempNode(node);
                    }
                    ctx = context || instance;
                    result = fn.apply(ctx, args);
                    if (result !== undefined && result !== instance) {
                        ret[ret.length] = result;
                    }
                });

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

    NodeList.importMethod = function(host, name, altName) {
        if (typeof name === 'string') {
            altName = altName || name;
            NodeList.addMethod(name, host[name]);
        } else {
            Y.Array.each(name, function(n) {
                NodeList.importMethod(host, n);
            });
        }
    };

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

        tmp._node = node;
        tmp._stateProxy = node;
        return tmp;
    };

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

            this.each(function(node) {
                var val = node[method].apply(node, args);
                if (getter) {
                    ret.push(val);
                }
            });

            return ret;
        },

        /**
         * Retrieves the Node instance at the given index.
         * @method item
         *
         * @param {Number} index The index of the target Node.
         * @return {Node} The Node instance at the given index.
         */
        item: function(index) {
            return Y.one((this._nodes || [])[index]);
        },

        /**
         * Applies the given function to each Node in the NodeList.
         * @method each
         * @param {Function} fn The function to apply. It receives 3 arguments:
         * the current node instance, the node's index, and the NodeList instance
         * @param {Object} context optional An optional context to apply the function with
         * Default context is the current Node instance
         * @chainable
         */
        each: function(fn, context) {
            var instance = this;
            Y.Array.each(this._nodes, function(node, index) {
                node = Y.one(node);
                return fn.call(context || node, node, index, instance);
            });
            return instance;
        },

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

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

                return fn.call(context || instance, instance, index, nodelist);
            });
            return nodelist;
        },

        /**
         * Executes the function once for each node until a true value is returned.
         * @method some
         * @param {Function} fn The function to apply. It receives 3 arguments:
         * the current node instance, the node's index, and the NodeList instance
         * @param {Object} context optional An optional context to execute the function from.
         * Default context is the current Node instance
         * @return {Boolean} Whether or not the function returned true for any node.
         */
        some: function(fn, context) {
            var instance = this;
            return Y.Array.some(this._nodes, function(node, index) {
                node = Y.one(node);
                context = context || node;
                return fn.call(context, node, index, instance);
            });
        },

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

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

        /**
         * Filters the NodeList instance down to only nodes matching the given selector.
         * @method filter
         * @param {String} selector The selector to filter against
         * @return {NodeList} NodeList containing the updated collection
         * @see Selector
         */
        filter: function(selector) {
            return Y.all(Y.Selector.filter(this._nodes, selector));
        },

        /**
         * Creates a new NodeList containing all nodes at every n indices, where
         * remainder n % index equals r.
         * (zero-based index).
         * @method modulus
         * @param {Int} n The offset to use (return every nth node)
         * @param {Int} r An optional remainder to use with the modulus operation (defaults to zero)
         * @return {NodeList} NodeList containing the updated collection
         */
        modulus: function(n, r) {
            r = r || 0;
            var nodes = [];
            NodeList.each(this, function(node, i) {
                if (i % n === r) {
                    nodes.push(node);
                }
            });

            return Y.all(nodes);
        },

        /**
         * Creates a new NodeList containing all nodes at odd indices
         * (zero-based index).
         * @method odd
         * @return {NodeList} NodeList containing the updated collection
         */
        odd: function() {
            return this.modulus(2, 1);
        },

        /**
         * Creates a new NodeList containing all nodes at even indices
         * (zero-based index), including zero.
         * @method even
         * @return {NodeList} NodeList containing the updated collection
         */
        even: function() {
            return this.modulus(2);
        },

        destructor: function() {
        },

        /**
         * Reruns the initial query, when created using a selector query
         * @method refresh
         * @chainable
         */
        refresh: function() {
            var doc,
                nodes = this._nodes,
                query = this._query,
                root = this._queryRoot;

            if (query) {
                if (!root) {
                    if (nodes && nodes[0] && nodes[0].ownerDocument) {
                        root = nodes[0].ownerDocument;
                    }
                }

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

            return this;
        },

        /**
         * Returns the current number of items in the NodeList.
         * @method size
         * @return {Int} The number of items in the NodeList.
         */
        size: function() {
            return this._nodes.length;
        },

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

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

            if (nodes && nodes[0]) {
                node = nodes[0];
                str += node[NODE_NAME];
                if (node.id) {
                    str += '#' + node.id;
                }

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

                if (nodes.length > 1) {
                    str += '...[' + nodes.length + ' items]';
                }
            }
            return str || errorMsg;
        },

        /**
         * Returns the DOM node bound to the Node instance
         * @method getDOMNodes
         * @return {Array}
         */
        getDOMNodes: function() {
            return this._nodes;
        }
    }, true);

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

         /**
          * Called on each Node instance. Removes and destroys all of the nodes
          * within the node
          * @method empty
          * @chainable
          * @see Node.empty
          */
        'empty',

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

         /**
          * Called on each Node instance. Sets an attribute on the Node instance.
          * Unless pre-configured (via Node.ATTRS), set hands
          * off to the underlying DOM node.  Only valid
          * attributes/properties for the node will be set.
          * To set custom attributes use setAttribute.
          * @method set
          * @param {String} attr The attribute to be set.
          * @param {any} val The value to set the attribute to.
          * @chainable
          * @see Node.set
          */
        'set'
    ]);

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

    /** Called on each Node instance
      * @method get
      * @see Node
      */
    NodeList.prototype.get = function(attr) {
        var ret = [],
            nodes = this._nodes,
            isNodeList = false,
            getTemp = NodeList._getTempNode,
            instance,
            val;

        if (nodes[0]) {
            instance = Y.Node._instances[nodes[0]._yuid] || getTemp(nodes[0]);
            val = instance._get(attr);
            if (val && val.nodeType) {
                isNodeList = true;
            }
        }

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

            if (!instance) {
                instance = getTemp(node);
            }

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

            ret.push(val);
        });

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

    Y.NodeList = NodeList;

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

    Y.Node.all = Y.all;