diff --git a/Readme.md b/Readme.md index a61ddcf..af94c77 100644 --- a/Readme.md +++ b/Readme.md @@ -3,6 +3,10 @@ JSON Diff Shows the differences between two JSON-encoded objects. -Originally created by Tom Robinson. Improvements by Sami Samhuri. +Originally created by Tom Robinson. Improvements by Sami Samhuri and hij1nx. -Copyright 2006-2010 [Tom Robinson](http://tlrobinson.net/). [Some rights reserved](http://creativecommons.org/licenses/by-nc/3.0/us/). +Copyright 2006-2010 [Tom Robinson](http://tlrobinson.net/). +[Some rights reserved](http://creativecommons.org/licenses/by-nc/3.0/us/). + +[Sami Samhuri] (http://github.com/samsonjs) +[hij1nx] (http://github.com/hij1nx) diff --git a/css/json-diff.css b/css/json-diff.css new file mode 100644 index 0000000..04dc054 --- /dev/null +++ b/css/json-diff.css @@ -0,0 +1,73 @@ +body { + background-color: lightblue; +} + +#results li > span, #results ul > span { + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + padding-right: 5px; + padding-left: 5px; +} + +#results li { + margin-top: 1px; + padding-left: 15px; +} +#results ul { + padding-left: 15px; + margin-left: -15px; + padding-top: 0px; + margin-top: 0px; + background: url(open.png) no-repeat 2px 5px; + list-style-type: none; +} +#results ul[closed="yes"] { + background: url(closed.png) no-repeat 2px 5px; +} +#results ul[closed="yes"] > * { + display: none; +} +#results ul[closed="yes"] > *:first-child { + display: block; +} +.typeName { + color: gray; +} +.changed { + background-color: #fcff7f; +} +.changed.key { + background-color: #eee; +} + +.added { + background-color: #8bff7f; +} +.removed { + background-color: #fd7f7f; +} + +textarea { + width: 49%; + height: 200px; +} + +.contentbox { + border: 1px dashed black; + background-color: white; + padding: 15px; + margin: 10px; +} + +h2 { + text-align: center; + margin: 0px;; +} + +#results { + padding-left: 40px; +} + +#inputs { + text-align: center; +} \ No newline at end of file diff --git a/index.html b/index.html index 73ec5e1..5cf1f0a 100644 --- a/index.html +++ b/index.html @@ -2,171 +2,7 @@ JSON Diff - - +

Courtesy of tlrobinson. @@ -186,39 +22,10 @@ function clickHandler(e) {

- + @@ -241,5 +48,9 @@ function clickHandler(e) {

© 2006-2010 Thomas Robinson. Some rights reserved.

+ + + + diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..e246325 --- /dev/null +++ b/js/app.js @@ -0,0 +1,137 @@ + +if(!window.console){ + window.console = { + log: function(){}, + debug: function(){}, + warn: function(){}, + dir: function(){} + }; +} + +(function() { + + var jsonBoxA, + jsonBoxB, + n; + + var samples = { + + a: { + "__class":"SLUser", + "displayName":"Sami Samhuri", + "accountSuspended": false, + "newAttribute":null, + "addressStreet1":null, + "url":null, + "addressZip":null, + "email":"foo@bar.com", + "addressRegion":null, + "businessName":null, + "addressStreet2":null, + "addressCountry":null, + "hashedPassword":"dc7754ea14e2d4f07bb3ec6a099480f318529dce", + "uuid":"dc80bc3053d135e52411ce67bd758211" + }, + + b: { + "__class":"SLUser", + "displayName":"Foo Bar", + "accountSuspended": false, + "addressStreet1":"123 Fake St", + "url":"bar.com", + "addressZip":"V9C 0E6", + "email":"foo@bar.com", + "addressRegion":"BC", + "businessName":"stuff", + "addressStreet2":null, + "addressCountry":"Canada", + "hashedPassword":"dc7754ea14e2d4f07bb3ec6a099480f318529dce", + "uuid":"d0e11b7c73d483335ac7697b042e36c5" + } + + }; + + return { + + Init: function() { + + console.log('>>> Init()'); + + document.addEventListener("click", this.clickHandler, false); + + jsonBoxA = document.getElementById("jsonA"); + jsonBoxB = document.getElementById("jsonB"); + + jsonDiff.feedback = this.markChanged; + + this.populteSamples(); + this.startCompare(); + } + + populteSamples: function() { + + console.log('>>> populateSamples()'); + + jsonBoxA.value = JSON.stringify(samples.a); + jsonBoxB.value = JSON.stringify(samples.b); + + }, + + startCompare: function () { + + console.log('>>> startCompare()'); + var objA, objB; + n = 0; + + jsonBoxA.style.backgroundColor = ""; + jsonBoxB.style.backgroundColor = ""; + + try { + objA = eval("(" + jsonBoxA.value + ")"); + } catch(e) { + jsonBoxA.style.backgroundColor = "rgba(255,0,0,0.5)"; + } + try { + objB = eval("(" + jsonBoxB.value + ")"); + } catch(e) { + jsonBoxB.style.backgroundColor = "rgba(255,0,0,0.5)"; + } + + results = document.getElementById("results"); + while (results.firstChild) + results.removeChild(results.firstChild); + + jsonDiff.compareTree(objA, objB, "root", results); + }, + + markChanged: function(node) { + + console.log('>>> markChanged()'); + + document.getElementById('first').style.display = 'block'; + node.setAttribute('id', 'change-' + n); + n += 1; + var nextNode = document.createElement('a'); + nextNode.setAttribute('href', '#change-' + n); + nextNode.appendChild(document.createTextNode('↓ next change ↓')) + node.appendChild(nextNode); + }, + + clickHandler: function(e) { + + console.log('>>> clickHandler()'); + + e = e || window.event; + if (e.target.nodeName.toUpperCase() === "UL") + { + if (e.target.getAttribute("closed") === "yes") + e.target.setAttribute("closed", "no"); + else + e.target.setAttribute("closed", "yes"); + } + } + + } + +})().Init(); + diff --git a/js/lib/json-diff.js b/js/lib/json-diff.js new file mode 100644 index 0000000..353a743 --- /dev/null +++ b/js/lib/json-diff.js @@ -0,0 +1,125 @@ + +var jsonDiff = (typeof exports !== "undefined" ? exports : window).jsonDiff = (function(){ + + function isArray(value) { + if(Array.isArray) { + return Array.isArray(value); + } + else { + return value && typeof value === "object" && value.constructor === Array; + } + } + + function typeofReal(value) { + return isArray(value) ? "array": value === null ? 'null' : typeof value; + } + + return { + + a: [], // first structure + b: [], // second structure + + feedback: function() { + + }, + + swapValues: function() { + console.log('>>> swapBoxes()'); + this.a = [this.b, this.b = this.a][0]; + }, + + clearValues: function() { + console.log('>>> clearBoxes()'); + this.a = this.b = null; + }, + + compareTree: function(a, b, name, results) { + + var self = this; + + var typeA = typeofReal(a); + var typeB = typeofReal(b); + + console.log('>>> compareTree(a=(' + typeA + ')' + a + + ', b=(' + typeB + ')' + b + + ', name=(' + typeof name + ')' + name + + ', results=(' + typeof results + ')' + results + ')'); + + var typeSpanA = document.createElement("span"); + typeSpanA.appendChild(document.createTextNode("(" + typeA + ")")) + typeSpanA.setAttribute("class", "typeName"); + + var typeSpanB = document.createElement("span"); + typeSpanB.appendChild(document.createTextNode("(" + typeB + ")")) + typeSpanB.setAttribute("class", "typeName"); + + var aString = (typeA === "object" || typeA === "array") ? "": String(a) + " "; + var bString = (typeB === "object" || typeB === "array") ? "": String(b) + " "; + + var leafNode = document.createElement("span"); + leafNode.appendChild(document.createTextNode(name)); + if (a === undefined) + { + leafNode.setAttribute("class", "added"); + leafNode.appendChild(document.createTextNode(": " + bString)); + leafNode.appendChild(typeSpanB); + self.feedback(leafNode); + } + else if (b === undefined) + { + leafNode.setAttribute("class", "removed"); + leafNode.appendChild(document.createTextNode(": " + aString)); + leafNode.appendChild(typeSpanA); + self.feedback(leafNode); + } + else if (typeA !== typeB || (typeA !== "object" && typeA !== "array" && a !== b)) + { + leafNode.setAttribute("class", "changed"); + leafNode.appendChild(document.createTextNode(": " + aString)); + leafNode.appendChild(typeSpanA); + leafNode.appendChild(document.createTextNode(" => " + bString)); + leafNode.appendChild(typeSpanB); + + if (name === 'key') leafNode.setAttribute('class', 'changed key'); + else self.feedback(leafNode); + } + else + { + leafNode.appendChild(document.createTextNode(": " + aString)); + leafNode.appendChild(typeSpanA); + } + + if (typeA === "object" || typeA === "array" || typeB === "object" || typeB === "array") + { + var keys = []; + for (var i in a) keys.push(i); + for (var i in b) keys.push(i); + keys.sort(); + + var listNode = document.createElement("ul"); + listNode.appendChild(leafNode); + + for (var i = 0; i < keys.length; i++) + { + if (keys[i] === keys[i - 1]) + continue; + + var li = document.createElement("li"); + listNode.appendChild(li); + + self.compareTree(a && a[keys[i]], b && b[keys[i]], keys[i], li); + } + + results.appendChild(listNode); + } + else + { + results.appendChild(leafNode); + } + + } + + }; + +})(); +