UNPKG

7.59 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2013-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var DOMLazyTree = require('./DOMLazyTree');
12var Danger = require('./Danger');
13var ReactDOMComponentTree = require('./ReactDOMComponentTree');
14var ReactInstrumentation = require('./ReactInstrumentation');
15
16var createMicrosoftUnsafeLocalFunction = require('./createMicrosoftUnsafeLocalFunction');
17var setInnerHTML = require('./setInnerHTML');
18var setTextContent = require('./setTextContent');
19
20function getNodeAfter(parentNode, node) {
21 // Special case for text components, which return [open, close] comments
22 // from getHostNode.
23 if (Array.isArray(node)) {
24 node = node[1];
25 }
26 return node ? node.nextSibling : parentNode.firstChild;
27}
28
29/**
30 * Inserts `childNode` as a child of `parentNode` at the `index`.
31 *
32 * @param {DOMElement} parentNode Parent node in which to insert.
33 * @param {DOMElement} childNode Child node to insert.
34 * @param {number} index Index at which to insert the child.
35 * @internal
36 */
37var insertChildAt = createMicrosoftUnsafeLocalFunction(function (parentNode, childNode, referenceNode) {
38 // We rely exclusively on `insertBefore(node, null)` instead of also using
39 // `appendChild(node)`. (Using `undefined` is not allowed by all browsers so
40 // we are careful to use `null`.)
41 parentNode.insertBefore(childNode, referenceNode);
42});
43
44function insertLazyTreeChildAt(parentNode, childTree, referenceNode) {
45 DOMLazyTree.insertTreeBefore(parentNode, childTree, referenceNode);
46}
47
48function moveChild(parentNode, childNode, referenceNode) {
49 if (Array.isArray(childNode)) {
50 moveDelimitedText(parentNode, childNode[0], childNode[1], referenceNode);
51 } else {
52 insertChildAt(parentNode, childNode, referenceNode);
53 }
54}
55
56function removeChild(parentNode, childNode) {
57 if (Array.isArray(childNode)) {
58 var closingComment = childNode[1];
59 childNode = childNode[0];
60 removeDelimitedText(parentNode, childNode, closingComment);
61 parentNode.removeChild(closingComment);
62 }
63 parentNode.removeChild(childNode);
64}
65
66function moveDelimitedText(parentNode, openingComment, closingComment, referenceNode) {
67 var node = openingComment;
68 while (true) {
69 var nextNode = node.nextSibling;
70 insertChildAt(parentNode, node, referenceNode);
71 if (node === closingComment) {
72 break;
73 }
74 node = nextNode;
75 }
76}
77
78function removeDelimitedText(parentNode, startNode, closingComment) {
79 while (true) {
80 var node = startNode.nextSibling;
81 if (node === closingComment) {
82 // The closing comment is removed by ReactMultiChild.
83 break;
84 } else {
85 parentNode.removeChild(node);
86 }
87 }
88}
89
90function replaceDelimitedText(openingComment, closingComment, stringText) {
91 var parentNode = openingComment.parentNode;
92 var nodeAfterComment = openingComment.nextSibling;
93 if (nodeAfterComment === closingComment) {
94 // There are no text nodes between the opening and closing comments; insert
95 // a new one if stringText isn't empty.
96 if (stringText) {
97 insertChildAt(parentNode, document.createTextNode(stringText), nodeAfterComment);
98 }
99 } else {
100 if (stringText) {
101 // Set the text content of the first node after the opening comment, and
102 // remove all following nodes up until the closing comment.
103 setTextContent(nodeAfterComment, stringText);
104 removeDelimitedText(parentNode, nodeAfterComment, closingComment);
105 } else {
106 removeDelimitedText(parentNode, openingComment, closingComment);
107 }
108 }
109
110 if (process.env.NODE_ENV !== 'production') {
111 ReactInstrumentation.debugTool.onHostOperation({
112 instanceID: ReactDOMComponentTree.getInstanceFromNode(openingComment)._debugID,
113 type: 'replace text',
114 payload: stringText
115 });
116 }
117}
118
119var dangerouslyReplaceNodeWithMarkup = Danger.dangerouslyReplaceNodeWithMarkup;
120if (process.env.NODE_ENV !== 'production') {
121 dangerouslyReplaceNodeWithMarkup = function (oldChild, markup, prevInstance) {
122 Danger.dangerouslyReplaceNodeWithMarkup(oldChild, markup);
123 if (prevInstance._debugID !== 0) {
124 ReactInstrumentation.debugTool.onHostOperation({
125 instanceID: prevInstance._debugID,
126 type: 'replace with',
127 payload: markup.toString()
128 });
129 } else {
130 var nextInstance = ReactDOMComponentTree.getInstanceFromNode(markup.node);
131 if (nextInstance._debugID !== 0) {
132 ReactInstrumentation.debugTool.onHostOperation({
133 instanceID: nextInstance._debugID,
134 type: 'mount',
135 payload: markup.toString()
136 });
137 }
138 }
139 };
140}
141
142/**
143 * Operations for updating with DOM children.
144 */
145var DOMChildrenOperations = {
146 dangerouslyReplaceNodeWithMarkup: dangerouslyReplaceNodeWithMarkup,
147
148 replaceDelimitedText: replaceDelimitedText,
149
150 /**
151 * Updates a component's children by processing a series of updates. The
152 * update configurations are each expected to have a `parentNode` property.
153 *
154 * @param {array<object>} updates List of update configurations.
155 * @internal
156 */
157 processUpdates: function (parentNode, updates) {
158 if (process.env.NODE_ENV !== 'production') {
159 var parentNodeDebugID = ReactDOMComponentTree.getInstanceFromNode(parentNode)._debugID;
160 }
161
162 for (var k = 0; k < updates.length; k++) {
163 var update = updates[k];
164 switch (update.type) {
165 case 'INSERT_MARKUP':
166 insertLazyTreeChildAt(parentNode, update.content, getNodeAfter(parentNode, update.afterNode));
167 if (process.env.NODE_ENV !== 'production') {
168 ReactInstrumentation.debugTool.onHostOperation({
169 instanceID: parentNodeDebugID,
170 type: 'insert child',
171 payload: {
172 toIndex: update.toIndex,
173 content: update.content.toString()
174 }
175 });
176 }
177 break;
178 case 'MOVE_EXISTING':
179 moveChild(parentNode, update.fromNode, getNodeAfter(parentNode, update.afterNode));
180 if (process.env.NODE_ENV !== 'production') {
181 ReactInstrumentation.debugTool.onHostOperation({
182 instanceID: parentNodeDebugID,
183 type: 'move child',
184 payload: { fromIndex: update.fromIndex, toIndex: update.toIndex }
185 });
186 }
187 break;
188 case 'SET_MARKUP':
189 setInnerHTML(parentNode, update.content);
190 if (process.env.NODE_ENV !== 'production') {
191 ReactInstrumentation.debugTool.onHostOperation({
192 instanceID: parentNodeDebugID,
193 type: 'replace children',
194 payload: update.content.toString()
195 });
196 }
197 break;
198 case 'TEXT_CONTENT':
199 setTextContent(parentNode, update.content);
200 if (process.env.NODE_ENV !== 'production') {
201 ReactInstrumentation.debugTool.onHostOperation({
202 instanceID: parentNodeDebugID,
203 type: 'replace text',
204 payload: update.content.toString()
205 });
206 }
207 break;
208 case 'REMOVE_NODE':
209 removeChild(parentNode, update.fromNode);
210 if (process.env.NODE_ENV !== 'production') {
211 ReactInstrumentation.debugTool.onHostOperation({
212 instanceID: parentNodeDebugID,
213 type: 'remove child',
214 payload: { fromIndex: update.fromIndex }
215 });
216 }
217 break;
218 }
219 }
220 }
221};
222
223module.exports = DOMChildrenOperations;
\No newline at end of file