clapton 0.0.13 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/app/helpers/clapton/clapton_helper.rb +16 -1
  4. data/lib/clapton/engine.rb +20 -10
  5. data/lib/clapton/javascripts/dist/client.js +38 -27
  6. data/lib/clapton/javascripts/dist/components-for-test.js +439 -0
  7. data/lib/clapton/javascripts/dist/components.js +356 -382
  8. data/lib/clapton/javascripts/node_modules/diff-dom/LICENSE.txt +165 -0
  9. data/lib/clapton/javascripts/node_modules/diff-dom/README.md +224 -0
  10. data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js +2 -0
  11. data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js.map +1 -0
  12. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/TraceLogger.d.ts +28 -0
  13. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/apply.d.ts +4 -0
  14. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/fromVirtual.d.ts +2 -0
  15. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/index.d.ts +2 -0
  16. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/undo.d.ts +3 -0
  17. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/helpers.d.ts +11 -0
  18. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/index.d.ts +10 -0
  19. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/types.d.ts +104 -0
  20. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/apply.d.ts +3 -0
  21. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/diff.d.ts +22 -0
  22. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromDOM.d.ts +2 -0
  23. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromString.d.ts +2 -0
  24. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/helpers.d.ts +40 -0
  25. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/index.d.ts +3 -0
  26. data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/index.d.ts +2 -0
  27. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.d.ts +136 -0
  28. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js +1996 -0
  29. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js.map +1 -0
  30. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js +2 -0
  31. data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js.map +1 -0
  32. data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js +1991 -0
  33. data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js.map +1 -0
  34. data/lib/clapton/javascripts/node_modules/diff-dom/index.html +62 -0
  35. data/lib/clapton/javascripts/node_modules/diff-dom/package.json +54 -0
  36. data/lib/clapton/javascripts/node_modules/diff-dom/rollup.config.mjs +67 -0
  37. data/lib/clapton/javascripts/node_modules/diff-dom/src/TraceLogger.ts +143 -0
  38. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/apply.ts +227 -0
  39. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/fromVirtual.ts +83 -0
  40. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/index.ts +2 -0
  41. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/undo.ts +90 -0
  42. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/helpers.ts +40 -0
  43. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/index.ts +121 -0
  44. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/types.ts +154 -0
  45. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/apply.ts +349 -0
  46. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/diff.ts +855 -0
  47. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromDOM.ts +74 -0
  48. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromString.ts +239 -0
  49. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/helpers.ts +461 -0
  50. data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/index.ts +3 -0
  51. data/lib/clapton/javascripts/node_modules/diff-dom/src/index.ts +2 -0
  52. data/lib/clapton/javascripts/node_modules/diff-dom/tsconfig.json +103 -0
  53. data/lib/clapton/javascripts/rollup.config.mjs +17 -2
  54. data/lib/clapton/javascripts/src/actions/handle-action.ts +6 -6
  55. data/lib/clapton/javascripts/src/actions/initialize-actions.ts +6 -3
  56. data/lib/clapton/javascripts/src/channel/clapton-channel.js +6 -3
  57. data/lib/clapton/javascripts/src/client.ts +15 -15
  58. data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
  59. data/lib/clapton/javascripts/src/components.ts +4 -1
  60. data/lib/clapton/javascripts/src/dom/update-component.ts +4 -4
  61. data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +2 -2
  62. data/lib/clapton/test_helper/base.rb +1 -1
  63. data/lib/clapton/version.rb +1 -2
  64. metadata +49 -3
  65. data/lib/clapton/javascripts/src/dom/update-component.spec.ts +0 -32
@@ -0,0 +1,121 @@
1
+ import {
2
+ ConstNames,
3
+ ConstNamesPartial,
4
+ DiffDOMOptions,
5
+ DiffDOMOptionsPartial,
6
+ diffType,
7
+ elementNodeType,
8
+ textNodeType,
9
+ } from "./types"
10
+ import { applyDOM, undoDOM } from "./dom/index"
11
+ import { Diff } from "./helpers"
12
+ import { DiffFinder } from "./virtual/index"
13
+ export { nodeToObj, stringToObj } from "./virtual/index"
14
+
15
+ const DEFAULT_OPTIONS = {
16
+ debug: false,
17
+ diffcap: 10, // Limit for how many diffs are accepting when debugging. Inactive when debug is false.
18
+ maxDepth: false, // False or a numeral. If set to a numeral, limits the level of depth that the the diff mechanism looks for differences. If false, goes through the entire tree.
19
+ maxChildCount: 50, // False or a numeral. If set to a numeral, only does a simplified form of diffing of contents so that the number of diffs cannot be higher than the number of child nodes.
20
+ valueDiffing: true, // Whether to take into consideration the values of forms that differ from auto assigned values (when a user fills out a form).
21
+ // syntax: textDiff: function (node, currentValue, expectedValue, newValue)
22
+ textDiff(
23
+ node: textNodeType,
24
+ currentValue: string,
25
+ expectedValue: string,
26
+ newValue: string,
27
+ ) {
28
+ node.data = newValue
29
+ return
30
+ },
31
+ // empty functions were benchmarked as running faster than both
32
+ // `f && f()` and `if (f) { f(); }`
33
+ preVirtualDiffApply() {}, // eslint-disable-line @typescript-eslint/no-empty-function
34
+ postVirtualDiffApply() {}, // eslint-disable-line @typescript-eslint/no-empty-function
35
+ preDiffApply() {}, // eslint-disable-line @typescript-eslint/no-empty-function
36
+ postDiffApply() {}, // eslint-disable-line @typescript-eslint/no-empty-function
37
+ filterOuterDiff: null,
38
+ compress: false, // Whether to work with compressed diffs
39
+ _const: false, // object with strings for every change types to be used in diffs.
40
+ document:
41
+ typeof window !== "undefined" && window.document
42
+ ? window.document
43
+ : false,
44
+ components: [], // list of components used for converting from string
45
+ }
46
+
47
+ export class DiffDOM {
48
+ options: DiffDOMOptions
49
+ constructor(options: DiffDOMOptionsPartial = {}) {
50
+ // IE11 doesn't have Object.assign and buble doesn't translate object spreaders
51
+ // by default, so this is the safest way of doing it currently.
52
+ Object.entries(DEFAULT_OPTIONS).forEach(([key, value]) => {
53
+ if (!Object.prototype.hasOwnProperty.call(options, key)) {
54
+ options[key] = value
55
+ }
56
+ })
57
+
58
+ if (!options._const) {
59
+ const varNames = [
60
+ "addAttribute",
61
+ "modifyAttribute",
62
+ "removeAttribute",
63
+ "modifyTextElement",
64
+ "relocateGroup",
65
+ "removeElement",
66
+ "addElement",
67
+ "removeTextElement",
68
+ "addTextElement",
69
+ "replaceElement",
70
+ "modifyValue",
71
+ "modifyChecked",
72
+ "modifySelected",
73
+ "modifyComment",
74
+ "action",
75
+ "route",
76
+ "oldValue",
77
+ "newValue",
78
+ "element",
79
+ "group",
80
+ "groupLength",
81
+ "from",
82
+ "to",
83
+ "name",
84
+ "value",
85
+ "data",
86
+ "attributes",
87
+ "nodeName",
88
+ "childNodes",
89
+ "checked",
90
+ "selected",
91
+ ]
92
+ const constNames: ConstNamesPartial = {}
93
+ if (options.compress) {
94
+ varNames.forEach(
95
+ (varName, index) => (constNames[varName] = index),
96
+ )
97
+ } else {
98
+ varNames.forEach((varName) => (constNames[varName] = varName))
99
+ }
100
+ options._const = constNames as ConstNames
101
+ }
102
+
103
+ this.options = options as DiffDOMOptions
104
+ }
105
+
106
+ apply(tree: Element, diffs: (Diff | diffType)[]) {
107
+ return applyDOM(tree, diffs, this.options)
108
+ }
109
+
110
+ undo(tree: Element, diffs: (Diff | diffType)[]) {
111
+ return undoDOM(tree, diffs, this.options)
112
+ }
113
+
114
+ diff(
115
+ t1Node: string | elementNodeType | Element,
116
+ t2Node: string | elementNodeType | Element,
117
+ ) {
118
+ const finder = new DiffFinder(t1Node, t2Node, this.options)
119
+ return finder.init()
120
+ }
121
+ }
@@ -0,0 +1,154 @@
1
+ import { Diff } from "./helpers"
2
+
3
+ interface subsetType {
4
+ oldValue: number
5
+ newValue: number
6
+ length: number
7
+ delete?: true
8
+ }
9
+
10
+ interface elementNodeType {
11
+ nodeName: string
12
+ attributes?: { [key: string]: string }
13
+ childNodes?: nodeType[] // eslint-disable-line no-use-before-define
14
+ checked?: boolean
15
+ value?: string | number
16
+ selected?: boolean
17
+ }
18
+
19
+ interface textNodeType {
20
+ nodeName: "#text" | "#comment"
21
+ data: string
22
+ childNodes?: never
23
+ }
24
+
25
+ type nodeType = elementNodeType | textNodeType
26
+
27
+ interface elementDiffNodeType extends elementNodeType {
28
+ childNodes?: diffNodeType[] // eslint-disable-line no-use-before-define
29
+ // The following are only used during diffing.
30
+ subsets?: subsetType[]
31
+ subsetsAge?: number
32
+ outerDone?: boolean
33
+ innerDone?: boolean
34
+ valueDone?: boolean
35
+ }
36
+
37
+ interface textDiffNodeType extends textNodeType {
38
+ // The following are only used during diffing.
39
+ outerDone?: boolean
40
+ }
41
+
42
+ type diffNodeType = elementDiffNodeType | textDiffNodeType
43
+
44
+ interface Document {
45
+ createElement: (arg: string) => Element
46
+ createElementNS: (namespaceURI: string, qualifiedName: string) => Element
47
+ createTextNode: (arg: string) => Text
48
+ createComment: (arg: string) => Comment
49
+ }
50
+
51
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
52
+ interface PreDiffApplyOptions {
53
+ diff: Diff
54
+ node: nodeType
55
+ }
56
+
57
+ type PreDiffApply = (PreDiffApplyOptions) => boolean
58
+
59
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
60
+ interface PostDiffApplyOptions {
61
+ diff: Diff
62
+ node: nodeType
63
+ newNode: nodeType
64
+ }
65
+
66
+ type PostDiffApply = (PostDiffApplyOptions) => void
67
+
68
+ interface ConstNames {
69
+ addAttribute: string | number
70
+ modifyAttribute: string | number
71
+ removeAttribute: string | number
72
+ modifyTextElement: string | number
73
+ relocateGroup: string | number
74
+ removeElement: string | number
75
+ addElement: string | number
76
+ removeTextElement: string | number
77
+ addTextElement: string | number
78
+ replaceElement: string | number
79
+ modifyValue: string | number
80
+ modifyChecked: string | number
81
+ modifySelected: string | number
82
+ modifyComment: string | number
83
+ action: string | number
84
+ route: string | number
85
+ oldValue: string | number
86
+ newValue: string | number
87
+ element: string | number
88
+ group: string | number
89
+ groupLength: string | number
90
+ from: string | number
91
+ to: string | number
92
+ name: string | number
93
+ value: string | number
94
+ data: string | number
95
+ attributes: string | number
96
+ nodeName: string | number
97
+ childNodes: string | number
98
+ checked: string | number
99
+ selected: string | number
100
+ }
101
+
102
+ interface DiffDOMOptions {
103
+ debug: boolean
104
+ diffcap: number // Limit for how many diffs are accepting when debugging. Inactive when debug is false.
105
+ maxDepth: number | false // False or a numeral. If set to a numeral, limits the level of depth that the the diff mechanism looks for differences. If false, goes through the entire tree.
106
+ maxChildCount: number // False or a numeral. If set to a numeral, only does a simplified form of diffing of contents so that the number of diffs cannot be higher than the number of child nodes.
107
+ valueDiffing: boolean // Whether to take into consideration the values of forms that differ from auto assigned values (when a user fills out a form).
108
+ caseSensitive: boolean // Whether to preserve the case of an input string. Important when including CML (XHTML, SVG, etc.)
109
+ // syntax: textDiff: function (node, currentValue, expectedValue, newValue)
110
+ textDiff: (
111
+ node: textNodeType | Text | Comment,
112
+ currentValue: string,
113
+ expectedValue: string,
114
+ newValue: string,
115
+ ) => void
116
+ preVirtualDiffApply: PreDiffApply
117
+ postVirtualDiffApply: PostDiffApply
118
+ preDiffApply: PreDiffApply
119
+ postDiffApply: PostDiffApply
120
+ filterOuterDiff: null | ((t1, t2, diffs: Diff[]) => void | Diff[])
121
+ compress: boolean // Whether to work with compressed diffs
122
+ _const: ConstNames // object with strings for every change types to be used in diffs.
123
+ document: Document
124
+ components: string[]
125
+ }
126
+
127
+ type DiffDOMOptionsPartial = Partial<DiffDOMOptions>
128
+
129
+ type ConstNamesPartial = Partial<ConstNames>
130
+
131
+ type diffType = {
132
+ [key: string | number]:
133
+ | string
134
+ | number
135
+ | boolean
136
+ | number[]
137
+ | { [key: string]: string | { [key: string]: string } }
138
+ | elementNodeType
139
+ }
140
+
141
+ export {
142
+ nodeType,
143
+ ConstNames,
144
+ ConstNamesPartial,
145
+ DiffDOMOptions,
146
+ DiffDOMOptionsPartial,
147
+ diffType,
148
+ diffNodeType,
149
+ elementDiffNodeType,
150
+ elementNodeType,
151
+ subsetType,
152
+ textDiffNodeType,
153
+ textNodeType,
154
+ }
@@ -0,0 +1,349 @@
1
+ import { DiffDOMOptions, elementNodeType, nodeType, subsetType } from "../types"
2
+ import { Diff } from "../helpers"
3
+ import { cleanNode } from "./helpers"
4
+ // ===== Apply a virtual diff =====
5
+
6
+ function getFromVirtualRoute(tree: elementNodeType, route: number[]) {
7
+ let node = tree
8
+ let parentNode
9
+ let nodeIndex
10
+
11
+ route = route.slice()
12
+ while (route.length > 0) {
13
+ nodeIndex = route.splice(0, 1)[0]
14
+ parentNode = node
15
+ node = node.childNodes ? node.childNodes[nodeIndex] : undefined
16
+ }
17
+ return {
18
+ node,
19
+ parentNode,
20
+ nodeIndex,
21
+ }
22
+ }
23
+
24
+ function applyVirtualDiff(
25
+ tree: elementNodeType,
26
+ diff: Diff,
27
+ options: DiffDOMOptions, // {preVirtualDiffApply, postVirtualDiffApply, _const}
28
+ ) {
29
+ let node, parentNode, nodeIndex
30
+
31
+ if (
32
+ ![options._const.addElement, options._const.addTextElement].includes(
33
+ diff[options._const.action],
34
+ )
35
+ ) {
36
+ // For adding nodes, we calculate the route later on. It's different because it includes the position of the newly added item.
37
+ const routeInfo = getFromVirtualRoute(tree, diff[options._const.route])
38
+ node = routeInfo.node
39
+ parentNode = routeInfo.parentNode
40
+ nodeIndex = routeInfo.nodeIndex
41
+ }
42
+
43
+ const newSubsets: subsetType[] = []
44
+
45
+ // pre-diff hook
46
+ const info = {
47
+ diff,
48
+ node,
49
+ }
50
+
51
+ if (options.preVirtualDiffApply(info)) {
52
+ return true
53
+ }
54
+
55
+ let newNode
56
+ let nodeArray
57
+ let route
58
+
59
+ switch (diff[options._const.action]) {
60
+ case options._const.addAttribute:
61
+ if (!node.attributes) {
62
+ node.attributes = {}
63
+ }
64
+
65
+ node.attributes[diff[options._const.name]] =
66
+ diff[options._const.value]
67
+
68
+ if (diff[options._const.name] === "checked") {
69
+ node.checked = true
70
+ } else if (diff[options._const.name] === "selected") {
71
+ node.selected = true
72
+ } else if (
73
+ node.nodeName === "INPUT" &&
74
+ diff[options._const.name] === "value"
75
+ ) {
76
+ node.value = diff[options._const.value]
77
+ }
78
+
79
+ break
80
+ case options._const.modifyAttribute:
81
+ node.attributes[diff[options._const.name]] =
82
+ diff[options._const.newValue]
83
+ break
84
+ case options._const.removeAttribute:
85
+ delete node.attributes[diff[options._const.name]]
86
+
87
+ if (Object.keys(node.attributes).length === 0) {
88
+ delete node.attributes
89
+ }
90
+
91
+ if (diff[options._const.name] === "checked") {
92
+ node.checked = false
93
+ } else if (diff[options._const.name] === "selected") {
94
+ delete node.selected
95
+ } else if (
96
+ node.nodeName === "INPUT" &&
97
+ diff[options._const.name] === "value"
98
+ ) {
99
+ delete node.value
100
+ }
101
+
102
+ break
103
+ case options._const.modifyTextElement:
104
+ node.data = diff[options._const.newValue]
105
+ if (parentNode.nodeName === "TEXTAREA") {
106
+ parentNode.value = diff[options._const.newValue]
107
+ }
108
+ break
109
+ case options._const.modifyValue:
110
+ node.value = diff[options._const.newValue]
111
+ break
112
+ case options._const.modifyComment:
113
+ node.data = diff[options._const.newValue]
114
+ break
115
+ case options._const.modifyChecked:
116
+ node.checked = diff[options._const.newValue]
117
+ break
118
+ case options._const.modifySelected:
119
+ node.selected = diff[options._const.newValue]
120
+ break
121
+ case options._const.replaceElement:
122
+ newNode = cleanNode(diff[options._const.newValue])
123
+ parentNode.childNodes[nodeIndex] = newNode
124
+ break
125
+ case options._const.relocateGroup:
126
+ nodeArray = node.childNodes
127
+ .splice(
128
+ diff[options._const.from],
129
+ diff[options._const.groupLength],
130
+ )
131
+ .reverse()
132
+ nodeArray.forEach((movedNode: nodeType) =>
133
+ node.childNodes.splice(diff[options._const.to], 0, movedNode),
134
+ )
135
+ if (node.subsets) {
136
+ node.subsets.forEach((map: subsetType) => {
137
+ if (
138
+ diff[options._const.from] < diff[options._const.to] &&
139
+ map.oldValue <= diff[options._const.to] &&
140
+ map.oldValue > diff[options._const.from]
141
+ ) {
142
+ map.oldValue -= diff[options._const.groupLength]
143
+ const splitLength =
144
+ map.oldValue + map.length - diff[options._const.to]
145
+ if (splitLength > 0) {
146
+ // new insertion splits map.
147
+ newSubsets.push({
148
+ oldValue:
149
+ diff[options._const.to] +
150
+ diff[options._const.groupLength],
151
+ newValue:
152
+ map.newValue + map.length - splitLength,
153
+ length: splitLength,
154
+ })
155
+ map.length -= splitLength
156
+ }
157
+ } else if (
158
+ diff[options._const.from] > diff[options._const.to] &&
159
+ map.oldValue > diff[options._const.to] &&
160
+ map.oldValue < diff[options._const.from]
161
+ ) {
162
+ map.oldValue += diff[options._const.groupLength]
163
+ const splitLength =
164
+ map.oldValue + map.length - diff[options._const.to]
165
+ if (splitLength > 0) {
166
+ // new insertion splits map.
167
+ newSubsets.push({
168
+ oldValue:
169
+ diff[options._const.to] +
170
+ diff[options._const.groupLength],
171
+ newValue:
172
+ map.newValue + map.length - splitLength,
173
+ length: splitLength,
174
+ })
175
+ map.length -= splitLength
176
+ }
177
+ } else if (map.oldValue === diff[options._const.from]) {
178
+ map.oldValue = diff[options._const.to]
179
+ }
180
+ })
181
+ }
182
+
183
+ break
184
+ case options._const.removeElement:
185
+ parentNode.childNodes.splice(nodeIndex, 1)
186
+ if (parentNode.subsets) {
187
+ parentNode.subsets.forEach((map: subsetType) => {
188
+ if (map.oldValue > nodeIndex) {
189
+ map.oldValue -= 1
190
+ } else if (map.oldValue === nodeIndex) {
191
+ map.delete = true
192
+ } else if (
193
+ map.oldValue < nodeIndex &&
194
+ map.oldValue + map.length > nodeIndex
195
+ ) {
196
+ if (map.oldValue + map.length - 1 === nodeIndex) {
197
+ map.length--
198
+ } else {
199
+ newSubsets.push({
200
+ newValue:
201
+ map.newValue + nodeIndex - map.oldValue,
202
+ oldValue: nodeIndex,
203
+ length:
204
+ map.length - nodeIndex + map.oldValue - 1,
205
+ })
206
+ map.length = nodeIndex - map.oldValue
207
+ }
208
+ }
209
+ })
210
+ }
211
+ node = parentNode
212
+ break
213
+ case options._const.addElement: {
214
+ route = diff[options._const.route].slice()
215
+ const c: number = route.splice(route.length - 1, 1)[0]
216
+ node = getFromVirtualRoute(tree, route)?.node
217
+ newNode = cleanNode(diff[options._const.element])
218
+
219
+ if (!node.childNodes) {
220
+ node.childNodes = []
221
+ }
222
+
223
+ if (c >= node.childNodes.length) {
224
+ node.childNodes.push(newNode)
225
+ } else {
226
+ node.childNodes.splice(c, 0, newNode)
227
+ }
228
+ if (node.subsets) {
229
+ node.subsets.forEach((map: subsetType) => {
230
+ if (map.oldValue >= c) {
231
+ map.oldValue += 1
232
+ } else if (
233
+ map.oldValue < c &&
234
+ map.oldValue + map.length > c
235
+ ) {
236
+ const splitLength = map.oldValue + map.length - c
237
+ newSubsets.push({
238
+ newValue: map.newValue + map.length - splitLength,
239
+ oldValue: c + 1,
240
+ length: splitLength,
241
+ })
242
+ map.length -= splitLength
243
+ }
244
+ })
245
+ }
246
+ break
247
+ }
248
+ case options._const.removeTextElement:
249
+ parentNode.childNodes.splice(nodeIndex, 1)
250
+ if (parentNode.nodeName === "TEXTAREA") {
251
+ delete parentNode.value
252
+ }
253
+ if (parentNode.subsets) {
254
+ parentNode.subsets.forEach((map: subsetType) => {
255
+ if (map.oldValue > nodeIndex) {
256
+ map.oldValue -= 1
257
+ } else if (map.oldValue === nodeIndex) {
258
+ map.delete = true
259
+ } else if (
260
+ map.oldValue < nodeIndex &&
261
+ map.oldValue + map.length > nodeIndex
262
+ ) {
263
+ if (map.oldValue + map.length - 1 === nodeIndex) {
264
+ map.length--
265
+ } else {
266
+ newSubsets.push({
267
+ newValue:
268
+ map.newValue + nodeIndex - map.oldValue,
269
+ oldValue: nodeIndex,
270
+ length:
271
+ map.length - nodeIndex + map.oldValue - 1,
272
+ })
273
+ map.length = nodeIndex - map.oldValue
274
+ }
275
+ }
276
+ })
277
+ }
278
+ node = parentNode
279
+ break
280
+ case options._const.addTextElement: {
281
+ route = diff[options._const.route].slice()
282
+ const c: number = route.splice(route.length - 1, 1)[0]
283
+ newNode = {
284
+ nodeName: "#text",
285
+ data: diff[options._const.value],
286
+ }
287
+ node = getFromVirtualRoute(tree, route).node
288
+ if (!node.childNodes) {
289
+ node.childNodes = []
290
+ }
291
+
292
+ if (c >= node.childNodes.length) {
293
+ node.childNodes.push(newNode)
294
+ } else {
295
+ node.childNodes.splice(c, 0, newNode)
296
+ }
297
+ if (node.nodeName === "TEXTAREA") {
298
+ node.value = diff[options._const.newValue]
299
+ }
300
+ if (node.subsets) {
301
+ node.subsets.forEach((map: subsetType) => {
302
+ if (map.oldValue >= c) {
303
+ map.oldValue += 1
304
+ }
305
+ if (map.oldValue < c && map.oldValue + map.length > c) {
306
+ const splitLength = map.oldValue + map.length - c
307
+ newSubsets.push({
308
+ newValue: map.newValue + map.length - splitLength,
309
+ oldValue: c + 1,
310
+ length: splitLength,
311
+ })
312
+ map.length -= splitLength
313
+ }
314
+ })
315
+ }
316
+ break
317
+ }
318
+ default:
319
+ console.log("unknown action")
320
+ }
321
+
322
+ if (node.subsets) {
323
+ node.subsets = node.subsets.filter(
324
+ (map: subsetType) => !map.delete && map.oldValue !== map.newValue,
325
+ )
326
+ if (newSubsets.length) {
327
+ node.subsets = node.subsets.concat(newSubsets)
328
+ }
329
+ }
330
+
331
+ options.postVirtualDiffApply({
332
+ node: info.node,
333
+ diff: info.diff,
334
+ newNode,
335
+ })
336
+
337
+ return
338
+ }
339
+
340
+ export function applyVirtual(
341
+ tree: elementNodeType,
342
+ diffs: Diff[],
343
+ options: DiffDOMOptions,
344
+ ) {
345
+ diffs.forEach((diff: Diff) => {
346
+ applyVirtualDiff(tree, diff, options)
347
+ })
348
+ return true
349
+ }