Hi i'm facing problem with validation for un-connected edges to cells in mxgraph
below image will show you my expectation:
Question: whenever i press validate button un-connected edges and cells must be highlighted with red color.
For full view Codepen:https://codepen.io/eabangalore/pen/pmELpL?editors=1100
NOTE: PLEASE SEE CODEPEN LINK (https://codepen.io/eabangalore/pen/pmELpL?editors=1100) as BELOW CODE IS NOT WORKING
Full Code:
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
function setGraphData(){
var graphState ={"tagName":"mxGraphModel","children":[{"tagName":"root","children":[{"tagName":"mxCell","attributes":{"id":"0"}},{"tagName":"mxCell","attributes":{"id":"1","parent":"0"}},{"tagName":"mxCell","attributes":{"id":"2","value":"A","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"271.56251525878906","y":"82.44792175292969","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"3","value":"B","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"678.2291717529297","y":"106.89236450195312","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"4","value":"Bangalore","parent":"1","edge":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"0.0511","y":"-20","relative":"1","as":"geometry"},"children":[{"tagName":"mxPoint","attributes":{"x":"370.06251525878906","y":"109.95338610053051","as":"sourcePoint"}},{"tagName":"mxPoint","attributes":{"x":"676.7291717529297","y":"128.3869001543523","as":"targetPoint"}},{"tagName":"mxPoint","attributes":{"as":"offset"}}]}]},{"tagName":"mxCell","attributes":{"id":"5","value":"C","style":"","parent":"1","vertex":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"1013.7847747802734","y":"83.55902862548828","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"6","parent":"1","source":"3","target":"5","edge":"1"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"7","value":"Not Connected","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"552","y":"267.640625","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"8","value":"Edge Not connected must highlight in red color","edge":"1","parent":"1","source":"5"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"},"children":[{"tagName":"mxPoint","attributes":{"x":"740","y":"260","as":"targetPoint"}}]}]}]}]};
localStorage.setItem('graphState',JSON.stringify(graphState));
}
function html2json(html){
if(html.nodeType==3){
return {
"tagName":"#text",
"content":html.textContent
}
}
var element = {
"tagName":html.tagName
};
if(html.getAttributeNames().length>0){
element.attributes = html.getAttributeNames().reduce(
function(acc,at){acc[at]=html.getAttribute(at); return acc;},
{}
);
}
if(html.childNodes.length>0){
element.children = Array.from(html.childNodes)
.filter(
function(el){
return el.nodeType!=3
||el.textContent.trim().length>0
})
.map(function(el){return html2json(el);});
}
return element;
}
function json2html(json){
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node){
if(jsonNode.attributes){
Object.keys(jsonNode.attributes).map(
function(name){
node.setAttribute(name,jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode,node){
if(jsonNode.children){
jsonNode.children.map(
function(jsonChildNode){
json2htmlNode(jsonChildNode,node);
}
);
}
}
var json2htmlNode = function(jsonNode,parent){
if(jsonNode.tagName=="#text"){
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode,node);
addChildren(jsonNode,node);
parent.appendChild(node);
}
addAttributes(json,xmlDoc.firstElementChild);
addChildren(json,xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
function main()
{
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported())
{
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
}
else
{
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS)
{
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
var graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style)
{
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function()
{
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if(window.localStorage.graphState){
var doc = json2html(JSON.parse(localStorage.graphState));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change',function(){
var codec = new mxCodec();
window.localStorage.graphState = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image)
{
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y)
{
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell)
{
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt)
{
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt)
{
if (img.enabled == false)
{
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
</script>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();" >
</body>
</html>
Please help me thanks in advance!!!
Edit: As per the comment, to validate the cells not connected to the parent cell or its branches, we can use a recursive function checking each connected edge starts from the parent cell and so on.
Due to the limit of characters, the original code snippet is removed.
.not_connected * {
color: red;
font-color: red;
stroke: red;
stroke-color: red;
}
<!--
Copyright (c) 2006-2013, JGraph Ltd
Dynamic toolbar example for mxGraph. This example demonstrates changing the
state of the toolbar at runtime.
-->
<html>
<head>
<title>Toolbar example for mxGraph</title>
</head>
<!-- Calls the main function after the page has loaded. Container is dynamically created. -->
<body onload="main();">
<button style="position:absolute; left:300px;padding:8px 40px;background:orangered;color:#fff;outline:none;border:none;z-index:100;" id="validate_btn">Validate</button>
</body>
</html>
<!-- Sets the basepath for the library if not in same directory -->
<script type="text/javascript">
mxBasePath = 'https://jgraph.github.io/mxgraph/javascript/src';
var graphStateJSON;
var graph;
function setGraphData() {
var graphState = {
"tagName": "mxGraphModel",
"children": [{
"tagName": "root",
"children": [{
"tagName": "mxCell",
"attributes": {
"id": "0"
}
}, {
"tagName": "mxCell",
"attributes": {
"id": "1",
"parent": "0"
}
}, {
"tagName": "mxCell",
"attributes": {
"id": "2",
"value": "A",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "460",
"y": "80",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "3",
"value": "C",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "460",
"y": "190",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "4",
"edge": "1",
"parent": "1",
"source": "2",
"target": "3"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "5",
"value": "B",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "280",
"y": "190",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "6",
"value": "D",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "663",
"y": "193",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "7",
"edge": "1",
"parent": "1",
"source": "2",
"target": "5"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "8",
"edge": "1",
"parent": "1",
"source": "2",
"target": "6"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "9",
"value": "E",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "660",
"y": "260",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "10",
"edge": "1",
"parent": "1",
"source": "6",
"target": "9"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "11",
"value": "F",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "280",
"y": "260",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "12",
"value": "G",
"style": "",
"vertex": "1",
"parent": "1"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"x": "459",
"y": "257",
"width": "100",
"height": "40",
"as": "geometry"
}
}]
}, {
"tagName": "mxCell",
"attributes": {
"id": "13",
"edge": "1",
"parent": "1",
"source": "11",
"target": "12"
},
"children": [{
"tagName": "mxGeometry",
"attributes": {
"relative": "1",
"as": "geometry"
}
}]
}]
}]
};
graphStateJSON = JSON.stringify(graphState);
}
function html2json(html) {
if (html.nodeType == 3) {
return {
"tagName": "#text",
"content": html.textContent
}
}
var element = {
"tagName": html.tagName
};
if (html.getAttributeNames().length > 0) {
element.attributes = html.getAttributeNames().reduce(
function(acc, at) {
acc[at] = html.getAttribute(at);
return acc;
}, {}
);
}
if (html.childNodes.length > 0) {
element.children = Array.from(html.childNodes)
.filter(
function(el) {
return el.nodeType != 3 ||
el.textContent.trim().length > 0
})
.map(function(el) {
return html2json(el);
});
}
return element;
}
function json2html(json) {
var xmlDoc = document.implementation.createDocument(null, json.tagName);
var addAttributes = function(jsonNode, node) {
if (jsonNode.attributes) {
Object.keys(jsonNode.attributes).map(
function(name) {
node.setAttribute(name, jsonNode.attributes[name]);
}
);
}
}
var addChildren = function(jsonNode, node) {
if (jsonNode.children) {
jsonNode.children.map(
function(jsonChildNode) {
json2htmlNode(jsonChildNode, node);
}
);
}
}
var json2htmlNode = function(jsonNode, parent) {
if (jsonNode.tagName == "#text") {
return xmlDoc.createTextNode(jsonNode.content);
}
var node = xmlDoc.createElement(jsonNode.tagName);
addAttributes(jsonNode, node);
addChildren(jsonNode, node);
parent.appendChild(node);
}
addAttributes(json, xmlDoc.firstElementChild);
addChildren(json, xmlDoc.firstElementChild);
return xmlDoc;
}
</script>
<!-- Loads and initializes the library -->
<script type="text/javascript" src="https://jgraph.github.io/mxgraph/javascript/src/js/mxClient.js"></script>
<!-- Example code -->
<script type="text/javascript">
// Program starts here. Creates a sample graph in the
// DOM node with the specified ID. This function is invoked
// from the onLoad event handler of the document (see below).
var graph;
var graphView;
var notConnectedCells = [];
var parentCellId = "2";
function main() {
setGraphData();
// Checks if browser is supported
if (!mxClient.isBrowserSupported()) {
// Displays an error message if the browser is
// not supported.
mxUtils.error('Browser is not supported!', 200, false);
} else {
// Defines an icon for creating new connections in the connection handler.
// This will automatically disable the highlighting of the source vertex.
mxConnectionHandler.prototype.connectImage = new mxImage('images/connector.gif', 16, 16);
// Creates the div for the toolbar
var tbContainer = document.createElement('div');
tbContainer.style.position = 'absolute';
tbContainer.style.overflow = 'hidden';
tbContainer.style.padding = '2px';
tbContainer.style.left = '0px';
tbContainer.style.top = '0px';
tbContainer.style.width = '24px';
tbContainer.style.bottom = '0px';
document.body.appendChild(tbContainer);
// Creates new toolbar without event processing
var toolbar = new mxToolbar(tbContainer);
toolbar.enabled = false
// Creates the div for the graph
var container = document.createElement('div');
container.style.position = 'absolute';
container.style.overflow = 'hidden';
container.style.left = '24px';
container.style.top = '0px';
container.style.right = '0px';
container.style.bottom = '0px';
container.style.background = 'url("editors/images/grid.gif")';
document.body.appendChild(container);
// Workaround for Internet Explorer ignoring certain styles
if (mxClient.IS_QUIRKS) {
document.body.style.overflow = 'hidden';
new mxDivResizer(tbContainer);
new mxDivResizer(container);
}
// Creates the model and the graph inside the container
// using the fastest rendering available on the browser
var model = new mxGraphModel();
graph = new mxGraph(container, model);
// Enables new connections in the graph
graph.setConnectable(true);
graph.setMultigraph(false);
// Stops editing on enter or escape keypress
var keyHandler = new mxKeyHandler(graph);
var rubberband = new mxRubberband(graph);
var addVertex = function(icon, w, h, style) {
var vertex = new mxCell(null, new mxGeometry(0, 0, w, h), style);
vertex.setVertex(true);
var img = addToolbarItem(graph, toolbar, vertex, icon);
img.enabled = true;
graph.getSelectionModel().addListener(mxEvent.CHANGE, function() {
var tmp = graph.isSelectionEmpty();
mxUtils.setOpacity(img, (tmp) ? 100 : 20);
img.enabled = tmp;
});
};
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rectangle.gif', 100, 40, '');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rounded.gif', 100, 40, 'shape=rounded');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/ellipse.gif', 40, 40, 'shape=ellipse');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/rhombus.gif', 40, 40, 'shape=rhombus');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/triangle.gif', 40, 40, 'shape=triangle');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/cylinder.gif', 40, 40, 'shape=cylinder');
addVertex('https://jgraph.github.io/mxgraph/javascript/examples/editors/images/actor.gif', 30, 40, 'shape=actor');
// read state on load
if (graphStateJSON) {
var doc = json2html(JSON.parse(graphStateJSON));
var dec = new mxCodec(doc);
dec.decode(doc.documentElement, graph.getModel());
}
// save state on change
graph.getModel().addListener('change', function() {
var codec = new mxCodec();
graphStateJSON = JSON.stringify(html2json(codec.encode(
graph.getModel()
)));
});
}
}
function addToolbarItem(graph, toolbar, prototype, image) {
// Function that is executed when the image is dropped on
// the graph. The cell argument points to the cell under
// the mousepointer if there is one.
var funct = function(graph, evt, cell, x, y) {
graph.stopEditing(false);
var vertex = graph.getModel().cloneCell(prototype);
vertex.geometry.x = x;
vertex.geometry.y = y;
graph.addCell(vertex);
graph.setSelectionCell(vertex);
}
// Creates the image which is used as the drag icon (preview)
var img = toolbar.addMode(null, image, function(evt, cell) {
var pt = this.graph.getPointForEvent(evt);
funct(graph, evt, cell, pt.x, pt.y);
});
// Disables dragging if element is disabled. This is a workaround
// for wrong event order in IE. Following is a dummy listener that
// is invoked as the last listener in IE.
mxEvent.addListener(img, 'mousedown', function(evt) {
// do nothing
});
// This listener is always called first before any other listener
// in all browsers.
mxEvent.addListener(img, 'mousedown', function(evt) {
if (img.enabled == false) {
mxEvent.consume(evt);
}
});
mxUtils.makeDraggable(img, graph, funct);
return img;
}
function Validate(mxCell){
let isConnected = true;
// check each cell that each edge connected to
for(let i=0;i<mxCell.getEdgeCount();i++){
let edge = mxCell.getEdgeAt(i);
if(edge.target === null) continue; // no target
if(mxCell.getId() === edge.target.getId()) continue; // target is mxCell itself
isConnected = edge.source !== null && edge.target !== null;
if(isConnected){
// remove source cell if found and so on
let sourceIndex = notConnectedCells.findIndex(c=>c.id === edge.source.getId());
if(sourceIndex !== -1) notConnectedCells.splice(sourceIndex,1);
let targetIndex = notConnectedCells.findIndex(c=>c.id === edge.target.getId());
if(targetIndex !== -1) notConnectedCells.splice(targetIndex,1);
let edgeIndex = notConnectedCells.findIndex(c=>c.id === edge.getId());
if(edgeIndex !== -1) notConnectedCells.splice(edgeIndex,1);
// check next cell and its edges
Validate(edge.target);
}
}
}
function ResetColor(state){
state.shape.node.classList.remove("not_connected");
if(state.text)
state.text.node.classList.remove("not_connected");
}
function SetNotConnectedColor(state){
for(let i=0;i<notConnectedCells.length;i++){
let mxCell = notConnectedCells[i];
let state = graphView.getState(mxCell);
state.shape.node.classList.add("not_connected");
if(state.text)
state.text.node.classList.add("not_connected");
}
}
document.querySelector("#validate_btn").addEventListener("click", function() {
let cells = graph.getModel().cells;
graphView = graph.getView();
notConnectedCells.length = 0;
// create an array of cells and reset the color
for(let key in cells){
if(!cells.hasOwnProperty(key)) continue;
let mxCell = cells[key];
if(!mxCell.isVertex() && !mxCell.isEdge()) continue;
notConnectedCells.push(mxCell);
let state = graphView.getState(mxCell);
ResetColor(state);
}
// starts with the parent cell
let parentCell = notConnectedCells.find(c=>c.id === parentCellId);
Validate(parentCell);
SetNotConnectedColor();
})
</script>
First, set graph to a global variable. (var graph = new mxGraph(container, model);)
Then, get all cells(as an object) and graphView from graph and iterate all the cells checking if it's connected or not.
function Validate(){
let cells = graph.getModel().cells;
let graphView = graph.getView();
for(let key in cells){
if(!cells.hasOwnProperty(key)) continue;
let mxCell = cells[key];
if(!mxCell.isVertex() && !mxCell.isEdge()) continue;
let state = graphView.getState(mxCell);
ResetColor(state);
let notConnected = true;
if(mxCell.isVertex()){
for(let i=0;i<mxCell.getEdgeCount();i++){
let edge = mxCell.getEdgeAt(i);
// if any of an edge connected to both source and target, it's connected
if(edge.source !== null && edge.target !== null){
notConnected = false;
break;
}
}
}
else { // mxCell.isEdge()
notConnected = mxCell.source === null || mxCell.target === null;
}
if(notConnected) SetNotConnectedColor(state);
}
}
function ResetColor(state){
state.shape.node.classList.remove("not_connected");
if(state.text)
state.text.node.classList.remove("not_connected");
}
function SetNotConnectedColor(state){
state.shape.node.classList.add("not_connected");
if(state.text)
state.text.node.classList.add("not_connected");
}
document.querySelector("#validate_btn").addEventListener("click", function(){
Validate();
})
And here's the localStorage version code if you want(codepen)
Related
I passed a global tree structured JSON string in jquery fancy tree. I can add a new node data to the global object. I am not able to update a specific node of the global JSON string although I can find that specific node.
I have tried the following way of doing what I wanted but at the end the global variable is not changed. I also tried recursive way but not worked too. Also this is my first question at stackoverflow. I simplified the data because real data is so big.
// global json object array
var treeViewData = [
{
"nodeData": {
"physicalid": "A",
"rootToNode": "A-0",
"level": "0",
"attribute": "English-english"
},
"children": [
{
"nodeData": {
"physicalid": "B",
"rootToNode": "A-0:B-1",
"level": "1",
"attribute": "Finish-finish"
},
"children": []
},
{
"nodeData": {
"physicalid": "C",
"rootToNode": "A-0:C-1",
"level": "1",
"attribute": "Arabic-arabic"
},
"children": [
{
"nodeData": {
"physicalid": "D",
"rootToNode": "A-0:C-1:D-2",
"level": "2",
"attribute": "Spanish-spanished"
},
"children": []
}
]
}
]
}
]
Here is the update method. rootToNode is unique.
findAndUpdateNodeIterative: function(selfNodeRootPath, updatedNode) {
console.log("+++++++++ findAndUpdateNodeIterative ++++++++");
var thisContext = this;
var currentNode = thisContext.treeViewData[0];
var i, currentChild, result;
var flag = true;
while (flag) {
if (currentNode.nodeData.rootToNode === selfNodeRootPath) {
currentNode.nodeData.attribute = updatedNode.nodeData.attribute;
flag = false;
} else {
let it = true;
for (i = 0; i < currentNode.children.length && it; i += 1) {
currentChild = currentNode.children[i];
var currentNodeLevel = parseInt(currentChild.nodeData.level);
var targetPhysicalId = selfNodeRootPath.split(':')[currentNodeLevel].split("-")[0];
if (targetPhysicalId === currentChild.nodeData.physicalid) {
// Search in the current child
currentNode = currentChild;
it = false;
}
}
}
}
console.log("---------- findAndUpdateNodeIterative ---------");
},
usage
var updatedNode = {
"nodeData": {
"physicalid": "D",
"rootToNode": "A-0:C-1:D-2",
"level": "2",
"attribute": "Spanish-spanish"
},
"children": []
};
thisContext.findAndUpdateNodeIterative(updatedNode.nodeData.rootToNode, updatedNode);
console.log(thisContext.treeViewData); // **not updated**
RequireJS Code structure,
// about 4000 lines are present in this module
define("CustomModule", ["fewModules"], function(fewModules) {
var widget = {
init: function() {
this.treeViewData = '';
},
// many methods are here
update: function() {
var thisContext = this;
// rest api call brings the data nodesMap
for (let selectedObject of nodesMap.values()) {
// many lines
thisContext.findAndUpdateNodeIterative(updatedNode.nodeData.rootToNode, updatedNode);
console.log(thisContext.treeViewData); // not updated treeViewData
}
}
};
return widget;
}
I have the following hierarchical JSON data retrieved from a PostgreSQL DB using PHP:
[{"treelevel":"1","app":"Front","lrflag":null,"ic":null,"price":null,"parentlevel":"0","seq":"27", "indexlistid":439755},
{"treelevel":"2","app":"V-Series","lrflag":null,"ic":null,"price":null,"parentlevel":"1","seq":"28", "indexlistid":439755},
{"treelevel":"3","app":"opt J56","lrflag":null,"ic":null,"price":null,"parentlevel":"2","seq":"29", "indexlistid":439755},
{"treelevel":"4","app":"R.","lrflag":"R","ic":"536-01132AR","price":"693.00","parentlevel":"3","seq":"30", "indexlistid":439755},
{"treelevel":"4","app":"L.","lrflag":"L","ic":"536-01133AL","price":"693.00","parentlevel":"3","seq":"31", "indexlistid":439755},
{"treelevel":"3","app":"opt J63","lrflag":null,"ic":null,"price":null,"parentlevel":"2","seq":"32", "indexlistid":439755},
{"treelevel":"4","app":"R.","lrflag":"R","ic":"536-01130R","price":null,"parentlevel":"3","seq":"33", "indexlistid":439755},
{"treelevel":"4","app":"L.","lrflag":"L","ic":"536-01131L","price":null,"parentlevel":"3","seq":"34", "indexlistid":439755}]
I need some way to reformat the data to this:
[{"app": "Front-V-Series-opt J56-R. R", "price": "$693", "ic": "536-01132AR"},
{"app": "Front-V-Series-opt J56-L. L", "price": "$693", "ic": "536-01132AL"},
{"app": "Front-V-Series-opt J63-R. R", "price": null, "ic": "536-01130R"},
{"app": "Front-V-Series-opt J63-L. L", "price": null, "ic": "536-01131L"}]
The parentlevel and treelevel are the 2 keys that make up the tree relationship.
The app value in the new format is a concatenation of the app and lrflag values from all the
nodes in one tree level + the price and ic from the deepest leaf node in the level. This is the
data visualized as a tree:
[
{
"app": "Front",
"children": [
{
"app": "V-Series",
"children": [
{
"app": "opt J56",
"children": [
{
"app": "R. ,
"lrflag": "R",
"ic": "536-01132AR",
"price": "$693"
},
{
"app": "L. ,
"lrflag": "L",
"ic": "536-01132AL",
"price": "$693"
}
]
},
{
"app": "opt J63",
"children": [
{
"app": "R. ,
"lrflag": "R",
"ic": "536-01130R"
},
{
"app": "L. ,
"lrflag": "L",
"ic": "536-01131L"
}
]
}
]
}
]
}
]
I've tried several different ways to do this but am just stuck. Here are some of the functions
that I've attempted to modify and use to no luck. I can't even get the tree correctly built from
these.
function buildTree(list) {
var map = {}, node, roots = [], i;
for (i = 0; i < list.length; i += 1) {
map[list[i].treelevel] = i; // initialize the map
list[i].children = []; // initialize the children
}
for (i = 0; i < list.length; i += 1) {
node = list[i];
if (node.parentlevel !== "0") {
// if you have dangling branches check that map[node.parentId] exists
list[map[node.parentlevel]].children.push(node);
} else {
roots.push(node);
}
}
return roots;
}
function listToTree(data, options) {
options = options || {};
var ID_KEY = options.idKey || 'treelevel';
var PARENT_KEY = options.parentKey || 'parentlevel';
var CHILDREN_KEY = options.childrenKey || 'children';
var tree = [],
childrenOf = {};
var item, id, parentId;
for (var i = 0, length = data.length; i < length; i++) {
item = data[i];
id = item[ID_KEY];
parentId = item[PARENT_KEY] || 0;
// every item may have children
childrenOf[id] = childrenOf[id] || [];
// init its children
item[CHILDREN_KEY] = childrenOf[id];
if (parentId != 0) {
// init its parent's children object
childrenOf[parentId] = childrenOf[parentId] || [];
// push it into its parent's children object
childrenOf[parentId].push(item);
} else {
tree.push(item);
}
};
return tree;
}
unflattenToObject = function(array, parent) {
var tree = {};
parent = typeof parent !== 'undefined' ? parent : {id: 0};
var childrenArray = array.filter(function(child) {
return child.treelevel == parent.parentlevel;
});
if (childrenArray.length > 0) {
var childrenObject = {};
// Transform children into a hash/object keyed on token
childrenArray.forEach(function(child) {
childrenObject[child.treelevel] = child;
});
if (parent.treelevel == 0) {
tree = childrenObject;
} else {
parent['children'] = childrenObject;
}
childrenArray.forEach(function(child) {
unflattenToObject(array, child);
})
}
return tree;
};
the idea is to get the first element's index in the max depth treelevel : 4 then start from there and loop backwards, making sure you don't go through the parents twice, and concatenate what you need along the way, once you reach the top level, you delete that element
wrap that in a function and call it recursively until there's no treelevel : 4 left
var data = [{
"treelevel": "1",
"app": "Front",
"lrflag": null,
"ic": null,
"price": null,
"parentlevel": "0",
"seq": "27",
"indexlistid": 439755
},
{
"treelevel": "2",
"app": "V-Series",
"lrflag": null,
"ic": null,
"price": null,
"parentlevel": "1",
"seq": "28",
"indexlistid": 439755
},
{
"treelevel": "3",
"app": "opt J56",
"lrflag": null,
"ic": null,
"price": null,
"parentlevel": "2",
"seq": "29",
"indexlistid": 439755
},
{
"treelevel": "4",
"app": "R.",
"lrflag": "R",
"ic": "536-01132AR",
"price": "693.00",
"parentlevel": "3",
"seq": "30",
"indexlistid": 439755
},
{
"treelevel": "4",
"app": "L.",
"lrflag": "L",
"ic": "536-01133AL",
"price": "693.00",
"parentlevel": "3",
"seq": "31",
"indexlistid": 439755
},
{
"treelevel": "3",
"app": "opt J63",
"lrflag": null,
"ic": null,
"price": null,
"parentlevel": "2",
"seq": "32",
"indexlistid": 439755
},
{
"treelevel": "4",
"app": "R.",
"lrflag": "R",
"ic": "536-01130R",
"price": null,
"parentlevel": "3",
"seq": "33",
"indexlistid": 439755
},
{
"treelevel": "4",
"app": "L.",
"lrflag": "L",
"ic": "536-01131L",
"price": null,
"parentlevel": "3",
"seq": "34",
"indexlistid": 439755
}
];
// first go look for the max depth in the tree, if it's always four, skip this part and put var max = 4
var max = 0;
data.forEach(function(elem) {
if (elem.treelevel >= max)
max = elem.treelevel;
});
// implement a function to get the index of the first matched element with treelevel = max depth
function getIndex() {
return data.indexOf(data.find((elem) => {
return elem.treelevel == max;
}));
}
var myTree = [];
function formObject(ndx) {
var myObj = {};
myObj.app = [];
var nextTreeLevel = 3;
// start looping from the index of the element in max depth backwards
for (var i = ndx; i >= 0; i--) {
// if the next element ( backwards ) is a parent , this is to avoid going through parent's siblings
if (nextTreeLevel == data[i].parentlevel) {
if (data[i].ic != null) myObj.ic = data[i].ic;
if (data[i].price != null) myObj.price = data[i].price;
if (data[i].lrflag != null) myObj.app.push(data[i].lrflag);
myObj.app.push(data[i].app);
nextTreeLevel = data[i].parentlevel - 1; // prent's level
}
}
data.splice(ndx, 1); // remove the lement once you're done with it
// glue the "app" together, would be better to use array.join but it's not the same join everywhere
myObj.app = myObj.app[4] + '-' + myObj.app[3] + '-' + myObj.app[2] + '-' + myObj.app[1] + ' ' + myObj.app[0];
// just to fill the price with null if there is none
if (myObj.price == undefined) myObj.price = null;
// push the object to the result's array
myTree.push(myObj);
// get the index of the next element in max depth ( 4 )
var nextIndex = getIndex();
// if there's still another element in the max depth, recall the same function with it's index
if (nextIndex > -1)
formObject(nextIndex)
}
formObject(getIndex())
console.log(myTree);
Hi (sorry for my english), I have this script:
<script type="text/javascript">
$(document).ready(function() {
var idPlato = decodeURI(getUrlVars()["idPl"]);
var url = "http://localhost/plato-datos.php?idPla="+idPlato+"";
});
};
</script>
It brings me this json from my database:
[{"category":"first","name":"green","idP":"1", "count":3},
{"category":"first","name":"blue","idP":"2","count":5},
{"category":"sec","name":"peter","idP":"3", "count":3},
{"category":"sec","name":"james","idP":"4", "count":2,},
{"category":"third","name":"dog","idP":"5", "count":4}]
I need to create one radiobuton for every name and group by categores
I create a solution. Kinda ugly but it will work:
var data = [{
"category": "first",
"name": "green",
"idP": "1",
"count": 3
}, {
"category": "first",
"name": "blue",
"idP": "2",
"count": 5
}, {
"category": "sec",
"name": "peter",
"idP": "3",
"count": 3
}, {
"category": "sec",
"name": "james",
"idP": "4",
"count": 2,
}, {
"category": "third",
"name": "dog",
"idP": "5",
"count": 4
}];
var result = {};
data.map(a => {
if (result[a.category]) {
result[a.category].push(a.name);
} else {
result[a.category] = [a.name];
}
});
Object.keys(result).map(category => {
var select = document.createElement('select');
result[category].map(name => {
var option = document.createElement('option');
option.value = name;
option.text = name;
select.appendChild(option);
});
document.body.appendChild(select);
});
Im working with jquery mobile then i used autodividersSelector function for group by the category JSON object, and build a radiobuton for every name
<script type="text/javascript">
//catch the JSON from my database
$(document).ready(function() {
var idPla = decodeURI(getUrlVars()["idPl"]);
var urlAder =
"http://localhost/lista-adereso.php?idPla=" + idPla + "";
//print the radiobutons
$.getJSON(urlAder, function(resultado) {
var allfiles = '';
for (var i = 0, aderesos = null; i <
resultado.length; i++) {
aderesos = resultado[i];
allfiles +='<li><label><input type="radio" data-
status="' + aderesos.aderesoCatNom +'"
name="name" id="id" value="' +
aderesos.aderNombre +'">'+
aderesos.aderNombre + '</label></li>'; }
//Group by categories
$('#linkList')
.empty()
.append(allfiles)
.listview({
autodividers:true,
autodividersSelector: function ( li ) {
var out = li.find('input').data("status");
return out;
}
})
.listview("refresh");
});
});
</script>
I want to create a json using object & javascript. the function object_merge is used to merge the object values.
here is the code i'm using.
var dat0 = [{
"type": "configuration",
"Process": [{
"type": "Source",
"value": ticket_id
}],
"attributes": {
}
}];
function object_merge(){
for (var i=1; i<arguments.length; i++)
for (var a in arguments[i])
arguments[0][a] = arguments[i][a];
return arguments[0];
};
var arry = [];
var listobj= [object0,object1,object2,object3,object4,object5,object6,object7,object8,object9];
var object1 = {TicketID: [{value: ticket_id }]};
var object2 = {Score:[{value: ticket_score }]};
var object3 = {Requestor:[{value: ticket_requestor_name }]};
var object4 = {Submitter:[{value: ticket_submitter_name }]};
var object5 = {Channel:[{value: ticket_channel }]};
var object6 = {Priority:[{value: ticket_priority }]};
var object7 = {Status:[{value: ticket_status }]};
var object8 = {Subject:[{value: ticket_subject }]};
var object9 = {Group:[{value: ticket_group_name }]};
var object0 = {TicketType:[{value: ticket_type }]};
if ((object1.TicketID[0].value!== (null)||(undefined)))
{
arry.push(object1);
}
if (object2.Score[0].value!== (null)||(undefined))
{
arry.push(object2);
}
if (object3.Requestor[0].value!== (null)||(undefined))
{
arry.push(object3);
}
if (object4.Submitter[0].value!== (null)||(undefined))
{
arry.push(object4);
}
if (object5.Channel[0].value!== (null)||(undefined))
{
arry.push(object5);
}
if (object6.Priority[0].value!== (null)||(undefined))
{
arry.push(object6);
}
if (object7.Status[0].value!== (null)||(undefined))
{
arry.push(object7);
}
if (object8.Subject[0].value!== (null)||(undefined))
{
arry.push(object8);
}
if (object9.Group[0].value!== (null)||(undefined))
{
arry.push(object9);
}
if (object0.TicketType[0].value!== (null)||(undefined))
{
arry.push(object0);
}
var attr = object_merge(arry);
console.info(JSON.stringify(attr));
dat0[0].attributes = attr;
console.info(JSON.stringify(dat0));
which returns a json like
[{"type":"configuration","Process":[{"type":"Source","value":902}],"attributes":[{"TicketID":[{"value":902}]},{"Score":[{"value":"unoffered"}]},{"Requestor":[{"value":"raj"}]},{"Submitter":[{"value":"raj"}]},{"Channel":[{"value":"api"}]},{"Status":[{"value":"open"}]},{"Subject":[{"value":"sub"}]},{"Group":[{"value":"Support"}]}]}]
where as the expected result is
[{"type":"configuration","Process":[{"type":"Source","value":"902"}],"attributes":{"TicketID":[{"value":"902"}],"Score":[{"value":"unoffered"}],"Requestor":[{"value":"raj"}],"Submitter":[{"value":"raj"}],"Channel":[{"value":"api"}],"Status":[{"value":"open"}],"Subject":[{"value":"sub"}],"Group":[{"value":"Support"}]}}]
How to achieve it?
Try below code which may help you.
var attributesObject = {
"TicketID": [{
"value": "902"
}],
"Score": [{
"value": "unoffered"
}],
"Requestor": [{
"value": "raj"
}],
"Submitter": [{
"value": "raj"
}],
"Channel": [{
"value": "api"
}],
"Status": [{
"value": "open"
}],
"Subject": [{
"value": "sub"
}],
"Group": [{
"value": "Support"
}]
};
var main = [{
"type": "configuration",
"Process": [{
"type": "Source",
"value": "ticket_id"
}],
"attributes": attributesObject
}];
use this function instead of your object_merge:
function object_merge(objects) {
var newObject = {};
for (var i = 0; i < objects.length; i++)
for (var key in objects[i])
newObject[key] = objects[i][key];
return newObject;
};
This is my saved localstorage,
[{"industry_Id":1,"merchant_id":2}]
I want to filter below result, to get HP.
{
"industries": [
{
"id": 1,
"name": "oil and gas",
"merchant": [
{
"id": 1,
"name": "ABC",
},
{
"id": 2,
"name": "DEF",
},
{
"id": 3,
"name": "GHJ",
}
]
},
{
"id": 2,
"name": "IT",
"merchant": [
{
"id": 1,
"name": "Apple",
},
{
"id": 2,
"name": "HP",
},
{
"id": 3,
"name": "Google",
}
]
}
]
}
I thought of using multiple $.each but it have to iterate few times and it's quite redundant.
I would prefer using Javascript for loop, that way you can skip iterating over every object once required element is found.
Without jQuery (using for)
var i, j, merchant = null;
for(i = 0; i < data['industries'].length; i++){
if(data['industries'][i]['id'] == arg[0]['industry_Id']){
for(j = 0; j < data['industries'][i]['merchant'].length; j++){
if(data['industries'][i]['merchant'][j]['id'] == arg[0]['merchant_id']){
merchant = data['industries'][i]['merchant'][j];
break;
}
}
if(merchant !== null){ break; }
}
}
With jQuery (using $.each)
var merchant_found = null;
$.each(data['industries'], function(i, industry){
if(industry['id'] == arg[0]['industry_Id']){
$.each(industry['merchant'], function(i, merchant){
if(merchant['id'] == arg[0]['merchant_id']){
merchant_found = merchant;
}
return (!merchant_found);
});
}
return (!merchant_found);
});
var arg = [{"industry_Id":1,"merchant_id":2}];
var data = {
"industries": [
{
"id": 1,
"name": "oil and gas",
"merchant": [
{
"id": 1,
"name": "ABC",
},
{
"id": 2,
"name": "DEF",
},
{
"id": 3,
"name": "GHJ",
}
]
},
{
"id": 2,
"name": "IT",
"merchant": [
{
"id": 1,
"name": "Apple",
},
{
"id": 2,
"name": "HP",
},
{
"id": 3,
"name": "Google",
}
]
}
]
};
var i, j, merchant = null;
for(i = 0; i < data['industries'].length; i++){
if(data['industries'][i]['id'] == arg[0]['industry_Id']){
for(j = 0; j < data['industries'][i]['merchant'].length; j++){
if(data['industries'][i]['merchant'][j]['id'] == arg[0]['merchant_id']){
merchant = data['industries'][i]['merchant'][j];
break;
}
}
if(merchant !== null){ break; }
}
}
console.log(merchant);
document.writeln("<b>Without jQuery:</b><br>");
document.writeln((merchant !== null) ? "Found " + merchant['name'] : "Not found");
var merchant_found = null;
$.each(data['industries'], function(i, industry){
if(industry['id'] == arg[0]['industry_Id']){
$.each(industry['merchant'], function(i, merchant){
if(merchant['id'] == arg[0]['merchant_id']){
merchant_found = merchant;
}
return (!merchant_found);
});
}
return (!merchant_found);
});
console.log(merchant_found);
document.writeln("<br><br><b>With jQuery:</b><br>");
document.writeln((merchant_found) ? "Found " + merchant_found['name'] : "Not found");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
selectors.map(function(selector) {
return data.industries.filter(function(industry) {
return industry.id == selector.industry_Id;
})[0].merchant.filter(function(merchant) {
return merchant.id == selector.merchant_id;
})[0].name;
});
// => DEF
If you want "HP", you want industry 2, not industry 1.
.filter(...)[0] is not really optimal. You could use .find(...), but that is not yet universally supported. Or you could use plain old JavaScript and write for loops instead to make it fast. Or you could use objects with ID keys instead of arrays to make lookups faster.
When it comes into a position where collection of data is what you're processing, I suggest you to take a look at underscore.js. It's not optimal choice for the best performance but it does make you code more readable and makes more sense especially when compared with loop.
Say data is a variable which stores your JSON data.
Try this:
// Given this selector criteria
var select = [{"industry_Id":1,"merchant_id":2}];
function filterByCriteria(criteria, data){
var match = [];
_.each(criteria, function(crit){
function matchIndustry(rec){ return rec.id===crit.industry_Id }
function matchMerchant(rec){ return rec.id===crit.merchant_id }
// Filter by industry id
var industry = _.first(_.where(data.industry, matchIndustry));
// Filter by merchant id
var merchant = _.where(industry.merchant, matchMerchant);
_.each(merchant, function addToMatchResult(m){
match.push(m.name);
});
});
return match;
}
var filteredData = filterByCriteria(select, data);
From snippet above, any merchants which match the search criteria will be taken to the match list. Is it more readable to you?
Do you even need numerical id's? Gets super easy when you don't.
/*
{
"industry": {
"oil and gas":{
"merchant": {
"ABC": {
"name": "ABC oil"
},
"DEF": {
"name": "DEF gas"
},
"GHJ" :{
"name": "GHJ oil and gas"
}
}
},
"IT": {
"merchant": {
"Apple" : {
"name": "Apple computers"
},
"HP": {
"name": "Hewlett Packard"
},
"Google": {
"name": "Google. Maw haw haw"
}
}
}
}
}
*/
var data = '{"industry": {"oil and gas":{"merchant": {"ABC": {"name": "ABC oil"},"DEF": {"name": "DEF gas"},"GHJ" :{"name": "GHJ oil and gas"}}},"IT": {"merchant": {"Apple" : {"name": "Apple computers"},"HP": {"name": "Hewlett Packard"},"Google": {"name": "Google. Maw haw haw"}}}}}';
data = JSON.parse(data);
var merchant = data.industry['IT'].merchant['HP'];
alert(merchant.name);
//console.log(merchant.name);