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.
135 lines
4.4 KiB
135 lines
4.4 KiB
10 years ago
|
/**
|
||
|
* 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 DOMChildrenOperations
|
||
|
* @typechecks static-only
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var Danger = require("./Danger");
|
||
|
var ReactMultiChildUpdateTypes = require("./ReactMultiChildUpdateTypes");
|
||
|
|
||
|
var setTextContent = require("./setTextContent");
|
||
|
var invariant = require("./invariant");
|
||
|
|
||
|
/**
|
||
|
* Inserts `childNode` as a child of `parentNode` at the `index`.
|
||
|
*
|
||
|
* @param {DOMElement} parentNode Parent node in which to insert.
|
||
|
* @param {DOMElement} childNode Child node to insert.
|
||
|
* @param {number} index Index at which to insert the child.
|
||
|
* @internal
|
||
|
*/
|
||
|
function insertChildAt(parentNode, childNode, index) {
|
||
|
// By exploiting arrays returning `undefined` for an undefined index, we can
|
||
|
// rely exclusively on `insertBefore(node, null)` instead of also using
|
||
|
// `appendChild(node)`. However, using `undefined` is not allowed by all
|
||
|
// browsers so we must replace it with `null`.
|
||
|
parentNode.insertBefore(
|
||
|
childNode,
|
||
|
parentNode.childNodes[index] || null
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Operations for updating with DOM children.
|
||
|
*/
|
||
|
var DOMChildrenOperations = {
|
||
|
|
||
|
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
|
||
|
|
||
|
updateTextContent: setTextContent,
|
||
|
|
||
|
/**
|
||
|
* Updates a component's children by processing a series of updates. The
|
||
|
* update configurations are each expected to have a `parentNode` property.
|
||
|
*
|
||
|
* @param {array<object>} updates List of update configurations.
|
||
|
* @param {array<string>} markupList List of markup strings.
|
||
|
* @internal
|
||
|
*/
|
||
|
processUpdates: function(updates, markupList) {
|
||
|
var update;
|
||
|
// Mapping from parent IDs to initial child orderings.
|
||
|
var initialChildren = null;
|
||
|
// List of children that will be moved or removed.
|
||
|
var updatedChildren = null;
|
||
|
|
||
|
for (var i = 0; i < updates.length; i++) {
|
||
|
update = updates[i];
|
||
|
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING ||
|
||
|
update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
|
||
|
var updatedIndex = update.fromIndex;
|
||
|
var updatedChild = update.parentNode.childNodes[updatedIndex];
|
||
|
var parentID = update.parentID;
|
||
|
|
||
|
("production" !== process.env.NODE_ENV ? invariant(
|
||
|
updatedChild,
|
||
|
'processUpdates(): Unable to find child %s of element. This ' +
|
||
|
'probably means the DOM was unexpectedly mutated (e.g., by the ' +
|
||
|
'browser), usually due to forgetting a <tbody> when using tables, ' +
|
||
|
'nesting tags like <form>, <p>, or <a>, or using non-SVG elements ' +
|
||
|
'in an <svg> parent. Try inspecting the child nodes of the element ' +
|
||
|
'with React ID `%s`.',
|
||
|
updatedIndex,
|
||
|
parentID
|
||
|
) : invariant(updatedChild));
|
||
|
|
||
|
initialChildren = initialChildren || {};
|
||
|
initialChildren[parentID] = initialChildren[parentID] || [];
|
||
|
initialChildren[parentID][updatedIndex] = updatedChild;
|
||
|
|
||
|
updatedChildren = updatedChildren || [];
|
||
|
updatedChildren.push(updatedChild);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList);
|
||
|
|
||
|
// Remove updated children first so that `toIndex` is consistent.
|
||
|
if (updatedChildren) {
|
||
|
for (var j = 0; j < updatedChildren.length; j++) {
|
||
|
updatedChildren[j].parentNode.removeChild(updatedChildren[j]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var k = 0; k < updates.length; k++) {
|
||
|
update = updates[k];
|
||
|
switch (update.type) {
|
||
|
case ReactMultiChildUpdateTypes.INSERT_MARKUP:
|
||
|
insertChildAt(
|
||
|
update.parentNode,
|
||
|
renderedMarkup[update.markupIndex],
|
||
|
update.toIndex
|
||
|
);
|
||
|
break;
|
||
|
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
|
||
|
insertChildAt(
|
||
|
update.parentNode,
|
||
|
initialChildren[update.parentID][update.fromIndex],
|
||
|
update.toIndex
|
||
|
);
|
||
|
break;
|
||
|
case ReactMultiChildUpdateTypes.TEXT_CONTENT:
|
||
|
setTextContent(
|
||
|
update.parentNode,
|
||
|
update.textContent
|
||
|
);
|
||
|
break;
|
||
|
case ReactMultiChildUpdateTypes.REMOVE_NODE:
|
||
|
// Already removed by the for-loop above.
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
module.exports = DOMChildrenOperations;
|