You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.9 KiB
188 lines
5.9 KiB
/** |
|
* Copyright (c) 2013-present, Facebook, Inc. |
|
* |
|
* This source code is licensed under the MIT license found in the |
|
* LICENSE file in the root directory of this source tree. |
|
* |
|
*/ |
|
|
|
'use strict'; |
|
|
|
var PooledClass = require('./PooledClass'); |
|
var ReactElement = require('./ReactElement'); |
|
|
|
var emptyFunction = require('fbjs/lib/emptyFunction'); |
|
var traverseAllChildren = require('./traverseAllChildren'); |
|
|
|
var twoArgumentPooler = PooledClass.twoArgumentPooler; |
|
var fourArgumentPooler = PooledClass.fourArgumentPooler; |
|
|
|
var userProvidedKeyEscapeRegex = /\/+/g; |
|
function escapeUserProvidedKey(text) { |
|
return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/'); |
|
} |
|
|
|
/** |
|
* PooledClass representing the bookkeeping associated with performing a child |
|
* traversal. Allows avoiding binding callbacks. |
|
* |
|
* @constructor ForEachBookKeeping |
|
* @param {!function} forEachFunction Function to perform traversal with. |
|
* @param {?*} forEachContext Context to perform context with. |
|
*/ |
|
function ForEachBookKeeping(forEachFunction, forEachContext) { |
|
this.func = forEachFunction; |
|
this.context = forEachContext; |
|
this.count = 0; |
|
} |
|
ForEachBookKeeping.prototype.destructor = function () { |
|
this.func = null; |
|
this.context = null; |
|
this.count = 0; |
|
}; |
|
PooledClass.addPoolingTo(ForEachBookKeeping, twoArgumentPooler); |
|
|
|
function forEachSingleChild(bookKeeping, child, name) { |
|
var func = bookKeeping.func, |
|
context = bookKeeping.context; |
|
|
|
func.call(context, child, bookKeeping.count++); |
|
} |
|
|
|
/** |
|
* Iterates through children that are typically specified as `props.children`. |
|
* |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.foreach |
|
* |
|
* The provided forEachFunc(child, index) will be called for each |
|
* leaf child. |
|
* |
|
* @param {?*} children Children tree container. |
|
* @param {function(*, int)} forEachFunc |
|
* @param {*} forEachContext Context for forEachContext. |
|
*/ |
|
function forEachChildren(children, forEachFunc, forEachContext) { |
|
if (children == null) { |
|
return children; |
|
} |
|
var traverseContext = ForEachBookKeeping.getPooled(forEachFunc, forEachContext); |
|
traverseAllChildren(children, forEachSingleChild, traverseContext); |
|
ForEachBookKeeping.release(traverseContext); |
|
} |
|
|
|
/** |
|
* PooledClass representing the bookkeeping associated with performing a child |
|
* mapping. Allows avoiding binding callbacks. |
|
* |
|
* @constructor MapBookKeeping |
|
* @param {!*} mapResult Object containing the ordered map of results. |
|
* @param {!function} mapFunction Function to perform mapping with. |
|
* @param {?*} mapContext Context to perform mapping with. |
|
*/ |
|
function MapBookKeeping(mapResult, keyPrefix, mapFunction, mapContext) { |
|
this.result = mapResult; |
|
this.keyPrefix = keyPrefix; |
|
this.func = mapFunction; |
|
this.context = mapContext; |
|
this.count = 0; |
|
} |
|
MapBookKeeping.prototype.destructor = function () { |
|
this.result = null; |
|
this.keyPrefix = null; |
|
this.func = null; |
|
this.context = null; |
|
this.count = 0; |
|
}; |
|
PooledClass.addPoolingTo(MapBookKeeping, fourArgumentPooler); |
|
|
|
function mapSingleChildIntoContext(bookKeeping, child, childKey) { |
|
var result = bookKeeping.result, |
|
keyPrefix = bookKeeping.keyPrefix, |
|
func = bookKeeping.func, |
|
context = bookKeeping.context; |
|
|
|
|
|
var mappedChild = func.call(context, child, bookKeeping.count++); |
|
if (Array.isArray(mappedChild)) { |
|
mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, emptyFunction.thatReturnsArgument); |
|
} else if (mappedChild != null) { |
|
if (ReactElement.isValidElement(mappedChild)) { |
|
mappedChild = ReactElement.cloneAndReplaceKey(mappedChild, |
|
// Keep both the (mapped) and old keys if they differ, just as |
|
// traverseAllChildren used to do for objects as children |
|
keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey); |
|
} |
|
result.push(mappedChild); |
|
} |
|
} |
|
|
|
function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) { |
|
var escapedPrefix = ''; |
|
if (prefix != null) { |
|
escapedPrefix = escapeUserProvidedKey(prefix) + '/'; |
|
} |
|
var traverseContext = MapBookKeeping.getPooled(array, escapedPrefix, func, context); |
|
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext); |
|
MapBookKeeping.release(traverseContext); |
|
} |
|
|
|
/** |
|
* Maps children that are typically specified as `props.children`. |
|
* |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.map |
|
* |
|
* The provided mapFunction(child, key, index) will be called for each |
|
* leaf child. |
|
* |
|
* @param {?*} children Children tree container. |
|
* @param {function(*, int)} func The map function. |
|
* @param {*} context Context for mapFunction. |
|
* @return {object} Object containing the ordered map of results. |
|
*/ |
|
function mapChildren(children, func, context) { |
|
if (children == null) { |
|
return children; |
|
} |
|
var result = []; |
|
mapIntoWithKeyPrefixInternal(children, result, null, func, context); |
|
return result; |
|
} |
|
|
|
function forEachSingleChildDummy(traverseContext, child, name) { |
|
return null; |
|
} |
|
|
|
/** |
|
* Count the number of children that are typically specified as |
|
* `props.children`. |
|
* |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.count |
|
* |
|
* @param {?*} children Children tree container. |
|
* @return {number} The number of children. |
|
*/ |
|
function countChildren(children, context) { |
|
return traverseAllChildren(children, forEachSingleChildDummy, null); |
|
} |
|
|
|
/** |
|
* Flatten a children object (typically specified as `props.children`) and |
|
* return an array with appropriately re-keyed children. |
|
* |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.children.toarray |
|
*/ |
|
function toArray(children) { |
|
var result = []; |
|
mapIntoWithKeyPrefixInternal(children, result, null, emptyFunction.thatReturnsArgument); |
|
return result; |
|
} |
|
|
|
var ReactChildren = { |
|
forEach: forEachChildren, |
|
map: mapChildren, |
|
mapIntoWithKeyPrefixInternal: mapIntoWithKeyPrefixInternal, |
|
count: countChildren, |
|
toArray: toArray |
|
}; |
|
|
|
module.exports = ReactChildren; |