Comparing version 0.3.8 to 1.0.0-pre.1
802
index.js
@@ -1,415 +0,501 @@ | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | ||
typeof define === 'function' && define.amd ? define(factory) : | ||
(global.DeepDiff = factory()); | ||
}(this, (function () { 'use strict'; | ||
(function (root, factory) { | ||
if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef | ||
define([], factory);// eslint-disable-line no-undef | ||
} else if (typeof module === 'object' && module.exports) { | ||
module.exports = factory(); | ||
} else { | ||
root.returnExports = factory(); | ||
} | ||
// eslint-disable-next-line no-undef | ||
}(typeof self !== 'undefined' ? self : this, function () { | ||
var root = this; | ||
var $conflict = root.DeepDiff; | ||
var $scope; | ||
var conflict; | ||
var 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 | ||
} | ||
}); | ||
} | ||
} | ||
// 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, 'kind', { | ||
value: kind, | ||
enumerable: true | ||
}); | ||
if (path && path.length) { | ||
Object.defineProperty(this, 'path', { | ||
value: path, | ||
enumerable: true | ||
}); | ||
} | ||
}); | ||
} | ||
} | ||
function Diff(kind, path) { | ||
Object.defineProperty(this, 'kind', { | ||
value: kind, | ||
enumerable: true | ||
}); | ||
if (path && path.length) { | ||
Object.defineProperty(this, 'path', { | ||
value: path, | ||
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 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 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 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, 'index', { | ||
value: index, | ||
enumerable: true | ||
}); | ||
Object.defineProperty(this, 'item', { | ||
value: item, | ||
enumerable: true | ||
}); | ||
} | ||
inherits(DiffArray, Diff); | ||
function DiffArray(path, index, item) { | ||
DiffArray.super_.call(this, 'A', path); | ||
Object.defineProperty(this, 'index', { | ||
value: index, | ||
enumerable: true | ||
}); | ||
Object.defineProperty(this, 'item', { | ||
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 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 realTypeOf(subject) { | ||
var type = typeof subject; | ||
if (type !== 'object') { | ||
return type; | ||
} | ||
function realTypeOf(subject) { | ||
var type = typeof subject; | ||
if (type !== 'object') { | ||
return type; | ||
if (subject === Math) { | ||
return 'math'; | ||
} else if (subject === null) { | ||
return 'null'; | ||
} else if (Array.isArray(subject)) { | ||
return 'array'; | ||
} else if (Object.prototype.toString.call(subject) === '[object Date]') { | ||
return 'date'; | ||
} else if (typeof subject.toString === 'function' && /^\/.*\//.test(subject.toString())) { | ||
return 'regexp'; | ||
} | ||
return 'object'; | ||
} | ||
if (subject === Math) { | ||
return 'math'; | ||
} else if (subject === null) { | ||
return 'null'; | ||
} else if (Array.isArray(subject)) { | ||
return 'array'; | ||
} else if (Object.prototype.toString.call(subject) === '[object Date]') { | ||
return 'date'; | ||
} else if (typeof subject.toString === 'function' && /^\/.*\//.test(subject.toString())) { | ||
return 'regexp'; | ||
// http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/ | ||
function hashThisString(string) { | ||
var hash = 0; | ||
if (string.length === 0) { return hash; } | ||
for (var i = 0; i < string.length; i++) { | ||
var char = string.charCodeAt(i); | ||
hash = ((hash << 5) - hash) + char; | ||
hash = hash & hash; // Convert to 32bit integer | ||
} | ||
return hash; | ||
} | ||
return 'object'; | ||
} | ||
function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) { | ||
path = path || []; | ||
stack = stack || []; | ||
var currentPath = path.slice(0); | ||
if (typeof key !== 'undefined') { | ||
if (prefilter) { | ||
if (typeof(prefilter) === 'function' && prefilter(currentPath, key)) { | ||
return; } else if (typeof(prefilter) === 'object') { | ||
if (prefilter.prefilter && prefilter.prefilter(currentPath, key)) { | ||
return; } | ||
if (prefilter.normalize) { | ||
var alt = prefilter.normalize(currentPath, key, lhs, rhs); | ||
if (alt) { | ||
lhs = alt[0]; | ||
rhs = alt[1]; | ||
} | ||
// Gets a hash of the given object in an array order-independent fashion | ||
// also object key order independent (easier since they can be alphabetized) | ||
function getOrderIndependentHash(object) { | ||
var accum = 0; | ||
var type = realTypeOf(object); | ||
if (type === 'array') { | ||
object.forEach(function (item) { | ||
// Addition is commutative so this is order indep | ||
accum += getOrderIndependentHash(item); | ||
}); | ||
var arrayString = '[type: array, hash: ' + accum + ']'; | ||
return accum + hashThisString(arrayString); | ||
} | ||
if (type === 'object') { | ||
for (var key in object) { | ||
if (object.hasOwnProperty(key)) { | ||
var keyValueString = '[ type: object, key: ' + key + ', value hash: ' + getOrderIndependentHash(object[key]) + ']'; | ||
accum += hashThisString(keyValueString); | ||
} | ||
} | ||
return accum; | ||
} | ||
currentPath.push(key); | ||
} | ||
// Use string comparison for regexes | ||
if (realTypeOf(lhs) === 'regexp' && realTypeOf(rhs) === 'regexp') { | ||
lhs = lhs.toString(); | ||
rhs = rhs.toString(); | ||
// Non object, non array...should be good? | ||
var stringToHash = '[ type: ' + type + ' ; value: ' + object + ']'; | ||
return accum + hashThisString(stringToHash); | ||
} | ||
var ltype = typeof lhs; | ||
var rtype = typeof rhs; | ||
var ldefined = ltype !== 'undefined' || (stack && stack[stack.length - 1].lhs && stack[stack.length - 1].lhs.hasOwnProperty(key)); | ||
var rdefined = rtype !== 'undefined' || (stack && stack[stack.length - 1].rhs && stack[stack.length - 1].rhs.hasOwnProperty(key)); | ||
if (!ldefined && rdefined) { | ||
changes(new DiffNew(currentPath, rhs)); | ||
} else if (!rdefined && ldefined) { | ||
changes(new DiffDeleted(currentPath, lhs)); | ||
} else if (realTypeOf(lhs) !== realTypeOf(rhs)) { | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
} else if (realTypeOf(lhs) === 'date' && (lhs - rhs) !== 0) { | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
} else if (ltype === 'object' && lhs !== null && rhs !== null) { | ||
if (!stack.filter(function(x) { | ||
return x.lhs === lhs; }).length) { | ||
stack.push({ lhs: lhs, rhs: rhs }); | ||
if (Array.isArray(lhs)) { | ||
var i, len = lhs.length; | ||
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], changes, prefilter, currentPath, i, stack); | ||
function deepDiff(lhs, rhs, changes, prefilter, path, key, stack, orderIndependent) { | ||
path = path || []; | ||
stack = stack || []; | ||
var currentPath = path.slice(0); | ||
if (typeof key !== 'undefined') { | ||
if (prefilter) { | ||
if (typeof (prefilter) === 'function' && prefilter(currentPath, key)) { | ||
return; | ||
} else if (typeof (prefilter) === 'object') { | ||
if (prefilter.prefilter && prefilter.prefilter(currentPath, key)) { | ||
return; | ||
} | ||
if (prefilter.normalize) { | ||
var alt = prefilter.normalize(currentPath, key, lhs, rhs); | ||
if (alt) { | ||
lhs = alt[0]; | ||
rhs = alt[1]; | ||
} | ||
} | ||
} | ||
while (i < rhs.length) { | ||
changes(new DiffArray(currentPath, i, new DiffNew(undefined, rhs[i++]))); | ||
} | ||
currentPath.push(key); | ||
} | ||
// Use string comparison for regexes | ||
if (realTypeOf(lhs) === 'regexp' && realTypeOf(rhs) === 'regexp') { | ||
lhs = lhs.toString(); | ||
rhs = rhs.toString(); | ||
} | ||
var ltype = typeof lhs; | ||
var rtype = typeof rhs; | ||
var ldefined = ltype !== 'undefined' || | ||
(stack && (stack.length > 0) && stack[stack.length - 1].lhs && | ||
Object.getOwnPropertyDescriptor(stack[stack.length - 1].lhs, key)); | ||
var rdefined = rtype !== 'undefined' || | ||
(stack && (stack.length > 0) && stack[stack.length - 1].rhs && | ||
Object.getOwnPropertyDescriptor(stack[stack.length - 1].rhs, key)); | ||
if (!ldefined && rdefined) { | ||
changes(new DiffNew(currentPath, rhs)); | ||
} else if (!rdefined && ldefined) { | ||
changes(new DiffDeleted(currentPath, lhs)); | ||
} else if (realTypeOf(lhs) !== realTypeOf(rhs)) { | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
} else if (realTypeOf(lhs) === 'date' && (lhs - rhs) !== 0) { | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
} else if (ltype === 'object' && lhs !== null && rhs !== null) { | ||
if (!stack.filter(function (x) { | ||
return x.lhs === lhs; | ||
}).length) { | ||
stack.push({ lhs: lhs, rhs: rhs }); | ||
if (Array.isArray(lhs)) { | ||
// If order doesn't matter, we need to sort our arrays | ||
if (orderIndependent) { | ||
lhs.sort(function (a, b) { | ||
return getOrderIndependentHash(a) - getOrderIndependentHash(b); | ||
}); | ||
rhs.sort(function (a, b) { | ||
return getOrderIndependentHash(a) - getOrderIndependentHash(b); | ||
}); | ||
} | ||
var i; | ||
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], changes, prefilter, currentPath, i, stack, orderIndependent); | ||
} | ||
} | ||
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 other = pkeys.indexOf(k); | ||
if (other >= 0) { | ||
deepDiff(lhs[k], rhs[k], changes, prefilter, currentPath, k, stack, orderIndependent); | ||
pkeys = arrayRemove(pkeys, other); | ||
} else { | ||
deepDiff(lhs[k], undefined, changes, prefilter, currentPath, k, stack, orderIndependent); | ||
} | ||
}); | ||
pkeys.forEach(function (k) { | ||
deepDiff(undefined, rhs[k], changes, prefilter, currentPath, k, stack, orderIndependent); | ||
}); | ||
} | ||
} else { | ||
var akeys = Object.keys(lhs); | ||
var pkeys = Object.keys(rhs); | ||
akeys.forEach(function(k, i) { | ||
var other = pkeys.indexOf(k); | ||
if (other >= 0) { | ||
deepDiff(lhs[k], rhs[k], changes, prefilter, currentPath, k, stack); | ||
pkeys = arrayRemove(pkeys, other); | ||
} else { | ||
deepDiff(lhs[k], undefined, changes, prefilter, currentPath, k, stack); | ||
} | ||
}); | ||
pkeys.forEach(function(k) { | ||
deepDiff(undefined, rhs[k], changes, prefilter, currentPath, k, stack); | ||
}); | ||
stack.length = stack.length - 1; | ||
} else if (lhs !== rhs) { | ||
// lhs is contains a cycle at this element and it differs from rhs | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
} | ||
stack.length = stack.length - 1; | ||
} else if (lhs !== rhs) { | ||
// lhs is contains a cycle at this element and it differs from rhs | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
if (!(ltype === 'number' && isNaN(lhs) && isNaN(rhs))) { | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
} | ||
} | ||
} else if (lhs !== rhs) { | ||
if (!(ltype === 'number' && isNaN(lhs) && isNaN(rhs))) { | ||
changes(new DiffEdit(currentPath, lhs, rhs)); | ||
} | ||
} | ||
} | ||
function accumulateDiff(lhs, rhs, prefilter, accum) { | ||
accum = accum || []; | ||
deepDiff(lhs, rhs, | ||
function(diff) { | ||
if (diff) { | ||
accum.push(diff); | ||
} | ||
}, | ||
prefilter); | ||
return (accum.length) ? accum : undefined; | ||
} | ||
function orderIndependentDeepDiff(lhs, rhs, changes, prefilter, path, key, stack) { | ||
return deepDiff(lhs, rhs, changes, prefilter, path, key, stack, true); | ||
} | ||
function applyArrayChange(arr, index, change) { | ||
if (change.path && change.path.length) { | ||
var it = arr[index], | ||
i, u = change.path.length - 1; | ||
for (i = 0; i < u; i++) { | ||
it = it[change.path[i]]; | ||
} | ||
switch (change.kind) { | ||
case 'A': | ||
applyArrayChange(it[change.path[i]], change.index, change.item); | ||
break; | ||
case 'D': | ||
delete it[change.path[i]]; | ||
break; | ||
case 'E': | ||
case 'N': | ||
it[change.path[i]] = change.rhs; | ||
break; | ||
} | ||
} else { | ||
switch (change.kind) { | ||
case 'A': | ||
applyArrayChange(arr[index], change.index, change.item); | ||
break; | ||
case 'D': | ||
arr = arrayRemove(arr, index); | ||
break; | ||
case 'E': | ||
case 'N': | ||
arr[index] = change.rhs; | ||
break; | ||
} | ||
function accumulateDiff(lhs, rhs, prefilter, accum) { | ||
accum = accum || []; | ||
deepDiff(lhs, rhs, | ||
function (diff) { | ||
if (diff) { | ||
accum.push(diff); | ||
} | ||
}, | ||
prefilter); | ||
return (accum.length) ? accum : undefined; | ||
} | ||
return arr; | ||
} | ||
function applyChange(target, source, change) { | ||
if (target && source && change && change.kind) { | ||
var it = target, | ||
i = -1, | ||
last = change.path ? change.path.length - 1 : 0; | ||
while (++i < last) { | ||
if (typeof it[change.path[i]] === 'undefined') { | ||
it[change.path[i]] = (typeof change.path[i] === 'number') ? [] : {}; | ||
function accumulateOrderIndependentDiff(lhs, rhs, prefilter, accum) { | ||
accum = accum || []; | ||
deepDiff(lhs, rhs, | ||
function (diff) { | ||
if (diff) { | ||
accum.push(diff); | ||
} | ||
}, | ||
prefilter, null, null, null, true); | ||
return (accum.length) ? accum : undefined; | ||
} | ||
function applyArrayChange(arr, index, change) { | ||
if (change.path && change.path.length) { | ||
var it = arr[index], | ||
i, u = change.path.length - 1; | ||
for (i = 0; i < u; i++) { | ||
it = it[change.path[i]]; | ||
} | ||
it = it[change.path[i]]; | ||
switch (change.kind) { | ||
case 'A': | ||
applyArrayChange(it[change.path[i]], change.index, change.item); | ||
break; | ||
case 'D': | ||
delete it[change.path[i]]; | ||
break; | ||
case 'E': | ||
case 'N': | ||
it[change.path[i]] = change.rhs; | ||
break; | ||
} | ||
} else { | ||
switch (change.kind) { | ||
case 'A': | ||
applyArrayChange(arr[index], change.index, change.item); | ||
break; | ||
case 'D': | ||
arr = arrayRemove(arr, index); | ||
break; | ||
case 'E': | ||
case 'N': | ||
arr[index] = change.rhs; | ||
break; | ||
} | ||
} | ||
switch (change.kind) { | ||
case 'A': | ||
applyArrayChange(change.path ? it[change.path[i]] : it, change.index, change.item); | ||
break; | ||
case 'D': | ||
delete it[change.path[i]]; | ||
break; | ||
case 'E': | ||
case 'N': | ||
it[change.path[i]] = change.rhs; | ||
break; | ||
} | ||
return arr; | ||
} | ||
} | ||
function revertArrayChange(arr, index, change) { | ||
if (change.path && change.path.length) { | ||
// the structure of the object at the index has changed... | ||
var it = arr[index], | ||
i, u = change.path.length - 1; | ||
for (i = 0; i < u; i++) { | ||
it = it[change.path[i]]; | ||
function applyChange(target, source, change) { | ||
if (target && source && change && change.kind) { | ||
var it = target, | ||
i = -1, | ||
last = change.path ? change.path.length - 1 : 0; | ||
while (++i < last) { | ||
if (typeof it[change.path[i]] === 'undefined') { | ||
it[change.path[i]] = (typeof change.path[i + 1] !== 'undefined' && typeof change.path[i + 1] === 'number') ? [] : {}; | ||
} | ||
it = it[change.path[i]]; | ||
} | ||
switch (change.kind) { | ||
case 'A': | ||
if (change.path && typeof it[change.path[i]] === 'undefined') { | ||
it[change.path[i]] = []; | ||
} | ||
applyArrayChange(change.path ? it[change.path[i]] : it, change.index, change.item); | ||
break; | ||
case 'D': | ||
delete it[change.path[i]]; | ||
break; | ||
case 'E': | ||
case 'N': | ||
it[change.path[i]] = change.rhs; | ||
break; | ||
} | ||
} | ||
switch (change.kind) { | ||
case 'A': | ||
revertArrayChange(it[change.path[i]], change.index, change.item); | ||
break; | ||
case 'D': | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'E': | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'N': | ||
delete it[change.path[i]]; | ||
break; | ||
} | ||
} else { | ||
// the array item is different... | ||
switch (change.kind) { | ||
case 'A': | ||
revertArrayChange(arr[index], change.index, change.item); | ||
break; | ||
case 'D': | ||
arr[index] = change.lhs; | ||
break; | ||
case 'E': | ||
arr[index] = change.lhs; | ||
break; | ||
case 'N': | ||
arr = arrayRemove(arr, index); | ||
break; | ||
} | ||
} | ||
return arr; | ||
} | ||
function revertChange(target, source, change) { | ||
if (target && source && change && change.kind) { | ||
var it = target, | ||
i, u; | ||
u = change.path.length - 1; | ||
for (i = 0; i < u; i++) { | ||
if (typeof it[change.path[i]] === 'undefined') { | ||
it[change.path[i]] = {}; | ||
function revertArrayChange(arr, index, change) { | ||
if (change.path && change.path.length) { | ||
// the structure of the object at the index has changed... | ||
var it = arr[index], | ||
i, u = change.path.length - 1; | ||
for (i = 0; i < u; i++) { | ||
it = it[change.path[i]]; | ||
} | ||
it = it[change.path[i]]; | ||
switch (change.kind) { | ||
case 'A': | ||
revertArrayChange(it[change.path[i]], change.index, change.item); | ||
break; | ||
case 'D': | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'E': | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'N': | ||
delete it[change.path[i]]; | ||
break; | ||
} | ||
} else { | ||
// the array item is different... | ||
switch (change.kind) { | ||
case 'A': | ||
revertArrayChange(arr[index], change.index, change.item); | ||
break; | ||
case 'D': | ||
arr[index] = change.lhs; | ||
break; | ||
case 'E': | ||
arr[index] = change.lhs; | ||
break; | ||
case 'N': | ||
arr = arrayRemove(arr, index); | ||
break; | ||
} | ||
} | ||
switch (change.kind) { | ||
case 'A': | ||
// Array was modified... | ||
// it will be an array... | ||
revertArrayChange(it[change.path[i]], change.index, change.item); | ||
break; | ||
case 'D': | ||
// Item was deleted... | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'E': | ||
// Item was edited... | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'N': | ||
// Item is new... | ||
delete it[change.path[i]]; | ||
break; | ||
} | ||
return arr; | ||
} | ||
} | ||
function applyDiff(target, source, filter) { | ||
if (target && source) { | ||
var onChange = function(change) { | ||
if (!filter || filter(target, source, change)) { | ||
applyChange(target, source, change); | ||
function revertChange(target, source, change) { | ||
if (target && source && change && change.kind) { | ||
var it = target, | ||
i, u; | ||
u = change.path.length - 1; | ||
for (i = 0; i < u; i++) { | ||
if (typeof it[change.path[i]] === 'undefined') { | ||
it[change.path[i]] = {}; | ||
} | ||
it = it[change.path[i]]; | ||
} | ||
}; | ||
deepDiff(target, source, onChange); | ||
switch (change.kind) { | ||
case 'A': | ||
// Array was modified... | ||
// it will be an array... | ||
revertArrayChange(it[change.path[i]], change.index, change.item); | ||
break; | ||
case 'D': | ||
// Item was deleted... | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'E': | ||
// Item was edited... | ||
it[change.path[i]] = change.lhs; | ||
break; | ||
case 'N': | ||
// Item is new... | ||
delete it[change.path[i]]; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
Object.defineProperties(accumulateDiff, { | ||
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); | ||
} | ||
} | ||
diff: { | ||
value: accumulateDiff, | ||
enumerable: true | ||
}, | ||
observableDiff: { | ||
value: deepDiff, | ||
enumerable: true | ||
}, | ||
applyDiff: { | ||
value: applyDiff, | ||
enumerable: true | ||
}, | ||
applyChange: { | ||
value: applyChange, | ||
enumerable: true | ||
}, | ||
revertChange: { | ||
value: revertChange, | ||
enumerable: true | ||
}, | ||
isConflict: { | ||
value: function() { | ||
return 'undefined' !== typeof conflict; | ||
Object.defineProperties(accumulateDiff, { | ||
diff: { | ||
value: accumulateDiff, | ||
enumerable: true | ||
}, | ||
enumerable: true | ||
}, | ||
noConflict: { | ||
value: function() { | ||
if (conflictResolution) { | ||
conflictResolution.forEach(function(it) { | ||
it(); | ||
}); | ||
conflictResolution = null; | ||
} | ||
return accumulateDiff; | ||
orderIndependentDiff: { | ||
value: accumulateOrderIndependentDiff, | ||
enumerable: true | ||
}, | ||
enumerable: true | ||
} | ||
}); | ||
observableDiff: { | ||
value: deepDiff, | ||
enumerable: true | ||
}, | ||
orderIndependentObservableDiff: { | ||
value: orderIndependentDeepDiff, | ||
enumerable: true | ||
}, | ||
orderIndepHash: { | ||
value: getOrderIndependentHash, | ||
enumerable: true | ||
}, | ||
applyDiff: { | ||
value: applyDiff, | ||
enumerable: true | ||
}, | ||
applyChange: { | ||
value: applyChange, | ||
enumerable: true | ||
}, | ||
revertChange: { | ||
value: revertChange, | ||
enumerable: true | ||
}, | ||
isConflict: { | ||
value: function () { | ||
return typeof $conflict !== 'undefined'; | ||
}, | ||
enumerable: true | ||
}, | ||
noConflict: { | ||
value: function () { | ||
if ($conflict) { | ||
root.DeepDiff = accumulateDiff; | ||
$conflict = null; | ||
} | ||
return accumulateDiff; | ||
}, | ||
enumerable: true | ||
} | ||
}); | ||
return accumulateDiff; | ||
// hackish... | ||
accumulateDiff.DeepDiff = accumulateDiff; | ||
// ...but works with: | ||
// import DeepDiff from 'deep-diff' | ||
// import { DeepDiff } from 'deep-diff' | ||
// const DeepDiff = require('deep-diff'); | ||
// const { DeepDiff } = require('deep-diff'); | ||
}))); | ||
root.DeepDiff = accumulateDiff; | ||
return accumulateDiff; | ||
})); |
{ | ||
"name": "deep-diff", | ||
"description": "Javascript utility for calculating deep difference, capturing changes, and applying changes across objects; for nodejs and the browser.", | ||
"version": "0.3.8", | ||
"version": "1.0.0-pre.1", | ||
"license": "MIT", | ||
@@ -41,7 +41,2 @@ "keywords": [ | ||
], | ||
"files": [ | ||
"index.js", | ||
"index.es.js", | ||
"releases/" | ||
], | ||
"repository": { | ||
@@ -52,30 +47,28 @@ "type": "git", | ||
"main": "./index.js", | ||
"module": "./index.es.js", | ||
"jsnext:main": "/index.es.js", | ||
"directories": { | ||
"examples": "./examples", | ||
"releases": "./releases", | ||
"test": "./test" | ||
"scripts": { | ||
"prerelease": "npm run clean && npm run test", | ||
"release": "uglifyjs -c -m -o dist/deep-diff.min.js --source-map -r '$,require,exports,self,module,define' index.js", | ||
"clean": "rimraf dist && mkdir dist", | ||
"preversion": "npm run release", | ||
"postversion": "git push && git push --tags", | ||
"pretest": "npm run lint", | ||
"test": "mocha test/**/*.js", | ||
"test:watch": "nodemon --ext js,json --ignore dist/ --exec 'npm test'", | ||
"preci": "npm run lint", | ||
"ci": "mocha --reporter mocha-junit-reporter test/**/*.js", | ||
"lint": "eslint index.js test" | ||
}, | ||
"devDependencies": { | ||
"deep-equal": "~1.0.0", | ||
"bluebird": "^3.5.1", | ||
"deep-equal": "^1.0.1", | ||
"eslint": "^4.18.1", | ||
"eslint-plugin-mocha": "^4.11.0", | ||
"expect.js": "^0.3.1", | ||
"jscs": "^1.12.0", | ||
"jshint": "^2.6.3", | ||
"mocha": "^2.2.1", | ||
"rollup": "^0.41.6", | ||
"uglifyjs": "^2.4.10" | ||
}, | ||
"scripts": { | ||
"lint": "jscs index.es.js test/ -e && jshint index.es.js test/", | ||
"build": "rollup index.es.js -f umd -o index.js -n DeepDiff", | ||
"test": "mocha test/", | ||
"release": "uglifyjs index.js -o releases/deep-diff-$npm_package_version.min.js -r '$,require,exports,module,window,global' -m --comments '/^!/'", | ||
"prepublish": "npm run build", | ||
"prerelease": "npm test", | ||
"prebuild": "npm run lint", | ||
"pretest": "npm run build" | ||
"json": "^9.0.6", | ||
"mocha": "^5.0.1", | ||
"mocha-junit-reporter": "^1.17.0", | ||
"nodemon": "^1.15.1", | ||
"rimraf": "^2.6.2", | ||
"uglify-js": "^3.3.12" | ||
} | ||
} |
116
Readme.md
@@ -1,2 +0,5 @@ | ||
# deep-diff [![Build Status](https://travis-ci.org/flitbit/diff.png?branch=master)](https://travis-ci.org/flitbit/diff) | ||
# deep-diff | ||
[![Build Status](https://travis-ci.org/flitbit/diff.png?branch=master)](https://travis-ci.org/flitbit/diff) | ||
[![NPM](https://nodei.co/npm/deep-diff.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/deep-diff/) | ||
@@ -15,50 +18,8 @@ | ||
`0.3.8` - 2017-05-03 | ||
* reconciled recently introduced difference between `index.es.js` and `index.js` | ||
* improved npm commands for more reliable contributions | ||
* added a few notes to README regarding contributing. | ||
## Installation | ||
`0.3.7` - 2017-05-01 | ||
* fixed issue #98 by merging @sberan's pull request #99 — better handling of property with `undefined` value existing on either operand. Unit tests supplied. | ||
`0.3.6` - 2017-04-25 — Fixed, closed lingering issues: | ||
* fixed #74 — comparing objects with longer cycles | ||
* fixed #70 — was not properly detecting a deletion when a property on the operand (lhs) had a value of `undefined` and was _undefined_ on the comparand (rhs). :o). | ||
* WARNING! [Still broken when importing in Typescript](https://github.com/flitbit/diff/issues/97). | ||
`0.3.5` - 2017-04-23 — Rolled up recent fixes; patches: | ||
* @stevemao — #79, #80: now testing latest version of node4 | ||
* @mortonfox — #85: referencing mocha's new home | ||
* @tdebarochez — #90: fixed error when .toString not a function | ||
* @thiamsantos — #92, #93: changed module system for improved es2015 modules. WARNING! [This PR broke importing `deep-diff` in Typescript as reported by @kgentes in #97](https://github.com/flitbit/diff/issues/97) | ||
`0.3.4` - Typescript users, reference this version until #97 is fixed! | ||
`0.3.3` - Thanks @SimenB: enabled npm script for release (alternate to the Makefile). Also linting as part of `npm test`. Thanks @joeldenning: Fixed issue #35; diffs of top level arrays now working. | ||
`0.3.3` - Thanks @SimenB: enabled npm script for release (alternate to the Makefile). Also linting as part of `npm test`. Thanks @joeldenning: Fixed issue #35; diffs of top level arrays now working. | ||
`0.3.2` - Resolves #46; support more robust filters by including `lhs` and `rhs` in the filter callback. By @Orlando80 | ||
`0.3.1` - Better type checking by @Drinks, UMD wrapper by @SimenB. Now certifies against nodejs 12 and iojs (Thanks @SimenB). | ||
`0.2.0` - [Fixes Bug #17](https://github.com/flitbit/diff/issues/17), [Fixes Bug #19](https://github.com/flitbit/diff/issues/19), [Enhancement #21](https://github.com/flitbit/diff/issues/21) Applying changes that are properly structured can now be applied as a change (no longer requires typeof Diff) - supports differences being applied after round-trip serialization to JSON format. Prefilter now reports the path of all changes - it was not showing a path for arrays and anything in the structure below (reported by @ravishvt). | ||
*Breaking Change* – The structure of change records for differences below an array element has changed. Array indexes are now reported as numeric elements in the `path` if the changes is merely edited (an `E` kind). Changes of kind `A` (array) are only reported for changes in the terminal array itself and will have a nested `N` (new) item or a nested `D` (deleted) item. | ||
`0.1.7` - [Enhancement #11](https://github.com/flitbit/diff/issues/11) Added the ability to filter properties that should not be analyzed while calculating differences. Makes `deep-diff` more usable with frameworks that attach housekeeping properties to existing objects. AngularJS does this, and the new filter ability should ease working with it. | ||
`0.1.6` - Changed objects within nested arrays can now be applied. They were previously recording the changes appropriately but `applyDiff` would error. Comparison of `NaN` works more sanely - comparison to number shows difference, comparison to another `Nan` does not. | ||
## Installation | ||
``` | ||
```bash | ||
npm install deep-diff | ||
``` | ||
For the browser, you can install with [bower](http://bower.io/): | ||
``` | ||
bower install deep-diff | ||
``` | ||
## Tests | ||
@@ -86,12 +47,26 @@ | ||
**nodejs** | ||
#### nodejs | ||
```javascript | ||
var deep = require('deep-diff') | ||
var diff = require('deep-diff') | ||
// or: | ||
// const diff = require('deep-diff'); | ||
// const { diff } = require('deep-diff'); | ||
// or: | ||
// const DeepDiff = require('deep-diff'); | ||
// const { DeepDiff } = require('deep-diff'); | ||
// es6+: | ||
// import diff from 'deep-diff'; | ||
// import { diff } from 'deep-diff'; | ||
// es6+: | ||
// import DeepDiff from 'deep-diff'; | ||
// import { DeepDiff } from 'deep-diff'; | ||
``` | ||
**browser** | ||
#### browser | ||
```html | ||
<script src="deep-diff-0.3.1.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/deep-diff@1/dist/deep-diff.min.js"></script> | ||
``` | ||
> Minified, browser release of the current version of the module is under the `releases` folder. | ||
> In a browser, `deep-diff` defines a global variable `DeepDiff`. If there is a conflict in the global namespace you can restore the conflicting definition and assign `deep-diff` to another variable like this: `var deep = DeepDiff.noConflict();`. | ||
@@ -128,3 +103,5 @@ | ||
``` | ||
*up to v 0.1.7* The code snippet above would result in the following structure describing the differences: | ||
``` javascript | ||
@@ -151,2 +128,3 @@ // Versions < 0.2.0 | ||
*v 0.2.0 and above* The code snippet above would result in the following structure describing the differences: | ||
``` javascript | ||
@@ -176,6 +154,6 @@ [ { kind: 'E', | ||
* `kind` - indicates the kind of change; will be one of the following: | ||
* `N` - indicates a newly added property/element | ||
* `D` - indicates a property/element was deleted | ||
* `E` - indicates a property/element was edited | ||
* `A` - indicates a change occurred within an array | ||
* `N` - indicates a newly added property/element | ||
* `D` - indicates a property/element was deleted | ||
* `E` - indicates a property/element was edited | ||
* `A` - indicates a change occurred within an array | ||
* `path` - the property path (from the left-hand-side root) | ||
@@ -195,2 +173,3 @@ * `lhs` - the value on the left-hand-side of the comparison (undefined if kind === 'N') | ||
When two objects differ, you can observe the differences as they are calculated and selectively apply those changes to the origin object (left-hand-side). | ||
``` javascript | ||
@@ -244,6 +223,6 @@ var observableDiff = require('deep-diff').observableDiff, | ||
+ `lhs` - the left-hand operand; the origin object. | ||
+ `rhs` - the right-hand operand; the object being compared structurally with the origin object. | ||
+ `prefilter` - an optional function that determines whether difference analysis should continue down the object graph. | ||
+ `acc` - an optional accumulator/array (requirement is that it have a `push` function). Each difference is pushed to the specified accumulator. | ||
* `lhs` - the left-hand operand; the origin object. | ||
* `rhs` - the right-hand operand; the object being compared structurally with the origin object. | ||
* `prefilter` - an optional function that determines whether difference analysis should continue down the object graph. | ||
* `acc` - an optional accumulator/array (requirement is that it have a `push` function). Each difference is pushed to the specified accumulator. | ||
@@ -254,23 +233,10 @@ #### Pre-filtering Object Properties | ||
# Compatibility | ||
## Contributing | ||
Currently testing on Travis CI against: | ||
+ nodejs `6` | ||
+ nodejs `5` | ||
+ nodejs `4` | ||
+ nodejs `0.12` | ||
+ nodejs `0.11` | ||
+ nodejs `0.10` | ||
# Contributing | ||
When contributing, keep in mind that it is an objective of `deep-diff` to have no package dependencies. This may change in the future, but for now, no-dependencies. | ||
As of release 0.3.5, all edits/changes should be made to `index.es.js`. You must run the unit tests before submitting your PR: `npm test`. Hopefully your PR includes additional unit tests to illustrate your change/modification! | ||
Please run the unit tests before submitting your PR: `npm test`. Hopefully your PR includes additional unit tests to illustrate your change/modification! | ||
When you run `npm test`, linting will be performed and `index.js` will be built from `index.es.js`. Any linting errors will fail the tests... this includes code formatting. | ||
When you run `npm test`, linting will be performed and any linting errors will fail the tests... this includes code formatting. | ||
This module still uses `jshint` but the plan is to switch to `eslint` very soon as I have done in several of my other modules. | ||
**Thanks to all those who have contributed so far!** | ||
> Thanks to all those who have contributed so far! |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
539168
30
4153
11
235
1