double click on nodes in cytoscape.js is not working - cytoscape.js

I have a problem with this plugin. I want to double click on nodes and the id of nodes should be displayed in console. I added the link of this extension from https://github.com/fixpoint/cytoscape-dblclick and followed by this post Cytoscape js - Call a function whenever a node is clicked, but still is not working.
Is anybody have any idea why it is not working, it would be nice if you share it.
You can see my code:
$(function() {
var elements = {
nodes: [
],
edges: [
]
};
function randomNumber(a) {
return Math.floor(Math.random() * (a));
}
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
ready: function() {},
style: [{
selector: "node", //edge
style: {
content: "data(id)",
shape: "roundrectangle", //square, circle
"text-valign": "center",
"text-halign": "center",
height: "60px", //new
width: "100px", //new
//padding: "10px", //new
"text-wrap": 'wrap', //new
color: "#fff",
"background-color": "#11479e",
// "background-color": "data(faveColor)"
}
},
{
//arrows
selector: "edge",
style: {
"curve-style": "taxi",
//'background-color': '#008000',
width: 4,
"target-arrow-shape": "triangle",
"line-color": "#9dbaea",
"target-arrow-color": "#9dbaea"
}
}
],
});
cy.dblclick();
cy.on('dblclick', function(evt) {
console.log('dblclick');
cy.animate({
fit: {
eles: evt.target,
padding: 10,
},
});
});
cy.on('dblclick:timeout', function(evt) {
console.log('dblclick:timeout');
});
var ab = 12;
for (var i = 0; i < ab; i++) {
//elements.nodes.push({ "data": { "id": i } });
cy.add([{
group: "nodes",
data: {
id: i
}
}
])
}
var cb = 20;
for (var i = 0; i < cb; i++) {
cy.add([{
group: "edges",
data: {
source: randomNumber(ab),
target: randomNumber(ab)
}
}])
}
cy.layout({
name: "dagre", //dagre, grid
directed: true,
nodeDimensionsIncludeLabels: true,
boxSelectionEnabled: true,
autounselectify: true,
zoomingEnabled: true,
userZoomingEnabled: true,
styleEnabled: true
}).run();
cy.elements().qtip({
content: function() {
return 'Text, Test ' + this.id()
},
position: {
my: 'center left', //top center
at: 'center right' //bottom center
},
style: {
classes: 'qtip-bootstrap', //qtip-dark
tip: {
width: 16,
height: 10
}
}
});
// });
}); //end
body {
font-family: helvetica;
font-size: 14px;
}
#cy {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 999;
}
h1 {
opacity: 0.5;
font-size: 1em;
}
<!DOCTYPE>
<html>
<head>
<title>cytoscape-panzoom.js demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<link href="cytoscape.js-panzoom.css" rel="stylesheet" type="text/css" />
<link href="font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" type="text/css" />
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<!-- partial -->
<script src="https://js.cytoscape.org/js/cytoscape.min.js"></script>
<script src="https://cdn.rawgit.com/cpettitt/dagre/v0.7.4/dist/dagre.min.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
<!-- qtip Links -->
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.0/jquery.qtip.min.js"></script>
<link href="http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.0/jquery.qtip.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-qtip/2.7.0/cytoscape-qtip.js"></script>
<!-- for testing with local version of cytoscape.js -->
<!-- <script src="../cytoscape.js/build/cytoscape.js"></script> -->
<script src="https://unpkg.com/cytoscape-dblclick/dist/index.js"></script>
<script src="cytoscape-panzoom.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

Just follow the post you already linked and log the node information instead of fitting the graph to the clicked node. Also, your demo had some static scripts in the header, I removed them for that reason. That way, the qtip works again too:
$(function() {
var elements = {
nodes: [
],
edges: [
]
};
function randomNumber(a) {
return Math.floor(Math.random() * (a));
}
var cy = window.cy = cytoscape({
container: document.getElementById('cy'),
ready: function() {},
style: [{
selector: "node", //edge
style: {
content: "data(id)",
shape: "roundrectangle", //square, circle
"text-valign": "center",
"text-halign": "center",
height: "60px", //new
width: "100px", //new
//padding: "10px", //new
"text-wrap": 'wrap', //new
color: "#fff",
"background-color": "#11479e",
// "background-color": "data(faveColor)"
}
},
{
//arrows
selector: "edge",
style: {
"curve-style": "taxi",
//'background-color': '#008000',
width: 4,
"target-arrow-shape": "triangle",
"line-color": "#9dbaea",
"target-arrow-color": "#9dbaea"
}
}
],
});
cy.dblclick();
cy.on('dblclick', function(evt) {
console.log(evt.target.id());
});
var ab = 12;
for (var i = 0; i < ab; i++) {
//elements.nodes.push({ "data": { "id": i } });
cy.add([{
group: "nodes",
data: {
id: i
}
}
])
}
var cb = 20;
for (var i = 0; i < cb; i++) {
cy.add([{
group: "edges",
data: {
source: randomNumber(ab),
target: randomNumber(ab)
}
}])
}
cy.layout({
name: "dagre", //dagre, grid
directed: true,
nodeDimensionsIncludeLabels: true,
boxSelectionEnabled: true,
autounselectify: true,
zoomingEnabled: true,
userZoomingEnabled: true,
styleEnabled: true
}).run();
cy.elements().qtip({
content: function() {
return 'Text, Test ' + this.id()
},
position: {
my: 'center left', //top center
at: 'center right' //bottom center
},
style: {
classes: 'qtip-bootstrap', //qtip-dark
tip: {
width: 16,
height: 10
}
}
});
// });
}); //end
body {
font-family: helvetica;
font-size: 14px;
}
#cy {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 999;
}
h1 {
opacity: 0.5;
font-size: 1em;
}
<!DOCTYPE>
<html>
<head>
<title>cytoscape-panzoom.js demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<!--<link href="cytoscape.js-panzoom.css" rel="stylesheet" type="text/css" />-->
<!--<link href="font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet" type="text/css" />-->
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<!-- partial -->
<script src="https://js.cytoscape.org/js/cytoscape.min.js"></script>
<script src="https://cdn.rawgit.com/cpettitt/dagre/v0.7.4/dist/dagre.min.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
<!-- qtip Links -->
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.0/jquery.qtip.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.0/jquery.qtip.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-qtip/2.7.0/cytoscape-qtip.js"></script>
<!-- for testing with local version of cytoscape.js -->
<!-- <script src="../cytoscape.js/build/cytoscape.js"></script> -->
<script src="https://unpkg.com/cytoscape-dblclick/dist/index.js"></script>
<!--<script src="cytoscape-panzoom.js"></script>-->
</head>
<body>
<div id="cy"></div>
</body>
</html>

Related

arcgis goTo feature and open popup

I am new to Arcgis maps and using ArcGIS Javascript 4.2 library. Currently the features are showing up on the map and I am trying to go to feature and open it's popup programmatically. below is my code to query the features which is working fine.
var query = layer.createQuery();
query.where = "key= " + dataItem.key+ "";
query.returnGeometry = true;
query.returnCentroid = true;
query.returnQueryGeometry = true;
layer.queryFeatures(query).then(function (results) {
//I am getting the feature results here.
//trying to navigate to feature and open popup
});
Note: I tried using the following code from documentation which is working fine but I don't have the center as the features are polylines in my case.
view.goTo({center: [-126, 49]})
First, View goTo method has several options, including just using a geometry wich I think would be a better option for your case, zoom to a polyline.
Second to open the popup you just need to use the open method and you can pass there the features to show.
Check this example I put for you, has both suggestions,
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<title>ArcGIS API for JavaScript Hello World App</title>
<style>
html,
body {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#selectDiv {
height: 30px;
width: 100%;
margin: 5px;
}
#cableNameSelect {
height: 30px;
width: 300px;
}
#cableGoToButton {
height: 30px;
width: 100px;
}
#viewDiv {
height: 500px;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.15/esri/css/main.css">
</head>
<body>
<div id="selectDiv">
<select id="cableNameSelect"></select>
<button id="cableGoToButton">GO TO</button>
</div>
<div id="viewDiv">
</div>
<script src="https://js.arcgis.com/4.15/"></script>
<script>
require([
'esri/Map',
'esri/views/MapView',
'esri/layers/FeatureLayer'
], function (Map, MapView, FeatureLayer) {
const cableNameSelect = document.getElementById("cableNameSelect");
const cableGoToButton = document.getElementById("cableGoToButton");
const map = new Map({
basemap: 'hybrid'
});
const view = new MapView({
container: 'viewDiv',
map: map,
zoom: 10,
center: {
latitude: 47.4452,
longitude: -121.4234
}
});
view.popup.set("dockOptions", {
buttonEnabled: false,
position: "top-right"
});
const layer = new FeatureLayer({
url: "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/ArcGIS/rest/services/cables/FeatureServer/0",
popupTemplate: {
title: "{NAME}",
outFields: ["*"],
content: [{
type: 'fields',
fieldInfos: [
{
fieldName: "length"
},
{
fieldName: "owners"
},
{
fieldName: "rfs"
}
]
}],
}
});
map.add(layer);
layer.queryFeatures({
where: "1=1",
outFields: ["Name"],
returnGeometry: false
}).then(function(results) {
for(const graphic of results.features) {
cableNameSelect.appendChild(new Option(graphic.attributes.Name, graphic.attributes.Name));
}
});
cableGoToButton.onclick = function() {
if (!cableNameSelect.value) {
return;
}
cableGoToButton.disabled = true;
layer.queryFeatures({
where: `Name='${cableNameSelect.value}'`,
outFields: ["*"],
returnGeometry: true
}).then(function (results) {
cableGoToButton.disabled = false;
if (!results.features) {
return;
}
view.goTo(results.features[0].geometry);
view.popup.open({
features: [results.features[0]]
})
})
};
});
</script>
</body>
</html>

How can I make the label of an edge displayed just in the middle position?

I'm trying to make the label displayed in the middle position, say source --<sometext>--> target.
I'm reading the official document but I can't find an answer.
Can this be implemented?
as you can see here, there is a simple solution to this provided by cytoscape:
window.cy = cytoscape({
container: document.getElementById('cy'),
layout: {
name: 'grid',
cols: 2
},
style: [{
"selector": "node[label]",
"style": {
"label": "data(label)"
}
},
{
"selector": "edge[label]",
"style": {
"label": "data(label)",
"width": 3,
"text-background-opacity": 1,
"text-background-color": "#fff"
}
},
{
"selector": ".autorotate",
"style": {
"edge-text-rotation": "autorotate",
}
}
],
elements: [{
data: {
id: 'one'
}
},
{
data: {
id: 'two'
}
}, {
data: {
source: 'one',
target: 'two',
label: 'autorotate (move my nodes)'
},
classes: 'autorotate'
}
]
});
body {
font-family: helvetica, sans-serif;
font-size: 14px;
}
#cy {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 999;
}
<html>
<head>
<title>Labels demo</title>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape#3.3.0/dist/cytoscape.min.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>

cytoscape js multiline label - different css rule for second line

I am using a multiline option on a label (using \n to delineate lines) and would like to know if it is possible to use a different font size for the second line (in the example having 'test' be a smaller font size
if you really need this, I would use the cytoscape-node-html-label extension (you can find it here). According to the documentation, you can use this code to get a multiline multistyle label:
document.addEventListener("DOMContentLoaded", function() {
var sampleDataset = [{
group: "nodes",
data: {
id: "16150999",
name: "xps plrmr",
type: 0,
code: "7704322293"
},
classes: "class1"
}];
var mainNodeDiameter = 20;
var otherNodesDiameter = 17;
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
minZoom: 0.1,
maxZoom: 3,
zoom: 0.5,
style: [{
selector: "node", // default node style
style: {
width: mainNodeDiameter + "px",
height: mainNodeDiameter + "px",
"overlay-padding": "5px",
"overlay-opacity": 0,
"z-index": 10,
"border-width": 2,
"border-opacity": 0
}
},
{
selector: "node[type=0]",
style: {
"background-color": "#7CACC2"
}
},
{
selector: "edge", // default edge style
style: {
"curve-style": "unbundled-bezier",
"control-point-distance": 30,
"control-point-weight": 0.5,
opacity: 0.9,
"overlay-padding": "3px",
"overlay-opacity": 0,
label: "data(title)",
"font-family": "FreeSet,Arial,sans-serif",
"font-size": 9,
"font-weight": "bold",
"text-background-opacity": 1,
"text-background-color": "#ffffff",
"text-background-padding": 3,
"text-background-shape": "roundrectangle",
width: 1
}
},
{
selector: "node:selected",
style: {
"border-width": 2,
"border-style": "solid",
"border-color": "#3f3f3f",
"border-opacity": 1
}
}
],
layout: {
name: "random"
},
elements: sampleDataset
}));
// add html labels to cytoscape
cy.nodeHtmlLabel([{
query: "node[type=0]",
cssClass: "cy-title",
valign: "top",
valignBox: "top",
tpl: function(data) {
return '<div><p class="cy-title__name">' + data.name + '</p><p class="cy-title__info">' + data.code + "</p></div>";
}
}]);
// fit cy to viewport
cy.ready(function () {
cy.fit();
cy.center();
});
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 100%;
float: left;
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 999;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
p {
margin: 0;
padding: 0;
}
.cy-title {
text-align: center;
font-size: 13px;
width: 130px;
color: #2b2b2b;
background: radial-gradient(#87CeFa, #7B68EE);
text-transform: capitalize;
}
.cy-title__name {
font-size: 1.5em;
}
.cy-title__info {
font-size: 0.9em;
}
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.17/cytoscape.min.js"></script>
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-node-html-label#1.1.3/dist/cytoscape-node-html-label.min.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>
This way, you can use html styling for each label.
The node-html-label is a good one, as Stephen mentioned. You also may be interested in the popper extension:
https://www.npmjs.com/package/cytoscape-popper
The popper extension is a bit more flexible, but the html-label one is a bit more automatic. Take a look at both and see which suits your project best.
Always check the extension list for an up-to-date list: http://js.cytoscape.org/#extensions

Show and hide node info on mouseover in cytoscape

I am working on a cytoscape.js graph in browser. I want to show some information of nodes (e.g. node label) as the mouse hovers over the nodes in a cytoscape graph. The following code is working for console.log() but I want to show the information in the browser:
cy.on('mouseover', 'node', function(evt){
var node = evt.target;
console.log( 'mouse on node' + node.data('label') );
});
Please help !
Cytoscape.js has popper.js with tippy. I can give you a working exapmle for popper.js:
popper with tippy:
document.addEventListener("DOMContentLoaded", function() {
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
style: [{
selector: "node",
style: {
content: "data(id)"
}
},
{
selector: "edge",
style: {
"curve-style": "bezier",
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "a"
}
}, {
data: {
id: "b"
}
}],
edges: [{
data: {
id: "ab",
source: "a",
target: "b"
}
}]
},
layout: {
name: "grid"
}
}));
function makePopper(ele) {
let ref = ele.popperRef(); // used only for positioning
ele.tippy = tippy(ref, { // tippy options:
content: () => {
let content = document.createElement('div');
content.innerHTML = ele.id();
return content;
},
trigger: 'manual' // probably want manual mode
});
}
cy.ready(function() {
cy.elements().forEach(function(ele) {
makePopper(ele);
});
});
cy.elements().unbind('mouseover');
cy.elements().bind('mouseover', (event) => event.target.tippy.show());
cy.elements().unbind('mouseout');
cy.elements().bind('mouseout', (event) => event.target.tippy.hide());
});
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px
}
#cy {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 1;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/popper.js#1.14.7/dist/umd/popper.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-popper#1.0.4/cytoscape-popper.min.js"></script>
<script src="https://unpkg.com/tippy.js#4.0.1/umd/index.all.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/tippy.js#4.0.1/index.css" />
</head>
<body>
<div id="cy"></div>
</body>
Hi guys the Stephan's answer won't work using module based code like in React.js so I'm giving you an alternative without tippy
popper.css // popper style
.popper-div { // fill free to modify as you prefer
position: relative;
background-color: #333;
color: #fff;
border-radius: 4px;
font-size: 14px;
line-height: 1.4;
outline: 0;
padding: 5px 9px;
}
App.js
import cytoscape from "cytoscape";
import popper from "cytoscape-popper"; // you have to install it
import "./popper.css";
cytoscape.use(popper);
const cy = cytoscape({....});
cy.elements().unbind("mouseover");
cy.elements().bind("mouseover", (event) => {
event.target.popperRefObj = event.target.popper({
content: () => {
let content = document.createElement("div");
content.classList.add("popper-div");
content.innerHTML = event.target.id();
document.body.appendChild(content);
return content;
},
});
});
cy.elements().unbind("mouseout");
cy.elements().bind("mouseout", (event) => {
if (event.target.popper) {
event.target.popperRefObj.state.elements.popper.remove();
event.target.popperRefObj.destroy();
}
});

Cytoscape.js: Overlapping edge labels in multigraph

There are several posts about edge or edge label overlaps in Cytoscape.js but the answers given there, like smaller labels and more spaced out nodes, don't help for a multigraph, i.e. a graph where two nodes can have multiple edges between them.
Is there a way to spread out edge labels even if they all belong to edges between the same two nodes? The edge pairs occur both in the same direction and in the reverse one.
As my graph is verly large, I would prefer a solution with haystack edges as the performance is already low.
Well you can rotate the text with "text-rotation": "autorotate" in the labels css. You can also define the "text-margin-x/y" for each node itself, but how do you plan on defining the position for each and every label? Lets assume, you did that perfectly: you would still need to do a reposition event after moving just one node, because the offsets will be off by then:
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
boxSelectionEnabled: false,
autounselectify: true,
style: [{
selector: "node",
css: {
content: "data(id)",
"text-valign": "center",
"text-halign": "center",
height: "60px",
width: "60px",
"border-color": "black",
"border-opacity": "1",
"border-width": "10px"
}
},
{
selector: "$node > node",
css: {
"padding-top": "10px",
"padding-left": "10px",
"padding-bottom": "10px",
"padding-right": "10px",
"text-valign": "top",
"text-halign": "center",
"background-color": "#bbb"
}
},
{
selector: "edge",
css: {
"target-arrow-shape": "triangle"
}
},
{
selector: "edge[label]",
css: {
"label": "data(label)",
"text-rotation": "autorotate",
"text-margin-x": "data(xalign)",
"text-margin-y": "data(yalign)"
}
},
{
selector: ":selected",
css: {
"background-color": "black",
"line-color": "black",
"target-arrow-color": "black",
"source-arrow-color": "black"
}
}
],
layout: {
name: "circle"
}
}));
var info = [{
name: "Peter",
next_op_name: "Claire"
},
{
name: "Claire",
next_op_name: "Mike"
},
{
name: "Mike",
next_op_name: "Rosa"
},
{
name: "Rosa",
next_op_name: "Peter"
}
];
cy.ready(function() {
var array = [];
// iterate over info once
for (var i = 0; i < info.length; i++) {
array.push({
group: "nodes",
data: {
id: info[i].name, // id is name!!!
label: info[i].name
}
});
array.push({
group: "edges",
data: {
id: "e" + i,
source: info[i].name,
target: info[i].next_op_name,
label: "e" + i,
xalign: (i == 0 || i == 1 ? '15px' : '-15px'),
yalign: (i == 0 || i == 3 ? '15px' : '-15px')
}
});
}
cy.add(array);
cy.layout({
name: "circle"
}).run();
});
cy.on("mouseover", "node", function(event) {
var node = event.target;
node.qtip({
content: "hello",
show: {
event: event.type,
ready: true
},
hide: {
event: "mouseout unfocus"
}
},
event
);
});
cy.on("tapdrag", "node", function(event) {
// update all relevant labels
var labels = event.target.connectedEdges();
for (var i = 0; i < labels.length; i++) {
// render with the right positions?
}
});
body {
font: 14px helvetica neue, helvetica, arial, sans-serif;
}
#cy {
height: 100%;
width: 75%;
position: absolute;
left: 0;
top: 0;
float: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head>
<meta charset=utf-8 />
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.17/cytoscape.min.js"></script>
<!-- qtip imports -->
<script src="https://unpkg.com/jquery#3.3.1/dist/jquery.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.0/jquery.qtip.min.js"></script>
<link href="http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.0/jquery.qtip.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-qtip/2.7.0/cytoscape-qtip.js"></script>
<!-- dagre imports -->
<script src="https://unpkg.com/dagre#0.7.4/dist/dagre.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-dagre/1.5.0/cytoscape-dagre.js"></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>
The problem breaks down to this: the task seems simple, but keeping track of the positions of all nodes and the edges aaaaand your rendered labels is a sisyphean task.