1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | 'use strict';
|
12 |
|
13 | var BlockMapBuilder = require("./BlockMapBuilder");
|
14 |
|
15 | var ContentBlockNode = require("./ContentBlockNode");
|
16 |
|
17 | var Immutable = require("immutable");
|
18 |
|
19 | var insertIntoList = require("./insertIntoList");
|
20 |
|
21 | var invariant = require("fbjs/lib/invariant");
|
22 |
|
23 | var randomizeBlockMapKeys = require("./randomizeBlockMapKeys");
|
24 |
|
25 | var List = Immutable.List;
|
26 |
|
27 | var updateExistingBlock = function updateExistingBlock(contentState, selectionState, blockMap, fragmentBlock, targetKey, targetOffset) {
|
28 | var mergeBlockData = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'REPLACE_WITH_NEW_DATA';
|
29 | var targetBlock = blockMap.get(targetKey);
|
30 | var text = targetBlock.getText();
|
31 | var chars = targetBlock.getCharacterList();
|
32 | var finalKey = targetKey;
|
33 | var finalOffset = targetOffset + fragmentBlock.getText().length;
|
34 | var data = null;
|
35 |
|
36 | switch (mergeBlockData) {
|
37 | case 'MERGE_OLD_DATA_TO_NEW_DATA':
|
38 | data = fragmentBlock.getData().merge(targetBlock.getData());
|
39 | break;
|
40 |
|
41 | case 'REPLACE_WITH_NEW_DATA':
|
42 | data = fragmentBlock.getData();
|
43 | break;
|
44 | }
|
45 |
|
46 | var type = targetBlock.getType();
|
47 |
|
48 | if (text && type === 'unstyled') {
|
49 | type = fragmentBlock.getType();
|
50 | }
|
51 |
|
52 | var newBlock = targetBlock.merge({
|
53 | text: text.slice(0, targetOffset) + fragmentBlock.getText() + text.slice(targetOffset),
|
54 | characterList: insertIntoList(chars, fragmentBlock.getCharacterList(), targetOffset),
|
55 | type: type,
|
56 | data: data
|
57 | });
|
58 | return contentState.merge({
|
59 | blockMap: blockMap.set(targetKey, newBlock),
|
60 | selectionBefore: selectionState,
|
61 | selectionAfter: selectionState.merge({
|
62 | anchorKey: finalKey,
|
63 | anchorOffset: finalOffset,
|
64 | focusKey: finalKey,
|
65 | focusOffset: finalOffset,
|
66 | isBackward: false
|
67 | })
|
68 | });
|
69 | };
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | var updateHead = function updateHead(block, targetOffset, fragment) {
|
77 | var text = block.getText();
|
78 | var chars = block.getCharacterList();
|
79 |
|
80 | var headText = text.slice(0, targetOffset);
|
81 | var headCharacters = chars.slice(0, targetOffset);
|
82 | var appendToHead = fragment.first();
|
83 | return block.merge({
|
84 | text: headText + appendToHead.getText(),
|
85 | characterList: headCharacters.concat(appendToHead.getCharacterList()),
|
86 | type: headText ? block.getType() : appendToHead.getType(),
|
87 | data: appendToHead.getData()
|
88 | });
|
89 | };
|
90 |
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 | var updateTail = function updateTail(block, targetOffset, fragment) {
|
97 |
|
98 | var text = block.getText();
|
99 | var chars = block.getCharacterList();
|
100 |
|
101 | var blockSize = text.length;
|
102 | var tailText = text.slice(targetOffset, blockSize);
|
103 | var tailCharacters = chars.slice(targetOffset, blockSize);
|
104 | var prependToTail = fragment.last();
|
105 | return prependToTail.merge({
|
106 | text: prependToTail.getText() + tailText,
|
107 | characterList: prependToTail.getCharacterList().concat(tailCharacters),
|
108 | data: prependToTail.getData()
|
109 | });
|
110 | };
|
111 |
|
112 | var getRootBlocks = function getRootBlocks(block, blockMap) {
|
113 | var headKey = block.getKey();
|
114 | var rootBlock = block;
|
115 | var rootBlocks = [];
|
116 |
|
117 |
|
118 |
|
119 |
|
120 | if (blockMap.get(headKey)) {
|
121 | rootBlocks.push(headKey);
|
122 | }
|
123 |
|
124 | while (rootBlock && rootBlock.getNextSiblingKey()) {
|
125 | var lastSiblingKey = rootBlock.getNextSiblingKey();
|
126 |
|
127 | if (!lastSiblingKey) {
|
128 | break;
|
129 | }
|
130 |
|
131 | rootBlocks.push(lastSiblingKey);
|
132 | rootBlock = blockMap.get(lastSiblingKey);
|
133 | }
|
134 |
|
135 | return rootBlocks;
|
136 | };
|
137 |
|
138 | var updateBlockMapLinks = function updateBlockMapLinks(blockMap, originalBlockMap, targetBlock, fragmentHeadBlock) {
|
139 | return blockMap.withMutations(function (blockMapState) {
|
140 | var targetKey = targetBlock.getKey();
|
141 | var headKey = fragmentHeadBlock.getKey();
|
142 | var targetNextKey = targetBlock.getNextSiblingKey();
|
143 | var targetParentKey = targetBlock.getParentKey();
|
144 | var fragmentRootBlocks = getRootBlocks(fragmentHeadBlock, blockMap);
|
145 | var lastRootFragmentBlockKey = fragmentRootBlocks[fragmentRootBlocks.length - 1];
|
146 |
|
147 | if (blockMapState.get(headKey)) {
|
148 |
|
149 | blockMapState.setIn([targetKey, 'nextSibling'], headKey);
|
150 | blockMapState.setIn([headKey, 'prevSibling'], targetKey);
|
151 | } else {
|
152 |
|
153 | blockMapState.setIn([targetKey, 'nextSibling'], fragmentHeadBlock.getNextSiblingKey());
|
154 | blockMapState.setIn([fragmentHeadBlock.getNextSiblingKey(), 'prevSibling'], targetKey);
|
155 | }
|
156 |
|
157 |
|
158 | blockMapState.setIn([lastRootFragmentBlockKey, 'nextSibling'], targetNextKey);
|
159 |
|
160 | if (targetNextKey) {
|
161 | blockMapState.setIn([targetNextKey, 'prevSibling'], lastRootFragmentBlockKey);
|
162 | }
|
163 |
|
164 |
|
165 | fragmentRootBlocks.forEach(function (blockKey) {
|
166 | return blockMapState.setIn([blockKey, 'parent'], targetParentKey);
|
167 | });
|
168 |
|
169 | if (targetParentKey) {
|
170 | var targetParent = blockMap.get(targetParentKey);
|
171 | var originalTargetParentChildKeys = targetParent.getChildKeys();
|
172 | var targetBlockIndex = originalTargetParentChildKeys.indexOf(targetKey);
|
173 | var insertionIndex = targetBlockIndex + 1;
|
174 | var newChildrenKeysArray = originalTargetParentChildKeys.toArray();
|
175 |
|
176 | newChildrenKeysArray.splice.apply(newChildrenKeysArray, [insertionIndex, 0].concat(fragmentRootBlocks));
|
177 | blockMapState.setIn([targetParentKey, 'children'], List(newChildrenKeysArray));
|
178 | }
|
179 | });
|
180 | };
|
181 |
|
182 | var insertFragment = function insertFragment(contentState, selectionState, blockMap, fragment, targetKey, targetOffset) {
|
183 | var isTreeBasedBlockMap = blockMap.first() instanceof ContentBlockNode;
|
184 | var newBlockArr = [];
|
185 | var fragmentSize = fragment.size;
|
186 | var target = blockMap.get(targetKey);
|
187 | var head = fragment.first();
|
188 | var tail = fragment.last();
|
189 | var finalOffset = tail.getLength();
|
190 | var finalKey = tail.getKey();
|
191 | var shouldNotUpdateFromFragmentBlock = isTreeBasedBlockMap && (!target.getChildKeys().isEmpty() || !head.getChildKeys().isEmpty());
|
192 | blockMap.forEach(function (block, blockKey) {
|
193 | if (blockKey !== targetKey) {
|
194 | newBlockArr.push(block);
|
195 | return;
|
196 | }
|
197 |
|
198 | if (shouldNotUpdateFromFragmentBlock) {
|
199 | newBlockArr.push(block);
|
200 | } else {
|
201 | newBlockArr.push(updateHead(block, targetOffset, fragment));
|
202 | }
|
203 |
|
204 |
|
205 | fragment
|
206 |
|
207 |
|
208 | .slice(shouldNotUpdateFromFragmentBlock ? 0 : 1, fragmentSize - 1).forEach(function (fragmentBlock) {
|
209 | return newBlockArr.push(fragmentBlock);
|
210 | });
|
211 |
|
212 | newBlockArr.push(updateTail(block, targetOffset, fragment));
|
213 | });
|
214 | var updatedBlockMap = BlockMapBuilder.createFromArray(newBlockArr);
|
215 |
|
216 | if (isTreeBasedBlockMap) {
|
217 | updatedBlockMap = updateBlockMapLinks(updatedBlockMap, blockMap, target, head);
|
218 | }
|
219 |
|
220 | return contentState.merge({
|
221 | blockMap: updatedBlockMap,
|
222 | selectionBefore: selectionState,
|
223 | selectionAfter: selectionState.merge({
|
224 | anchorKey: finalKey,
|
225 | anchorOffset: finalOffset,
|
226 | focusKey: finalKey,
|
227 | focusOffset: finalOffset,
|
228 | isBackward: false
|
229 | })
|
230 | });
|
231 | };
|
232 |
|
233 | var insertFragmentIntoContentState = function insertFragmentIntoContentState(contentState, selectionState, fragmentBlockMap) {
|
234 | var mergeBlockData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'REPLACE_WITH_NEW_DATA';
|
235 | !selectionState.isCollapsed() ? process.env.NODE_ENV !== "production" ? invariant(false, '`insertFragment` should only be called with a collapsed selection state.') : invariant(false) : void 0;
|
236 | var blockMap = contentState.getBlockMap();
|
237 | var fragment = randomizeBlockMapKeys(fragmentBlockMap);
|
238 | var targetKey = selectionState.getStartKey();
|
239 | var targetOffset = selectionState.getStartOffset();
|
240 | var targetBlock = blockMap.get(targetKey);
|
241 |
|
242 | if (targetBlock instanceof ContentBlockNode) {
|
243 | !targetBlock.getChildKeys().isEmpty() ? process.env.NODE_ENV !== "production" ? invariant(false, '`insertFragment` should not be called when a container node is selected.') : invariant(false) : void 0;
|
244 | }
|
245 |
|
246 |
|
247 |
|
248 | if (fragment.size === 1) {
|
249 | return updateExistingBlock(contentState, selectionState, blockMap, fragment.first(), targetKey, targetOffset, mergeBlockData);
|
250 | }
|
251 |
|
252 | return insertFragment(contentState, selectionState, blockMap, fragment, targetKey, targetOffset);
|
253 | };
|
254 |
|
255 | module.exports = insertFragmentIntoContentState; |
\ | No newline at end of file |