123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- var json_diff = (function() {
- function equal(a, b) {
- if (a === b)
- return true;
- if (typeof a !== 'object' || typeof b !== 'object')
- return false;
- if (a === null || b === null)
- return false;
- if (a instanceof Array !== b instanceof Array)
- return false;
- if (a instanceof Array) {
- if (a.length != b.length)
- return false;
- for (var n = 0; n < a.length; n++)
- if (!equal(a[n], b[n]))
- return false;
- } else {
- for (var key in a) {
- if (!(key in b) || !equal(a[key], b[key]))
- return false;
- }
- for (var key in b) {
- if (!(key in a))
- return false;
- }
- }
- return true;
- }
- function type(v) {
- if (typeof v === 'object') {
- if (v instanceof Array)
- return 'array';
- if (v === null)
- return 'null';
- }
- return typeof v
- }
-
- return function(a, b) {
- var path = [], operations = [];
- function json_pointer(tail) {
- if (tail === undefined)
- return '/';
- else {
- path.push(tail);
- var pointer = '';
- for (var n = 0; n < path.length; n++)
- pointer = pointer + '/' + path[n].toString().replace(/~/g, '~0').replace(/\//g, '~1');
- path.pop();
- return pointer;
- }
- }
- function result(op, tail, value) {
- var pointer = json_pointer(tail);
- if (op === 'remove')
- operations.push({op: op, path: pointer});
- else
- operations.push({op: op, path: pointer,
- value: value === undefined ? null : value});
- }
-
-
- function diffseq(a, b) {
- var i, j, m, n,
- row = [], c = [],
- left, diag, latch;
- var skip, edit = [];
- m = a.length;
- n = b.length;
-
- for (; m && n; m--, n--)
- if (!equal(a[m - 1], b[n - 1]))
- break;
- for (skip = 0; m && n; skip++, m--, n--)
- if (!equal(a[skip], b[skip]))
- break;
- if (m && n) {
-
- for (j = 0; j < n; row[j++] = 0);
- for (i = 0 ; i < m; i++) {
- c[i] = row = row.slice();
- for (diag = 0, j = 0; j < n; j++, diag = latch) {
- latch = row[j];
- if (equal(a[i + skip], b[j + skip]))
- row[j] = diag + 1;
- else {
- left = row[j - 1] || 0;
- if (left > row[j])
- row[j] = left;
- }
- }
- }
- i--, j--;
-
-
- while (i > -1 && j > -1) {
- switch (c[i][j]) {
- default:
- edit.unshift('=');
- i--; j--;
- break;
- case j && c[i][j - 1]:
- edit.unshift('+');
- j--;
- break;
- case i && c[i - 1][j]:
- edit.unshift('-');
- i--;
- break;
- }
- }
- } else {
- i = m - 1;
- j = n - 1;
- }
- for (; j > -1; j--)
- edit.unshift('+');
- for (; i > -1; i--)
- edit.unshift('-');
- var edit_replace = [];
- for (n = 0; n < edit.length; n++) {
- if (edit[n] === '-') {
- for (i = n + 1; i < edit.length && edit[i] === '-'; i++);
- for (j = i; j < edit.length && edit[j] === '+'; j++);
- if (i - n == j - i) {
- while (i++ < j)
- edit_replace.push('*');
- n = j - 1;
- } else {
- for (; n < j; n++)
- edit_replace.push(edit[n]);
- n--;
- }
- } else
- edit_replace.push(edit[n]);
- }
-
- edit = edit_replace;
- for (i = 0, j = 0, n = 0; n < edit.length; n++) {
- switch(edit[n]) {
- case '*':
- diff_recursive(a[i + skip], b[j + skip], j + skip);
- case '=':
- i++;
- j++;
- break;
- case '-':
- result('remove', j + skip);
- i++;
- break;
- case '+':
- result('add', j + skip, b[j + skip]);
- j++;
- break;
- }
- }
- }
- function diff_arrays(a, b, tail) {
- if (tail !== undefined)
- path.push(tail);
- diffseq(a, b);
- if (tail !== undefined)
- path.pop();
- }
- function diff_objects(a, b, tail) {
- if (tail !== undefined)
- path.push(tail);
- for (var key in a) {
- if (key in b)
- diff_recursive(a[key], b[key], key);
- else
- result('remove', key);
- }
- for (var key in b) {
- if (!(key in a))
- result('add', key, b[key]);
- }
- if (tail !== undefined)
- path.pop();
- }
- function diff_recursive(a, b, tail) {
- if (a === b)
- return;
- var ta = type(a), tb = type(b);
- if (ta !== tb)
- result('replace', tail, b);
- else {
- switch(ta) {
- case 'object':
- diff_objects(a, b, tail);
- break;
- case 'array':
- diff_arrays(a, b, tail);
- break;
- default:
- result('replace', tail, b);
- }
- }
- }
- diff_recursive(a, b, undefined);
- return operations;
- };
- })();
- if (typeof exports !== 'undefined')
- exports.diff = json_diff;
|