-
Notifications
You must be signed in to change notification settings - Fork 0
/
unpatch.js
89 lines (78 loc) · 2.35 KB
/
unpatch.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const assert = require('assert');
function unpatch(b, diff_ab)
{
if (diff_ab === undefined) {
return b;
}
if (Array.isArray(diff_ab)) {
switch (diff_ab.length) {
case 1: // [new]
return undefined;
case 2: // [old,new]
assert.deepStrictEqual(b, diff_ab[1]);
return diff_ab[0];
case 3: // [new,0,0]
if (diff_ab[2] !== 0) {
throw new Error(`Invalid diff command: ${diff_ab[2]}, expected 0`);
}
return diff_ab[0];
default:
throw new Error(`Invalid diff length: ${diff_ab.length}`);
}
}
if (diff_ab._t === 'a') {
return unpatch_array(b, diff_ab);
}
return unpatch_object(b, diff_ab);
}
function unpatch_array(b, diff_ab)
{
// 1. Undo modification first
// 2. Then perform removal and insertion
const keys = Object.keys(diff_ab);
const remove = [];
const insert = [];
keys.forEach(function (key) {
if (key === '_t') {
return;
}
if (key[0] === '_') {
// Schedule insert and remove
switch (diff_ab[key][2]) {
case 0: // "_555": [old,0,0]
insert.push({i: parseInt(key.substring(1)), value: diff_ab[key][0]});
break;
case 3: // "_555": ['',x,3]
insert.push({i: parseInt(key.substring(1)), value: b[diff_ab[key][1]]});
remove.push(diff_ab[key][1]);
break;
}
return;
}
if (Array.isArray(diff_ab[key]) && (diff_ab[key].length === 1)) { // [new]
assert.deepStrictEqual(b[key], diff_ab[key][0]);
remove.push(parseInt(key));
}
else {
b[key] = unpatch(b[key], diff_ab[key]);
}
})
// Remove in reverse order
remove.sort((b,a) => a-b).forEach(i => b.splice(i, 1));
// Insert in direct order
insert.sort((a,b) => a.i-b.i).forEach(v => b.splice(v.i, 0, v.value));
return b;
}
function unpatch_object(b, diff_ab)
{
Object.keys(diff_ab).forEach(function (key) {
if (Array.isArray(diff_ab[key]) && (diff_ab[key].length === 1)) { // [new]
delete b[key];
}
else {
b[key] = unpatch(b[key], diff_ab[key]);
}
});
return b;
}
module.exports = unpatch;