190 lines
6.0 KiB
JavaScript
190 lines
6.0 KiB
JavaScript
/**
|
|
* Copyright 2013-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
*/
|
|
|
|
'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; |