react-addons
Version:
Simple packaging of react addons to avoid fiddly 'react/addons' npm module.
166 lines (147 loc) • 5.12 kB
JavaScript
/**
* 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 DOMChildrenOperations
* @typechecks static-only
*/
;
var Danger = require("./Danger");
var ReactMultiChildUpdateTypes = require("./ReactMultiChildUpdateTypes");
var getTextContentAccessor = require("./getTextContentAccessor");
/**
* The DOM property to use when setting text content.
*
* @type {string}
* @private
*/
var textContentAccessor = getTextContentAccessor();
/**
* 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) {
var childNodes = parentNode.childNodes;
if (childNodes[index] === childNode) {
return;
}
// If `childNode` is already a child of `parentNode`, remove it so that
// computing `childNodes[index]` takes into account the removal.
if (childNode.parentNode === parentNode) {
parentNode.removeChild(childNode);
}
if (index >= childNodes.length) {
parentNode.appendChild(childNode);
} else {
parentNode.insertBefore(childNode, childNodes[index]);
}
}
/**
* Sets the text content of `node` to `text`.
*
* @param {DOMElement} node Node to change
* @param {string} text New text content
*/
var updateTextContent;
if (textContentAccessor === 'textContent') {
updateTextContent = function(node, text) {
node.textContent = text;
};
} else {
updateTextContent = function(node, text) {
// In order to preserve newlines correctly, we can't use .innerText to set
// the contents (see #1080), so we empty the element then append a text node
while (node.firstChild) {
node.removeChild(node.firstChild);
}
if (text) {
var doc = node.ownerDocument || document;
node.appendChild(doc.createTextNode(text));
}
};
}
/**
* Operations for updating with DOM children.
*/
var DOMChildrenOperations = {
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
updateTextContent: updateTextContent,
/**
* 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; update = updates[i]; 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;
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; update = updates[k]; 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:
updateTextContent(
update.parentNode,
update.textContent
);
break;
case ReactMultiChildUpdateTypes.REMOVE_NODE:
// Already removed by the for-loop above.
break;
}
}
}
};
module.exports = DOMChildrenOperations;