From 9fc6c8b6e85f9bb9cc13b5252442c3c9f6f7a461 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Sat, 22 Apr 2017 16:12:14 -0300 Subject: [PATCH 1/2] Change module system of the main file #92 --- .jshintrc | 4 +- index.js | 730 +++++++++++++++++++++++++-------------------------- package.json | 9 +- 3 files changed, 368 insertions(+), 375 deletions(-) diff --git a/.jshintrc b/.jshintrc index 7ce8cf6..6ce9143 100644 --- a/.jshintrc +++ b/.jshintrc @@ -3,5 +3,7 @@ "strict": true, "laxcomma": true, "nomen": false, - "indent": 2 + "indent": 2, + "esversion": 6, + "browser": true } diff --git a/index.js b/index.js index 5416df0..37842a2 100644 --- a/index.js +++ b/index.js @@ -1,422 +1,408 @@ -/*! - * deep-diff. - * Licensed under the MIT License. - */ -;(function(root, factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define([], function() { - return factory(); - }); - } else if (typeof exports === 'object') { - // Node. Does not work with strict CommonJS, but - // only CommonJS-like environments that support module.exports, - // like Node. - module.exports = factory(); - } else { - // Browser globals (root is window) - root.DeepDiff = factory(); - } -}(this, 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; - } - }); - } +(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'; - // 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 +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; } }); - } +} - function Diff(kind, path) { - Object.defineProperty(this, 'kind', { - value: kind, - enumerable: true - }); - if (path && path.length) { - Object.defineProperty(this, 'path', { - value: path, - enumerable: 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 DiffEdit(path, origin, value) { - DiffEdit.super_.call(this, 'E', path); - Object.defineProperty(this, 'lhs', { - value: origin, - enumerable: true - }); - Object.defineProperty(this, 'rhs', { - value: value, +function Diff(kind, path) { + Object.defineProperty(this, 'kind', { + value: kind, + enumerable: true + }); + if (path && path.length) { + Object.defineProperty(this, 'path', { + value: path, 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 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 DiffDeleted(path, value) { - DiffDeleted.super_.call(this, 'D', path); - Object.defineProperty(this, 'lhs', { - value: value, - enumerable: true - }); - } - inherits(DiffDeleted, Diff); +function DiffNew(path, value) { + DiffNew.super_.call(this, 'N', path); + Object.defineProperty(this, 'rhs', { + value: value, + enumerable: true + }); +} +inherits(DiffNew, 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 DiffDeleted(path, value) { + DiffDeleted.super_.call(this, 'D', path); + Object.defineProperty(this, 'lhs', { + value: value, + enumerable: true + }); +} +inherits(DiffDeleted, 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 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 realTypeOf(subject) { - var type = typeof subject; - if (type !== 'object') { - return type; - } +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; +} - 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'; +function realTypeOf(subject) { + var type = typeof subject; + if (type !== 'object') { + return type; } - function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) { - path = path || []; - 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]; - } + 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'; +} + +function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) { + path = path || []; + 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]; } } } - currentPath.push(key); } + currentPath.push(key); + } - // Use string comparison for regexes - if (realTypeOf(lhs) === 'regexp' && realTypeOf(rhs) === 'regexp') { - lhs = lhs.toString(); - rhs = rhs.toString(); - } + // 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; - if (ltype === 'undefined') { - if (rtype !== 'undefined') { - changes(new DiffNew(currentPath, rhs)); - } - } else if (rtype === 'undefined') { - changes(new DiffDeleted(currentPath, lhs)); - } else if (realTypeOf(lhs) !== realTypeOf(rhs)) { - changes(new DiffEdit(currentPath, lhs, rhs)); - } else if (Object.prototype.toString.call(lhs) === '[object Date]' && Object.prototype.toString.call(rhs) === '[object 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; - 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); - } - } - while (i < rhs.length) { - changes(new DiffArray(currentPath, i, new DiffNew(undefined, rhs[i++]))); + 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 (realTypeOf(lhs) !== realTypeOf(rhs)) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } else if (Object.prototype.toString.call(lhs) === '[object Date]' && Object.prototype.toString.call(rhs) === '[object 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; + 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); } - } 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) { - if (!(ltype === 'number' && isNaN(lhs) && isNaN(rhs))) { - changes(new DiffEdit(currentPath, lhs, rhs)); + 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, 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) { + 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 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; +} + +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; } - return arr; } + 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') ? [] : {}; - } - it = it[change.path[i]]; - } - 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; +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') ? [] : {}; } + it = it[change.path[i]]; + } + 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; } } +} - 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]]; - } - 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; - } +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]]; + } + 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; } + 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]] = {}; - } - it = it[change.path[i]]; - } - 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; +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]]; + } + 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; } } +} - 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 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); } +} - Object.defineProperties(accumulateDiff, { +Object.defineProperties(accumulateDiff, { - diff: { - value: accumulateDiff, - enumerable: true - }, - observableDiff: { - value: deepDiff, - enumerable: true + 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; }, - applyDiff: { - value: applyDiff, - enumerable: true - }, - applyChange: { - value: applyChange, - enumerable: true - }, - revertChange: { - value: revertChange, - enumerable: true - }, - isConflict: { - value: function() { - return 'undefined' !== typeof conflict; - }, - enumerable: true + enumerable: true + }, + noConflict: { + value: function() { + if (conflictResolution) { + conflictResolution.forEach(function(it) { + it(); + }); + conflictResolution = null; + } + return accumulateDiff; }, - noConflict: { - value: function() { - if (conflictResolution) { - conflictResolution.forEach(function(it) { - it(); - }); - conflictResolution = null; - } - return accumulateDiff; - }, - enumerable: true - } - }); + enumerable: true + } +}); + +return accumulateDiff; - return accumulateDiff; -})); +}))); diff --git a/package.json b/package.json index eae05fb..dc8a8b8 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ ], "files": [ "index.js", + "index.es.js", "releases/" ], "repository": { @@ -39,6 +40,8 @@ "url": "git://github.com/flitbit/diff.git" }, "main": "./index.js", + "module": "./index.es.js", + "jsnext:main": "/index.es.js", "directories": { "examples": "./examples", "releases": "./releases", @@ -50,11 +53,13 @@ "jscs": "^1.12.0", "jshint": "^2.6.3", "mocha": "^2.2.1", + "rollup": "^0.41.6", "uglifyjs": "^2.4.10" }, "scripts": { "release": "uglifyjs index.js -o releases/deep-diff-$npm_package_version.min.js -r '$,require,exports,module,window,global' -m --comments '/^!/'", - "pretest": "jscs index.js test/ && jshint index.js test/", - "test": "mocha" + "pretest": "jscs index.es.js test/ -e && jshint index.es.js test/", + "test": "mocha", + "build": "rollup index.es.js -f umd -o index.js -n DeepDiff" } } From caa7973d04bb05782bf797fa2d99e51da5e5d894 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Sat, 22 Apr 2017 20:13:21 -0300 Subject: [PATCH 2/2] add file with esmodule syntax --- index.es.js | 399 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 index.es.js diff --git a/index.es.js b/index.es.js new file mode 100644 index 0000000..f354fb5 --- /dev/null +++ b/index.es.js @@ -0,0 +1,399 @@ +'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, 'kind', { + value: kind, + enumerable: true + }); + if (path && path.length) { + Object.defineProperty(this, 'path', { + 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, '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 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'; +} + +function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) { + path = path || []; + 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]; + } + } + } + } + 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; + if (ltype === 'undefined') { + if (rtype !== 'undefined') { + changes(new DiffNew(currentPath, rhs)); + } + } else if (rtype === 'undefined') { + changes(new DiffDeleted(currentPath, lhs)); + } else if (realTypeOf(lhs) !== realTypeOf(rhs)) { + changes(new DiffEdit(currentPath, lhs, rhs)); + } else if (Object.prototype.toString.call(lhs) === '[object Date]' && Object.prototype.toString.call(rhs) === '[object 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; + 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); + } + } + 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, 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) { + 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 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; + } + } + 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') ? [] : {}; + } + it = it[change.path[i]]; + } + 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; + } + } +} + +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]]; + } + 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]] = {}; + } + it = it[change.path[i]]; + } + 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; + } + } +} + +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); + } +} + +Object.defineProperties(accumulateDiff, { + + 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; + }, + enumerable: true + }, + noConflict: { + value: function() { + if (conflictResolution) { + conflictResolution.forEach(function(it) { + it(); + }); + conflictResolution = null; + } + return accumulateDiff; + }, + enumerable: true + } +}); + +export default accumulateDiff;