UNPKG

built.io-browserify

Version:

SDK for Built.io Backend which is compatible with Browserify

304 lines (277 loc) 8.94 kB
/* This is a patched version of the "deep-diff" project: https://github.com/flitbit/diff Specifically, we have patched the library with shorter names in the resulting diff object: kind -> k path -> p index -> i item -> m The `lhs` portion of the diff object has been removed (it was unnecessary). A new function is part of the API, `applyPatch`: var Diff = require('diff') var rhs = Diff.applyPatch(lhs, differences) The function takes an lhs and the differences calculated, and returns the rhs as a modified lhs. */ ;(function(undefined) { "use strict"; var $scope , conflict, conflictResolution = []; if (typeof global == 'object' && global) { $scope = global; } else if (typeof window !== 'undefined'){ $scope = window; } else { $scope = {}; } conflict = $scope.DeepDiff; if (conflict) { conflictResolution.push( function() { if ('undefined' !== typeof conflict && $scope.DeepDiff === accumulateDiff) { $scope.DeepDiff = conflict; conflict = undefined; } }); } // nodejs compatible on server side and in the browser. function inherits(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); } function Diff(kind, path) { Object.defineProperty(this, 'k', { value: kind, enumerable: true }); if (path && path.length) { Object.defineProperty(this, 'p', { value: path, enumerable: true }); } } function DiffEdit(path, origin, value) { DiffEdit.super_.call(this, 'E', path); // Object.defineProperty(this, 'lhs', { value: origin, enumerable: true }); Object.defineProperty(this, 'rhs', { value: value, enumerable: true }); } inherits(DiffEdit, Diff); function DiffNew(path, value) { DiffNew.super_.call(this, 'N', path); Object.defineProperty(this, 'rhs', { value: value, enumerable: true }); } inherits(DiffNew, Diff); function DiffDeleted(path, value) { DiffDeleted.super_.call(this, 'D', path); // Object.defineProperty(this, 'lhs', { value: value, enumerable: true }); } inherits(DiffDeleted, Diff); function DiffArray(path, index, item) { DiffArray.super_.call(this, 'A', path); Object.defineProperty(this, 'i', { value: index, enumerable: true }); Object.defineProperty(this, 'm', { value: item, enumerable: true }); } inherits(DiffArray, Diff); function arrayRemove(arr, from, to) { var rest = arr.slice((to || from) + 1 || arr.length); arr.length = from < 0 ? arr.length + from : from; arr.push.apply(arr, rest); return arr; } function deepDiff(lhs, rhs, changes, path, key, stack) { path = path || []; var currentPath = path.slice(0); if (key) { currentPath.push(key); } var ltype = typeof lhs; var rtype = typeof rhs; if (ltype === 'undefined') { if (rtype !== 'undefined') { changes(new DiffNew(currentPath, rhs )); } } else if (rtype === 'undefined') { changes(new DiffDeleted(currentPath, lhs)); } else if (ltype !== rtype) { changes(new DiffEdit(currentPath, lhs, rhs)); } else if (lhs instanceof Date && rhs instanceof Date && ((lhs-rhs) != 0) ) { changes(new DiffEdit(currentPath, lhs, rhs)); } else if (ltype === 'object' && lhs != null && rhs != null) { stack = stack || []; if (stack.indexOf(lhs) < 0) { stack.push(lhs); if (Array.isArray(lhs)) { var i , len = lhs.length , ea = function(d) { changes(new DiffArray(currentPath, i, d)); }; for(i = 0; i < lhs.length; i++) { if (i >= rhs.length) { changes(new DiffArray(currentPath, i, new DiffDeleted(undefined, lhs[i]))); } else { deepDiff(lhs[i], rhs[i], ea, [], null, stack); } } while(i < rhs.length) { changes(new DiffArray(currentPath, i, new DiffNew(undefined, rhs[i++]))); } } else { var akeys = Object.keys(lhs); var pkeys = Object.keys(rhs); akeys.forEach(function(k) { var i = pkeys.indexOf(k); if (i >= 0) { deepDiff(lhs[k], rhs[k], changes, currentPath, k, stack); pkeys = arrayRemove(pkeys, i); } else { deepDiff(lhs[k], undefined, changes, currentPath, k, stack); } }); pkeys.forEach(function(k) { deepDiff(undefined, rhs[k], changes, currentPath, k, stack); }); } stack.length = stack.length - 1; } } else if (lhs !== rhs) { if(!(ltype === "number" && isNaN(lhs) && isNaN(rhs))) { changes(new DiffEdit(currentPath, lhs, rhs)); } } } function accumulateDiff(lhs, rhs, accum) { accum = accum || []; deepDiff(lhs, rhs, function(diff) { if (diff) { accum.push(diff); } }); return (accum.length) ? accum : undefined; } function applyArrayChange(arr, index, change) { if (change.p && change.p.length) { // the structure of the object at the index has changed... var it = arr[index], i, u = change.p.length - 1; for(i = 0; i < u; i++){ it = it[change.p[i]]; } switch(change.k) { case 'A': // Array was modified... // it will be an array... applyArrayChange(it[change.p[i]], change.i, change.m); break; case 'D': // Item was deleted... delete it[change.p[i]]; break; case 'E': case 'N': // Item was edited or is new... it[change.p[i]] = change.rhs; break; } } else { // the array item is different... switch(change.k) { case 'A': // Array was modified... // it will be an array... applyArrayChange(arr[index], change.i, change.m); break; case 'D': // Item was deleted... arr = arrayRemove(arr, index); break; case 'E': case 'N': // Item was edited or is new... arr[index] = change.rhs; break; } } return arr; } function applyChange(target, source, change) { // if (!(change instanceof Diff)) { // throw new TypeError('[Object] change must be instanceof Diff'); // } if (target && source && change) { var it = target, i, u; u = change.p.length - 1; for(i = 0; i < u; i++){ if (typeof it[change.p[i]] === 'undefined') { it[change.p[i]] = {}; } it = it[change.p[i]]; } switch(change.k) { case 'A': // Array was modified... // it will be an array... applyArrayChange(it[change.p[i]], change.i, change.m); break; case 'D': // Item was deleted... delete it[change.p[i]]; break; case 'E': case 'N': // Item was edited or is new... it[change.p[i]] = change.rhs; break; } } } function applyDiff(target, source, filter) { if (target && source) { var onChange = function(change) { if (!filter || filter(target, source, change)) { applyChange(target, source, change); } }; deepDiff(target, source, onChange); } } function applyPatch(lhs, diff) { var arrOps = [] for (var i = 0; i < diff.length; i++) { var d = diff[i] if (d.i || (d.i === 0)) arrOps.push(d) else applyChange(lhs, {}, d) }; arrOps.sort(function(a, b) {return a.i < b.i}) for (var i = 0; i < arrOps.length; i++) { var d = arrOps[i] applyChange(lhs, {}, d) }; return lhs } Object.defineProperties(accumulateDiff, { diff: { value: accumulateDiff, enumerable:true }, observableDiff: { value: deepDiff, enumerable:true }, applyDiff: { value: applyDiff, enumerable:true }, applyChange: { value: applyChange, enumerable:true }, applyPatch: {value: applyPatch, enumerable:true }, isConflict: { get: function() { return 'undefined' !== typeof conflict; }, enumerable: true }, noConflict: { value: function () { if (conflictResolution) { conflictResolution.forEach(function (it) { it(); }); conflictResolution = null; } return accumulateDiff; }, enumerable: true } }); if (typeof module != 'undefined' && module && typeof exports == 'object' && exports && module.exports === exports) { module.exports = accumulateDiff; // nodejs } else { $scope.DeepDiff = accumulateDiff; // other... browser? } }());