Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

3 лет назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. "use strict";
  2. /**
  3. * Copyright (c) Facebook, Inc. and its affiliates.
  4. *
  5. * This source code is licensed under the MIT license found in the
  6. * LICENSE file in the root directory of this source tree.
  7. *
  8. * @format
  9. *
  10. * @emails oncall+draft_js
  11. *
  12. * This is unstable and not part of the public API and should not be used by
  13. * production systems. This file may be update/removed without notice.
  14. */
  15. var ContentBlockNode = require("./ContentBlockNode");
  16. var DraftTreeInvariants = require("./DraftTreeInvariants");
  17. var generateRandomKey = require("./generateRandomKey");
  18. var Immutable = require("immutable");
  19. var invariant = require("fbjs/lib/invariant");
  20. var verifyTree = function verifyTree(tree) {
  21. if (process.env.NODE_ENV !== "production") {
  22. !DraftTreeInvariants.isValidTree(tree) ? process.env.NODE_ENV !== "production" ? invariant(false, 'The tree is not valid') : invariant(false) : void 0;
  23. }
  24. };
  25. /**
  26. * This is a utility method for setting B as a child of A, ensuring
  27. * that parent <-> child operations are correctly mirrored
  28. *
  29. * The child is inserted at 'position' index in the list
  30. *
  31. * The block map returned by this method may not be a valid tree (siblings are
  32. * unaffected)
  33. */
  34. var updateParentChild = function updateParentChild(blockMap, parentKey, childKey, position) {
  35. var parent = blockMap.get(parentKey);
  36. var child = blockMap.get(childKey);
  37. !(parent != null && child != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'parent & child should exist in the block map') : invariant(false) : void 0;
  38. var newBlocks = {};
  39. var existingChildren = parent.getChildKeys();
  40. !(existingChildren != null && position >= 0 && position <= existingChildren.count()) ? process.env.NODE_ENV !== "production" ? invariant(false, 'position is not valid for the number of children') : invariant(false) : void 0; // add as parent's child
  41. newBlocks[parentKey] = parent.merge({
  42. children: existingChildren.splice(position, 0, childKey)
  43. });
  44. var nextSiblingKey = null;
  45. var prevSiblingKey = null; // link new child as next sibling to the correct existing child
  46. if (position > 0) {
  47. prevSiblingKey = existingChildren.get(position - 1);
  48. newBlocks[prevSiblingKey] = blockMap.get(prevSiblingKey).merge({
  49. nextSibling: childKey
  50. });
  51. } // link new child as previous sibling to the correct existing child
  52. if (position < existingChildren.count()) {
  53. nextSiblingKey = existingChildren.get(position);
  54. newBlocks[nextSiblingKey] = blockMap.get(nextSiblingKey).merge({
  55. prevSibling: childKey
  56. });
  57. } // add parent & siblings to the child
  58. newBlocks[childKey] = child.merge({
  59. parent: parentKey,
  60. prevSibling: prevSiblingKey,
  61. nextSibling: nextSiblingKey
  62. });
  63. return blockMap.merge(newBlocks);
  64. };
  65. /**
  66. * This is a utility method for setting B as the next sibling of A, ensuring
  67. * that sibling operations are correctly mirrored
  68. *
  69. * The block map returned by this method may not be a valid tree (parent/child/
  70. * other siblings are unaffected)
  71. */
  72. var updateSibling = function updateSibling(blockMap, prevKey, nextKey) {
  73. var prevSibling = blockMap.get(prevKey);
  74. var nextSibling = blockMap.get(nextKey);
  75. !(prevSibling != null && nextSibling != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'siblings should exist in the block map') : invariant(false) : void 0;
  76. var newBlocks = {};
  77. newBlocks[prevKey] = prevSibling.merge({
  78. nextSibling: nextKey
  79. });
  80. newBlocks[nextKey] = nextSibling.merge({
  81. prevSibling: prevKey
  82. });
  83. return blockMap.merge(newBlocks);
  84. };
  85. /**
  86. * This is a utility method for replacing B by C as a child of A, ensuring
  87. * that parent <-> child connections between A & C are correctly mirrored
  88. *
  89. * The block map returned by this method may not be a valid tree (siblings are
  90. * unaffected)
  91. */
  92. var replaceParentChild = function replaceParentChild(blockMap, parentKey, existingChildKey, newChildKey) {
  93. var parent = blockMap.get(parentKey);
  94. var newChild = blockMap.get(newChildKey);
  95. !(parent != null && newChild != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'parent & child should exist in the block map') : invariant(false) : void 0;
  96. var existingChildren = parent.getChildKeys();
  97. var newBlocks = {};
  98. newBlocks[parentKey] = parent.merge({
  99. children: existingChildren.set(existingChildren.indexOf(existingChildKey), newChildKey)
  100. });
  101. newBlocks[newChildKey] = newChild.merge({
  102. parent: parentKey
  103. });
  104. return blockMap.merge(newBlocks);
  105. };
  106. /**
  107. * This is a utility method that abstracts the operation of creating a new parent
  108. * for a particular node in the block map.
  109. *
  110. * This operation respects the tree data invariants - it expects and returns a
  111. * valid tree.
  112. */
  113. var createNewParent = function createNewParent(blockMap, key) {
  114. verifyTree(blockMap);
  115. var block = blockMap.get(key);
  116. !(block != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'block must exist in block map') : invariant(false) : void 0;
  117. var newParent = new ContentBlockNode({
  118. key: generateRandomKey(),
  119. text: '',
  120. depth: block.depth,
  121. type: block.type,
  122. children: Immutable.List([])
  123. }); // add the parent just before the child in the block map
  124. var newBlockMap = blockMap.takeUntil(function (block) {
  125. return block.getKey() === key;
  126. }).concat(Immutable.OrderedMap([[newParent.getKey(), newParent]])).concat(blockMap.skipUntil(function (block) {
  127. return block.getKey() === key;
  128. })); // set parent <-> child connection
  129. newBlockMap = updateParentChild(newBlockMap, newParent.getKey(), key, 0); // set siblings & parent for the new parent key to child's siblings & parent
  130. var prevSibling = block.getPrevSiblingKey();
  131. var nextSibling = block.getNextSiblingKey();
  132. var parent = block.getParentKey();
  133. if (prevSibling != null) {
  134. newBlockMap = updateSibling(newBlockMap, prevSibling, newParent.getKey());
  135. }
  136. if (nextSibling != null) {
  137. newBlockMap = updateSibling(newBlockMap, newParent.getKey(), nextSibling);
  138. }
  139. if (parent != null) {
  140. newBlockMap = replaceParentChild(newBlockMap, parent, key, newParent.getKey());
  141. }
  142. verifyTree(newBlockMap);
  143. return newBlockMap;
  144. };
  145. /**
  146. * This is a utility method that abstracts the operation of adding a node as the child
  147. * of its previous or next sibling.
  148. *
  149. * The previous (or next) sibling must be a valid parent node.
  150. *
  151. * This operation respects the tree data invariants - it expects and returns a
  152. * valid tree.
  153. */
  154. var updateAsSiblingsChild = function updateAsSiblingsChild(blockMap, key, position) {
  155. verifyTree(blockMap);
  156. var block = blockMap.get(key);
  157. !(block != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'block must exist in block map') : invariant(false) : void 0;
  158. var newParentKey = position === 'previous' ? block.getPrevSiblingKey() : block.getNextSiblingKey();
  159. !(newParentKey != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'sibling is null') : invariant(false) : void 0;
  160. var newParent = blockMap.get(newParentKey);
  161. !(newParent !== null && newParent.getText() === '') ? process.env.NODE_ENV !== "production" ? invariant(false, 'parent must be a valid node') : invariant(false) : void 0;
  162. var newBlockMap = blockMap;
  163. switch (position) {
  164. case 'next':
  165. newBlockMap = updateParentChild(newBlockMap, newParentKey, key, 0);
  166. var prevSibling = block.getPrevSiblingKey();
  167. if (prevSibling != null) {
  168. newBlockMap = updateSibling(newBlockMap, prevSibling, newParentKey);
  169. } else {
  170. newBlockMap = newBlockMap.set(newParentKey, newBlockMap.get(newParentKey).merge({
  171. prevSibling: null
  172. }));
  173. } // we also need to flip the order of the sibling & block in the ordered map
  174. // for this case
  175. newBlockMap = newBlockMap.takeUntil(function (block) {
  176. return block.getKey() === key;
  177. }).concat(Immutable.OrderedMap([[newParentKey, newBlockMap.get(newParentKey)], [key, newBlockMap.get(key)]])).concat(newBlockMap.skipUntil(function (block) {
  178. return block.getKey() === newParentKey;
  179. }).slice(1));
  180. break;
  181. case 'previous':
  182. newBlockMap = updateParentChild(newBlockMap, newParentKey, key, newParent.getChildKeys().count());
  183. var nextSibling = block.getNextSiblingKey();
  184. if (nextSibling != null) {
  185. newBlockMap = updateSibling(newBlockMap, newParentKey, nextSibling);
  186. } else {
  187. newBlockMap = newBlockMap.set(newParentKey, newBlockMap.get(newParentKey).merge({
  188. nextSibling: null
  189. }));
  190. }
  191. break;
  192. } // remove the node as a child of its current parent
  193. var parentKey = block.getParentKey();
  194. if (parentKey != null) {
  195. var parent = newBlockMap.get(parentKey);
  196. newBlockMap = newBlockMap.set(parentKey, parent.merge({
  197. children: parent.getChildKeys()["delete"](parent.getChildKeys().indexOf(key))
  198. }));
  199. }
  200. verifyTree(newBlockMap);
  201. return newBlockMap;
  202. };
  203. /**
  204. * This is a utility method that abstracts the operation of moving a node up to become
  205. * a sibling of its parent. If the operation results in a parent with no children,
  206. * also delete the parent node.
  207. *
  208. * Can only operate on the first or last child (this is an invariant)
  209. *
  210. * This operation respects the tree data invariants - it expects and returns a
  211. * valid tree.
  212. */
  213. var moveChildUp = function moveChildUp(blockMap, key) {
  214. verifyTree(blockMap);
  215. var block = blockMap.get(key);
  216. !(block != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'block must exist in block map') : invariant(false) : void 0; // if there is no parent, do nothing
  217. var parentKey = block.getParentKey();
  218. if (parentKey == null) {
  219. return blockMap;
  220. }
  221. var parent = blockMap.get(parentKey);
  222. !(parent !== null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'parent must exist in block map') : invariant(false) : void 0;
  223. var newBlockMap = blockMap;
  224. var childIndex = parent.getChildKeys().indexOf(key);
  225. !(childIndex === 0 || childIndex === parent.getChildKeys().count() - 1) ? process.env.NODE_ENV !== "production" ? invariant(false, 'block is not first or last child of its parent') : invariant(false) : void 0; // If it's the first child, move as previous sibling of parent
  226. if (childIndex === 0) {
  227. var parentPrevSibling = parent.getPrevSiblingKey();
  228. newBlockMap = updateSibling(newBlockMap, key, parentKey); // link to parent's previous sibling
  229. if (parentPrevSibling != null) {
  230. newBlockMap = updateSibling(newBlockMap, parentPrevSibling, key);
  231. } // remove as parent's child
  232. parent = newBlockMap.get(parentKey);
  233. newBlockMap = newBlockMap.set(parentKey, parent.merge({
  234. children: parent.getChildKeys().slice(1)
  235. }));
  236. parent = newBlockMap.get(parentKey); // remove as previous sibling of parent's children
  237. if (parent.getChildKeys().count() > 0) {
  238. var firstChildKey = parent.getChildKeys().first();
  239. var firstChild = newBlockMap.get(firstChildKey);
  240. newBlockMap = newBlockMap.set(firstChildKey, firstChild.merge({
  241. prevSibling: null
  242. }));
  243. } // add the node just before its former parent in the block map
  244. newBlockMap = newBlockMap.takeUntil(function (block) {
  245. return block.getKey() === parentKey;
  246. }).concat(Immutable.OrderedMap([[key, newBlockMap.get(key)], [parentKey, newBlockMap.get(parentKey)]])).concat(newBlockMap.skipUntil(function (block) {
  247. return block.getKey() === key;
  248. }).slice(1)); // If it's the last child, move as next sibling of parent
  249. } else if (childIndex === parent.getChildKeys().count() - 1) {
  250. var parentNextSibling = parent.getNextSiblingKey();
  251. newBlockMap = updateSibling(newBlockMap, parentKey, key); // link to parent's next sibling
  252. if (parentNextSibling != null) {
  253. newBlockMap = updateSibling(newBlockMap, key, parentNextSibling);
  254. } // remove as parent's child
  255. parent = newBlockMap.get(parentKey);
  256. newBlockMap = newBlockMap.set(parentKey, parent.merge({
  257. children: parent.getChildKeys().slice(0, -1)
  258. }));
  259. parent = newBlockMap.get(parentKey); // remove as next sibling of parent's children
  260. if (parent.getChildKeys().count() > 0) {
  261. var lastChildKey = parent.getChildKeys().last();
  262. var lastChild = newBlockMap.get(lastChildKey);
  263. newBlockMap = newBlockMap.set(lastChildKey, lastChild.merge({
  264. nextSibling: null
  265. }));
  266. }
  267. } // For both cases, also link to parent's parent
  268. var grandparentKey = parent.getParentKey();
  269. if (grandparentKey != null) {
  270. var grandparentInsertPosition = newBlockMap.get(grandparentKey).getChildKeys().findIndex(function (n) {
  271. return n === parentKey;
  272. });
  273. newBlockMap = updateParentChild(newBlockMap, grandparentKey, key, childIndex === 0 ? grandparentInsertPosition : grandparentInsertPosition + 1);
  274. } else {
  275. newBlockMap = newBlockMap.set(key, newBlockMap.get(key).merge({
  276. parent: null
  277. }));
  278. } // Delete parent if it has no children
  279. parent = newBlockMap.get(parentKey);
  280. if (parent.getChildKeys().count() === 0) {
  281. var prevSiblingKey = parent.getPrevSiblingKey();
  282. var nextSiblingKey = parent.getNextSiblingKey();
  283. if (prevSiblingKey != null && nextSiblingKey != null) {
  284. newBlockMap = updateSibling(newBlockMap, prevSiblingKey, nextSiblingKey);
  285. }
  286. if (prevSiblingKey == null && nextSiblingKey != null) {
  287. newBlockMap = newBlockMap.set(nextSiblingKey, newBlockMap.get(nextSiblingKey).merge({
  288. prevSibling: null
  289. }));
  290. }
  291. if (nextSiblingKey == null && prevSiblingKey != null) {
  292. newBlockMap = newBlockMap.set(prevSiblingKey, newBlockMap.get(prevSiblingKey).merge({
  293. nextSibling: null
  294. }));
  295. }
  296. if (grandparentKey != null) {
  297. var grandparent = newBlockMap.get(grandparentKey);
  298. var oldChildren = grandparent.getChildKeys();
  299. newBlockMap = newBlockMap.set(grandparentKey, grandparent.merge({
  300. children: oldChildren["delete"](oldChildren.indexOf(parentKey))
  301. }));
  302. }
  303. newBlockMap = newBlockMap["delete"](parentKey);
  304. }
  305. verifyTree(newBlockMap);
  306. return newBlockMap;
  307. };
  308. /**
  309. * This is a utility method to merge two non-leaf blocks into one. The next block's
  310. * children are added to the provided block & the next block is deleted.
  311. *
  312. * This operation respects the tree data invariants - it expects and returns a
  313. * valid tree.
  314. */
  315. var mergeBlocks = function mergeBlocks(blockMap, key) {
  316. verifyTree(blockMap); // current block must be a non-leaf
  317. var block = blockMap.get(key);
  318. !(block !== null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'block must exist in block map') : invariant(false) : void 0;
  319. !(block.getChildKeys().count() > 0) ? process.env.NODE_ENV !== "production" ? invariant(false, 'block must be a non-leaf') : invariant(false) : void 0; // next block must exist & be a non-leaf
  320. var nextBlockKey = block.getNextSiblingKey();
  321. !(nextBlockKey != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'block must have a next block') : invariant(false) : void 0;
  322. var nextBlock = blockMap.get(nextBlockKey);
  323. !(nextBlock != null) ? process.env.NODE_ENV !== "production" ? invariant(false, 'next block must exist in block map') : invariant(false) : void 0;
  324. !(nextBlock.getChildKeys().count() > 0) ? process.env.NODE_ENV !== "production" ? invariant(false, 'next block must be a non-leaf') : invariant(false) : void 0;
  325. var childKeys = block.getChildKeys().concat(nextBlock.getChildKeys());
  326. var newBlockMap = blockMap.set(key, block.merge({
  327. nextSibling: nextBlock.getNextSiblingKey(),
  328. children: childKeys
  329. }));
  330. newBlockMap = newBlockMap.merge(Immutable.OrderedMap(childKeys.map(function (k, i) {
  331. return [k, blockMap.get(k).merge({
  332. parent: key,
  333. prevSibling: i - 1 < 0 ? null : childKeys.get(i - 1),
  334. nextSibling: i + 1 === childKeys.count() ? null : childKeys.get(i + 1)
  335. })];
  336. })));
  337. newBlockMap = newBlockMap["delete"](nextBlockKey);
  338. var nextNextBlockKey = nextBlock.getNextSiblingKey();
  339. if (nextNextBlockKey != null) {
  340. newBlockMap = newBlockMap.set(nextNextBlockKey, blockMap.get(nextNextBlockKey).merge({
  341. prevSibling: key
  342. }));
  343. }
  344. verifyTree(newBlockMap);
  345. return newBlockMap;
  346. };
  347. module.exports = {
  348. updateParentChild: updateParentChild,
  349. replaceParentChild: replaceParentChild,
  350. updateSibling: updateSibling,
  351. createNewParent: createNewParent,
  352. updateAsSiblingsChild: updateAsSiblingsChild,
  353. moveChildUp: moveChildUp,
  354. mergeBlocks: mergeBlocks
  355. };