'use strict'; var _ = require('lodash'); var jsondiffpatch = require('jsondiffpatch'); var ChangeHandler = require('./change-handler'); function Differ() { } module.exports = Differ; Differ.prototype.createDiff = function(a, b) { // create a configured instance, match objects by name var diffpatcher = jsondiffpatch.create({ objectHash: function(obj) { return obj.id || JSON.stringify(obj); } }); return diffpatcher.diff(a, b); }; Differ.prototype.diff = function(a, b, handler) { handler = handler || new ChangeHandler(); function walk(diff, model) { _.forEach(diff, function(d, key) { // is array if (d._t === 'a') { _.forEach(d, function(val, idx) { if (idx === '_t') { return; } var removed = /^_/.test(idx), added = !removed && _.isArray(val), moved = removed && val[0] === ''; idx = parseInt(removed ? idx.slice(1) : idx, 10); if (added || (removed && !moved)) { handler[removed ? 'removed' : 'added'](model, key, val[0], idx); } else if (moved) { handler.moved(model, key, val[1], val[2]); } else { walk(val, model[key][idx]); } }); } else { if (_.isArray(d)) { handler.changed(model, key, d[0], d[1]); } else { handler.changed(model, key); walk(d, model[key]); } } }); } var diff = this.createDiff(a, b); walk(diff, b, handler); return handler; }; module.exports.diff = function(a, b, handler) { return new Differ().diff(a, b, handler); };