clapton 0.0.13 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
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,67 @@
1
+ import typescript from "@rollup/plugin-typescript"
2
+ import dts from "rollup-plugin-dts"
3
+ import buble from "@rollup/plugin-buble"
4
+ import terser from "@rollup/plugin-terser"
5
+
6
+ export default [
7
+ {
8
+ input: "src/index.ts",
9
+ output: [
10
+ {
11
+ file: "dist/index.js",
12
+ format: "cjs",
13
+ sourcemap: true,
14
+ },
15
+ {
16
+ file: "dist/module.js",
17
+ format: "es",
18
+ sourcemap: true,
19
+ },
20
+ ],
21
+ plugins: [
22
+ typescript(),
23
+ buble(),
24
+ ],
25
+ },
26
+ {
27
+ input: "src/index.ts",
28
+ output: [
29
+ {
30
+ file: "dist/index.min.js",
31
+ format: "cjs",
32
+ sourcemap: true,
33
+ },
34
+ ],
35
+ plugins: [
36
+ typescript(),
37
+ buble(),
38
+ terser(),
39
+ ],
40
+ },
41
+ {
42
+ input: './dist/dts/index.d.ts',
43
+ output: [{ file: 'dist/index.d.ts', format: 'es' }],
44
+ plugins: [dts()],
45
+ },
46
+ {
47
+ input: "src/index.ts",
48
+ output: [
49
+ {
50
+ file: "browser/diffDOM.js",
51
+ format: "iife",
52
+ name: "diffDOM",
53
+ sourcemap: true,
54
+ },
55
+ ],
56
+ plugins: [
57
+ typescript({
58
+ compilerOptions: {
59
+ declaration: false,
60
+ declarationDir: undefined
61
+ }
62
+ }),
63
+ buble(),
64
+ terser()
65
+ ],
66
+ },
67
+ ]
@@ -0,0 +1,143 @@
1
+ import { checkElementType } from "./diffDOM/helpers"
2
+
3
+ /**
4
+ * Use TraceLogger to figure out function calls inside
5
+ * JS objects by wrapping an object with a TraceLogger
6
+ * instance.
7
+ *
8
+ * Pretty-prints the call trace (using unicode box code)
9
+ * when tracelogger.toString() is called.
10
+ */
11
+
12
+ /**
13
+ * Wrap an object by calling new TraceLogger(obj)
14
+ *
15
+ * If you're familiar with Python decorators, this
16
+ * does roughly the same thing, adding pre/post
17
+ * call hook logging calls so that you can see
18
+ * what's going on.
19
+ */
20
+ export class TraceLogger {
21
+ messages: string[]
22
+ pad: string
23
+ padding: string
24
+ tick: number
25
+ constructor(obj = {}) {
26
+ this.pad = "│ "
27
+ this.padding = ""
28
+ this.tick = 1
29
+ this.messages = []
30
+ const wrapkey = (obj: object, key: string) => {
31
+ // trace this function
32
+ const oldfn = obj[key]
33
+ obj[key] = (
34
+ ...args: ((
35
+ ...args: (
36
+ | string
37
+ | HTMLElement
38
+ | number
39
+ | boolean
40
+ | false
41
+ | (string | HTMLElement | number | boolean | false)[]
42
+ )[]
43
+ ) => void)[]
44
+ ) => {
45
+ this.fin(key, Array.prototype.slice.call(args))
46
+ const result = oldfn.apply(obj, args)
47
+ this.fout(key, result)
48
+ return result
49
+ }
50
+ }
51
+ // can't use Object.keys for prototype walking
52
+ for (let key in obj) {
53
+ if (typeof obj[key] === "function") {
54
+ wrapkey(obj, key)
55
+ }
56
+ }
57
+ this.log("┌ TRACELOG START")
58
+ }
59
+ // called when entering a function
60
+ fin(
61
+ fn: string,
62
+ args:
63
+ | string
64
+ | HTMLElement
65
+ | number
66
+ | boolean
67
+ | false
68
+ | (string | HTMLElement | number | boolean | false)[],
69
+ ) {
70
+ this.padding += this.pad
71
+ this.log(`├─> entering ${fn}`, args)
72
+ }
73
+ // called when exiting a function
74
+ fout(
75
+ fn: string,
76
+ result:
77
+ | string
78
+ | HTMLElement
79
+ | number
80
+ | boolean
81
+ | false
82
+ | (string | HTMLElement | number | boolean | false)[],
83
+ ) {
84
+ this.log("│<──┘ generated return value", result)
85
+ this.padding = this.padding.substring(
86
+ 0,
87
+ this.padding.length - this.pad.length,
88
+ )
89
+ }
90
+ // log message formatting
91
+ format(s: string, tick: number) {
92
+ let nf = function (t: number) {
93
+ let tStr = `${t}`
94
+ while (tStr.length < 4) {
95
+ tStr = `0${t}`
96
+ }
97
+ return tStr
98
+ }
99
+ return `${nf(tick)}> ${this.padding}${s}`
100
+ }
101
+ // log a trace message
102
+ log(...args) {
103
+ const stringCollapse = function (
104
+ v:
105
+ | string
106
+ | HTMLElement
107
+ | number
108
+ | boolean
109
+ | false
110
+ | (string | HTMLElement | number | boolean | false)[],
111
+ ) {
112
+ if (!v) {
113
+ return "<falsey>"
114
+ }
115
+ if (typeof v === "string") {
116
+ return v
117
+ }
118
+ if (checkElementType(v, "HTMLElement")) {
119
+ return (v as HTMLElement).outerHTML || "<empty>"
120
+ }
121
+ if (v instanceof Array) {
122
+ return `[${v.map(stringCollapse).join(",")}]`
123
+ }
124
+ return v.toString() || v.valueOf() || "<unknown>"
125
+ }
126
+ const s = args.map(stringCollapse).join(", ")
127
+ this.messages.push(this.format(s, this.tick++))
128
+ }
129
+ // turn the log into a structured string with
130
+ // unicode box codes to make it a sensible trace.
131
+ toString() {
132
+ let cap = "× "
133
+ let terminator = "└───"
134
+ while (terminator.length <= this.padding.length + this.pad.length) {
135
+ terminator += cap
136
+ }
137
+ let _ = this.padding
138
+ this.padding = ""
139
+ terminator = this.format(terminator, this.tick)
140
+ this.padding = _
141
+ return `${this.messages.join("\n")}\n${terminator}`
142
+ }
143
+ }
@@ -0,0 +1,227 @@
1
+ import { DiffDOMOptions, diffType, nodeType } from "../types"
2
+ import { Diff, checkElementType } from "../helpers"
3
+
4
+ import { objToNode } from "./fromVirtual"
5
+
6
+ // ===== Apply a diff =====
7
+
8
+ const getFromRoute = (
9
+ node: Element,
10
+ route: number[],
11
+ ): Element | Text | false => {
12
+ route = route.slice()
13
+ while (route.length > 0) {
14
+ const c = route.splice(0, 1)[0]
15
+ node = node.childNodes[c] as Element
16
+ }
17
+ return node
18
+ }
19
+
20
+ export function applyDiff(
21
+ tree: Element,
22
+ diff: diffType,
23
+ options: DiffDOMOptions, // {preDiffApply, postDiffApply, textDiff, valueDiffing, _const}
24
+ ) {
25
+ const action = diff[options._const.action] as string | number
26
+ const route = diff[options._const.route] as number[]
27
+ let node
28
+
29
+ if (
30
+ ![options._const.addElement, options._const.addTextElement].includes(
31
+ action,
32
+ )
33
+ ) {
34
+ // For adding nodes, we calculate the route later on. It's different because it includes the position of the newly added item.
35
+ node = getFromRoute(tree, route)
36
+ }
37
+
38
+ let newNode
39
+ let reference: Element
40
+ let nodeArray
41
+
42
+ // pre-diff hook
43
+ const info = {
44
+ diff,
45
+ node,
46
+ }
47
+
48
+ if (options.preDiffApply(info)) {
49
+ return true
50
+ }
51
+
52
+ switch (action) {
53
+ case options._const.addAttribute:
54
+ if (!node || !checkElementType(node, "Element")) {
55
+ return false
56
+ }
57
+ node.setAttribute(
58
+ diff[options._const.name] as string,
59
+ diff[options._const.value] as string,
60
+ )
61
+ break
62
+ case options._const.modifyAttribute:
63
+ if (!node || !checkElementType(node, "Element")) {
64
+ return false
65
+ }
66
+ node.setAttribute(
67
+ diff[options._const.name] as string,
68
+ diff[options._const.newValue] as string,
69
+ )
70
+ if (
71
+ checkElementType(node, "HTMLInputElement") &&
72
+ diff[options._const.name] === "value"
73
+ ) {
74
+ node.value = diff[options._const.newValue] as string
75
+ }
76
+ break
77
+ case options._const.removeAttribute:
78
+ if (!node || !checkElementType(node, "Element")) {
79
+ return false
80
+ }
81
+ node.removeAttribute(diff[options._const.name] as string)
82
+ break
83
+ case options._const.modifyTextElement:
84
+ if (!node || !checkElementType(node, "Text")) {
85
+ return false
86
+ }
87
+ options.textDiff(
88
+ node,
89
+ node.data,
90
+ diff[options._const.oldValue] as string,
91
+ diff[options._const.newValue] as string,
92
+ )
93
+ if (checkElementType(node.parentNode, "HTMLTextAreaElement")) {
94
+ node.parentNode.value = diff[options._const.newValue] as string
95
+ }
96
+ break
97
+ case options._const.modifyValue:
98
+ if (!node || typeof node.value === "undefined") {
99
+ return false
100
+ }
101
+ node.value = diff[options._const.newValue]
102
+ break
103
+ case options._const.modifyComment:
104
+ if (!node || !checkElementType(node, "Comment")) {
105
+ return false
106
+ }
107
+ options.textDiff(
108
+ node,
109
+ node.data,
110
+ diff[options._const.oldValue] as string,
111
+ diff[options._const.newValue] as string,
112
+ )
113
+ break
114
+ case options._const.modifyChecked:
115
+ if (!node || typeof node.checked === "undefined") {
116
+ return false
117
+ }
118
+ node.checked = diff[options._const.newValue]
119
+ break
120
+ case options._const.modifySelected:
121
+ if (!node || typeof node.selected === "undefined") {
122
+ return false
123
+ }
124
+ node.selected = diff[options._const.newValue]
125
+ break
126
+ case options._const.replaceElement: {
127
+ const insideSvg =
128
+ (
129
+ diff[options._const.newValue] as nodeType
130
+ ).nodeName.toLowerCase() === "svg" ||
131
+ node.parentNode.namespaceURI === "http://www.w3.org/2000/svg"
132
+ node.parentNode.replaceChild(
133
+ objToNode(
134
+ diff[options._const.newValue] as nodeType,
135
+ insideSvg,
136
+ options,
137
+ ),
138
+ node,
139
+ )
140
+ break
141
+ }
142
+ case options._const.relocateGroup:
143
+ nodeArray = [...new Array(diff[options._const.groupLength])].map(
144
+ () =>
145
+ node.removeChild(
146
+ node.childNodes[diff[options._const.from] as number],
147
+ ),
148
+ )
149
+ nodeArray.forEach((childNode, index) => {
150
+ if (index === 0) {
151
+ reference =
152
+ node.childNodes[diff[options._const.to] as number]
153
+ }
154
+ node.insertBefore(childNode, reference || null)
155
+ })
156
+ break
157
+ case options._const.removeElement:
158
+ node.parentNode.removeChild(node)
159
+ break
160
+ case options._const.addElement: {
161
+ const parentRoute = route.slice()
162
+ const c: number = parentRoute.splice(parentRoute.length - 1, 1)[0]
163
+ node = getFromRoute(tree, parentRoute)
164
+ if (!checkElementType(node, "Element")) {
165
+ return false
166
+ }
167
+ node.insertBefore(
168
+ objToNode(
169
+ diff[options._const.element] as nodeType,
170
+ node.namespaceURI === "http://www.w3.org/2000/svg",
171
+ options,
172
+ ),
173
+ node.childNodes[c] || null,
174
+ )
175
+ break
176
+ }
177
+ case options._const.removeTextElement: {
178
+ if (!node || node.nodeType !== 3) {
179
+ return false
180
+ }
181
+ const parentNode = node.parentNode
182
+ parentNode.removeChild(node)
183
+ if (checkElementType(parentNode, "HTMLTextAreaElement")) {
184
+ parentNode.value = ""
185
+ }
186
+ break
187
+ }
188
+ case options._const.addTextElement: {
189
+ const parentRoute = route.slice()
190
+ const c: number = parentRoute.splice(parentRoute.length - 1, 1)[0]
191
+ newNode = options.document.createTextNode(
192
+ diff[options._const.value] as string,
193
+ )
194
+ node = getFromRoute(tree, parentRoute)
195
+ if (!node.childNodes) {
196
+ return false
197
+ }
198
+ node.insertBefore(newNode, node.childNodes[c] || null)
199
+ if (checkElementType(node.parentNode, "HTMLTextAreaElement")) {
200
+ node.parentNode.value = diff[options._const.value] as string
201
+ }
202
+ break
203
+ }
204
+ default:
205
+ console.log("unknown action")
206
+ }
207
+
208
+ // if a new node was created, we might be interested in its
209
+ // post diff hook
210
+ options.postDiffApply({
211
+ diff: info.diff,
212
+ node: info.node,
213
+ newNode,
214
+ })
215
+
216
+ return true
217
+ }
218
+
219
+ export function applyDOM(
220
+ tree: Element,
221
+ diffs: (Diff | diffType)[],
222
+ options: DiffDOMOptions,
223
+ ) {
224
+ return diffs.every((diff: Diff | diffType) =>
225
+ applyDiff(tree, diff as diffType, options),
226
+ )
227
+ }
@@ -0,0 +1,83 @@
1
+ import { DiffDOMOptions, elementNodeType, textNodeType } from "../types"
2
+ import { checkElementType } from "../helpers"
3
+
4
+ export function objToNode(
5
+ objNode: elementNodeType,
6
+ insideSvg: boolean,
7
+ options: DiffDOMOptions,
8
+ ) {
9
+ let node: Element | Text | Comment
10
+ if (objNode.nodeName === "#text") {
11
+ node = options.document.createTextNode((objNode as textNodeType).data)
12
+ } else if (objNode.nodeName === "#comment") {
13
+ node = options.document.createComment((objNode as textNodeType).data)
14
+ } else {
15
+ if (insideSvg) {
16
+ node = options.document.createElementNS(
17
+ "http://www.w3.org/2000/svg",
18
+ objNode.nodeName,
19
+ )
20
+ if (objNode.nodeName === "foreignObject") {
21
+ insideSvg = false
22
+ }
23
+ } else if (objNode.nodeName.toLowerCase() === "svg") {
24
+ node = options.document.createElementNS(
25
+ "http://www.w3.org/2000/svg",
26
+ "svg",
27
+ )
28
+ insideSvg = true
29
+ } else {
30
+ node = options.document.createElement(objNode.nodeName)
31
+ }
32
+ if (objNode.attributes) {
33
+ Object.entries(objNode.attributes).forEach(([key, value]) =>
34
+ (node as Element).setAttribute(key, value),
35
+ )
36
+ }
37
+ if (objNode.childNodes) {
38
+ node = node as Element
39
+ objNode.childNodes.forEach(
40
+ (childNode: elementNodeType | textNodeType) =>
41
+ node.appendChild(objToNode(childNode, insideSvg, options)),
42
+ )
43
+ }
44
+ if (options.valueDiffing) {
45
+ if (
46
+ objNode.value &&
47
+ checkElementType(
48
+ node,
49
+ "HTMLButtonElement",
50
+ "HTMLDataElement",
51
+ "HTMLInputElement",
52
+ "HTMLLIElement",
53
+ "HTMLMeterElement",
54
+ "HTMLOptionElement",
55
+ "HTMLProgressElement",
56
+ "HTMLParamElement",
57
+ )
58
+ ) {
59
+ ;(
60
+ node as
61
+ | HTMLButtonElement
62
+ | HTMLDataElement
63
+ | HTMLInputElement
64
+ | HTMLLIElement
65
+ | HTMLMeterElement
66
+ | HTMLOptionElement
67
+ | HTMLProgressElement
68
+ | HTMLParamElement
69
+ ).value = objNode.value
70
+ }
71
+ if (objNode.checked && checkElementType(node, "HTMLInputElement")) {
72
+ ;(node as HTMLInputElement).checked = objNode.checked
73
+ }
74
+ if (
75
+ objNode.selected &&
76
+ checkElementType(node, "HTMLOptionElement")
77
+ ) {
78
+ ;(node as HTMLOptionElement).selected = objNode.selected
79
+ }
80
+ }
81
+ }
82
+ return node
83
+ }
@@ -0,0 +1,2 @@
1
+ export { applyDOM } from "./apply"
2
+ export { undoDOM } from "./undo"
@@ -0,0 +1,90 @@
1
+ import { DiffDOMOptions, diffType } from "../types"
2
+ import { Diff } from "../helpers"
3
+ import { applyDiff } from "./apply"
4
+
5
+ // ===== Undo a diff =====
6
+
7
+ function swap(obj: object, p1: string | number, p2: string | number) {
8
+ const tmp = obj[p1]
9
+ obj[p1] = obj[p2]
10
+ obj[p2] = tmp
11
+ }
12
+
13
+ function undoDiff(
14
+ tree: Element,
15
+ diff: diffType,
16
+ options: DiffDOMOptions, // {preDiffApply, postDiffApply, textDiff, valueDiffing, _const}
17
+ ) {
18
+ switch (diff[options._const.action]) {
19
+ case options._const.addAttribute:
20
+ diff[options._const.action] = options._const.removeAttribute
21
+ applyDiff(tree, diff, options)
22
+ break
23
+ case options._const.modifyAttribute:
24
+ swap(diff, options._const.oldValue, options._const.newValue)
25
+ applyDiff(tree, diff, options)
26
+ break
27
+ case options._const.removeAttribute:
28
+ diff[options._const.action] = options._const.addAttribute
29
+ applyDiff(tree, diff, options)
30
+ break
31
+ case options._const.modifyTextElement:
32
+ swap(diff, options._const.oldValue, options._const.newValue)
33
+ applyDiff(tree, diff, options)
34
+ break
35
+ case options._const.modifyValue:
36
+ swap(diff, options._const.oldValue, options._const.newValue)
37
+ applyDiff(tree, diff, options)
38
+ break
39
+ case options._const.modifyComment:
40
+ swap(diff, options._const.oldValue, options._const.newValue)
41
+ applyDiff(tree, diff, options)
42
+ break
43
+ case options._const.modifyChecked:
44
+ swap(diff, options._const.oldValue, options._const.newValue)
45
+ applyDiff(tree, diff, options)
46
+ break
47
+ case options._const.modifySelected:
48
+ swap(diff, options._const.oldValue, options._const.newValue)
49
+ applyDiff(tree, diff, options)
50
+ break
51
+ case options._const.replaceElement:
52
+ swap(diff, options._const.oldValue, options._const.newValue)
53
+ applyDiff(tree, diff, options)
54
+ break
55
+ case options._const.relocateGroup:
56
+ swap(diff, options._const.from, options._const.to)
57
+ applyDiff(tree, diff, options)
58
+ break
59
+ case options._const.removeElement:
60
+ diff[options._const.action] = options._const.addElement
61
+ applyDiff(tree, diff, options)
62
+ break
63
+ case options._const.addElement:
64
+ diff[options._const.action] = options._const.removeElement
65
+ applyDiff(tree, diff, options)
66
+ break
67
+ case options._const.removeTextElement:
68
+ diff[options._const.action] = options._const.addTextElement
69
+ applyDiff(tree, diff, options)
70
+ break
71
+ case options._const.addTextElement:
72
+ diff[options._const.action] = options._const.removeTextElement
73
+ applyDiff(tree, diff, options)
74
+ break
75
+ default:
76
+ console.log("unknown action")
77
+ }
78
+ }
79
+
80
+ export function undoDOM(
81
+ tree: Element,
82
+ diffs: (diffType | Diff)[],
83
+ options: DiffDOMOptions,
84
+ ) {
85
+ diffs = diffs.slice()
86
+ diffs.reverse()
87
+ diffs.forEach((diff: diffType | Diff) => {
88
+ undoDiff(tree, diff as diffType, options)
89
+ })
90
+ }
@@ -0,0 +1,40 @@
1
+ import { elementNodeType } from "./types"
2
+
3
+ export class Diff {
4
+ constructor(options = {}) {
5
+ Object.entries(options).forEach(([key, value]) => (this[key] = value))
6
+ }
7
+
8
+ toString() {
9
+ return JSON.stringify(this)
10
+ }
11
+
12
+ setValue(
13
+ aKey: string | number,
14
+ aValue:
15
+ | string
16
+ | number
17
+ | boolean
18
+ | number[]
19
+ | { [key: string]: string | { [key: string]: string } }
20
+ | elementNodeType,
21
+ ) {
22
+ this[aKey] = aValue
23
+ return this
24
+ }
25
+ }
26
+
27
+ export function checkElementType(element, ...elementTypeNames: string[]) {
28
+ if (typeof element === "undefined" || element === null) {
29
+ return false
30
+ }
31
+ return elementTypeNames.some(
32
+ (elementTypeName) =>
33
+ // We need to check if the specified type is defined
34
+ // because otherwise instanceof throws an exception.
35
+ typeof element?.ownerDocument?.defaultView?.[elementTypeName] ===
36
+ "function" &&
37
+ element instanceof
38
+ element.ownerDocument.defaultView[elementTypeName],
39
+ )
40
+ }