clapton 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/app/helpers/clapton/clapton_helper.rb +16 -1
- data/lib/clapton/engine.rb +14 -10
- data/lib/clapton/javascripts/dist/client.js +31 -19
- data/lib/clapton/javascripts/dist/components-for-test.js +439 -0
- data/lib/clapton/javascripts/dist/components.js +356 -382
- data/lib/clapton/javascripts/node_modules/diff-dom/LICENSE.txt +165 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/README.md +224 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/browser/diffDOM.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/TraceLogger.d.ts +28 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/apply.d.ts +4 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/fromVirtual.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/index.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/dom/undo.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/helpers.d.ts +11 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/index.d.ts +10 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/types.d.ts +104 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/apply.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/diff.d.ts +22 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromDOM.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/fromString.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/helpers.d.ts +40 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/diffDOM/virtual/index.d.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/dts/index.d.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.d.ts +136 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js +1996 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/index.min.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js +1991 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/dist/module.js.map +1 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/index.html +62 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/package.json +54 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/rollup.config.mjs +67 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/TraceLogger.ts +143 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/apply.ts +227 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/fromVirtual.ts +83 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/index.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/dom/undo.ts +90 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/helpers.ts +40 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/index.ts +121 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/types.ts +154 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/apply.ts +349 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/diff.ts +855 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromDOM.ts +74 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/fromString.ts +239 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/helpers.ts +461 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/diffDOM/virtual/index.ts +3 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/src/index.ts +2 -0
- data/lib/clapton/javascripts/node_modules/diff-dom/tsconfig.json +103 -0
- data/lib/clapton/javascripts/rollup.config.mjs +17 -2
- data/lib/clapton/javascripts/src/actions/initialize-actions.ts +6 -3
- data/lib/clapton/javascripts/src/channel/clapton-channel.js +6 -3
- data/lib/clapton/javascripts/src/client.ts +15 -15
- data/lib/clapton/javascripts/src/components-for-test.ts +29 -0
- data/lib/clapton/javascripts/src/components.ts +4 -1
- data/lib/clapton/javascripts/src/dom/update-component.ts +3 -2
- data/lib/clapton/javascripts/src/inputs/initialize-inputs.ts +2 -2
- data/lib/clapton/test_helper/base.rb +1 -1
- data/lib/clapton/version.rb +1 -1
- metadata +49 -3
- data/lib/clapton/javascripts/src/dom/update-component.spec.ts +0 -32
|
@@ -0,0 +1,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,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
|
+
}
|