react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / utils / traverseAllChildren.js
81152 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule traverseAllChildren9*/1011"use strict";1213var ReactElement = require('ReactElement');14var ReactInstanceHandles = require('ReactInstanceHandles');1516var invariant = require('invariant');1718var SEPARATOR = ReactInstanceHandles.SEPARATOR;19var SUBSEPARATOR = ':';2021/**22* TODO: Test that:23* 1. `mapChildren` transforms strings and numbers into `ReactTextComponent`.24* 2. it('should fail when supplied duplicate key', function() {25* 3. That a single child and an array with one item have the same key pattern.26* });27*/2829var userProvidedKeyEscaperLookup = {30'=': '=0',31'.': '=1',32':': '=2'33};3435var userProvidedKeyEscapeRegex = /[=.:]/g;3637function userProvidedKeyEscaper(match) {38return userProvidedKeyEscaperLookup[match];39}4041/**42* Generate a key string that identifies a component within a set.43*44* @param {*} component A component that could contain a manual key.45* @param {number} index Index that is used if a manual key is not provided.46* @return {string}47*/48function getComponentKey(component, index) {49if (component && component.key != null) {50// Explicit key51return wrapUserProvidedKey(component.key);52}53// Implicit key determined by the index in the set54return index.toString(36);55}5657/**58* Escape a component key so that it is safe to use in a reactid.59*60* @param {*} key Component key to be escaped.61* @return {string} An escaped string.62*/63function escapeUserProvidedKey(text) {64return ('' + text).replace(65userProvidedKeyEscapeRegex,66userProvidedKeyEscaper67);68}6970/**71* Wrap a `key` value explicitly provided by the user to distinguish it from72* implicitly-generated keys generated by a component's index in its parent.73*74* @param {string} key Value of a user-provided `key` attribute75* @return {string}76*/77function wrapUserProvidedKey(key) {78return '$' + escapeUserProvidedKey(key);79}8081/**82* @param {?*} children Children tree container.83* @param {!string} nameSoFar Name of the key path so far.84* @param {!number} indexSoFar Number of children encountered until this point.85* @param {!function} callback Callback to invoke with each child found.86* @param {?*} traverseContext Used to pass information throughout the traversal87* process.88* @return {!number} The number of children in this subtree.89*/90var traverseAllChildrenImpl =91function(children, nameSoFar, indexSoFar, callback, traverseContext) {92var nextName, nextIndex;93var subtreeCount = 0; // Count of children found in the current subtree.94if (Array.isArray(children)) {95for (var i = 0; i < children.length; i++) {96var child = children[i];97nextName = (98nameSoFar +99(nameSoFar ? SUBSEPARATOR : SEPARATOR) +100getComponentKey(child, i)101);102nextIndex = indexSoFar + subtreeCount;103subtreeCount += traverseAllChildrenImpl(104child,105nextName,106nextIndex,107callback,108traverseContext109);110}111} else {112var type = typeof children;113var isOnlyChild = nameSoFar === '';114// If it's the only child, treat the name as if it was wrapped in an array115// so that it's consistent if the number of children grows116var storageName =117isOnlyChild ? SEPARATOR + getComponentKey(children, 0) : nameSoFar;118if (children == null || type === 'boolean') {119// All of the above are perceived as null.120callback(traverseContext, null, storageName, indexSoFar);121subtreeCount = 1;122} else if (type === 'string' || type === 'number' ||123ReactElement.isValidElement(children)) {124callback(traverseContext, children, storageName, indexSoFar);125subtreeCount = 1;126} else if (type === 'object') {127invariant(128!children || children.nodeType !== 1,129'traverseAllChildren(...): Encountered an invalid child; DOM ' +130'elements are not valid children of React components.'131);132for (var key in children) {133if (children.hasOwnProperty(key)) {134nextName = (135nameSoFar + (nameSoFar ? SUBSEPARATOR : SEPARATOR) +136wrapUserProvidedKey(key) + SUBSEPARATOR +137getComponentKey(children[key], 0)138);139nextIndex = indexSoFar + subtreeCount;140subtreeCount += traverseAllChildrenImpl(141children[key],142nextName,143nextIndex,144callback,145traverseContext146);147}148}149}150}151return subtreeCount;152};153154/**155* Traverses children that are typically specified as `props.children`, but156* might also be specified through attributes:157*158* - `traverseAllChildren(this.props.children, ...)`159* - `traverseAllChildren(this.props.leftPanelChildren, ...)`160*161* The `traverseContext` is an optional argument that is passed through the162* entire traversal. It can be used to store accumulations or anything else that163* the callback might find relevant.164*165* @param {?*} children Children tree object.166* @param {!function} callback To invoke upon traversing each child.167* @param {?*} traverseContext Context for traversal.168* @return {!number} The number of children in this subtree.169*/170function traverseAllChildren(children, callback, traverseContext) {171if (children == null) {172return 0;173}174175return traverseAllChildrenImpl(children, '', 0, callback, traverseContext);176}177178module.exports = traverseAllChildren;179180181