forked from eugeneware/changeset
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
112 lines (97 loc) · 2.42 KB
/
index.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
var _ = require('underscore');
module.exports = diff;
function diff(old, new_) {
var changes = [];
changes = changes.concat(compare([], old, new_));
comparing = [];
return changes;
}
function delCheck(op) {
if (op.type === 'put' && op.value === undefined) {
op.type = 'del';
delete op.value;
}
return op;
}
var comparing = [];
function compare(path, old, new_) {
var changes = [];
if (old !== null && new_ !== null &&
typeof old === 'object' &&
!_.contains(comparing, old)) {
comparing.push(old);
var oldKeys = Object.keys(old);
var newKeys = Object.keys(new_);
var sameKeys = _.intersection(oldKeys, newKeys);
sameKeys.forEach(function (k) {
var childChanges = compare(path.concat(k), old[k], new_[k]);
changes = changes.concat(childChanges);
});
var delKeys = _.difference(oldKeys, newKeys);
delKeys.forEach(function (k) {
changes.push({ type: 'del', key: path.concat(k) });
});
var newKeys_ = _.difference(newKeys, oldKeys);
newKeys_.forEach(function (k) {
changes.push(delCheck(
{ type: 'put', key: path.concat(k), value: new_[k] }));
});
} else if (old !== new_) {
changes.push(delCheck({ type: 'put', key: path, value: new_ }));
}
return changes;
}
module.exports.apply = apply;
function apply(changes, target, modify) {
var obj, clone;
if (modify) {
obj = target;
} else {
clone = require('udc');
obj = clone(target);
}
changes.forEach(function (ch) {
var ptr, keys, len;
switch (ch.type) {
case 'put':
ptr = obj;
keys = ch.key;
len = keys.length;
if (len) {
keys.forEach(function (prop, i) {
if (!(prop in ptr)) {
ptr[prop] = {};
}
if (i < len - 1) {
ptr = ptr[prop];
} else {
ptr[prop] = ch.value;
}
});
} else {
obj = ch.value;
}
break;
case 'del':
ptr = obj;
keys = ch.key;
len = keys.length;
if (len) {
keys.forEach(function (prop, i) {
if (!(prop in ptr)) {
ptr[prop] = {};
}
if (i < len - 1) {
ptr = ptr[prop];
} else {
delete ptr[prop];
}
});
} else {
obj = null;
}
break;
}
});
return obj;
}