Version 3.18.1
Show:

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');
                }
            };
            
            /**
             * Import the named method, or methods from the host onto NodeList.
             *
             * @method importMethod
             * @static
             * @param {Object} host The object containing the methods to copy. Typically a prototype.
             * @param {String|String[]} name The name, or an Array of names of the methods to import onto NodeList.
             * @param {String} [altName] An alternative name to use for the method added to NodeList, which may differ from the name
             * of the original host object. Has no effect if <em>name</em> is an array of method names.
             */
            NodeList.importMethod = function(host, name, altName) {
                if (typeof name === 'string') {
                    altName = altName || name;
                    NodeList.addMethod(altName, 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 | HTMLElement} node the node to search for
                 * @return {Number} 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 {Number} n The offset to use (return every nth node)
                 * @param {Number} 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 {Number} 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;