UNPKG

5.38 kBJavaScriptView Raw
1"use strict";
2
3/**
4 * Copyright (c) Facebook, Inc. and its affiliates.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE file in the root directory of this source tree.
8 *
9 * @format
10 *
11 * @emails oncall+draft_js
12 *
13 * This is unstable and not part of the public API and should not be used by
14 * production systems. This file may be update/removed without notice.
15 */
16var warning = require("fbjs/lib/warning");
17
18var DraftTreeInvariants = {
19 /**
20 * Check if the block is valid
21 */
22 isValidBlock: function isValidBlock(block, blockMap) {
23 var key = block.getKey(); // is its parent's child
24
25 var parentKey = block.getParentKey();
26
27 if (parentKey != null) {
28 var parent = blockMap.get(parentKey);
29
30 if (!parent.getChildKeys().includes(key)) {
31 process.env.NODE_ENV !== "production" ? warning(true, 'Tree is missing parent -> child pointer on %s', key) : void 0;
32 return false;
33 }
34 } // is its children's parent
35
36
37 var children = block.getChildKeys().map(function (k) {
38 return blockMap.get(k);
39 });
40
41 if (!children.every(function (c) {
42 return c.getParentKey() === key;
43 })) {
44 process.env.NODE_ENV !== "production" ? warning(true, 'Tree is missing child -> parent pointer on %s', key) : void 0;
45 return false;
46 } // is its previous sibling's next sibling
47
48
49 var prevSiblingKey = block.getPrevSiblingKey();
50
51 if (prevSiblingKey != null) {
52 var prevSibling = blockMap.get(prevSiblingKey);
53
54 if (prevSibling.getNextSiblingKey() !== key) {
55 process.env.NODE_ENV !== "production" ? warning(true, "Tree is missing nextSibling pointer on %s's prevSibling", key) : void 0;
56 return false;
57 }
58 } // is its next sibling's previous sibling
59
60
61 var nextSiblingKey = block.getNextSiblingKey();
62
63 if (nextSiblingKey != null) {
64 var nextSibling = blockMap.get(nextSiblingKey);
65
66 if (nextSibling.getPrevSiblingKey() !== key) {
67 process.env.NODE_ENV !== "production" ? warning(true, "Tree is missing prevSibling pointer on %s's nextSibling", key) : void 0;
68 return false;
69 }
70 } // no 2-node cycles
71
72
73 if (nextSiblingKey !== null && prevSiblingKey !== null) {
74 if (prevSiblingKey === nextSiblingKey) {
75 process.env.NODE_ENV !== "production" ? warning(true, 'Tree has a two-node cycle at %s', key) : void 0;
76 return false;
77 }
78 } // if it's a leaf node, it has text but no children
79
80
81 if (block.text != '') {
82 if (block.getChildKeys().size > 0) {
83 process.env.NODE_ENV !== "production" ? warning(true, 'Leaf node %s has children', key) : void 0;
84 return false;
85 }
86 }
87
88 return true;
89 },
90
91 /**
92 * Checks that this is a connected tree on all the blocks
93 * starting from the first block, traversing nextSibling and child pointers
94 * should be a tree (preorder traversal - parent, then children)
95 * num of connected node === number of blocks
96 */
97 isConnectedTree: function isConnectedTree(blockMap) {
98 // exactly one node has no previous sibling + no parent
99 var eligibleFirstNodes = blockMap.toArray().filter(function (block) {
100 return block.getParentKey() == null && block.getPrevSiblingKey() == null;
101 });
102
103 if (eligibleFirstNodes.length !== 1) {
104 process.env.NODE_ENV !== "production" ? warning(true, 'Tree is not connected. More or less than one first node') : void 0;
105 return false;
106 }
107
108 var firstNode = eligibleFirstNodes.shift();
109 var nodesSeen = 0;
110 var currentKey = firstNode.getKey();
111 var visitedStack = [];
112
113 while (currentKey != null) {
114 var currentNode = blockMap.get(currentKey);
115 var childKeys = currentNode.getChildKeys();
116 var nextSiblingKey = currentNode.getNextSiblingKey(); // if the node has children, add parent's next sibling to stack and go to children
117
118 if (childKeys.size > 0) {
119 if (nextSiblingKey != null) {
120 visitedStack.unshift(nextSiblingKey);
121 }
122
123 var children = childKeys.map(function (k) {
124 return blockMap.get(k);
125 });
126
127 var _firstNode = children.find(function (block) {
128 return block.getPrevSiblingKey() == null;
129 });
130
131 if (_firstNode == null) {
132 process.env.NODE_ENV !== "production" ? warning(true, '%s has no first child', currentKey) : void 0;
133 return false;
134 }
135
136 currentKey = _firstNode.getKey(); // TODO(T32490138): Deal with multi-node cycles here
137 } else {
138 if (currentNode.getNextSiblingKey() != null) {
139 currentKey = currentNode.getNextSiblingKey();
140 } else {
141 currentKey = visitedStack.shift();
142 }
143 }
144
145 nodesSeen++;
146 }
147
148 if (nodesSeen !== blockMap.size) {
149 process.env.NODE_ENV !== "production" ? warning(true, 'Tree is not connected. %s nodes were seen instead of %s', nodesSeen, blockMap.size) : void 0;
150 return false;
151 }
152
153 return true;
154 },
155
156 /**
157 * Checks that the block map is a connected tree with valid blocks
158 */
159 isValidTree: function isValidTree(blockMap) {
160 var _this = this;
161
162 var blocks = blockMap.toArray();
163
164 if (!blocks.every(function (block) {
165 return _this.isValidBlock(block, blockMap);
166 })) {
167 return false;
168 }
169
170 return this.isConnectedTree(blockMap);
171 }
172};
173module.exports = DraftTreeInvariants;
\No newline at end of file