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,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
+ }