forked from steemit/slate
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnormalize.js
More file actions
266 lines (215 loc) · 5.92 KB
/
normalize.js
File metadata and controls
266 lines (215 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
import warning from '../utils/warning'
import { default as defaultSchema } from '../plugins/schema'
import Normalize from '../utils/normalize'
// Maximum recursive calls for normalization
const MAX_CALLS = 50
/**
* Normalize a node (itself and its children) using a schema.
*
* @param {Transform} transform
* @param {Schema} schema
* @param {Node} node
* @return {Transform}
*/
export function normalizeNodeWith(transform, schema, node) {
// For performance considerations, we will check if the transform was changed
const opCount = transform.operations.length
// Iterate over its children
normalizeChildrenWith(transform, schema, node)
const hasChanged = transform.operations.length != opCount
if (hasChanged) {
// Refresh the node reference
node = refreshNode(transform, node)
}
// Now normalize the node itself if it still exist
if (node) {
normalizeNodeOnly(transform, schema, node)
}
return transform
}
/**
* Normalize a node its parents using a schema.
*
* @param {Transform} transform
* @param {Schema} schema
* @param {Node} node
* @return {Transform}
*/
export function normalizeParentsWith(transform, schema, node) {
normalizeNodeOnly(transform, schema, node)
// Normalize went back up to the document
if (node.kind == 'document') {
return transform
}
// We search for the new parent
node = refreshNode(transform, node)
if (!node) {
return transform
}
const { state } = transform
const { document } = state
const parent = document.getParent(node.key)
return normalizeParentsWith(transform, schema, parent)
}
/**
* Normalize state using a schema.
*
* @param {Transform} transform
* @param {Schema} schema
* @return {Transform} transform
*/
export function normalizeWith(transform, schema) {
const { state } = transform
const { document } = state
if (!schema.hasValidators) {
// Schema has no normalization rules
return transform
}
return transform.normalizeNodeWith(schema, document)
}
/**
* Normalize the state using the core schema.
*
* @param {Transform} transform
* @return {Transform} transform
*/
export function normalize(transform) {
transform
.normalizeDocument()
.normalizeSelection()
return transform
}
/**
* Normalize only the document
*
* @param {Transform} transform
* @return {Transform} transform
*/
export function normalizeDocument(transform) {
return transform.normalizeWith(defaultSchema)
}
/**
* Normalize a node and its children using core schema
*
* @param {Transform} transform
* @param {Node or String} key
* @return {Transform} transform
*/
export function normalizeNodeByKey(transform, key) {
key = Normalize.key(key)
const { state } = transform
const { document } = state
const node = document.key == key ? document : document.assertDescendant(key)
transform.normalizeNodeWith(defaultSchema, node)
return transform
}
/**
* Normalize a node and its parent using core schema
*
* @param {Transform} transform
* @param {Node or String} key
* @return {Transform} transform
*/
export function normalizeParentsByKey(transform, key) {
key = Normalize.key(key)
const { state } = transform
const { document } = state
const node = document.key == key ? document : document.assertDescendant(key)
transform.normalizeParentsWith(defaultSchema, node)
return transform
}
/**
* Normalize only the selection.
*
* @param {Transform} transform
* @return {Transform} transform
*/
export function normalizeSelection(transform) {
let { state } = transform
let { document, selection } = state
selection = selection.normalize(document)
// If the selection is nulled (not normal)
if (
selection.isUnset ||
!document.hasDescendant(selection.anchorKey) ||
!document.hasDescendant(selection.focusKey)
) {
warning('Selection was invalid and reset to start of the document')
const firstText = document.getFirstText()
selection = selection.merge({
anchorKey: firstText.key,
anchorOffset: 0,
focusKey: firstText.key,
focusOffset: 0,
isBackward: false
})
}
state = state.merge({ selection })
transform.state = state
return transform
}
/**
* Refresh a reference to a node that have been modified in a transform.
* @param {Transform} transform
* @param {Node} node
* @return {Node} newNode
*/
function refreshNode(transform, node) {
const { state } = transform
const { document } = state
if (node.kind == 'document') {
return document
}
return document.getDescendant(node.key)
}
/**
* Normalize all children of a node
* @param {Transform} transform
* @param {Schema} schema
* @param {Node} node
* @return {Transform} transform
*/
function normalizeChildrenWith(transform, schema, node) {
if (node.kind == 'text') {
return transform
}
return node.nodes.reduce(
(t, child) => t.normalizeNodeWith(schema, child),
transform
)
}
/**
* Normalize a node, but not its children
*
* @param {Transform} transform
* @param {Schema} schema
* @param {Node} node
* @return {Transform} transform
*/
function normalizeNodeOnly(transform, schema, node) {
let recursiveCount = 0
// Auxiliary function, called recursively, with a maximum calls safety net.
function _recur(_transform, _node) {
// _node.validate should be memoized
const failure = _node.validate(schema)
// Node is valid?
if (!failure) {
return _transform
}
const { value, rule } = failure
// Normalize and get the new state
rule.normalize(_transform, _node, value)
// Search for the updated node in the new state
const newNode = refreshNode(_transform, _node)
// Node no longer exist, go back to normalize parents
if (!newNode) {
return _transform
}
recursiveCount++
if (recursiveCount > MAX_CALLS) {
throw new Error('Unexpected number of successive normalizations. Aborting.')
}
return _recur(_transform, newNode)
}
return _recur(transform, node)
}