mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-28 20:25:33 +08:00
[react-packager][streamline oss] Move open sourced JS source to react-native-github
This commit is contained in:
348
Libraries/vendor/react/core/ReactInstanceHandles.js
vendored
Normal file
348
Libraries/vendor/react/core/ReactInstanceHandles.js
vendored
Normal file
@@ -0,0 +1,348 @@
|
||||
/**
|
||||
* Copyright 2013-2014 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @providesModule ReactInstanceHandles
|
||||
* @typechecks static-only
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var ReactRootIndex = require('ReactRootIndex');
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var SEPARATOR = '.';
|
||||
var SEPARATOR_LENGTH = SEPARATOR.length;
|
||||
|
||||
/**
|
||||
* Maximum depth of traversals before we consider the possibility of a bad ID.
|
||||
*/
|
||||
var MAX_TREE_DEPTH = 100;
|
||||
|
||||
/**
|
||||
* Creates a DOM ID prefix to use when mounting React components.
|
||||
*
|
||||
* @param {number} index A unique integer
|
||||
* @return {string} React root ID.
|
||||
* @internal
|
||||
*/
|
||||
function getReactRootIDString(index) {
|
||||
return SEPARATOR + index.toString(36);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a character in the supplied ID is a separator or the end.
|
||||
*
|
||||
* @param {string} id A React DOM ID.
|
||||
* @param {number} index Index of the character to check.
|
||||
* @return {boolean} True if the character is a separator or end of the ID.
|
||||
* @private
|
||||
*/
|
||||
function isBoundary(id, index) {
|
||||
return id.charAt(index) === SEPARATOR || index === id.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the supplied string is a valid React DOM ID.
|
||||
*
|
||||
* @param {string} id A React DOM ID, maybe.
|
||||
* @return {boolean} True if the string is a valid React DOM ID.
|
||||
* @private
|
||||
*/
|
||||
function isValidID(id) {
|
||||
return id === '' || (
|
||||
id.charAt(0) === SEPARATOR && id.charAt(id.length - 1) !== SEPARATOR
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the first ID is an ancestor of or equal to the second ID.
|
||||
*
|
||||
* @param {string} ancestorID
|
||||
* @param {string} descendantID
|
||||
* @return {boolean} True if `ancestorID` is an ancestor of `descendantID`.
|
||||
* @internal
|
||||
*/
|
||||
function isAncestorIDOf(ancestorID, descendantID) {
|
||||
return (
|
||||
descendantID.indexOf(ancestorID) === 0 &&
|
||||
isBoundary(descendantID, ancestorID.length)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent ID of the supplied React DOM ID, `id`.
|
||||
*
|
||||
* @param {string} id ID of a component.
|
||||
* @return {string} ID of the parent, or an empty string.
|
||||
* @private
|
||||
*/
|
||||
function getParentID(id) {
|
||||
return id ? id.substr(0, id.lastIndexOf(SEPARATOR)) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
|
||||
* supplied `destinationID`. If they are equal, the ID is returned.
|
||||
*
|
||||
* @param {string} ancestorID ID of an ancestor node of `destinationID`.
|
||||
* @param {string} destinationID ID of the destination node.
|
||||
* @return {string} Next ID on the path from `ancestorID` to `destinationID`.
|
||||
* @private
|
||||
*/
|
||||
function getNextDescendantID(ancestorID, destinationID) {
|
||||
invariant(
|
||||
isValidID(ancestorID) && isValidID(destinationID),
|
||||
'getNextDescendantID(%s, %s): Received an invalid React DOM ID.',
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
invariant(
|
||||
isAncestorIDOf(ancestorID, destinationID),
|
||||
'getNextDescendantID(...): React has made an invalid assumption about ' +
|
||||
'the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.',
|
||||
ancestorID,
|
||||
destinationID
|
||||
);
|
||||
if (ancestorID === destinationID) {
|
||||
return ancestorID;
|
||||
}
|
||||
// Skip over the ancestor and the immediate separator. Traverse until we hit
|
||||
// another separator or we reach the end of `destinationID`.
|
||||
var start = ancestorID.length + SEPARATOR_LENGTH;
|
||||
for (var i = start; i < destinationID.length; i++) {
|
||||
if (isBoundary(destinationID, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return destinationID.substr(0, i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nearest common ancestor ID of two IDs.
|
||||
*
|
||||
* Using this ID scheme, the nearest common ancestor ID is the longest common
|
||||
* prefix of the two IDs that immediately preceded a "marker" in both strings.
|
||||
*
|
||||
* @param {string} oneID
|
||||
* @param {string} twoID
|
||||
* @return {string} Nearest common ancestor ID, or the empty string if none.
|
||||
* @private
|
||||
*/
|
||||
function getFirstCommonAncestorID(oneID, twoID) {
|
||||
var minLength = Math.min(oneID.length, twoID.length);
|
||||
if (minLength === 0) {
|
||||
return '';
|
||||
}
|
||||
var lastCommonMarkerIndex = 0;
|
||||
// Use `<=` to traverse until the "EOL" of the shorter string.
|
||||
for (var i = 0; i <= minLength; i++) {
|
||||
if (isBoundary(oneID, i) && isBoundary(twoID, i)) {
|
||||
lastCommonMarkerIndex = i;
|
||||
} else if (oneID.charAt(i) !== twoID.charAt(i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
|
||||
invariant(
|
||||
isValidID(longestCommonID),
|
||||
'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
|
||||
oneID,
|
||||
twoID,
|
||||
longestCommonID
|
||||
);
|
||||
return longestCommonID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses the parent path between two IDs (either up or down). The IDs must
|
||||
* not be the same, and there must exist a parent path between them. If the
|
||||
* callback returns `false`, traversal is stopped.
|
||||
*
|
||||
* @param {?string} start ID at which to start traversal.
|
||||
* @param {?string} stop ID at which to end traversal.
|
||||
* @param {function} cb Callback to invoke each ID with.
|
||||
* @param {?boolean} skipFirst Whether or not to skip the first node.
|
||||
* @param {?boolean} skipLast Whether or not to skip the last node.
|
||||
* @private
|
||||
*/
|
||||
function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
|
||||
start = start || '';
|
||||
stop = stop || '';
|
||||
invariant(
|
||||
start !== stop,
|
||||
'traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.',
|
||||
start
|
||||
);
|
||||
var traverseUp = isAncestorIDOf(stop, start);
|
||||
invariant(
|
||||
traverseUp || isAncestorIDOf(start, stop),
|
||||
'traverseParentPath(%s, %s, ...): Cannot traverse from two IDs that do ' +
|
||||
'not have a parent path.',
|
||||
start,
|
||||
stop
|
||||
);
|
||||
// Traverse from `start` to `stop` one depth at a time.
|
||||
var depth = 0;
|
||||
var traverse = traverseUp ? getParentID : getNextDescendantID;
|
||||
for (var id = start; /* until break */; id = traverse(id, stop)) {
|
||||
var ret;
|
||||
if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
|
||||
ret = cb(id, traverseUp, arg);
|
||||
}
|
||||
if (ret === false || id === stop) {
|
||||
// Only break //after// visiting `stop`.
|
||||
break;
|
||||
}
|
||||
invariant(
|
||||
depth++ < MAX_TREE_DEPTH,
|
||||
'traverseParentPath(%s, %s, ...): Detected an infinite loop while ' +
|
||||
'traversing the React DOM ID tree. This may be due to malformed IDs: %s',
|
||||
start, stop
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the IDs assigned to DOM representations of React components. This
|
||||
* uses a specific scheme in order to traverse the DOM efficiently (e.g. in
|
||||
* order to simulate events).
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
var ReactInstanceHandles = {
|
||||
|
||||
/**
|
||||
* Constructs a React root ID
|
||||
* @return {string} A React root ID.
|
||||
*/
|
||||
createReactRootID: function() {
|
||||
return getReactRootIDString(ReactRootIndex.createReactRootIndex());
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructs a React ID by joining a root ID with a name.
|
||||
*
|
||||
* @param {string} rootID Root ID of a parent component.
|
||||
* @param {string} name A component's name (as flattened children).
|
||||
* @return {string} A React ID.
|
||||
* @internal
|
||||
*/
|
||||
createReactID: function(rootID, name) {
|
||||
return rootID + name;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the DOM ID of the React component that is the root of the tree that
|
||||
* contains the React component with the supplied DOM ID.
|
||||
*
|
||||
* @param {string} id DOM ID of a React component.
|
||||
* @return {?string} DOM ID of the React component that is the root.
|
||||
* @internal
|
||||
*/
|
||||
getReactRootIDFromNodeID: function(id) {
|
||||
if (id && id.charAt(0) === SEPARATOR && id.length > 1) {
|
||||
var index = id.indexOf(SEPARATOR, 1);
|
||||
return index > -1 ? id.substr(0, index) : id;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
|
||||
* should would receive a `mouseEnter` or `mouseLeave` event.
|
||||
*
|
||||
* NOTE: Does not invoke the callback on the nearest common ancestor because
|
||||
* nothing "entered" or "left" that element.
|
||||
*
|
||||
* @param {string} leaveID ID being left.
|
||||
* @param {string} enterID ID being entered.
|
||||
* @param {function} cb Callback to invoke on each entered/left ID.
|
||||
* @param {*} upArg Argument to invoke the callback with on left IDs.
|
||||
* @param {*} downArg Argument to invoke the callback with on entered IDs.
|
||||
* @internal
|
||||
*/
|
||||
traverseEnterLeave: function(leaveID, enterID, cb, upArg, downArg) {
|
||||
var ancestorID = getFirstCommonAncestorID(leaveID, enterID);
|
||||
if (ancestorID !== leaveID) {
|
||||
traverseParentPath(leaveID, ancestorID, cb, upArg, false, true);
|
||||
}
|
||||
if (ancestorID !== enterID) {
|
||||
traverseParentPath(ancestorID, enterID, cb, downArg, true, false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulates the traversal of a two-phase, capture/bubble event dispatch.
|
||||
*
|
||||
* NOTE: This traversal happens on IDs without touching the DOM.
|
||||
*
|
||||
* @param {string} targetID ID of the target node.
|
||||
* @param {function} cb Callback to invoke.
|
||||
* @param {*} arg Argument to invoke the callback with.
|
||||
* @internal
|
||||
*/
|
||||
traverseTwoPhase: function(targetID, cb, arg) {
|
||||
if (targetID) {
|
||||
traverseParentPath('', targetID, cb, arg, true, false);
|
||||
traverseParentPath(targetID, '', cb, arg, false, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Same as `traverseTwoPhase` but skips the `targetID`.
|
||||
*/
|
||||
traverseTwoPhaseSkipTarget: function(targetID, cb, arg) {
|
||||
if (targetID) {
|
||||
traverseParentPath('', targetID, cb, arg, true, true);
|
||||
traverseParentPath(targetID, '', cb, arg, true, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Traverse a node ID, calling the supplied `cb` for each ancestor ID. For
|
||||
* example, passing `.0.$row-0.1` would result in `cb` getting called
|
||||
* with `.0`, `.0.$row-0`, and `.0.$row-0.1`.
|
||||
*
|
||||
* NOTE: This traversal happens on IDs without touching the DOM.
|
||||
*
|
||||
* @param {string} targetID ID of the target node.
|
||||
* @param {function} cb Callback to invoke.
|
||||
* @param {*} arg Argument to invoke the callback with.
|
||||
* @internal
|
||||
*/
|
||||
traverseAncestors: function(targetID, cb, arg) {
|
||||
traverseParentPath('', targetID, cb, arg, true, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Exposed for unit testing.
|
||||
* @private
|
||||
*/
|
||||
_getFirstCommonAncestorID: getFirstCommonAncestorID,
|
||||
|
||||
/**
|
||||
* Exposed for unit testing.
|
||||
* @private
|
||||
*/
|
||||
_getNextDescendantID: getNextDescendantID,
|
||||
|
||||
isAncestorIDOf: isAncestorIDOf,
|
||||
|
||||
SEPARATOR: SEPARATOR
|
||||
|
||||
};
|
||||
|
||||
module.exports = ReactInstanceHandles;
|
||||
Reference in New Issue
Block a user