UNPKG

@danielkalen/simplybind

Version:

Magically simple, framework-less one-way/two-way data binding for frontend/backend in ~5kb.

215 lines (191 loc) 6.08 kB
/** * Copyright 2013-2015, 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. * * @providesModule ReactChildren */ var React = require('../dist/react-lite.common') var PooledClass = require('./utils/PooledClass') var emptyFunction = require('./utils/emptyFunction') var traverseAllChildren = require('./utils/traverseAllChildren') var isValidElement = React.isValidElement var cloneElement = React.cloneElement var twoArgumentPooler = PooledClass.twoArgumentPooler; var fourArgumentPooler = PooledClass.fourArgumentPooler; var cloneAndReplaceKey = function(oldElement, newKey) { var newElement = cloneElement( oldElement, { key, newKey } ); return newElement; } 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, context} = bookKeeping; func.call(context, child, bookKeeping.count++); } /** * Iterates through children that are typically specified as `props.children`. * * 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, keyPrefix, func, context} = bookKeeping; var mappedChild = func.call(context, child, bookKeeping.count++); if (Array.isArray(mappedChild)) { mapIntoWithKeyPrefixInternal( mappedChild, result, childKey, emptyFunction.thatReturnsArgument ); } else if (mappedChild != null) { if (isValidElement(mappedChild)) { mappedChild = cloneAndReplaceKey( mappedChild, // Keep both the (mapped) and old keys if they differ, just as // traverseAllChildren used to do for objects as children keyPrefix + ( mappedChild !== child ? 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`. * * 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`. * * @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. */ 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;