Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
81159 views
1
/**
2
* Copyright 2013-2014, Facebook, Inc.
3
* All rights reserved.
4
*
5
* This source code is licensed under the BSD-style license found in the
6
* LICENSE file in the root directory of this source tree. An additional grant
7
* of patent rights can be found in the PATENTS file in the same directory.
8
*
9
* @providesModule DOMChildrenOperations
10
* @typechecks static-only
11
*/
12
13
"use strict";
14
15
var Danger = require('Danger');
16
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
17
18
var getTextContentAccessor = require('getTextContentAccessor');
19
var invariant = require('invariant');
20
21
/**
22
* The DOM property to use when setting text content.
23
*
24
* @type {string}
25
* @private
26
*/
27
var textContentAccessor = getTextContentAccessor();
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
*/
37
function insertChildAt(parentNode, childNode, index) {
38
// By exploiting arrays returning `undefined` for an undefined index, we can
39
// rely exclusively on `insertBefore(node, null)` instead of also using
40
// `appendChild(node)`. However, using `undefined` is not allowed by all
41
// browsers so we must replace it with `null`.
42
parentNode.insertBefore(
43
childNode,
44
parentNode.childNodes[index] || null
45
);
46
}
47
48
var updateTextContent;
49
if (textContentAccessor === 'textContent') {
50
/**
51
* Sets the text content of `node` to `text`.
52
*
53
* @param {DOMElement} node Node to change
54
* @param {string} text New text content
55
*/
56
updateTextContent = function(node, text) {
57
node.textContent = text;
58
};
59
} else {
60
/**
61
* Sets the text content of `node` to `text`.
62
*
63
* @param {DOMElement} node Node to change
64
* @param {string} text New text content
65
*/
66
updateTextContent = function(node, text) {
67
// In order to preserve newlines correctly, we can't use .innerText to set
68
// the contents (see #1080), so we empty the element then append a text node
69
while (node.firstChild) {
70
node.removeChild(node.firstChild);
71
}
72
if (text) {
73
var doc = node.ownerDocument || document;
74
node.appendChild(doc.createTextNode(text));
75
}
76
};
77
}
78
79
/**
80
* Operations for updating with DOM children.
81
*/
82
var DOMChildrenOperations = {
83
84
dangerouslyReplaceNodeWithMarkup: Danger.dangerouslyReplaceNodeWithMarkup,
85
86
updateTextContent: updateTextContent,
87
88
/**
89
* Updates a component's children by processing a series of updates. The
90
* update configurations are each expected to have a `parentNode` property.
91
*
92
* @param {array<object>} updates List of update configurations.
93
* @param {array<string>} markupList List of markup strings.
94
* @internal
95
*/
96
processUpdates: function(updates, markupList) {
97
var update;
98
// Mapping from parent IDs to initial child orderings.
99
var initialChildren = null;
100
// List of children that will be moved or removed.
101
var updatedChildren = null;
102
103
for (var i = 0; update = updates[i]; i++) {
104
if (update.type === ReactMultiChildUpdateTypes.MOVE_EXISTING ||
105
update.type === ReactMultiChildUpdateTypes.REMOVE_NODE) {
106
var updatedIndex = update.fromIndex;
107
var updatedChild = update.parentNode.childNodes[updatedIndex];
108
var parentID = update.parentID;
109
110
invariant(
111
updatedChild,
112
'processUpdates(): Unable to find child %s of element. This ' +
113
'probably means the DOM was unexpectedly mutated (e.g., by the ' +
114
'browser), usually due to forgetting a <tbody> when using tables, ' +
115
'nesting tags like <form>, <p>, or <a>, or using non-SVG elements '+
116
'in an <svg> parent. Try inspecting the child nodes of the element ' +
117
'with React ID `%s`.',
118
updatedIndex,
119
parentID
120
);
121
122
initialChildren = initialChildren || {};
123
initialChildren[parentID] = initialChildren[parentID] || [];
124
initialChildren[parentID][updatedIndex] = updatedChild;
125
126
updatedChildren = updatedChildren || [];
127
updatedChildren.push(updatedChild);
128
}
129
}
130
131
var renderedMarkup = Danger.dangerouslyRenderMarkup(markupList);
132
133
// Remove updated children first so that `toIndex` is consistent.
134
if (updatedChildren) {
135
for (var j = 0; j < updatedChildren.length; j++) {
136
updatedChildren[j].parentNode.removeChild(updatedChildren[j]);
137
}
138
}
139
140
for (var k = 0; update = updates[k]; k++) {
141
switch (update.type) {
142
case ReactMultiChildUpdateTypes.INSERT_MARKUP:
143
insertChildAt(
144
update.parentNode,
145
renderedMarkup[update.markupIndex],
146
update.toIndex
147
);
148
break;
149
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
150
insertChildAt(
151
update.parentNode,
152
initialChildren[update.parentID][update.fromIndex],
153
update.toIndex
154
);
155
break;
156
case ReactMultiChildUpdateTypes.TEXT_CONTENT:
157
updateTextContent(
158
update.parentNode,
159
update.textContent
160
);
161
break;
162
case ReactMultiChildUpdateTypes.REMOVE_NODE:
163
// Already removed by the for-loop above.
164
break;
165
}
166
}
167
}
168
169
};
170
171
module.exports = DOMChildrenOperations;
172
173