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.
Files changed (64) 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 +14 -10
  5. data/lib/clapton/javascripts/dist/client.js +31 -19
  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/initialize-actions.ts +6 -3
  55. data/lib/clapton/javascripts/src/channel/clapton-channel.js +6 -3
  56. data/lib/clapton/javascripts/src/client.ts +15 -15
  57. data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
  58. data/lib/clapton/javascripts/src/components.ts +4 -1
  59. data/lib/clapton/javascripts/src/dom/update-component.ts +3 -2
  60. data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +2 -2
  61. data/lib/clapton/test_helper/base.rb +1 -1
  62. data/lib/clapton/version.rb +1 -1
  63. metadata +49 -3
  64. 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
+ }