RED.diff = (function() { function init() { RED.actions.add("core:show-current-diff",showLocalDiff); RED.actions.add("core:show-remote-diff",showRemoteDiff); RED.keyboard.add("*","ctrl-shift-l","core:show-current-diff"); RED.keyboard.add("*","ctrl-shift-r","core:show-remote-diff"); var dialog = $('
",{class:"node-diff-property-cell-label"}).html("position").appendTo(row); localCell = $(" | ",{class:"node-diff-property-cell"}).appendTo(row); if (localNode) { localCell.addClass("node-diff-node-"+(localChanged?"changed":"unchanged")); $(''+(localChanged?'':'')+'').appendTo(localCell); RED.utils.createObjectElement({x:localNode.x,y:localNode.y}).appendTo(localCell); } else { localCell.addClass("node-diff-empty"); } if (remoteNode !== undefined) { remoteCell = $(" | ",{class:"node-diff-property-cell"}).appendTo(row); remoteCell.addClass("node-diff-node-"+(remoteChanged?"changed":"unchanged")); if (remoteNode) { $(''+(remoteChanged?'':'')+'').appendTo(remoteCell); RED.utils.createObjectElement({x:remoteNode.x,y:remoteNode.y}).appendTo(remoteCell); } else { remoteCell.addClass("node-diff-empty"); } } } // localChanged = remoteChanged = conflict = false; if (node.hasOwnProperty('wires')) { currentValue = JSON.stringify(node.wires); if (localNode) { localValue = JSON.stringify(localNode.wires); if (currentValue !== localValue) { localChanged = true; localChanges++; } } if (remoteNode) { remoteValue = JSON.stringify(remoteNode.wires); if (currentValue !== remoteValue) { remoteChanged = true; remoteChanges++; } } if ( (remoteChanged && localChanged && (localValue !== remoteValue)) || (!localChanged && remoteChanged && localNodeObj.diff.deleted[node.id]) || (localChanged && !remoteChanged && remoteNodeObj.diff.deleted[node.id]) ){ conflicted = true; conflict = true; } row = $(" | ||||||||
",{class:"node-diff-property-cell-label"}).html("wires").appendTo(row); localCell = $(" | ",{class:"node-diff-property-cell"}).appendTo(row); if (localNode) { if (!conflict) { localCell.addClass("node-diff-node-"+(localChanged?"changed":"unchanged")); $(''+(localChanged?'':'')+'').appendTo(localCell); } else { localCell.addClass("node-diff-node-conflict"); $('').appendTo(localCell); } formatWireProperty(localNode.wires,localNodeObj.all).appendTo(localCell); } else { localCell.addClass("node-diff-empty"); } if (remoteNode !== undefined) { remoteCell = $(" | ",{class:"node-diff-property-cell"}).appendTo(row); if (remoteNode) { if (!conflict) { remoteCell.addClass("node-diff-node-"+(remoteChanged?"changed":"unchanged")); $(''+(remoteChanged?'':'')+'').appendTo(remoteCell); } else { remoteCell.addClass("node-diff-node-conflict"); $('').appendTo(remoteCell); } formatWireProperty(remoteNode.wires,remoteNodeObj.all).appendTo(remoteCell); } else { remoteCell.addClass("node-diff-empty"); } } } var properties = Object.keys(node).filter(function(p) { return p!='z'&&p!='wires'&&p!=='x'&&p!=='y'&&p!=='id'&&p!=='type'&&(!def.defaults||!def.defaults.hasOwnProperty(p))}); if (def.defaults) { properties = properties.concat(Object.keys(def.defaults)); } properties.forEach(function(d) { localChanged = false; remoteChanged = false; conflict = false; currentValue = JSON.stringify(node[d]); if (localNode) { localValue = JSON.stringify(localNode[d]); if (currentValue !== localValue) { localChanged = true; localChanges++; } } if (remoteNode) { remoteValue = JSON.stringify(remoteNode[d]); if (currentValue !== remoteValue) { remoteChanged = true; remoteChanges++; } } if ( (remoteChanged && localChanged && (localValue !== remoteValue)) || (!localChanged && remoteChanged && localNodeObj.diff.deleted[node.id]) || (localChanged && !remoteChanged && remoteNodeObj.diff.deleted[node.id]) ){ conflicted = true; conflict = true; } row = $(" | ||||||||
",{class:"node-diff-property-cell-label"}).html(d).appendTo(row); localCell = $(" | ",{class:"node-diff-property-cell"}).appendTo(row); if (localNode) { if (!conflict) { localCell.addClass("node-diff-node-"+(localChanged?"changed":"unchanged")); $(''+(localChanged?'':'')+'').appendTo(localCell); } else { localCell.addClass("node-diff-node-conflict"); $('').appendTo(localCell); } RED.utils.createObjectElement(localNode[d]).appendTo(localCell); } else { localCell.addClass("node-diff-empty"); } if (remoteNode !== undefined) { remoteCell = $(" | ",{class:"node-diff-property-cell"}).appendTo(row); if (remoteNode) { if (!conflict) { remoteCell.addClass("node-diff-node-"+(remoteChanged?"changed":"unchanged")); $(''+(remoteChanged?'':'')+'').appendTo(remoteCell); } else { remoteCell.addClass("node-diff-node-conflict"); $('').appendTo(remoteCell); } RED.utils.createObjectElement(remoteNode[d]).appendTo(remoteCell); } else { remoteCell.addClass("node-diff-empty"); } } }); // row = $(" | ||||||||
").appendTo(row); // var cell; // if (localNode) { // if (localNodeObj.diff.added[node.id]) { // $(' | added | ').appendTo(row); // } else if (localChanges > 0) { // cell = $('').appendTo(row); // if (conflicted) { // $(' conflict ').appendTo(cell); // } // $(' changed').appendTo(cell); // } else { // $(' | unchanged | ').appendTo(row); // } // } else if (localNodeObj.diff.deleted[node.id]) { // cell = $('').appendTo(row); // if (conflicted) { // $(' conflict ').appendTo(cell); // } // $(' deleted').appendTo(cell); // } else { // $(' | ').appendTo(row); // } // if (remoteNode) { // if (remoteNodeObj.diff.added[node.id]) { // $(' | added | ').appendTo(row); // } else if (remoteChanges > 0) { // cell = $('').appendTo(row); // if (conflicted) { // $(' conflict ').appendTo(cell); // } // $(' changed').appendTo(cell); // } else { // $(' | unchanged | ').appendTo(row); // } // } else if (remoteNodeObj.diff.deleted[node.id]) { // cell = $('').appendTo(row); // if (conflicted) { // $(' conflict ').appendTo(cell); // } // $(' deleted').appendTo(cell); // } else { // $(' | ').appendTo(row); // } if (conflicted) { stats.conflicts++; } return nodePropertiesDiv; } function showLocalDiff() { var nns = RED.nodes.createCompleteNodeSet(); var originalFlow = RED.nodes.originalFlow(); var diff = generateDiff(originalFlow,nns); showDiff(diff); } function showRemoteDiff() { $.ajax({ headers: { "Accept":"application/json", }, cache: false, url: 'flows', success: function(nodes) { var localFlow = RED.nodes.createCompleteNodeSet(); var originalFlow = RED.nodes.originalFlow(); var remoteFlow = nodes.flows; var localDiff = generateDiff(originalFlow,localFlow); var remoteDiff = generateDiff(originalFlow,remoteFlow); showDiff(localDiff,remoteDiff); } }); } function parseNodes(nodeList) { var tabOrder = []; var tabs = {}; var subflows = {}; var globals = []; var all = {}; nodeList.forEach(function(node) { all[node.id] = node; if (node.type === 'tab') { tabOrder.push(node.id); tabs[node.id] = {n:node,nodes:[]}; } else if (node.type === 'subflow') { subflows[node.id] = {n:node,nodes:[]}; } }); nodeList.forEach(function(node) { if (node.type !== 'tab' && node.type !== 'subflow') { if (tabs[node.z]) { tabs[node.z].nodes.push(node); } else if (subflows[node.z]) { subflows[node.z].nodes.push(node); } else { globals.push(node); } } }); return { all: all, tabOrder: tabOrder, tabs: tabs, subflows: subflows, globals: globals } } function generateDiff(currentNodes,newNodes) { var currentConfig = parseNodes(currentNodes); var newConfig = parseNodes(newNodes); var pending = RED.nodes.pending(); var added = {}; var deleted = {}; var changed = {}; Object.keys(currentConfig.all).forEach(function(id) { var node = RED.nodes.workspace(id)||RED.nodes.subflow(id)||RED.nodes.node(id); if (!newConfig.all.hasOwnProperty(id)) { if (!pending.added.hasOwnProperty(id)) { deleted[id] = true; } } else if (JSON.stringify(currentConfig.all[id]) !== JSON.stringify(newConfig.all[id])) { changed[id] = true; } }); Object.keys(newConfig.all).forEach(function(id) { if (!currentConfig.all.hasOwnProperty(id) && !pending.deleted.hasOwnProperty(id)) { added[id] = true; } }); return { currentConfig: currentConfig, newConfig: newConfig, added: added, deleted: deleted, changed: changed, } } function formatNodeProperty(prop) { var formattedProperty = prop; if (formattedProperty === null) { formattedProperty = 'null'; } else if (formattedProperty === undefined) { formattedProperty = 'undefined'; } else if (typeof formattedProperty === 'object') { formattedProperty = JSON.stringify(formattedProperty); } if (/\n/.test(formattedProperty)) { formattedProperty = " |