jsPlumb unnable to connect mutiple points - javascript

I'm trying to use jsPlumb, version 1.4.1 with the jquery dependency, to bind some divs in my UI together.
My initial code:
jsPlumb.bind("ready", function() {
var eclipse = jsPlumb.addEndpoint("java-eclipse");
var netbeans = jsPlumb.addEndpoint("java-netbeans");
jsPlumb.connect({
source:eclipse,
target:netbeans,
connector:"Straight",
paintStyle:{ lineWidth:5, strokeStyle:'rgba(0, 0, 200, 0.5)' },
endpoint:"Dot",
anchor:[ "Perimeter", { shape:"Circle" }]
});
});
Which works as intended, but as soon as I try to add more end points to make another connection:
//Innitial working endpoints
var eclipse = jsPlumb.addEndpoint("java-eclipse");
var netbeans = jsPlumb.addEndpoint("java-netbeans");
//Just adding these endpoints causes my script to crash
var javaSE = jsPlumb.addEndpoint("java-se");
var javaSW = jsPlumb.addEndpoint("java-sw");
This gets me the following error:
Error: H is undefined
r#https://cdnjs.cloudflare.com/ajax/libs/jsPlumb/1.4.1/jquery.jsPlumb-1.4.1-all-min.js:1:9455
I have no idea why the second set of endpoints I create causes the whole thing to crash, the divs exist and have the right ids and looking at the js plumb demos and docs making two separate connections (se->sw and eclipse->netbeans) should be possible.

Try this;
jsPlumb.connect({
source:eclipse,
target:netbeans,
connector:"Straight",
paintStyle:{ lineWidth:5, strokeStyle:'rgba(0, 0, 200, 0.5)' },
endpoint:"Dot",
anchor:[ "Perimeter", { shape:"Circle" }],
maxConnections: -1
});

Related

Instead of creating new chart in ChartJS, the new updated chart keeps the old data and adds the new

I have been trying to solve this problem with ChartJS for a few days now, and I am completely stumped
My program shows the user a set of input elements they use to select data needing to be charted, plus a button that has an event to chart their data. The first chart works great. If they make a change to the data and click the button a second, third, or more time, all the data from the previous charts is plotted, PLUS their most recent selection.
It is behaving exactly like you might expect if the chart.destroy() object is not working, or perhaps would work if I created the chart object using a CONST (and could therefore add new data but not delete the beginning data).
I have tried all combinations of the browsers, chartjs and jquery libraries below:
Three different browsers:
• Chrome: Version 107.0.5304.121 (Official Build) (64-bit)
• Microsoft Edge: Version 107.0.1418.56 (Official build) (64-bit)
• Firefox: 107.0 64-bit
I have tried at least three different versions of Chart.js, including
• Versions 3.9.1
• 3.6.2
• 3.7.0
Jquery.js
• v3.6.1
• v1.11.1
Other things I have tried:
"use strict" (no luck)
In addition to destroying the chart object, removed the div containing the canvas, and appending it again.
using setTimeout() function before updating the chart after destroying it (because I thought maybe giving the destroy method more time might help)
type here
Software:
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/chart.js"></script>
<script type="text/javascript" src="js/dropdownLists.js"></script>
<script type="text/javascript" src="js/chartDataFunctions.js"></script>
<script type="text/javascript" src="js/chartJSFunctions.js"></script>
<body>
<div class = metadatasetup4" id = "buttons">
<button class="download" id="getchart" value="Get Chart">Chart</button>
<button class="download" id="downloadchart" value="Download">Download</button>
</div>
<div id = "bigchartdiv" class="bigchart">
<canvas id="myChart"></canvas>
</div>
</body>
<script>
$(window).on('load',function(){
//NOTE 1: In of my attempts to troubleshoot I tried strict mode (it didn't work)
//"use strict";
let data = {
labels: lbl,
datasets: [
]
};
let config = {
type: 'line',
data: data,
options: {
scales: {
y: {
type: 'linear',
display: true,
position: 'left',
min:0,
pointStyle:'circle',
},
y1: {
type: 'linear',
display: true,
position: 'right',
suggestedMax: 25,
min: 0,
pointStyle: 'cross',
// grid line settings
grid: {
drawOnChartArea: false, // only want the grid lines for one axis to show up
},
},
}
}
};
// NOTE 2: The next line below, beginning with "var bigChartHTML =" was one of my later attempts to
// solve the problem. It didn't work, but my thought process was that if I removed
// the div containing the canvas, AND destroyed the chart object, that appending a "fresh"
// chart div to the body might be a work-around. This did not work.
var bigChartHTML = '<div id = "bigchartdiv" class="bigchart"><canvas id="myChart"></canvas></div>'
let ctx = document.getElementById('myChart').getContext('2d');
let bigChart = null;
// The getChartData() function below uses Ajax to populate various dropdown lists
// which enable the user to select the data is to be charted.
// There are no chartjs-related operations in getChartData()
getChartData();
$('#buttons').on('click','#getchart',function(){
if (bigChart!=null) {
//removeData(bigChart);
bigChart.destroy();
//bigChart = 1;
}
$("#bigchartdiv").empty(); //for this and next 2 lines, see NOTE 2 above
$("#bigchartdiv").remove();
$(bigChartHTML).insertAfter("#chartcontrols");
bigChart = new Chart(document.getElementById('myChart'),config);
//NOTE 3: I thought maybe bigChart.destroy() took time, so I tried
// using the setTimeout function to delay updating the chart
// (didn't work, but I left it in the code, anyway.)
setTimeout(function() {updateChart(bigChart)}, 2000);
//updateChart(bigChart);
});
// NOTE: The updateChart() function is actually included in "js/chartDataFunctions.js"
function updateChart(chart) {
/*
This section of the program reads the HTML elements then uses them
to make an Ajax request to sql server, and these become the
parameters for the newDataSet() function below.
*/
newDataset(chart,firstElement,newdataset,backgroundcolor,color);
}
// NOTE: The newDataSet() function is actually included in "js/chartJSFunctions.js"
// I show it here for brevity.
// It decides which axis (y or y1) to use to plot the datasets
// the dataset is pushed into the data, and chart.update() puts it in the chart object
function newDataset(chart,label,data,bgcolor='white',color='rgb(255,255,255)') {
var maxValue = Math.max(...data);
if (Number.isNaN(maxValue)) {
return;
}
if (maxValue == 0) {
return;
}
var axisID = 'y';
var ptStyle = 'circle';
//var pStyle = 'circle';
if (maxValue < 50) {
axisID = 'y1';
bgcolor = 'white';
//ptStyle = 'Star'
}
chart.data.datasets.push({
label:label,
yAxisID:axisID,
data:data,
borderColor:color,
backgroundColor:bgcolor,
//pointStyle:ptStyle
});
chart.update();
}
});
</script>
I found a work-around that solves my problem, but I still think this is a bug in ChartJS. Before calling bigChart.destroy(), I now do two things: First, reset the data object back to it's original value, and second, reset the config object back to it's original value, THEN call bigChart.destroy().
I think the destroy() method should handle that for me, but in my case, for whatever reason, it doesn't.
So, what I have is a work-around, not really a solution, but I'll take it.

Dynamically created elements not draggable

I have a simple block its element is dynamically added to DOM, I want the user to be able to create a block and it should be draggable using jsplumb library.
Unfortunately, now I can create element but their not draggable but if I add them manually to the dom, it's draggable.
Here is what I have so far
function addMovieButton() {
var newMovieBlockButton = $("<div class='movie-button w'>Button New<div class='ep' action='begin'></div><div>");
}
Here is plumb.js
jsPlumb.ready(function () {
// setup some defaults for jsPlumb.
var instance = jsPlumb.getInstance({
Endpoint: ["Dot", {radius: 5}],
Connector:"StateMachine",
HoverPaintStyle: {stroke: "#1e8151", strokeWidth: 2 },
ConnectionOverlays: [
[ "Arrow", {
location: 1,
id: "arrow",
length: 14,
foldback: 0.8
} ],
[ "Label", { label: "FOO", id: "label", cssClass: "aLabel" }]
],
Container: "canvas"
});
instance.registerConnectionType("basic", { anchor:"Continuous", connector:"StateMachine" });
window.jsp = instance;
var canvas = document.getElementById("canvas");
var windows = jsPlumb.getSelector(".statemachine-demo .w");
var windows_movie = jsPlumb.getSelector(".statemachine-demo .movie-block ");
// bind a click listener to each connection; the connection is deleted. you could of course
// just do this: jsPlumb.bind("click", jsPlumb.detach), but I wanted to make it clear what was
// happening.
instance.bind("click", function (c) {
instance.deleteConnection(c);
});
// bind a connection listener. note that the parameter passed to this function contains more than
// just the new connection - see the documentation for a full list of what is included in 'info'.
// this listener sets the connection's internal
// id as the label overlay's text.
instance.bind("connection", function (info) {
info.connection.getOverlay("label").setLabel(info.connection.id);
});
// bind a double click listener to "canvas"; add new node when this occurs.
jsPlumb.on(canvas, "dblclick", function(e) {
// newNode(e.offsetX, e.offsetY);
});
//
// initialise element as connection targets and source.
//
var initNode = function(el) {
// initialise draggable elements.
instance.draggable(el);
instance.makeSource(el, {
filter: ".ep",
anchor: "Continuous",
connectorStyle: { stroke: "#5c96bc", strokeWidth: 2, outlineStroke: "transparent", outlineWidth: 4 },
connectionType:"basic",
extract:{
"action":"the-action"
},
maxConnections: 6,
onMaxConnections: function (info, e) {
alert("Maximum connections (" + info.maxConnections + ") reached");
}
});
instance.makeTarget(el, {
dropOptions: { hoverClass: "dragHover" },
anchor: "Continuous",
allowLoopback: true
});
// this is not part of the core demo functionality; it is a means for the Toolkit edition's wrapped
// version of this demo to find out about new nodes being added.
//
instance.fire("jsPlumbDemoNodeAdded", el);
};
// suspend drawing and initialise.
instance.batch(function () {
for (var i = 0; i < windows.length; i++) {
initNode(windows[i], true);
console.log(windows[i]);
}
for (var j = 0; j < windows_movie.length; j++) {
initNode(windows_movie[j], true);
console.log(windows_movie[j]);
}
});
jsPlumb.fire("jsPlumbDemoLoaded", instance);
});
Here is live demo live demo
Here is plunker full source code
On the demo above just right click to add movie block for testing
Why does draggable not working for dynamically created elements?
here is a sample page I made a while ago when I first discovered 'jsplumb', it does exactly what you want so you might wanna use it or build on top of it.
Remember, indeed you should call the draggable method after the elements are added to the DOM, my example is so simple:
it doesn't need the jsplumb.fire
it doesn't need the .ready binding
it doesn't need the 'batch' processing offered by jsplumb
so you get to avoid problems like the scope of ready and other I'm still trying to master.

updating chartjs pie chart by using .keypress() not working

I am trying to create a dynamic chartjs pie diagram. So far I have only been able to successfully adding an "update" function by using the jQuery .click function. However, I would like to also be able to update the chart by using the jQuery .keypress() function. I have therefore tried inserting this function into the that creates the pie chart. This does for some reason, however, not seem to work.
I have tried changing the order of the functions, however, nothing seems to do the trick. I know the code is correct since it works if I copy and paste it into the console. So my suspicion is that it has something to do with firing the function after the page load, which I have tried doing by wrapping the .keypress() function into a window.ready() function, however, this did not do the trick either.
This is the code i use to insert the pie chart:
$(document).ready(function(chartData) {
var childText = $('.noDisplay tbody').text();
//Number of positives
var positiveCount = (childText.match(/Positiv/g) || []).length;
//Number of negatives
var negativeCount = (childText.match(/Negativ/g) || []).length;
//Find total number of tests
var totalTests = positiveCount + negativeCount;
//Calculate into %
var positiveProcent = positiveCount / totalTests * 100;
var negativeProcent = negativeCount / totalTests * 100;
//With 2 decimals
var positiveProcent2 = positiveProcent.toFixed(2);
var negativeProcent2 = negativeProcent.toFixed(2);
var ctx = document.getElementById("myChart").getContext('2d');
var myChart = new Chart(ctx, {
type: 'pie',
data: {
labels: ["Positive tests i %", "Negative tests i %", ],
datasets: [{
label: 'Positiv vs negativ',
data: [positiveProcent2, negativeProcent2],
backgroundColor: ['rgba(39, 174, 96, 0.5)', 'rgba(231, 76, 60, 0.5)', ],
borderColor: ['rgba(39, 174, 96, 1)', 'rgba(231, 76, 60, 1)', ],
borderWidth: 1
}]
},
options: {}
});
function updateChart() {
var childText = $('.col-sm-12 #dataTable_wrapper').text();
//Number of positives
var positiveCount = (childText.match(/Positiv/g) || []).length;
//Number of negatives
var negativeCount = (childText.match(/Negativ/g) || []).length;
//Find total number of tests
var totalTests = positiveCount + negativeCount;
//Calculate into %
var positiveProcent = positiveCount / totalTests * 100;
var negativeProcent = negativeCount / totalTests * 100;
//With 2 decimals
var positiveProcent2 = positiveProcent.toFixed(2);
var negativeProcent2 = negativeProcent.toFixed(2);
myChart.data.datasets[0].data = [positiveProcent2, negativeProcent2];
myChart.update();
}
$('.kkTable #dataTable_filter > label').keypress(function() {
console.log('1');
updateChart();
});
$('button').click(function() {
updateChart();
});
});
How can i make the keypress() function work?
Thanks in advance :-)
The problem was fixed by switching the listener from a very specific set of elements to the document with the previous selector as selector of the on method. Complete discussion enclosed:
The label element will not emit a keypress event as far as I know. Are you sure you don't mean .kkTable #dataTable_filter > label input ? – Douwe de Haan
Yeah i did. Just tried changing it to that, still doesn't work however :/ – LENide300_KK
Does it log the 1? Because if not, there must be some other problem. Can you also append your HTML to the question? – Douwe de Haan
No it does not, which also confuses me. I would do that, but the thing is I pull the data from a table that gets generated through PHP and then some sorting + search functions gets applied to the table using datatables.net. So I can't quite post the HTML without it being ridiculously long. – LENide300_KK
When you set the listener (the keypress part), is the HTML already there? Or are you generating the HTML at a later moment? Because that would explain why it doesn't work. Try changing your function to the following: $(document).on('keypress', '.kkTable #dataTable_filter > label input', function() { – Douwe de Haan
The HTML is already there. However, your suggestion worked. But I dont understand why? I thought $(document).on('keypress', '.kkTable #dataTable_filter > label input', function() {' is the same as $('.kkTable #dataTable_filter > label').keypress(function() { ? Do you have an idea about why that is? – LENide300_KK
The difference is that all events bubble up (propagate) the DOM tree and eventually end up on the document. The listener I provided listens to keypress events on the document and afterwards checks wether or not the event came from something that is according to the filter. So even with HTML that changes after the listener is set it would still work, the one you had only works with elements that both checks with the filter and are present at the moment it is called. – Douwe de Haan

can't use jsplumb with second function

I'm getting my instance like this:
jsp = jsPlumb.getInstance();
jsp.setContainer(_domnodeId);
jsp.ready(function(){
//doing some stuff - connecting boxes with arrows...
var conn2 = jsp.connect({
source: boxSST_IPMRS_COBRAIP.boxId,
target: boxCOBRA_IM.boxId
});
}
result:
in another function I'm doing the same:
jsp = jsPlumb.getInstance();
jsp.setContainer(_domnodeId);
jsp.ready(function(){
//var dynamicAnchor = [ [ 0.2,1,0.5 ], [ 0.2, 1, 0.5 ], "Top", "Bottom" ];
var common = {
anchor:[ "Continuous", { faces:["bottom","right"] }],
endpoint: "Blank",
connector:[ "Bezier", { curviness:50 }, common ],
overlays: [
["Arrow", {location:1, width:10, length:10}],
]
};
jsp.connect({
source: boxes.b1.boxId,
target: boxes.b2.boxId
}, common);
}
The arrows are all moving to the left,top corner...
var jsp is global and I cleared _domnodeId at the beginning of my second function. Any suggestions?
clearing my domnodeID:
function clean(container){
//remove everything
$("#" + container)
.children()
.not('nav')
.remove();
// box id counter
window.EvmClasses.chartBox.boxId = 0;
}
I cleared _domnodeId at the beginning of my second function
How did you done that? It seems to me that you didn't clear it properly.
Did you read "Removing" section of the manual?
If you have configured a DOM element with jsPlumb in any way you
should use jsPlumb to remove the element from the DOM (as opposed to
using something like jQuery's remove function, for example).
Please read it thoroughly. You may need either jsPlumb.empty, deleteEveryEndpoint, or reset.

Add javascript to Wordpress loop with class selection

I would like to add category icons to a Wordpress page, each icon animated with snap.svg.
I added the div and inside an svg in the loop that prints the page (index.php). All divs are appearing with the right size of the svg, but blank.
The svg has a class that is targeted by the js file.
The js file is loaded and works fine by itself, but the animation appears only in the first div of that class, printed on each other as many times it is counted by the loop (how many posts there are on the actual page from that category).
I added "each()" and the beginning of the js, but is not allocating the animations on their proper places. I also tried to add double "each()" for the svg location and adding the snap object to svg too, but that was not working either.
I tried to add unique id to each svg with the post-id, but i could not pass the id from inside the loop to the js file. I went through many possible solutions I found here and else, but none were adaptable, because my php and js is too poor.
If you know how should I solve this, please answer me. Thank you!
// This is the js code (a little trimmed, because the path is long with many randoms, but everything else is there):
jQuery(document).ready(function(){
jQuery(".d-icon").each(function() {
var dicon = Snap(".d-icon");
var dfirepath = dicon.path("M250 377 C"+ ......+ z").attr({ id: "dfirepath", class: "dfire", fill: "none", });
function animpath(){ dfirepath.animate({ 'd':"M250 377 C"+(Math.floor(Math.random() * 20 + 271))+ .....+ z" }, 200, mina.linear);};
function setIntervalX(callback, delay, repetitions, complete) { var x = 0; var intervalID = window.setInterval(function () { callback(); if (++x === repetitions) { window.clearInterval(intervalID); complete();} }, delay); }
var dman = dicon.path("m136 ..... 0z").attr({ id: "dman", class:"dman", fill: "#222", transform: "r70", });
var dslip = dicon.path("m307 ..... 0z").attr({ id: "dslip", class:"dslip", fill: "#196ff1", transform:"s0 0"});
var dani1 = function() { dslip.animate({ transform: "s1 1"}, 500, dani2); }
var dani2 = function() { dman.animate({ transform: 'r0 ' + dman.getBBox().cx + ' ' + dman.getBBox(0).cy, opacity:"1" }, 500, dani3 ); }
var dani3 = function() { dslip.animate({ transform: "s0 0"}, 300); dman.animate({ transform: "s0 0"}, 300, dani4); }
var dani4 = function() { dfirepath.animate({fill: "#d62a2a"}, 30, dani5); }
var dani5 = function() { setIntervalX(animpath, 200, 10, dani6); }
var dani6 = function() { dfirepath.animate({fill: "#fff"}, 30); dman.animate({ transform: "s1 1"}, 100); }
dani1(); }); });
I guess your error is here:
var dicon = Snap(".d-icon");
You are passing a query selector to the Snap constructor, this means Snap always tries to get the first DOM element with that class, hence why you're getting the animations at the wrong place.
You can either correct that in two ways:
Declare width and height inside the constructor, for example var dicon = Snap(800, 600);
Since you are using jQuery you can access to the current element inside .each() with the $(this) keyword. Since you are using jQuery instead of the dollar you could use jQuery(this).
Please keep in mind this is a jQuery object and probably Snap will require a DOM object. In jQuery you can access the dom object by appending a [0] after the this keyword. If var dicon = Snap( jQuery(this) ); does not work you can try with var dicon = Snap( jQuery(this)[0] );
Additionally, you have several .attr({id : '...', in your code. I assume you are trying to associate to the paths an ID which are not unique. These should be relatively safe since they sit inside a SVG element and I don't see you are using those ID for future selection.
But if you have to select those at a later time I would suggest to append to these a numerical value so you wont have colliding ID names.

Categories