UNPKG

10.8 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.12.7
2(function() {
3 (function() {
4 var _, addKeyValue, applyArrayChange, applyBranchChange, applyLeafChange, changeset, compare, compareArray, compareObject, comparePrimitives, convertArrayToObj, exports, getKey, getTypeOfObj, indexOfItemInArray, isEmbeddedKey, modifyKeyValue, parseEmbeddedKeyValue, removeKey, revertArrayChange, revertBranchChange, revertLeafChange;
5 changeset = {
6 VERSION: '0.1.4'
7 };
8 if (typeof module === 'object' && module.exports) {
9 _ = require('lodash');
10 module.exports = exports = changeset;
11 } else {
12 this.changeset = changeset;
13 }
14 getTypeOfObj = function(obj) {
15 if (typeof obj === 'undefined') {
16 return 'undefined';
17 }
18 if (obj === null) {
19 return null;
20 }
21 return Object.prototype.toString.call(obj).match(/^\[object\s(.*)\]$/)[1];
22 };
23 getKey = function(path) {
24 var ref;
25 return (ref = path[path.length - 1]) != null ? ref : '$root';
26 };
27 compare = function(oldObj, newObj, path, embededObjKeys, keyPath) {
28 var changes, diffs, typeOfNewObj, typeOfOldObj;
29 changes = [];
30 typeOfOldObj = getTypeOfObj(oldObj);
31 typeOfNewObj = getTypeOfObj(newObj);
32 if (typeOfOldObj !== typeOfNewObj) {
33 changes.push({
34 type: changeset.op.REMOVE,
35 key: getKey(path),
36 value: oldObj
37 });
38 changes.push({
39 type: changeset.op.ADD,
40 key: getKey(path),
41 value: newObj
42 });
43 return changes;
44 }
45 switch (typeOfOldObj) {
46 case 'Date':
47 changes = changes.concat(comparePrimitives(oldObj.getTime(), newObj.getTime(), path));
48 break;
49 case 'Object':
50 diffs = compareObject(oldObj, newObj, path, embededObjKeys, keyPath);
51 if (diffs.length) {
52 if (path.length) {
53 changes.push({
54 type: changeset.op.UPDATE,
55 key: getKey(path),
56 changes: diffs
57 });
58 } else {
59 changes = changes.concat(diffs);
60 }
61 }
62 break;
63 case 'Array':
64 changes = changes.concat(compareArray(oldObj, newObj, path, embededObjKeys, keyPath));
65 break;
66 case 'Function':
67 break;
68 default:
69 changes = changes.concat(comparePrimitives(oldObj, newObj, path));
70 }
71 return changes;
72 };
73 compareObject = function(oldObj, newObj, path, embededObjKeys, keyPath, skipPath) {
74 var addedKeys, changes, deletedKeys, diffs, i, intersectionKeys, j, k, l, len, len1, len2, newKeyPath, newObjKeys, newPath, oldObjKeys;
75 if (skipPath == null) {
76 skipPath = false;
77 }
78 changes = [];
79 oldObjKeys = Object.keys(oldObj);
80 newObjKeys = Object.keys(newObj);
81 intersectionKeys = _.intersection(oldObjKeys, newObjKeys);
82 for (i = 0, len = intersectionKeys.length; i < len; i++) {
83 k = intersectionKeys[i];
84 newPath = path.concat([k]);
85 newKeyPath = skipPath ? keyPath : keyPath.concat([k]);
86 diffs = compare(oldObj[k], newObj[k], newPath, embededObjKeys, newKeyPath);
87 if (diffs.length) {
88 changes = changes.concat(diffs);
89 }
90 }
91 addedKeys = _.difference(newObjKeys, oldObjKeys);
92 for (j = 0, len1 = addedKeys.length; j < len1; j++) {
93 k = addedKeys[j];
94 newPath = path.concat([k]);
95 newKeyPath = skipPath ? keyPath : keyPath.concat([k]);
96 changes.push({
97 type: changeset.op.ADD,
98 key: getKey(newPath),
99 value: newObj[k]
100 });
101 }
102 deletedKeys = _.difference(oldObjKeys, newObjKeys);
103 for (l = 0, len2 = deletedKeys.length; l < len2; l++) {
104 k = deletedKeys[l];
105 newPath = path.concat([k]);
106 newKeyPath = skipPath ? keyPath : keyPath.concat([k]);
107 changes.push({
108 type: changeset.op.REMOVE,
109 key: getKey(newPath),
110 value: oldObj[k]
111 });
112 }
113 return changes;
114 };
115 compareArray = function(oldObj, newObj, path, embededObjKeys, keyPath) {
116 var diffs, indexedNewObj, indexedOldObj, ref, uniqKey;
117 uniqKey = (ref = embededObjKeys != null ? embededObjKeys[keyPath.join('.')] : void 0) != null ? ref : '$index';
118 indexedOldObj = convertArrayToObj(oldObj, uniqKey);
119 indexedNewObj = convertArrayToObj(newObj, uniqKey);
120 diffs = compareObject(indexedOldObj, indexedNewObj, path, embededObjKeys, keyPath, true);
121 if (diffs.length) {
122 return [
123 {
124 type: changeset.op.UPDATE,
125 key: getKey(path),
126 embededKey: uniqKey,
127 changes: diffs
128 }
129 ];
130 } else {
131 return [];
132 }
133 };
134 convertArrayToObj = function(arr, uniqKey) {
135 var index, obj, value;
136 obj = {};
137 if (uniqKey !== '$index') {
138 obj = _.keyBy(arr, uniqKey);
139 } else {
140 for (index in arr) {
141 value = arr[index];
142 obj[index] = value;
143 }
144 }
145 return obj;
146 };
147 comparePrimitives = function(oldObj, newObj, path) {
148 var changes;
149 changes = [];
150 if (oldObj !== newObj) {
151 changes.push({
152 type: changeset.op.UPDATE,
153 key: getKey(path),
154 value: newObj,
155 oldValue: oldObj
156 });
157 }
158 return changes;
159 };
160 isEmbeddedKey = function(key) {
161 return /\$.*=/gi.test(key);
162 };
163 removeKey = function(obj, key, embededKey) {
164 var index;
165 if (Array.isArray(obj)) {
166 if (embededKey !== '$index' || !obj[key]) {
167 index = indexOfItemInArray(obj, embededKey, key);
168 }
169 return obj.splice(index != null ? index : key, 1);
170 } else {
171 return delete obj[key];
172 }
173 };
174 indexOfItemInArray = function(arr, key, value) {
175 var index, item;
176 for (index in arr) {
177 item = arr[index];
178 if (key === '$index') {
179 if (item === value) {
180 return index;
181 }
182 } else if (item[key] === value) {
183 return index;
184 }
185 }
186 return -1;
187 };
188 modifyKeyValue = function(obj, key, value) {
189 return obj[key] = value;
190 };
191 addKeyValue = function(obj, key, value) {
192 if (Array.isArray(obj)) {
193 return obj.push(value);
194 } else {
195 return obj[key] = value;
196 }
197 };
198 parseEmbeddedKeyValue = function(key) {
199 var uniqKey, value;
200 uniqKey = key.substring(1, key.indexOf('='));
201 value = key.substring(key.indexOf('=') + 1);
202 return {
203 uniqKey: uniqKey,
204 value: value
205 };
206 };
207 applyLeafChange = function(obj, change, embededKey) {
208 var key, type, value;
209 type = change.type, key = change.key, value = change.value;
210 switch (type) {
211 case changeset.op.ADD:
212 return addKeyValue(obj, key, value);
213 case changeset.op.UPDATE:
214 return modifyKeyValue(obj, key, value);
215 case changeset.op.REMOVE:
216 return removeKey(obj, key, embededKey);
217 }
218 };
219 applyArrayChange = function(arr, change) {
220 var element, i, len, ref, results, subchange;
221 ref = change.changes;
222 results = [];
223 for (i = 0, len = ref.length; i < len; i++) {
224 subchange = ref[i];
225 if ((subchange.value != null) || subchange.type === changeset.op.REMOVE) {
226 results.push(applyLeafChange(arr, subchange, change.embededKey));
227 } else {
228 if (change.embededKey === '$index') {
229 element = arr[+subchange.key];
230 } else {
231 element = _.find(arr, function(el) {
232 return el[change.embededKey] === subchange.key;
233 });
234 }
235 results.push(changeset.applyChanges(element, subchange.changes));
236 }
237 }
238 return results;
239 };
240 applyBranchChange = function(obj, change) {
241 if (Array.isArray(obj)) {
242 return applyArrayChange(obj, change);
243 } else {
244 return changeset.applyChanges(obj, change.changes);
245 }
246 };
247 revertLeafChange = function(obj, change, embededKey) {
248 var key, oldValue, type, value;
249 type = change.type, key = change.key, value = change.value, oldValue = change.oldValue;
250 switch (type) {
251 case changeset.op.ADD:
252 return removeKey(obj, key, embededKey);
253 case changeset.op.UPDATE:
254 return modifyKeyValue(obj, key, oldValue);
255 case changeset.op.REMOVE:
256 return addKeyValue(obj, key, value);
257 }
258 };
259 revertArrayChange = function(arr, change) {
260 var element, i, len, ref, results, subchange;
261 ref = change.changes;
262 results = [];
263 for (i = 0, len = ref.length; i < len; i++) {
264 subchange = ref[i];
265 if ((subchange.value != null) || subchange.type === changeset.op.REMOVE) {
266 results.push(revertLeafChange(arr, subchange, change.embededKey));
267 } else {
268 if (change.embededKey === '$index') {
269 element = arr[+subchange.key];
270 } else {
271 element = _.find(arr, function(el) {
272 return el[change.embededKey] === subchange.key;
273 });
274 }
275 results.push(changeset.revertChanges(element, subchange.changes));
276 }
277 }
278 return results;
279 };
280 revertBranchChange = function(obj, change) {
281 if (Array.isArray(obj)) {
282 return revertArrayChange(obj, change);
283 } else {
284 return changeset.revertChanges(obj, change.changes);
285 }
286 };
287 changeset.diff = function(oldObj, newObj, embededObjKeys) {
288 return compare(oldObj, newObj, [], embededObjKeys, []);
289 };
290 changeset.applyChanges = function(obj, changesets) {
291 var change, i, len, results;
292 results = [];
293 for (i = 0, len = changesets.length; i < len; i++) {
294 change = changesets[i];
295 if ((change.value != null) || change.type === changeset.op.REMOVE) {
296 results.push(applyLeafChange(obj, change, change.embededKey));
297 } else {
298 results.push(applyBranchChange(obj[change.key], change));
299 }
300 }
301 return results;
302 };
303 changeset.revertChanges = function(obj, changeset) {
304 var change, i, len, ref, results;
305 ref = changeset.reverse();
306 results = [];
307 for (i = 0, len = ref.length; i < len; i++) {
308 change = ref[i];
309 if (!change.changes) {
310 results.push(revertLeafChange(obj, change));
311 } else {
312 results.push(revertBranchChange(obj[change.key], change));
313 }
314 }
315 return results;
316 };
317 changeset.op = {
318 REMOVE: 'remove',
319 ADD: 'add',
320 UPDATE: 'update'
321 };
322 })();
323
324}).call(this);