clapton 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/app/helpers/clapton/clapton_helper.rb +16 -1
- data/lib/clapton/engine.rb +14 -10
- data/lib/clapton/javascripts/dist/client.js +31 -19
- data/lib/clapton/javascripts/dist/components-for-test.js +439 -0
- data/lib/clapton/javascripts/dist/components.js +356 -382
- data/lib/clapton/javascripts/node_modules/diff-dom/LICENSE.txt +165 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/README.md +224 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/TraceLogger.d.ts +28 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/apply.d.ts +4 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/fromVirtual.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/index.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/undo.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/helpers.d.ts +11 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/index.d.ts +10 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/types.d.ts +104 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/apply.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/diff.d.ts +22 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromDOM.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromString.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/helpers.d.ts +40 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/index.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/index.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.d.ts +136 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js +1996 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js +1991 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/index.html +62 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/package.json +54 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/rollup.config.mjs +67 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/TraceLogger.ts +143 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/apply.ts +227 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/fromVirtual.ts +83 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/index.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/undo.ts +90 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/helpers.ts +40 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/index.ts +121 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/types.ts +154 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/apply.ts +349 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/diff.ts +855 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromDOM.ts +74 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromString.ts +239 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/helpers.ts +461 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/index.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/index.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/tsconfig.json +103 -0
- data/lib/clapton/javascripts/rollup.config.mjs +17 -2
- data/lib/clapton/javascripts/src/actions/initialize-actions.ts +6 -3
- data/lib/clapton/javascripts/src/channel/clapton-channel.js +6 -3
- data/lib/clapton/javascripts/src/client.ts +15 -15
- data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
- data/lib/clapton/javascripts/src/components.ts +4 -1
- data/lib/clapton/javascripts/src/dom/update-component.ts +3 -2
- data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +2 -2
- data/lib/clapton/test_helper/base.rb +1 -1
- data/lib/clapton/version.rb +1 -1
- metadata +49 -3
- data/lib/clapton/javascripts/src/dom/update-component.spec.ts +0 -32
|
@@ -0,0 +1,855 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DiffDOMOptions,
|
|
3
|
+
diffNodeType,
|
|
4
|
+
elementDiffNodeType,
|
|
5
|
+
elementNodeType,
|
|
6
|
+
subsetType,
|
|
7
|
+
textDiffNodeType,
|
|
8
|
+
} from "../types"
|
|
9
|
+
import {
|
|
10
|
+
DiffTracker,
|
|
11
|
+
cleanNode,
|
|
12
|
+
getGapInformation,
|
|
13
|
+
isEqual,
|
|
14
|
+
markSubTrees,
|
|
15
|
+
removeDone,
|
|
16
|
+
roughlyEqual,
|
|
17
|
+
} from "./helpers"
|
|
18
|
+
import { Diff, checkElementType } from "../helpers"
|
|
19
|
+
import { applyVirtual } from "./apply"
|
|
20
|
+
import { nodeToObj } from "./fromDOM"
|
|
21
|
+
import { stringToObj } from "./fromString"
|
|
22
|
+
|
|
23
|
+
// ===== Create a diff =====
|
|
24
|
+
|
|
25
|
+
export class DiffFinder {
|
|
26
|
+
debug: boolean
|
|
27
|
+
diffcount: number
|
|
28
|
+
foundAll: boolean
|
|
29
|
+
options: DiffDOMOptions
|
|
30
|
+
t1: elementDiffNodeType
|
|
31
|
+
t1Orig: elementNodeType
|
|
32
|
+
t2: elementDiffNodeType
|
|
33
|
+
t2Orig: elementNodeType
|
|
34
|
+
tracker: DiffTracker
|
|
35
|
+
constructor(
|
|
36
|
+
t1Node: string | elementNodeType | Element,
|
|
37
|
+
t2Node: string | elementNodeType | Element,
|
|
38
|
+
options: DiffDOMOptions,
|
|
39
|
+
) {
|
|
40
|
+
this.options = options
|
|
41
|
+
this.t1 = (
|
|
42
|
+
typeof Element !== "undefined" &&
|
|
43
|
+
checkElementType(t1Node, "Element")
|
|
44
|
+
? nodeToObj(t1Node as Element, this.options)
|
|
45
|
+
: typeof t1Node === "string"
|
|
46
|
+
? stringToObj(t1Node, this.options)
|
|
47
|
+
: JSON.parse(JSON.stringify(t1Node))
|
|
48
|
+
) as elementDiffNodeType
|
|
49
|
+
this.t2 = (
|
|
50
|
+
typeof Element !== "undefined" &&
|
|
51
|
+
checkElementType(t2Node, "Element")
|
|
52
|
+
? nodeToObj(t2Node as Element, this.options)
|
|
53
|
+
: typeof t2Node === "string"
|
|
54
|
+
? stringToObj(t2Node, this.options)
|
|
55
|
+
: JSON.parse(JSON.stringify(t2Node))
|
|
56
|
+
) as elementDiffNodeType
|
|
57
|
+
this.diffcount = 0
|
|
58
|
+
this.foundAll = false
|
|
59
|
+
if (this.debug) {
|
|
60
|
+
this.t1Orig =
|
|
61
|
+
typeof Element !== "undefined" &&
|
|
62
|
+
checkElementType(t1Node, "Element")
|
|
63
|
+
? nodeToObj(t1Node as Element, this.options)
|
|
64
|
+
: typeof t1Node === "string"
|
|
65
|
+
? stringToObj(t1Node, this.options)
|
|
66
|
+
: JSON.parse(JSON.stringify(t1Node))
|
|
67
|
+
this.t2Orig =
|
|
68
|
+
typeof Element !== "undefined" &&
|
|
69
|
+
checkElementType(t2Node, "Element")
|
|
70
|
+
? nodeToObj(t2Node as Element, this.options)
|
|
71
|
+
: typeof t2Node === "string"
|
|
72
|
+
? stringToObj(t2Node, this.options)
|
|
73
|
+
: JSON.parse(JSON.stringify(t2Node))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.tracker = new DiffTracker()
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
init() {
|
|
80
|
+
return this.findDiffs(this.t1, this.t2)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
findDiffs(t1: elementDiffNodeType, t2: elementDiffNodeType) {
|
|
84
|
+
let diffs
|
|
85
|
+
do {
|
|
86
|
+
if (this.options.debug) {
|
|
87
|
+
this.diffcount += 1
|
|
88
|
+
if (this.diffcount > this.options.diffcap) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`surpassed diffcap:${JSON.stringify(
|
|
91
|
+
this.t1Orig,
|
|
92
|
+
)} -> ${JSON.stringify(this.t2Orig)}`,
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
diffs = this.findNextDiff(t1, t2, [])
|
|
97
|
+
|
|
98
|
+
if (diffs.length === 0) {
|
|
99
|
+
// Last check if the elements really are the same now.
|
|
100
|
+
// If not, remove all info about being done and start over.
|
|
101
|
+
// Sometimes a node can be marked as done, but the creation of subsequent diffs means that it has to be changed again.
|
|
102
|
+
if (!isEqual(t1, t2)) {
|
|
103
|
+
if (this.foundAll) {
|
|
104
|
+
console.error("Could not find remaining diffs!")
|
|
105
|
+
} else {
|
|
106
|
+
this.foundAll = true
|
|
107
|
+
removeDone(t1)
|
|
108
|
+
diffs = this.findNextDiff(t1, t2, [])
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (diffs.length > 0) {
|
|
113
|
+
this.foundAll = false
|
|
114
|
+
this.tracker.add(diffs)
|
|
115
|
+
applyVirtual(t1, diffs, this.options)
|
|
116
|
+
}
|
|
117
|
+
} while (diffs.length > 0)
|
|
118
|
+
|
|
119
|
+
return this.tracker.list
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
findNextDiff(t1: diffNodeType, t2: diffNodeType, route: number[]) {
|
|
123
|
+
let diffs
|
|
124
|
+
let fdiffs
|
|
125
|
+
|
|
126
|
+
if (this.options.maxDepth && route.length > this.options.maxDepth) {
|
|
127
|
+
return []
|
|
128
|
+
}
|
|
129
|
+
// outer differences?
|
|
130
|
+
if (!t1.outerDone) {
|
|
131
|
+
diffs = this.findOuterDiff(t1, t2, route)
|
|
132
|
+
if (this.options.filterOuterDiff) {
|
|
133
|
+
fdiffs = this.options.filterOuterDiff(t1, t2, diffs)
|
|
134
|
+
if (fdiffs) diffs = fdiffs
|
|
135
|
+
}
|
|
136
|
+
if (diffs.length > 0) {
|
|
137
|
+
t1.outerDone = true
|
|
138
|
+
return diffs
|
|
139
|
+
} else {
|
|
140
|
+
t1.outerDone = true
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (Object.prototype.hasOwnProperty.call(t1, "data")) {
|
|
144
|
+
// Comment or Text
|
|
145
|
+
return []
|
|
146
|
+
}
|
|
147
|
+
t1 = t1 as elementDiffNodeType
|
|
148
|
+
t2 = t2 as elementDiffNodeType
|
|
149
|
+
|
|
150
|
+
// inner differences?
|
|
151
|
+
if (!t1.innerDone) {
|
|
152
|
+
diffs = this.findInnerDiff(t1, t2, route)
|
|
153
|
+
if (diffs.length > 0) {
|
|
154
|
+
return diffs
|
|
155
|
+
} else {
|
|
156
|
+
t1.innerDone = true
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (this.options.valueDiffing && !t1.valueDone) {
|
|
161
|
+
// value differences?
|
|
162
|
+
diffs = this.findValueDiff(t1, t2, route)
|
|
163
|
+
|
|
164
|
+
if (diffs.length > 0) {
|
|
165
|
+
t1.valueDone = true
|
|
166
|
+
return diffs
|
|
167
|
+
} else {
|
|
168
|
+
t1.valueDone = true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// no differences
|
|
173
|
+
return []
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
findOuterDiff(t1: diffNodeType, t2: diffNodeType, route: number[]) {
|
|
177
|
+
const diffs = []
|
|
178
|
+
let attr
|
|
179
|
+
let attr1
|
|
180
|
+
let attr2
|
|
181
|
+
let attrLength
|
|
182
|
+
let pos
|
|
183
|
+
let i
|
|
184
|
+
if (t1.nodeName !== t2.nodeName) {
|
|
185
|
+
if (!route.length) {
|
|
186
|
+
throw new Error("Top level nodes have to be of the same kind.")
|
|
187
|
+
}
|
|
188
|
+
return [
|
|
189
|
+
new Diff()
|
|
190
|
+
.setValue(
|
|
191
|
+
this.options._const.action,
|
|
192
|
+
this.options._const.replaceElement,
|
|
193
|
+
)
|
|
194
|
+
.setValue(this.options._const.oldValue, cleanNode(t1))
|
|
195
|
+
.setValue(this.options._const.newValue, cleanNode(t2))
|
|
196
|
+
.setValue(this.options._const.route, route),
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
if (
|
|
200
|
+
route.length &&
|
|
201
|
+
this.options.diffcap <
|
|
202
|
+
Math.abs(
|
|
203
|
+
(t1.childNodes || []).length - (t2.childNodes || []).length,
|
|
204
|
+
)
|
|
205
|
+
) {
|
|
206
|
+
return [
|
|
207
|
+
new Diff()
|
|
208
|
+
.setValue(
|
|
209
|
+
this.options._const.action,
|
|
210
|
+
this.options._const.replaceElement,
|
|
211
|
+
)
|
|
212
|
+
.setValue(this.options._const.oldValue, cleanNode(t1))
|
|
213
|
+
.setValue(this.options._const.newValue, cleanNode(t2))
|
|
214
|
+
.setValue(this.options._const.route, route),
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (
|
|
219
|
+
Object.prototype.hasOwnProperty.call(t1, "data") &&
|
|
220
|
+
(t1 as textDiffNodeType).data !== (t2 as textDiffNodeType).data
|
|
221
|
+
) {
|
|
222
|
+
// Comment or text node.
|
|
223
|
+
if (t1.nodeName === "#text") {
|
|
224
|
+
return [
|
|
225
|
+
new Diff()
|
|
226
|
+
.setValue(
|
|
227
|
+
this.options._const.action,
|
|
228
|
+
this.options._const.modifyTextElement,
|
|
229
|
+
)
|
|
230
|
+
.setValue(this.options._const.route, route)
|
|
231
|
+
.setValue(
|
|
232
|
+
this.options._const.oldValue,
|
|
233
|
+
(t1 as textDiffNodeType).data,
|
|
234
|
+
)
|
|
235
|
+
.setValue(
|
|
236
|
+
this.options._const.newValue,
|
|
237
|
+
(t2 as textDiffNodeType).data,
|
|
238
|
+
),
|
|
239
|
+
]
|
|
240
|
+
} else {
|
|
241
|
+
return [
|
|
242
|
+
new Diff()
|
|
243
|
+
.setValue(
|
|
244
|
+
this.options._const.action,
|
|
245
|
+
this.options._const.modifyComment,
|
|
246
|
+
)
|
|
247
|
+
.setValue(this.options._const.route, route)
|
|
248
|
+
.setValue(
|
|
249
|
+
this.options._const.oldValue,
|
|
250
|
+
(t1 as textDiffNodeType).data,
|
|
251
|
+
)
|
|
252
|
+
.setValue(
|
|
253
|
+
this.options._const.newValue,
|
|
254
|
+
(t2 as textDiffNodeType).data,
|
|
255
|
+
),
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
t1 = t1 as elementDiffNodeType
|
|
261
|
+
t2 = t2 as elementDiffNodeType
|
|
262
|
+
|
|
263
|
+
attr1 = t1.attributes ? Object.keys(t1.attributes).sort() : []
|
|
264
|
+
attr2 = t2.attributes ? Object.keys(t2.attributes).sort() : []
|
|
265
|
+
|
|
266
|
+
attrLength = attr1.length
|
|
267
|
+
for (i = 0; i < attrLength; i++) {
|
|
268
|
+
attr = attr1[i]
|
|
269
|
+
pos = attr2.indexOf(attr)
|
|
270
|
+
if (pos === -1) {
|
|
271
|
+
diffs.push(
|
|
272
|
+
new Diff()
|
|
273
|
+
.setValue(
|
|
274
|
+
this.options._const.action,
|
|
275
|
+
this.options._const.removeAttribute,
|
|
276
|
+
)
|
|
277
|
+
.setValue(this.options._const.route, route)
|
|
278
|
+
.setValue(this.options._const.name, attr)
|
|
279
|
+
.setValue(
|
|
280
|
+
this.options._const.value,
|
|
281
|
+
t1.attributes[attr],
|
|
282
|
+
),
|
|
283
|
+
)
|
|
284
|
+
} else {
|
|
285
|
+
attr2.splice(pos, 1)
|
|
286
|
+
if (t1.attributes[attr] !== t2.attributes[attr]) {
|
|
287
|
+
diffs.push(
|
|
288
|
+
new Diff()
|
|
289
|
+
.setValue(
|
|
290
|
+
this.options._const.action,
|
|
291
|
+
this.options._const.modifyAttribute,
|
|
292
|
+
)
|
|
293
|
+
.setValue(this.options._const.route, route)
|
|
294
|
+
.setValue(this.options._const.name, attr)
|
|
295
|
+
.setValue(
|
|
296
|
+
this.options._const.oldValue,
|
|
297
|
+
t1.attributes[attr],
|
|
298
|
+
)
|
|
299
|
+
.setValue(
|
|
300
|
+
this.options._const.newValue,
|
|
301
|
+
t2.attributes[attr],
|
|
302
|
+
),
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
attrLength = attr2.length
|
|
309
|
+
for (i = 0; i < attrLength; i++) {
|
|
310
|
+
attr = attr2[i]
|
|
311
|
+
diffs.push(
|
|
312
|
+
new Diff()
|
|
313
|
+
.setValue(
|
|
314
|
+
this.options._const.action,
|
|
315
|
+
this.options._const.addAttribute,
|
|
316
|
+
)
|
|
317
|
+
.setValue(this.options._const.route, route)
|
|
318
|
+
.setValue(this.options._const.name, attr)
|
|
319
|
+
.setValue(this.options._const.value, t2.attributes[attr]),
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return diffs
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
findInnerDiff(
|
|
327
|
+
t1: elementDiffNodeType,
|
|
328
|
+
t2: elementDiffNodeType,
|
|
329
|
+
route: number[],
|
|
330
|
+
) {
|
|
331
|
+
const t1ChildNodes = t1.childNodes ? t1.childNodes.slice() : []
|
|
332
|
+
const t2ChildNodes = t2.childNodes ? t2.childNodes.slice() : []
|
|
333
|
+
const last = Math.max(t1ChildNodes.length, t2ChildNodes.length)
|
|
334
|
+
let childNodesLengthDifference = Math.abs(
|
|
335
|
+
t1ChildNodes.length - t2ChildNodes.length,
|
|
336
|
+
)
|
|
337
|
+
let diffs: Diff[] = []
|
|
338
|
+
let index = 0
|
|
339
|
+
if (!this.options.maxChildCount || last < this.options.maxChildCount) {
|
|
340
|
+
const cachedSubtrees = Boolean(t1.subsets && t1.subsetsAge--)
|
|
341
|
+
const subtrees = cachedSubtrees
|
|
342
|
+
? t1.subsets
|
|
343
|
+
: t1.childNodes && t2.childNodes
|
|
344
|
+
? markSubTrees(t1, t2)
|
|
345
|
+
: []
|
|
346
|
+
if (subtrees.length > 0) {
|
|
347
|
+
/* One or more groups have been identified among the childnodes of t1
|
|
348
|
+
* and t2.
|
|
349
|
+
*/
|
|
350
|
+
diffs = this.attemptGroupRelocation(
|
|
351
|
+
t1,
|
|
352
|
+
t2,
|
|
353
|
+
subtrees,
|
|
354
|
+
route,
|
|
355
|
+
cachedSubtrees,
|
|
356
|
+
)
|
|
357
|
+
if (diffs.length > 0) {
|
|
358
|
+
return diffs
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/* 0 or 1 groups of similar child nodes have been found
|
|
364
|
+
* for t1 and t2. 1 If there is 1, it could be a sign that the
|
|
365
|
+
* contents are the same. When the number of groups is below 2,
|
|
366
|
+
* t1 and t2 are made to have the same length and each of the
|
|
367
|
+
* pairs of child nodes are diffed.
|
|
368
|
+
*/
|
|
369
|
+
|
|
370
|
+
for (let i = 0; i < last; i += 1) {
|
|
371
|
+
const e1 = t1ChildNodes[i]
|
|
372
|
+
const e2 = t2ChildNodes[i]
|
|
373
|
+
|
|
374
|
+
if (childNodesLengthDifference) {
|
|
375
|
+
/* t1 and t2 have different amounts of childNodes. Add
|
|
376
|
+
* and remove as necessary to obtain the same length */
|
|
377
|
+
if (e1 && !e2) {
|
|
378
|
+
if (e1.nodeName === "#text") {
|
|
379
|
+
diffs.push(
|
|
380
|
+
new Diff()
|
|
381
|
+
.setValue(
|
|
382
|
+
this.options._const.action,
|
|
383
|
+
this.options._const.removeTextElement,
|
|
384
|
+
)
|
|
385
|
+
.setValue(
|
|
386
|
+
this.options._const.route,
|
|
387
|
+
route.concat(index),
|
|
388
|
+
)
|
|
389
|
+
.setValue(
|
|
390
|
+
this.options._const.value,
|
|
391
|
+
(e1 as textDiffNodeType).data,
|
|
392
|
+
),
|
|
393
|
+
)
|
|
394
|
+
index -= 1
|
|
395
|
+
} else {
|
|
396
|
+
diffs.push(
|
|
397
|
+
new Diff()
|
|
398
|
+
.setValue(
|
|
399
|
+
this.options._const.action,
|
|
400
|
+
this.options._const.removeElement,
|
|
401
|
+
)
|
|
402
|
+
.setValue(
|
|
403
|
+
this.options._const.route,
|
|
404
|
+
route.concat(index),
|
|
405
|
+
)
|
|
406
|
+
.setValue(
|
|
407
|
+
this.options._const.element,
|
|
408
|
+
cleanNode(e1),
|
|
409
|
+
),
|
|
410
|
+
)
|
|
411
|
+
index -= 1
|
|
412
|
+
}
|
|
413
|
+
} else if (e2 && !e1) {
|
|
414
|
+
if (e2.nodeName === "#text") {
|
|
415
|
+
diffs.push(
|
|
416
|
+
new Diff()
|
|
417
|
+
.setValue(
|
|
418
|
+
this.options._const.action,
|
|
419
|
+
this.options._const.addTextElement,
|
|
420
|
+
)
|
|
421
|
+
.setValue(
|
|
422
|
+
this.options._const.route,
|
|
423
|
+
route.concat(index),
|
|
424
|
+
)
|
|
425
|
+
.setValue(
|
|
426
|
+
this.options._const.value,
|
|
427
|
+
(e2 as textDiffNodeType).data,
|
|
428
|
+
),
|
|
429
|
+
)
|
|
430
|
+
} else {
|
|
431
|
+
diffs.push(
|
|
432
|
+
new Diff()
|
|
433
|
+
.setValue(
|
|
434
|
+
this.options._const.action,
|
|
435
|
+
this.options._const.addElement,
|
|
436
|
+
)
|
|
437
|
+
.setValue(
|
|
438
|
+
this.options._const.route,
|
|
439
|
+
route.concat(index),
|
|
440
|
+
)
|
|
441
|
+
.setValue(
|
|
442
|
+
this.options._const.element,
|
|
443
|
+
cleanNode(e2),
|
|
444
|
+
),
|
|
445
|
+
)
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
/* We are now guaranteed that childNodes e1 and e2 exist,
|
|
450
|
+
* and that they can be diffed.
|
|
451
|
+
*/
|
|
452
|
+
/* Diffs in child nodes should not affect the parent node,
|
|
453
|
+
* so we let these diffs be submitted together with other
|
|
454
|
+
* diffs.
|
|
455
|
+
*/
|
|
456
|
+
|
|
457
|
+
if (e1 && e2) {
|
|
458
|
+
if (
|
|
459
|
+
!this.options.maxChildCount ||
|
|
460
|
+
last < this.options.maxChildCount
|
|
461
|
+
) {
|
|
462
|
+
diffs = diffs.concat(
|
|
463
|
+
this.findNextDiff(e1, e2, route.concat(index)),
|
|
464
|
+
)
|
|
465
|
+
} else if (!isEqual(e1, e2)) {
|
|
466
|
+
if (t1ChildNodes.length > t2ChildNodes.length) {
|
|
467
|
+
if (e1.nodeName === "#text") {
|
|
468
|
+
diffs.push(
|
|
469
|
+
new Diff()
|
|
470
|
+
.setValue(
|
|
471
|
+
this.options._const.action,
|
|
472
|
+
this.options._const.removeTextElement,
|
|
473
|
+
)
|
|
474
|
+
.setValue(
|
|
475
|
+
this.options._const.route,
|
|
476
|
+
route.concat(index),
|
|
477
|
+
)
|
|
478
|
+
.setValue(
|
|
479
|
+
this.options._const.value,
|
|
480
|
+
(e1 as textDiffNodeType).data,
|
|
481
|
+
),
|
|
482
|
+
)
|
|
483
|
+
} else {
|
|
484
|
+
diffs.push(
|
|
485
|
+
new Diff()
|
|
486
|
+
.setValue(
|
|
487
|
+
this.options._const.action,
|
|
488
|
+
this.options._const.removeElement,
|
|
489
|
+
)
|
|
490
|
+
.setValue(
|
|
491
|
+
this.options._const.element,
|
|
492
|
+
cleanNode(e1),
|
|
493
|
+
)
|
|
494
|
+
.setValue(
|
|
495
|
+
this.options._const.route,
|
|
496
|
+
route.concat(index),
|
|
497
|
+
),
|
|
498
|
+
)
|
|
499
|
+
}
|
|
500
|
+
t1ChildNodes.splice(i, 1)
|
|
501
|
+
i -= 1
|
|
502
|
+
index -= 1
|
|
503
|
+
|
|
504
|
+
childNodesLengthDifference -= 1
|
|
505
|
+
} else if (t1ChildNodes.length < t2ChildNodes.length) {
|
|
506
|
+
diffs = diffs.concat([
|
|
507
|
+
new Diff()
|
|
508
|
+
.setValue(
|
|
509
|
+
this.options._const.action,
|
|
510
|
+
this.options._const.addElement,
|
|
511
|
+
)
|
|
512
|
+
.setValue(
|
|
513
|
+
this.options._const.element,
|
|
514
|
+
cleanNode(e2),
|
|
515
|
+
)
|
|
516
|
+
.setValue(
|
|
517
|
+
this.options._const.route,
|
|
518
|
+
route.concat(index),
|
|
519
|
+
),
|
|
520
|
+
])
|
|
521
|
+
t1ChildNodes.splice(i, 0, cleanNode(e2))
|
|
522
|
+
childNodesLengthDifference -= 1
|
|
523
|
+
} else {
|
|
524
|
+
diffs = diffs.concat([
|
|
525
|
+
new Diff()
|
|
526
|
+
.setValue(
|
|
527
|
+
this.options._const.action,
|
|
528
|
+
this.options._const.replaceElement,
|
|
529
|
+
)
|
|
530
|
+
.setValue(
|
|
531
|
+
this.options._const.oldValue,
|
|
532
|
+
cleanNode(e1),
|
|
533
|
+
)
|
|
534
|
+
.setValue(
|
|
535
|
+
this.options._const.newValue,
|
|
536
|
+
cleanNode(e2),
|
|
537
|
+
)
|
|
538
|
+
.setValue(
|
|
539
|
+
this.options._const.route,
|
|
540
|
+
route.concat(index),
|
|
541
|
+
),
|
|
542
|
+
])
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
index += 1
|
|
547
|
+
}
|
|
548
|
+
t1.innerDone = true
|
|
549
|
+
return diffs
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
attemptGroupRelocation(
|
|
553
|
+
t1: elementDiffNodeType,
|
|
554
|
+
t2: elementDiffNodeType,
|
|
555
|
+
subtrees: subsetType[],
|
|
556
|
+
route: number[],
|
|
557
|
+
cachedSubtrees: boolean,
|
|
558
|
+
) {
|
|
559
|
+
/* Either t1.childNodes and t2.childNodes have the same length, or
|
|
560
|
+
* there are at least two groups of similar elements can be found.
|
|
561
|
+
* attempts are made at equalizing t1 with t2. First all initial
|
|
562
|
+
* elements with no group affiliation (gaps=true) are removed (if
|
|
563
|
+
* only in t1) or added (if only in t2). Then the creation of a group
|
|
564
|
+
* relocation diff is attempted.
|
|
565
|
+
*/
|
|
566
|
+
const gapInformation = getGapInformation(t1, t2, subtrees)
|
|
567
|
+
const gaps1 = gapInformation.gaps1
|
|
568
|
+
const gaps2 = gapInformation.gaps2
|
|
569
|
+
const t1ChildNodes = t1.childNodes.slice()
|
|
570
|
+
const t2ChildNodes = t2.childNodes.slice()
|
|
571
|
+
let shortest = Math.min(gaps1.length, gaps2.length)
|
|
572
|
+
let destinationDifferent
|
|
573
|
+
let toGroup
|
|
574
|
+
let group
|
|
575
|
+
let node
|
|
576
|
+
let similarNode
|
|
577
|
+
const diffs = []
|
|
578
|
+
for (
|
|
579
|
+
let index2 = 0, index1 = 0;
|
|
580
|
+
index2 < shortest;
|
|
581
|
+
index1 += 1, index2 += 1
|
|
582
|
+
) {
|
|
583
|
+
if (
|
|
584
|
+
cachedSubtrees &&
|
|
585
|
+
(gaps1[index2] === true || gaps2[index2] === true)
|
|
586
|
+
) {
|
|
587
|
+
// pass
|
|
588
|
+
} else if (gaps1[index1] === true) {
|
|
589
|
+
node = t1ChildNodes[index1]
|
|
590
|
+
if (node.nodeName === "#text") {
|
|
591
|
+
if (t2ChildNodes[index2].nodeName === "#text") {
|
|
592
|
+
if (
|
|
593
|
+
(node as textDiffNodeType).data !==
|
|
594
|
+
(t2ChildNodes[index2] as textDiffNodeType).data
|
|
595
|
+
) {
|
|
596
|
+
// Check whether a text node with the same value follows later on.
|
|
597
|
+
let testI = index1
|
|
598
|
+
while (
|
|
599
|
+
t1ChildNodes.length > testI + 1 &&
|
|
600
|
+
t1ChildNodes[testI + 1].nodeName === "#text"
|
|
601
|
+
) {
|
|
602
|
+
testI += 1
|
|
603
|
+
if (
|
|
604
|
+
(t2ChildNodes[index2] as textDiffNodeType)
|
|
605
|
+
.data ===
|
|
606
|
+
(t1ChildNodes[testI] as textDiffNodeType)
|
|
607
|
+
.data
|
|
608
|
+
) {
|
|
609
|
+
similarNode = true
|
|
610
|
+
break
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
if (!similarNode) {
|
|
614
|
+
diffs.push(
|
|
615
|
+
new Diff()
|
|
616
|
+
.setValue(
|
|
617
|
+
this.options._const.action,
|
|
618
|
+
this.options._const
|
|
619
|
+
.modifyTextElement,
|
|
620
|
+
)
|
|
621
|
+
.setValue(
|
|
622
|
+
this.options._const.route,
|
|
623
|
+
route.concat(index1),
|
|
624
|
+
)
|
|
625
|
+
.setValue(
|
|
626
|
+
this.options._const.oldValue,
|
|
627
|
+
node.data,
|
|
628
|
+
)
|
|
629
|
+
.setValue(
|
|
630
|
+
this.options._const.newValue,
|
|
631
|
+
(
|
|
632
|
+
t2ChildNodes[
|
|
633
|
+
index2
|
|
634
|
+
] as textDiffNodeType
|
|
635
|
+
).data,
|
|
636
|
+
),
|
|
637
|
+
// t1ChildNodes at position index1 is not up-to-date, but that does not matter as
|
|
638
|
+
// index1 will increase +1
|
|
639
|
+
)
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
} else {
|
|
643
|
+
diffs.push(
|
|
644
|
+
new Diff()
|
|
645
|
+
.setValue(
|
|
646
|
+
this.options._const.action,
|
|
647
|
+
this.options._const.removeTextElement,
|
|
648
|
+
)
|
|
649
|
+
.setValue(
|
|
650
|
+
this.options._const.route,
|
|
651
|
+
route.concat(index1),
|
|
652
|
+
)
|
|
653
|
+
.setValue(this.options._const.value, node.data),
|
|
654
|
+
)
|
|
655
|
+
gaps1.splice(index1, 1)
|
|
656
|
+
t1ChildNodes.splice(index1, 1)
|
|
657
|
+
shortest = Math.min(gaps1.length, gaps2.length)
|
|
658
|
+
index1 -= 1
|
|
659
|
+
index2 -= 1
|
|
660
|
+
}
|
|
661
|
+
} else if (gaps2[index2] === true) {
|
|
662
|
+
// both gaps1[index1] and gaps2[index2] are true.
|
|
663
|
+
// We replace one element with another.
|
|
664
|
+
diffs.push(
|
|
665
|
+
new Diff()
|
|
666
|
+
.setValue(
|
|
667
|
+
this.options._const.action,
|
|
668
|
+
this.options._const.replaceElement,
|
|
669
|
+
)
|
|
670
|
+
.setValue(
|
|
671
|
+
this.options._const.oldValue,
|
|
672
|
+
cleanNode(node),
|
|
673
|
+
)
|
|
674
|
+
.setValue(
|
|
675
|
+
this.options._const.newValue,
|
|
676
|
+
cleanNode(t2ChildNodes[index2]),
|
|
677
|
+
)
|
|
678
|
+
.setValue(
|
|
679
|
+
this.options._const.route,
|
|
680
|
+
route.concat(index1),
|
|
681
|
+
),
|
|
682
|
+
)
|
|
683
|
+
// t1ChildNodes at position index1 is not up-to-date, but that does not matter as
|
|
684
|
+
// index1 will increase +1
|
|
685
|
+
} else {
|
|
686
|
+
diffs.push(
|
|
687
|
+
new Diff()
|
|
688
|
+
.setValue(
|
|
689
|
+
this.options._const.action,
|
|
690
|
+
this.options._const.removeElement,
|
|
691
|
+
)
|
|
692
|
+
.setValue(
|
|
693
|
+
this.options._const.route,
|
|
694
|
+
route.concat(index1),
|
|
695
|
+
)
|
|
696
|
+
.setValue(
|
|
697
|
+
this.options._const.element,
|
|
698
|
+
cleanNode(node),
|
|
699
|
+
),
|
|
700
|
+
)
|
|
701
|
+
gaps1.splice(index1, 1)
|
|
702
|
+
t1ChildNodes.splice(index1, 1)
|
|
703
|
+
shortest = Math.min(gaps1.length, gaps2.length)
|
|
704
|
+
index1 -= 1
|
|
705
|
+
index2 -= 1
|
|
706
|
+
}
|
|
707
|
+
} else if (gaps2[index2] === true) {
|
|
708
|
+
node = t2ChildNodes[index2]
|
|
709
|
+
if (node.nodeName === "#text") {
|
|
710
|
+
diffs.push(
|
|
711
|
+
new Diff()
|
|
712
|
+
.setValue(
|
|
713
|
+
this.options._const.action,
|
|
714
|
+
this.options._const.addTextElement,
|
|
715
|
+
)
|
|
716
|
+
.setValue(
|
|
717
|
+
this.options._const.route,
|
|
718
|
+
route.concat(index1),
|
|
719
|
+
)
|
|
720
|
+
.setValue(this.options._const.value, node.data),
|
|
721
|
+
)
|
|
722
|
+
gaps1.splice(index1, 0, true)
|
|
723
|
+
t1ChildNodes.splice(index1, 0, {
|
|
724
|
+
nodeName: "#text",
|
|
725
|
+
data: node.data,
|
|
726
|
+
})
|
|
727
|
+
shortest = Math.min(gaps1.length, gaps2.length)
|
|
728
|
+
//index1 += 1
|
|
729
|
+
} else {
|
|
730
|
+
diffs.push(
|
|
731
|
+
new Diff()
|
|
732
|
+
.setValue(
|
|
733
|
+
this.options._const.action,
|
|
734
|
+
this.options._const.addElement,
|
|
735
|
+
)
|
|
736
|
+
.setValue(
|
|
737
|
+
this.options._const.route,
|
|
738
|
+
route.concat(index1),
|
|
739
|
+
)
|
|
740
|
+
.setValue(
|
|
741
|
+
this.options._const.element,
|
|
742
|
+
cleanNode(node),
|
|
743
|
+
),
|
|
744
|
+
)
|
|
745
|
+
gaps1.splice(index1, 0, true)
|
|
746
|
+
t1ChildNodes.splice(index1, 0, cleanNode(node))
|
|
747
|
+
shortest = Math.min(gaps1.length, gaps2.length)
|
|
748
|
+
//index1 += 1
|
|
749
|
+
}
|
|
750
|
+
} else if (gaps1[index1] !== gaps2[index2]) {
|
|
751
|
+
if (diffs.length > 0) {
|
|
752
|
+
return diffs
|
|
753
|
+
}
|
|
754
|
+
// group relocation
|
|
755
|
+
group = subtrees[gaps1[index1] as number]
|
|
756
|
+
toGroup = Math.min(
|
|
757
|
+
group.newValue,
|
|
758
|
+
t1ChildNodes.length - group.length,
|
|
759
|
+
)
|
|
760
|
+
if (toGroup !== group.oldValue && toGroup > -1) {
|
|
761
|
+
// Check whether destination nodes are different than originating ones.
|
|
762
|
+
destinationDifferent = false
|
|
763
|
+
for (let j = 0; j < group.length; j += 1) {
|
|
764
|
+
if (
|
|
765
|
+
!roughlyEqual(
|
|
766
|
+
t1ChildNodes[toGroup + j],
|
|
767
|
+
t1ChildNodes[group.oldValue + j],
|
|
768
|
+
{},
|
|
769
|
+
false,
|
|
770
|
+
true,
|
|
771
|
+
)
|
|
772
|
+
) {
|
|
773
|
+
destinationDifferent = true
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (destinationDifferent) {
|
|
777
|
+
return [
|
|
778
|
+
new Diff()
|
|
779
|
+
.setValue(
|
|
780
|
+
this.options._const.action,
|
|
781
|
+
this.options._const.relocateGroup,
|
|
782
|
+
)
|
|
783
|
+
.setValue(
|
|
784
|
+
this.options._const.groupLength,
|
|
785
|
+
group.length,
|
|
786
|
+
)
|
|
787
|
+
.setValue(
|
|
788
|
+
this.options._const.from,
|
|
789
|
+
group.oldValue,
|
|
790
|
+
)
|
|
791
|
+
.setValue(this.options._const.to, toGroup)
|
|
792
|
+
.setValue(this.options._const.route, route),
|
|
793
|
+
]
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
return diffs
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
findValueDiff(
|
|
802
|
+
t1: elementDiffNodeType,
|
|
803
|
+
t2: elementDiffNodeType,
|
|
804
|
+
route: number[],
|
|
805
|
+
) {
|
|
806
|
+
// Differences of value. Only useful if the value/selection/checked value
|
|
807
|
+
// differs from what is represented in the DOM. For example in the case
|
|
808
|
+
// of filled out forms, etc.
|
|
809
|
+
const diffs = []
|
|
810
|
+
|
|
811
|
+
if (t1.selected !== t2.selected) {
|
|
812
|
+
diffs.push(
|
|
813
|
+
new Diff()
|
|
814
|
+
.setValue(
|
|
815
|
+
this.options._const.action,
|
|
816
|
+
this.options._const.modifySelected,
|
|
817
|
+
)
|
|
818
|
+
.setValue(this.options._const.oldValue, t1.selected)
|
|
819
|
+
.setValue(this.options._const.newValue, t2.selected)
|
|
820
|
+
.setValue(this.options._const.route, route),
|
|
821
|
+
)
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (
|
|
825
|
+
(t1.value || t2.value) &&
|
|
826
|
+
t1.value !== t2.value &&
|
|
827
|
+
t1.nodeName !== "OPTION"
|
|
828
|
+
) {
|
|
829
|
+
diffs.push(
|
|
830
|
+
new Diff()
|
|
831
|
+
.setValue(
|
|
832
|
+
this.options._const.action,
|
|
833
|
+
this.options._const.modifyValue,
|
|
834
|
+
)
|
|
835
|
+
.setValue(this.options._const.oldValue, t1.value || "")
|
|
836
|
+
.setValue(this.options._const.newValue, t2.value || "")
|
|
837
|
+
.setValue(this.options._const.route, route),
|
|
838
|
+
)
|
|
839
|
+
}
|
|
840
|
+
if (t1.checked !== t2.checked) {
|
|
841
|
+
diffs.push(
|
|
842
|
+
new Diff()
|
|
843
|
+
.setValue(
|
|
844
|
+
this.options._const.action,
|
|
845
|
+
this.options._const.modifyChecked,
|
|
846
|
+
)
|
|
847
|
+
.setValue(this.options._const.oldValue, t1.checked)
|
|
848
|
+
.setValue(this.options._const.newValue, t2.checked)
|
|
849
|
+
.setValue(this.options._const.route, route),
|
|
850
|
+
)
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return diffs
|
|
854
|
+
}
|
|
855
|
+
}
|