From a01b09ffc32b8764295451a2741d317b92fdd659 Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Fri, 9 Aug 2013 15:39:26 -0300 Subject: [PATCH 1/7] avoids stack overflow when comparing objects in the same way jasmine does --- objectDiff.js | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/objectDiff.js b/objectDiff.js index 36359b1..afed75c 100644 --- a/objectDiff.js +++ b/objectDiff.js @@ -13,11 +13,18 @@ objectDiff.diff = function diff(a, b) { value: a } } + if (a.__diffComparedTo__ === b && b.__diffComparedTo__ === a) { + return true; + } var value = {}; var equal = true; + a.__diffComparedTo__ = b; + b.__diffComparedTo__ = a; + for (var key in a) { + if (key == '__diffComparedTo__') { continue; } if (key in b) { if (a[key] === b[key]) { value[key] = { @@ -57,6 +64,7 @@ objectDiff.diff = function diff(a, b) { } for (key in b) { + if (key == '__diffComparedTo__') { continue; } if (!(key in a)) { equal = false; value[key] = { @@ -66,6 +74,9 @@ objectDiff.diff = function diff(a, b) { } } + delete a.__diffComparedTo__; + delete b.__diffComparedTo__; + if (equal) { return { changed: 'equal', @@ -93,13 +104,20 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { value: a } } + if (a.__diffComparedTo__ === b && b.__diffComparedTo__ === a) { + return true; + } var diff = {}; var equal = true; var keys = Object.keys(a); + a.__diffComparedTo__ = b; + b.__diffComparedTo__ = a; + for (var i = 0, length = keys.length; i < length; i++) { var key = keys[i]; + if (key == '__diffComparedTo__') { continue; } if (b.hasOwnProperty(key)) { if (a[key] === b[key]) { diff[key] = { @@ -142,6 +160,7 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { for (i = 0, length = keys.length; i < length; i++) { key = keys[i]; + if (key == '__diffComparedTo__') { continue; } if (!a.hasOwnProperty(key)) { equal = false; diff[key] = { @@ -151,6 +170,9 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { } } + delete a.__diffComparedTo__; + delete b.__diffComparedTo__; + if (equal) { return { value: a, From b70a3387f8915843ed32b9961676773e50fdfcf7 Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Fri, 9 Aug 2013 15:40:43 -0300 Subject: [PATCH 2/7] avoids stack overflow when building diff html for DOM elements --- objectDiff.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/objectDiff.js b/objectDiff.js index afed75c..1404cff 100644 --- a/objectDiff.js +++ b/objectDiff.js @@ -237,9 +237,7 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { * @return {string} */ function stringifyObjectKey(key) { - return /^[a-z0-9_$]*$/i.test(key) ? - key : - JSON.stringify(key); + return /^[a-z0-9_$]*$/i.test(key) ? key : JSON.stringify(key); } /** @@ -275,6 +273,8 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { var length = keys.length; if (length === 0) { accumulator += '{}'; + } else if (obj.nodeType > 0) { + accumulator += '' + toString(obj) + ''; } else { accumulator += '{\n
'; for (var i = 0; i < length; i++) { From aef38fca4eb5a8dbd5642f61b1e5dc64c2b22910 Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Fri, 9 Aug 2013 18:44:07 -0300 Subject: [PATCH 3/7] correct toString of functions and DOM elements --- objectDiff.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/objectDiff.js b/objectDiff.js index 1404cff..3d204f7 100644 --- a/objectDiff.js +++ b/objectDiff.js @@ -274,7 +274,7 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { if (length === 0) { accumulator += '{}'; } else if (obj.nodeType > 0) { - accumulator += '' + toString(obj) + ''; + accumulator += '' + Object.prototype.toString.call(obj) + ''; } else { accumulator += '{\n
'; for (var i = 0; i < length; i++) { @@ -292,6 +292,10 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { accumulator += JSON.stringify(escapeHTML(obj)); break; + case 'function': + accumulator += Object.prototype.toString.call(obj); + break; + case 'undefined': accumulator += 'undefined'; break; From 35486dd1a71023ec4b8cad6cea9fd879e8a6d5eb Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Sat, 10 Aug 2013 13:46:11 -0300 Subject: [PATCH 4/7] added option to ignore keys --- objectDiff.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/objectDiff.js b/objectDiff.js index 3d204f7..abe3470 100644 --- a/objectDiff.js +++ b/objectDiff.js @@ -5,7 +5,7 @@ var objectDiff = typeof exports != 'undefined' ? exports : {}; * @param {Object} b * @return {Object} */ -objectDiff.diff = function diff(a, b) { +objectDiff.diff = function diff(a, b, ignore) { if (a === b) { return { @@ -19,12 +19,14 @@ objectDiff.diff = function diff(a, b) { var value = {}; var equal = true; + var ignoredKeys = ['__diffComparedTo__'].concat(ignore || []); a.__diffComparedTo__ = b; b.__diffComparedTo__ = a; for (var key in a) { - if (key == '__diffComparedTo__') { continue; } + if (ignoredKeys.indexOf(key) != -1) { continue; } + if (key in b) { if (a[key] === b[key]) { value[key] = { @@ -35,7 +37,7 @@ objectDiff.diff = function diff(a, b) { var typeA = typeof a[key]; var typeB = typeof b[key]; if (a[key] && b[key] && (typeA == 'object' || typeA == 'function') && (typeB == 'object' || typeB == 'function')) { - var valueDiff = diff(a[key], b[key]); + var valueDiff = diff(a[key], b[key], ignore); if (valueDiff.changed == 'equal') { value[key] = { changed: 'equal', @@ -64,7 +66,8 @@ objectDiff.diff = function diff(a, b) { } for (key in b) { - if (key == '__diffComparedTo__') { continue; } + if (ignoredKeys.indexOf(key) != -1) { continue; } + if (!(key in a)) { equal = false; value[key] = { @@ -96,7 +99,7 @@ objectDiff.diff = function diff(a, b) { * @param {Object} b * @return {Object} */ -objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { +objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { if (a === b) { return { @@ -111,13 +114,15 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { var diff = {}; var equal = true; var keys = Object.keys(a); + var ignoredKeys = ['__diffComparedTo__'].concat(ignore || []); a.__diffComparedTo__ = b; b.__diffComparedTo__ = a; for (var i = 0, length = keys.length; i < length; i++) { var key = keys[i]; - if (key == '__diffComparedTo__') { continue; } + if (ignoredKeys.indexOf(key) != -1) { continue; } + if (b.hasOwnProperty(key)) { if (a[key] === b[key]) { diff[key] = { @@ -128,7 +133,7 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { var typeA = typeof a[key]; var typeB = typeof b[key]; if (a[key] && b[key] && (typeA == 'object' || typeA == 'function') && (typeB == 'object' || typeB == 'function')) { - var valueDiff = diffOwnProperties(a[key], b[key]); + var valueDiff = diffOwnProperties(a[key], b[key], ignore); if (valueDiff.changed == 'equal') { diff[key] = { changed: 'equal', @@ -160,7 +165,8 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b) { for (i = 0, length = keys.length; i < length; i++) { key = keys[i]; - if (key == '__diffComparedTo__') { continue; } + if (ignoredKeys.indexOf(key) != -1) { continue; } + if (!a.hasOwnProperty(key)) { equal = false; diff[key] = { From a64759739b0ec1626f2e575a94ddf9980473f526 Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Sat, 10 Aug 2013 13:47:19 -0300 Subject: [PATCH 5/7] considering empty objects of type different than 'object' as different --- objectDiff.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/objectDiff.js b/objectDiff.js index abe3470..c62004c 100644 --- a/objectDiff.js +++ b/objectDiff.js @@ -179,11 +179,17 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { delete a.__diffComparedTo__; delete b.__diffComparedTo__; - if (equal) { + if (equal && (typeof a === typeof b) && (typeof a === 'object')) { return { value: a, changed: 'equal' } + } else if (equal) { + return { + changed: 'primitive change', + removed: a, + added: b + } } else { return { changed: 'object change', @@ -205,6 +211,14 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { var diff = changes.value; if (changes.changed == 'equal') { return inspect(diff); + } else if (changes.changed == 'primitive change') { + return [ + '
', + '', inspect(changes.removed), '', + ',\n', + '', inspect(changes.added), '', + '
' + ].join(''); } for (var key in diff) { From 928a8b48bd4c046a488118f0346cf8bab5dd7167 Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Mon, 12 Aug 2013 14:16:52 -0300 Subject: [PATCH 6/7] supporting comparison of null values --- objectDiff.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/objectDiff.js b/objectDiff.js index c62004c..039545c 100644 --- a/objectDiff.js +++ b/objectDiff.js @@ -107,17 +107,20 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { value: a } } - if (a.__diffComparedTo__ === b && b.__diffComparedTo__ === a) { + if (a && b && a.__diffComparedTo__ === b && b.__diffComparedTo__ === a) { return true; } var diff = {}; var equal = true; - var keys = Object.keys(a); + var typeofA = typeof a; + var typeofB = typeof b; + var keys = []; var ignoredKeys = ['__diffComparedTo__'].concat(ignore || []); - a.__diffComparedTo__ = b; - b.__diffComparedTo__ = a; + if (a && typeofA === 'object') { keys = Object.keys(a); } + if (a) { a.__diffComparedTo__ = b; } + if (b) { b.__diffComparedTo__ = a; } for (var i = 0, length = keys.length; i < length; i++) { var key = keys[i]; @@ -161,7 +164,8 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { } } - keys = Object.keys(b); + keys = []; + if (b && typeofB === 'object') { keys = Object.keys(b); } for (i = 0, length = keys.length; i < length; i++) { key = keys[i]; @@ -176,10 +180,10 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { } } - delete a.__diffComparedTo__; - delete b.__diffComparedTo__; + if (a) { delete a.__diffComparedTo__; } + if (b) { delete b.__diffComparedTo__; } - if (equal && (typeof a === typeof b) && (typeof a === 'object')) { + if (equal && (typeofA === typeofB) && (typeofA === 'object')) { return { value: a, changed: 'equal' From 11b4935a3b6056a5cf79ac87f7b1694639820b47 Mon Sep 17 00:00:00 2001 From: JP Bochi Date: Tue, 3 Sep 2013 15:59:44 -0300 Subject: [PATCH 7/7] put limit on inspect of objects to avoid stack overflow --- objectDiff.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/objectDiff.js b/objectDiff.js index 039545c..9f1addf 100644 --- a/objectDiff.js +++ b/objectDiff.js @@ -286,7 +286,8 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { * @see http://jsperf.com/continuation-passing-style/3 * @return {string} */ - function _inspect(accumulator, obj) { + function _inspect(accumulator, obj, deep) { + deep = deep || 0; switch(typeof obj) { case 'object': if (!obj) { @@ -300,15 +301,20 @@ objectDiff.diffOwnProperties = function diffOwnProperties(a, b, ignore) { } else if (obj.nodeType > 0) { accumulator += '' + Object.prototype.toString.call(obj) + ''; } else { - accumulator += '{\n
'; - for (var i = 0; i < length; i++) { - var key = keys[i]; - accumulator = _inspect(accumulator + stringifyObjectKey(escapeHTML(key)) + ': ', obj[key]); - if (i < length - 1) { - accumulator += ',\n'; + deep += 1; + if (deep > 3) { + accumulator += '[too deep...]'; + } else { + accumulator += '{\n
'; + for (var i = 0; i < length; i++) { + var key = keys[i]; + accumulator = _inspect(accumulator + stringifyObjectKey(escapeHTML(key)) + ': ', obj[key], deep); + if (i < length - 1) { + accumulator += ',\n'; + } } + accumulator += '\n
}' } - accumulator += '\n
}' } break;