mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
147 lines
5.4 KiB
JavaScript
147 lines
5.4 KiB
JavaScript
/**
|
|
* Copyright 2013 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 DOMChildrenOperations
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
var Danger = require("./Danger");
|
|
|
|
var insertNodeAt = require("./insertNodeAt");
|
|
var keyOf = require("./keyOf");
|
|
var throwIf = require("./throwIf");
|
|
|
|
var NON_INCREASING_OPERATIONS;
|
|
if (true) {
|
|
NON_INCREASING_OPERATIONS =
|
|
'DOM child management operations must be provided in order ' +
|
|
'of increasing destination index. This is likely an issue with ' +
|
|
'the core framework.';
|
|
}
|
|
|
|
var MOVE_NODE_AT_ORIG_INDEX = keyOf({moveFrom: null});
|
|
var INSERT_MARKUP = keyOf({insertMarkup: null});
|
|
var REMOVE_AT = keyOf({removeAt: null});
|
|
|
|
/**
|
|
* In order to carry out movement of DOM nodes without knowing their IDs, we
|
|
* have to first store knowledge about nodes' original indices before beginning
|
|
* to carry out the sequence of operations. Once we begin the sequence, the DOM
|
|
* indices in future instructions are no longer valid.
|
|
*
|
|
* @param {Element} parent Parent DOM node.
|
|
* @param {Object} childOperations Description of child operations.
|
|
* @returns {Array?} Sparse array containing elements by their current index in
|
|
* the DOM.
|
|
*/
|
|
var _getNodesByOriginalIndex = function(parent, childOperations) {
|
|
var nodesByOriginalIndex; // Sparse array.
|
|
var childOperation;
|
|
var origIndex;
|
|
for (var i = 0; i < childOperations.length; i++) {
|
|
childOperation = childOperations[i];
|
|
if (MOVE_NODE_AT_ORIG_INDEX in childOperation) {
|
|
nodesByOriginalIndex = nodesByOriginalIndex || [];
|
|
origIndex = childOperation.moveFrom;
|
|
nodesByOriginalIndex[origIndex] = parent.childNodes[origIndex];
|
|
} else if (REMOVE_AT in childOperation) {
|
|
nodesByOriginalIndex = nodesByOriginalIndex || [];
|
|
origIndex = childOperation.removeAt;
|
|
nodesByOriginalIndex[origIndex] = parent.childNodes[origIndex];
|
|
}
|
|
}
|
|
return nodesByOriginalIndex;
|
|
};
|
|
|
|
/**
|
|
* Removes DOM elements from their parent, or moved.
|
|
* @param {Element} parent Parent DOM node.
|
|
* @param {Array} nodesByOriginalIndex Child nodes by their original index
|
|
* (potentially sparse.)
|
|
*/
|
|
var _removeChildrenByOriginalIndex = function(parent, nodesByOriginalIndex) {
|
|
for (var j = 0; j < nodesByOriginalIndex.length; j++) {
|
|
var nodeToRemove = nodesByOriginalIndex[j];
|
|
if (nodeToRemove) { // We used a sparse array.
|
|
parent.removeChild(nodesByOriginalIndex[j]);
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Once all nodes that will be removed or moved - are removed from the parent
|
|
* node, we can begin the process of placing nodes into their final locations.
|
|
* We must perform all operations in the order of the final DOM index -
|
|
* otherwise, we couldn't count on the fact that an insertion at index X, will
|
|
* remain at index X. This will iterate through the child operations, adding
|
|
* content where needed, skip over removals (they've already been removed) and
|
|
* insert "moved" Elements that were previously removed. The "moved" elements
|
|
* are only temporarily removed from the parent, so that index calculations can
|
|
* be manageable and perform well in the cases that matter.
|
|
*/
|
|
var _placeNodesAtDestination =
|
|
function(parent, childOperations, nodesByOriginalIndex) {
|
|
var origNode;
|
|
var finalIndex;
|
|
var lastFinalIndex = -1;
|
|
var childOperation;
|
|
for (var k = 0; k < childOperations.length; k++) {
|
|
childOperation = childOperations[k];
|
|
if (MOVE_NODE_AT_ORIG_INDEX in childOperation) {
|
|
origNode = nodesByOriginalIndex[childOperation.moveFrom];
|
|
finalIndex = childOperation.finalIndex;
|
|
insertNodeAt(parent, origNode, finalIndex);
|
|
if (true) {
|
|
throwIf(finalIndex <= lastFinalIndex, NON_INCREASING_OPERATIONS);
|
|
lastFinalIndex = finalIndex;
|
|
}
|
|
} else if (REMOVE_AT in childOperation) {
|
|
} else if (INSERT_MARKUP in childOperation) {
|
|
finalIndex = childOperation.finalIndex;
|
|
var markup = childOperation.insertMarkup;
|
|
Danger.dangerouslyInsertMarkupAt(parent, markup, finalIndex);
|
|
if (true) {
|
|
throwIf(finalIndex <= lastFinalIndex, NON_INCREASING_OPERATIONS);
|
|
lastFinalIndex = finalIndex;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var manageChildren = function(parent, childOperations) {
|
|
var nodesByOriginalIndex = _getNodesByOriginalIndex(parent, childOperations);
|
|
if (nodesByOriginalIndex) {
|
|
_removeChildrenByOriginalIndex(parent, nodesByOriginalIndex);
|
|
}
|
|
_placeNodesAtDestination(parent, childOperations, nodesByOriginalIndex);
|
|
};
|
|
|
|
var setTextNodeValueAtIndex = function(parent, index, val) {
|
|
parent.childNodes[index].nodeValue = val;
|
|
};
|
|
|
|
/**
|
|
* Also reexport all of the dangerous functions. It helps to have all dangerous
|
|
* functions located in a single module `Danger`.
|
|
*/
|
|
var DOMChildrenOperations = {
|
|
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
|
|
manageChildren: manageChildren,
|
|
setTextNodeValueAtIndex: setTextNodeValueAtIndex
|
|
};
|
|
|
|
module.exports = DOMChildrenOperations;
|