I'm trying to get simplified relationship between connected mxgraph
Question: I'm trying to get the simplified relationship, once the graph is drawn.
i'm trying to get relationship between connected nodes in json.
Note: solution must work for every drawn state.
Here is codepen:
https://codepen.io/eabangalore/pen/pmELpL
i want to get the relationship from above code snippet.
Expected output (from drawn relationship):
[
{"id":0,"parent":"#","text":"A","child":[{"cid":1,"connectionText":"Bangalore"}]},
{"id":1,"parent":0,"text":"B","child":[{"cid":2,"connectionText":""}]},
{"id":2,"parent":1,"text":"C","child":[{"cid":null,"connectionText":""}]}
];
Please refer codepen, as below snippet is not working.
<!--
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":"","vertex":"1","parent":"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":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"678.2291717529297","y":"106.89236450195312","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"4","value":"Bangalore","edge":"1","parent":"1","source":"2","target":"3"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"5","value":"C","style":"","vertex":"1","parent":"1"},"children":[{"tagName":"mxGeometry","attributes":{"x":"1013.7847747802734","y":"83.55902862548828","width":"100","height":"40","as":"geometry"}}]},{"tagName":"mxCell","attributes":{"id":"6","edge":"1","parent":"1","source":"3","target":"5"},"children":[{"tagName":"mxGeometry","attributes":{"relative":"1","as":"geometry"}}]}]}]};
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!!!
Check out this CodePen link which has the modified version of the snippet you gave. In it, you can add new elements and change texts and see an updated json string representing the relationships in the graph: https://codepen.io/tien-q-nguyen2/pen/GaQXBO
I also edited the function a little from the first time I posted this answer.
Note: based on the expected output you put in the original question, there is only one parent per vertex element ({"id":1,"parent":0 <= in the example you gave), so if there are multiple nodes pointing to the same child , the child's parent property will only refer to the first parent's id. I can change the parent's property to be an array that can keep multiple parent's ids if you want.
function getNodesFrom(graph){
var cells = graph.getModel().cells;
var vertices = [], edges = [];
for (var key in cells) {
if (cells[key].isVertex()){
vertices.push(cells[key]);
}
else if (cells[key].isEdge()){
edges.push(cells[key]);
}
}
var simpleVertices = [], simpleEdges = [];
var newIndex = 0;
var newIndexOf = [];
vertices.forEach(function(vertex){
simpleVertices.push({id: newIndex, value: vertex.value});
newIndexOf[vertex.id] = newIndex++;
});
edges.forEach(function(edge){
if (edge.target === null || edge.source === null) return;
simpleEdges.push({
parentId: newIndexOf[edge.source.id],
childId: newIndexOf[edge.target.id],
value: edge.value
});
});
var edgesForParentIndex = [];
var edgesForChildIndex = [];
simpleEdges.forEach(function(edge){
if (edgesForParentIndex[edge.parentId] === undefined){
edgesForParentIndex[edge.parentId] = [];
}
edgesForParentIndex[edge.parentId].push(edge);
if (edgesForChildIndex[edge.childId] === undefined){
edgesForChildIndex[edge.childId] = [];
}
edgesForChildIndex[edge.childId].push(edge);
});
var nodes = [];
simpleVertices.forEach(function(vertex){
var node = {};
node.id = vertex.id;
if (edgesForChildIndex[node.id] === undefined){
node.parent = '#';
} else {
node.parent = edgesForChildIndex[node.id][0].parentId;
}
node.text = (vertex.value === undefined || vertex.value === null) ? '' : vertex.value;
node.child = [];
if (edgesForParentIndex[node.id] === undefined){
node.child.push({cid: null, connectionText: ""});
} else {
edgesForParentIndex[node.id].forEach(function(edge){
var text = (edge.value === undefined || edge.value === null) ? '' : edge.value;
node.child.push({cid: edge.childId, connectionText: text});
});
}
nodes.push(node);
});
return nodes;
}
I am using Easeljs in my spare time. I tried to inherit the Container from createjs object in easeljs library.
var innovative = {};
innovative.object = {};
innovative.object.Basic = function () {
};
//innovative.object.Basic.prototype = new createjs.Container;
innovative.object.LocalPhoto = function (data) {
};
innovative.object.LocalPhoto.prototype = new innovative.object.Basic;
innovative.object.LocalPhoto.prototype.constructor = innovative.object.LocalPhoto;
I have function in LocalPhoto which will add itself to the stage like this
innovative.object.LocalPhoto.prototype.load = function (stage, event) {
var self = this;
stage.addChild(self);
stage.update();
};
This is how i create LocalPhoto object
var self = this;
for (var i = 0; i < values.length; i++) {
this.download (values[i], function (evt) {
var photo;
photo = new innovative.object.LocalPhoto(evt.target.object);
photo.load(self.stage, evt);
});
}
Problem i am facing is when i add a LocalPhoto into the stage, the rest of the localPhoto within that stage will append the same photo as well.
This is what i mean in steps : 1) insert a image within a container and added to the stage.
2) another image within a new container and added to the stage.
At the same time, the later image also added to the other child container which i have added to the stage.
(function() {
function YourFunc(param) {
this.Container_constructor();
this.param = param;
}
var p = createjs.extend(YourFunc, createjs.Container);
p.draw = function() {
this.Container_draw();
// add custom logic here.
}
window.Button = createjs.promote(Button, "Container");
}());
Or Just
var p = YourFunc.prototype = new createjs.Container();
I have small application in Famo.us framework. I have added four images in mainContext with draggable modifier using four loop. I want fire an event when user drag the event for that I have added following code.
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var MouseSync = require("famous/inputs/MouseSync");
var StateModifier = require('famous/modifiers/StateModifier');
var Draggable = require("famous/modifiers/Draggable");
var ImageSurface = require('famous/surfaces/ImageSurface');
var Transitionable = require("famous/transitions/Transitionable");
var SnapTransition = require("famous/transitions/SnapTransition");
Transitionable.registerMethod('snap', SnapTransition);
// create the main context
var mainContext = Engine.createContext();
var mouseSync = new MouseSync();
var isPlus;
var isFirst = true;
var oldValues = [];
var mySurfaces=[];
var draggables = [];
var myModifiers= [];
var images = [
['img/svelteMan.png', -0.4375 * window.innerWidth, -1.5 * window.innerWidth],
['img/swimmer.png', 0 * window.innerWidth, -0.96875 * window.innerWidth],
['img/soccerPlayer.png', -0.21875 * window.innerWidth, -0.984375 * window.innerWidth],
['img/breakDancer.png', 0 * window.innerWidth, -0.6875 * window.innerWidth]
];
for(var i=0;i<images.length;i++) {
var mySurface = new ImageSurface({ });
mySurface.setContent(images[i][0]);
var myModifier = new StateModifier({
origin: [0.5, 0.5]
});
var draggable = new Draggable({ });
mySurface.pipe(draggable);
mySurfaces.push(mySurface);
myModifiers.push(myModifier);
draggables.push(draggable);
mainContext.add(draggables[i]).add(myModifiers[i]).add(mySurfaces[i]);
var oldValue =0;
oldValues.push(oldValue);
}
ActivateEvent();
function ActivateEvent(){
for(var i=0;i<images.length;i++) {
draggables[i].on('update', function (data) {
MakeMove(i);
});
}
};
function MakeMove(i) {
//alert(i);
if (oldValues[i] > draggables[i].getPosition()[0]) {
myModifiers[i].setTransform(Transform.rotateZ(-0.1));
} else {
myModifiers[i].setTransform(Transform.rotateZ(0.1));
}
oldValues[i] = draggables[i].getPosition()[0];
}
I have added onUpdate event of Draggable as following :
for(var i=0;i<images.length;i++) {
draggables[i].on('update', function (data) {
MakeMove(i);
});
}
But I am getting error saying draggables[i] is not defined
If I want to call an event of Third image on draggable then how to call event? How to register an events when surfaces added in for loop?
Thanks
Add a closure:
var cl = function(n) {
return function(data){
MakeMove(n);
};
};
for(var i=0;i<images.length;i++)
{
draggables[i].on('update', cl(i));
}
Hope it helps.
You have a closure problem.
Take a look of this:
var draggables = [];
for(i=0;i<10;i++) {
var draggable = new Draggable({ });
draggables.push(draggable);
}
ActivateEvent();
function ActivateEvent(){
for(var i=0;i<10;i++) {
setTimeout(function() {
MakeMove(i);
}(i),0); //<----- notice the (i), this how you can send the correct loop for MakeMove function, without that (i) the anonymous function scope breaks the loop
}
}
function MakeMove(i) {
console.log(draggables[i]); //==> this outputs the draggables
}
Maybe you can try this:
draggables[i].on('update', function (data) {
MakeMove(i);
}(i)); <----- now the loop is send to makeMove
I am working on a card game with HTML5 canvas and javascript with create.js library. The problem i have is that, I have an onclick function that deletes the clicked card from player's hand(removes the object from the array with splice and then redraws the canvas/redraws on canvas the new array of player's cards without the deleted one). When i do this for first time it does well.But if I click again on the card that it should be for example at index 1 in the array(after the splice method) it doesn't work.
Look at this code:
Here is my code for class Card:
function Card(suit,rank,imageFrontUrl,imageBackUrl)
{
this.imageFront = new createjs.Bitmap(imageFrontUrl);
this.imageBack = new createjs.Bitmap(imageBackUrl);
this.suit = suit;
this.rank = rank;
}
function Deck(){
this.cards = new Array();
this.makeDeck = function()
{
this.cards[0]= new Card("clubs",1,"images/114.png","images/155.png");
this.cards[1]= new Card("clubs",2,"images/115.png","images/155.png");
this.cards[2]= new Card("clubs",3,"images/116.png","images/155.png");
this.cards[3]= new Card("clubs",4,"images/117.png","images/155.png");
this.cards[4]= new Card("clubs",5,"images/118.png","images/155.png");
this.cards[5]= new Card("clubs",6,"images/119.png","images/155.png");
...
}
this.shuffleDeck = function()
{
var j,k;
for (j = 0; j < this.cards.length; j++) {
k = Math.floor(Math.random() * this.cards.length);
temp = this.cards[j];
this.cards[j] = this.cards[k];
this.cards[k] = temp;
}
}
this.dealCardsPlayer = function()
{
var playerDeck = new Array();
for(var i = 0; i<6;i++)
{
var x = this.cards.pop();
playerDeck.push(x);
}
return playerDeck;
}
}
Here is my code for class Player:
function Player()
{
this.playerTurn = false;
this.id = this;
this.name = this;
this.score = this;
this.playerHand = new Array();
this.playerTakenCards = new Array();
this.playerPickCard = function(n)
{
var card = this.playerHand(n);
return card;
}
this.tempArray = this;}
Here is my init function:
function init(){
var canvas = document.getElementById("tutorialCanvas");
var stage = new createjs.Stage(canvas);
var deck = new Deck();
var player1 = new Player();
deck.makeDeck();
deck.shuffleDeck();
player1.playerHand = deck.dealCardsPlayer();
function drawPlayerCards(){
var rotation=280;
for(var i =0;i<player1.playerHand.length;i++)
{
player1.playerHand[i].imageFront.x=330;
player1.playerHand[i].imageFront.y=750;
player1.playerHand[i].imageFront.regX = 0;
player1.playerHand[i].imageFront.regY = 96;
player1.playerHand[i].imageFront.rotation = rotation;
rotation = player1.playerHand[i].imageFront.rotation+20;
stage.addChild(player1.playerHand[i].imageFront);
}
}
createjs.Ticker.addEventListener("tick", stage);}
And here is the function i have problem with:
player1.playerHand[1].imageFront.addEventListener('click',function(event)
{
stage.removeAllChildren();
player1.playerHand.splice(1,1);
drawTableDeck();
drawPlayerCards();
}
After some researching I realized that the function works just for the first bitmap player1.playerHand[1].imageFront . If i change the array with the first onclick event and after that player1.playerHand[1].imageFront is some other image/bitmap, it doesn't work for it. Please help!
Why are you "hardcoding" the index of a card in your hand?
player1.playerHand.forEach(function(card) {
card.imageFront.addEventListener("click", function() {
var index = player1.playerHand.indexOf(card);
if (index != -1) {
stage.removeAllChildren();
player1.playerHand.splice(index, 1);
drawPlayerCards();
}
});
});
Some other notes:
It's a convention to use [] instead of new Array()
We normally put the opening brace { on the same line
Your Player constructor has an error: this.playerHand[n] instead of this.playerHand(n)
I'm trying to give each marker/shape on leaflet different information for onClick. The data I'm using is from a JSON array filled with objects.
The code I used to do this is:
var map = L.map('map').setView([43.16556, -77.61139], 13);
var OpenStreetMap_DE = L.tileLayer('http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
attribution: '©>'
}).addTo(map);
//Adding a circle. (point[long,Lat], radius in meters
// Saving for later :-)
// var zip = "/javascripts/zcta5.json",
// hsa = "/javascripts/hsa.json";
var host = new Array();
var temp;
//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
$.getJSON("/allHospitals",function(data,status){
var color;
var thefill;
for (var i = 0; i < data.length; i++) {
temp = data[i];
if (temp.name === "rgh") {
color = '#FF9755';
thefill = '#009666';
} else if (temp.name === "strong") {
color = '#FFE03A';
thefill = '#7C83FF';
}
host.push(L.circle(data[i].lnglat, data[i].pplNum/2, {
color: color,
fillColor: thefill,
fillOpacity: 1,
weight:3
}));
host[i].on("click", onClick);
host[i].addTo(map);
}
});
var svg = d3.select(map.getPanes().overlayPane).append("svg"),
g = svg.append("g").attr("class", "leaflet-zoom-hide");
function onOver(e){
}
function onClick (e) {
console.log(temp);
}
It returns the last entry in the json file. How can I just make this work?
function onClick (e) {
console.log(temp);
}
You want to be looking at e not temp. e is what was clicked, temp is just leftover from the loop above.