Identify Which Label was Clicked in jsPlumb - jsplumb

To manipulate labeled graphs, I think I need to add an additional property on each label of jsPlumb connections. For example, when I click on a connection label, even if source and target points can be deduced from label id (or attributes) I cannot know which type of edge was clicked.
Merely changing jsPlumb graph is not enough because there are underyling data structures represented by jsPlumb graphics. Codes using jsPlumb.ready and jsPlumb.bind are already well implemented and work fine.
var newConn = jsPlumb.connect({ source: from, target: to,
endpoint: epStyle, detachable: false,
anchor: "Continuous", connector: ["StateMachine", { curviness: 20}],
paintStyle: lineSt, hoverPaintStyle: lineHoverSt,
overlays: [
["Arrow", { width: 10, length: 10, foldback: 1, location: 0.25,
id: "arrow_" + from + to + 1}],
["Arrow", { width: 10, length: 10, foldback: 1, location: 0.75,
id: "arrow_" + from + to + 2}],
["Label",
{ label: relation.name, id: "label_" + from + to + rel_id, cssClass: "edgeLabel"}
// Any additional property here?
]
]
});
//Or add property here?
newConn.type = SOME_EDGE_TYPE;
jsPlumb.ready(function () {
jsPlumb.bind("click", function (c) {
jsPlumb.detach(c);
});
jsPlumb.bind("beforeDetach", function (conn) {
return confirm(
"Are you sure you want to disconnect ["
+ conn.sourceId + "=>" + conn.targetId + "]?");
});
});

You can bind an event jsPlumbConnection to get notified when a new connection is created:
jsPlumb.bind("jsPlumbConnection", function(ci) {
console.log(ci.connection);
console.log(ci.sourceId+" is connected to "+ci.targetId);
});
In order to manipulate jsPlumb connection refer API Document.
The ci(Connection Info.) object consists of following data:
connection - the newly established Connection.
source - the source element in the Connection
sourceEndpoint - the source Endpoint in the Connection.
sourceId - the ID of the source element.
target - the target element in the Connection.
targetEndpoint - the target Endpoint in the Connection.
targetId - the ID of the target element.

Related

Cant get a grouped kendo ui column chart to hide when some items in group don't have a value

I've been trying to get a telerik kendo ui column chart to display grouped data but where the groups might not have entries for all possible values and I don't want to show space/empty columns in these empty cases.
Telerik dojo of problem
Is anyone aware of anyway to get this to work more like the screenshot below
Excel has grouped the data but doesn't display a column at all if the data is null/zero
I couldn't find a built-in way to do this, so I ended up just placing the bars manually by overriding the visual function. In my case, I only needed to move one bar and that bar will always be the same category, which made it a whole lot easier in that I only had to identify it by matching the category. I could then move it with a transform. You cannot move it by setting the coordinates because the visual has already been created.
It would be more complex to do this dynamically, but it's certainly possible. This may give someone a start in the right direction.
One downside of this method is that you must also place the labels manually, which I have also done below. You can override the visual function of the labels, as well, but no references to any other elements are passed with the event data. Note how the documentation says the sender field may be undefined; in my experience, this is always the case.
It also does not move the tooltip or the highlight. You could use the same method to move the highlight (override the visual function, though on the series instead of the seriesDefaults) and draw the tooltip manually while moving the highlight -- similar to how the method below draws the label while moving the column.
Telerik Dojo Example
$(document).ready(function () {
$("#chart").kendoChart({
legend: { visible: false },
tooltip: { visible: false },
categoryAxis: {
name: "categoryAxis",
categories: ["1", "2", "3"],
},
series: [
{
data: [1, 2, 3],
highlight: { visible: false },
},
{
data: [1.5, null, 3.5],
highlight: { visible: false },
}
],
seriesDefaults: {
type: "column",
labels: { visible: false },
visual: function (e) {
if (e.value === null) return;
var visual = e.createVisual();
var axisRect = e.sender.getAxis("categoryAxis").slot("2");
var group = new kendo.drawing.Group();
var label = new kendo.drawing.Text(e.value, [0, 0], {
font: "20px sans-serif",
fill: { color: "black" }
});
var lbox = label.clippedBBox();
label.position([
e.rect.origin.x + e.rect.size.width / 2 - lbox.size.width / 2,
e.rect.origin.y - label.bbox().size.height * 1.5
]);
group.append(visual, label);
if (e.category === "2") {
var x = (axisRect.origin.x + axisRect.size.width / 2) - e.rect.size.width / 2;
group.transform(kendo.geometry.transform().translate(x - e.rect.origin.x, 0));
}
return group;
},
}
});
});

How to integrate KendoUI chart with SignalR

i want to create a real time chart, using Kendo ui chart and signalr. I see this example, but has no code. So i try alone.
A little demonstration of my code:
At first I created a kendo chart
function queueActivityChart() {
$("#queueActivityChart").kendoChart({
legend: {
visible: true
},
seriesDefaults: {
labels: {
visible: true,
format: "{0}",
background: "transparent"
}
},
series: [{
type: "line",
field: "Incoming",
categoryField: "DateTime",
}],
valueAxis: {
labels: {
format: "{0}"
},
line: {
visible: false
}
},
categoryAxis: {
labels:
{
rotation: -90,
dateFormats:
{
seconds: "ss",
minutes: "HH:mm:ss",
hours: "HH:mm",
days: "dd/MM",
months: "MMM 'yy",
years: "yyyy"
}
}, type: "Date", field: "DateTime", baseUnit: "seconds"
}
});
var chart = $("#queueActivityChart").data("kendoChart");
chart.options.transitions = false;
}
$(document).ready(queueActivityChart);
$(document).bind("kendo:skinChange", queueActivityChart);
Then I have this part of code, that get from server data
$scope.signalRData = [];
$scope.signalR.on('receiveCounters', function (data) {
$scope.queueData = data;
for (var i = 0; i < data.length; i++) {
$scope.signalRData.push(data[i]);
}
while ($scope.signalRData.length > 12) {
$scope.signalRData.splice(0, 1);
}
$("#queueActivityChart").data("kendoChart").setDataSource(
new kendo.data.DataSource({
group: {
field: "Name"
},
data: $scope.signalRData
}));
});
This works! And I get a picture of the latest updated items.
But the problem is that this chart is like to put one picture in front of other. I mean that this is the first time that load Data Source; that creates a chart of my data, the second time my data has changed, some values are still in my array some others has move out, the third too.
It seems like it puts a picture of my current data in front of the
previous data. It's not smoothie and cannot use chart's legend
property because I initialize my Data Source everytime.
Can someone help me how can create a smoothie kendo chart with real time data like the kendo official example? Also can somehow to add scroller to bottom?
I looked at the code for the benchmark and I think you may be missing in your chart which is renderAs: "canvas"
Also, in the example, the data is kept locally (saved) and then moved so it creates that "smooth" effect you may be talking about.
Here is the code that you can be of interest:
function step() {
addPoint();
$("#chart").data("kendoChart").refresh();
frames++;
if (playing) {
kendo.animationFrame(step);
}
}
function addPoint() {
var stockData,
change,
lastValue;
// Shift existing categories to the left and add the next date at the end
lastDate = new Date(lastDate.getTime() + TICKS_PER_DAY);
categoryList.push((lastDate.getMonth() + 1) + "/" + (lastDate.getDay() + 1));
if (categoryList.length > POINTS) {
categoryList.shift();
}
for (var i = 0; i < stocks.length; i++) {
stockData = stocks[i];
change = (Math.random() > 0.5 ? 1 : - 1) * Math.random() * 10;
lastValue = stockData[stockData.length - 1] || Math.random() * 10;
// Add a new pseudo-random data point
stockData.push(Math.min((i + 1) * 20, Math.max((i + 1) * 10, lastValue + change)));
// Shift the data points of each series to the left
if (stockData.length > POINTS) {
stockData.shift();
}
}
}
Check out the source code of your example for the full source code and use the dojo to test our their code and play around with it easily

JsPlumb Dynamic Endpoints as Connection Overlays

I am trying to create dynamic endpoints as overlays on my connections and running into issues. I am trying to model what this person has here on SO:
jsPlumb connecting custom overlays - endpoint not moved
However, no matter what I try to do when I get to this point:
var overlay_div = $(connection.overlays[0].canvas);
I cannot get the connection to be recognized. I've tried to put this logic in the bind connection but that didn't work either when trying to establish the connection overlay. Any assistance on this would be extremely helpful.
http://jsfiddle.net/nitincool4urchat/c3b514wf/14/
First, Create custom elements as overlays
Second, Make sure that these elements have unique IDs
Third, Bind to the connection event to create endPoints on these custom-overlays.
jsPlumb.ready(function() {
// setup some defaults for jsPlumb.
instance = jsPlumb.getInstance({
Endpoint : ["Dot", {radius:2}],
HoverPaintStyle : {strokeStyle:"#1e8151", lineWidth:4 },
ConnectionOverlays : [
[ "Arrow", {
location:1,
id:"arrow",
length:14,
foldback:0.8
}]
,["Custom", {
create: function(component) {
return connectionNode();
},
location:0.5
}]
//,[ "Label", { label:"Connect To", id:"label", cssClass:"aLabel" }]
],
Container: "flowchart-demo"
});
var relationEndpoint = {
endpoint: ["Dot", { radius: 2 }],
isSource: true,
connector: ["Flowchart", { stub: [40, 60], cornerRadius: 5, alwaysRespectStubs: true }],
connectorStyle: { strokeStyle: "#5c96bc", lineWidth: 4, outlineColor: "transparent", outlineWidth: 4 },
maxConnections: 5,
onMaxConnections: function (info, e) {
alert("Maximum connections (" + info.maxConnections + ") reached");
},
isTarget: true,
dropOptions: {
tolerance: "touch",
hoverClass: "dropHover",
activeClass: "dragActive"
},
beforeDetach: function (conn) {
return confirm("Detach connection?");
}
};
function connectionNode() {
//var overlay_div = $(connection.ConnectionOverlays[0].canvas);
//jsPlumb.addEndpoint({ anchor: ["Perimeter", { shape: "Rectangle" }] }, relationEndpoint);
return $("<div>Custom</div>",{id:Date.now()}).css({border:'1px solid black',background:'green'});
}
var windows = jsPlumb.getSelector(".flowchart-demo .window");
instance.draggable(windows);
instance.bind("connection", function(info) {
console.dir(info.connection);
console.dir(info.connection.getOverlays());
console.dir(info.connection.getOverlays()[1].canvas);
var overlay_div = $(info.connection.getOverlays()[1].canvas);
jsPlumb.addEndpoint(overlay_div,{ anchor: ["Perimeter", { shape: "Rectangle" }] }, relationEndpoint);
});
// suspend drawing and initialise.
instance.doWhileSuspended(function() {
var isFilterSupported = instance.isDragFilterSupported();
// make each ".ep" div a source and give it some parameters to work with. here we tell it
// to use a Continuous anchor and the StateMachine connectors, and also we give it the
// connector's paint style. note that in this demo the strokeStyle is dynamically generated,
// which prevents us from just setting a jsPlumb.Defaults.PaintStyle. but that is what i
// would recommend you do. Note also here that we use the 'filter' option to tell jsPlumb
// which parts of the element should actually respond to a drag start.
// here we test the capabilities of the library, to see if we
// can provide a `filter` (our preference, support by vanilla
// jsPlumb and the jQuery version), or if that is not supported,
// a `parent` (YUI and MooTools). I want to make it perfectly
// clear that `filter` is better. Use filter when you can.
if (isFilterSupported) {
instance.makeSource(windows, {
filter:".ep",
anchor:"Continuous",
connector: ["Flowchart", { stub: [40, 60], cornerRadius: 5, alwaysRespectStubs: true }],
connectorStyle:{ strokeStyle:"#5c96bc", lineWidth:4, outlineColor:"transparent", outlineWidth:4 },
maxConnections:5,
onMaxConnections:function(info, e) {
alert("Maximum connections (" + info.maxConnections + ") reached");
}
});
}
else {
var eps = jsPlumb.getSelector(".ep");
for (var i = 0; i < eps.length; i++) {
var e = eps[i], p = e.parentNode;
instance.makeSource(e, {
parent:p,
anchor:"Continuous",
connector: ["Flowchart", { stub: [40, 60], cornerRadius: 5, alwaysRespectStubs: true }],
connectorStyle:{ strokeStyle:"#5c96bc",lineWidth:4, outlineColor:"transparent", outlineWidth:4 },
maxConnections:5,
onMaxConnections:function(info, e) {
alert("Maximum connections (" + info.maxConnections + ") reached");
}
});
}
}
});
// initialise all '.w' elements as connection targets.
instance.makeTarget(windows, {
dropOptions:{ hoverClass:"dragHover" },
anchor:"Continuous",
allowLoopback:true,
anchor:"Continuous"
});
jsPlumb.fire("jsPlumbDemoLoaded", instance);
});

Multiple calls to xively.feed.history()

I am trying to build a plotting webpage which gets data from multiple xively feeds and plots them with Rickshaw (based on D3). The problem I have is that I can't access the data which is returned from the server outside of the calling function. I'm new to js so I'm not sure how to deal with the asynchronous nature of the calls. I would like to call the xively.feed.history() to get various sections of historical data and them concatenate them onto a single graph. Anyway, here is something similar to what I've tried. It based on the xively tutorial
xively.setKey('xxxx my key ')
// Replace with your own values
var feedID = 12345678, // Feed ID
selector = "#myelement"; // Your element on the page
var series = [];
xively.feed.history(feedID, { duration: "10days", interval: 1800 ,limit: 1000}, function (mydata) {
mydata.datastreams.forEach( function(stream){
if ((stream.id == "tempinside") || (stream.id == "tempoutside")) {
var points = [];
stream.datapoints.forEach( function(datapoint){
points.push({x: new Date(datapoint.at).getTime()/1000.0, y: parseFloat(datapoint.value)});
// Add Datapoints Array to Graph Series Array
});
series.push({
name: stream.id,
data: points
});
};
});
var graph2 = new Rickshaw.Graph( {
element: document.querySelector("#chart2"),
width: 600,
height: 400,
renderer: 'line',
series: [{
data: series[0].data,
name: series[0].name,
color: 'red'
},{
data: series[1].data,
name: series[1].name,
color: 'blue'
}]
})
})
As I said, I'm new to javascript so maybe I'm missing something. The graph works fine if it is placed inside the callback part of the xively.feed.history function. If I try to make multiple calls to the function then the data isn't ready by the time the graph code runs.
Ok, so I made it work and I've put a subset of it here so others can see how I did it.
var starttime = moment().subtract("day", 3);
series['tempoutside']=[
{x:starttime.subtract("second",2).unix(),y:15},
{x:starttime.subtract("second",1).unix(),y:15}
];
graph = new Rickshaw.Graph({
element: document.querySelector("#chart"),
height: 400,
width: 600,
renderer: 'line',
interpolation: 'step-before',
series: new Rickshaw.Series([
{
name: 'temperature',
data: series['tempoutside'],
color: 'blue',
}
], series)
});
xively.datastream.history( "123456789", "tempoutside", query, loadData);
function loadData(data) {
//console.log('Getting ' + data.id + 'data from Xively');
if (data.datapoints != undefined){
//console.log('received ' + data.datapoints.length +' datapoints');
lastdata=data;
var filtedData = data.datapoints.filter(function(x) { return (x.value >0.005); });
for (var i=0; i < filtedData.length; i++ ) {
var date = moment(filtedData[i].at);
var value = parseFloat(filtedData[i].value+0.000001);
series[data.id].push({x: date.unix(), y: value});
}
graph.render();
}
}
In my final code I also removed the dummy initialisation values from the series['tempoutside'] array with .pop(). I needed them in the array because the graph gets built first off before the data has been returned from xively. When the data does return, the xively.datastream.history() function calls the loaddata() callback which pushes the data into the series array. Also, I'd not understood that you don't need to push data into the graph series array, because the array is passed as a reference when the graph is built. Update the array and then call graph.render() and it replots it all. Win! This means that multiple calls can be made to Xively to get more data than is available with one API call (limited to 1000 data points). Remember to sort the data by x value before plotting....

DOJO onclick on a pie chart slice for drill down

I tried to find several places how this work but could not.
The requirement is to drill down by clicking on the slice of the pie to next level. I can get the onclick even but not sure how to get the value from the chart. Everywhere it is pointing to http://www.sitepen.com/blog/2008/05/27/dojo-charting-event-support-has-landed/ but nowhere any live demo is given. Till now i have managed to get the onclick.
chart.addSeries("Monthly Sales - 2010", chartData);
var h = chart.connectToPlot("default", function(o){
if(o.type == "onclick"){
alert("clicked!");
}
});
var store = new dojo.store.Memory({data: [
{ id: '2', value: 10, usedForDrillDown:'x' },
{ id: '3', value: 5, usedForDrillDown: 'y' },
{ id: '4', value: 8, usedForDrillDown:'z' }
]});
// adapter needed, because the chart uses the dojo.data API
var storeAdapter = new dojo.data.ObjectStore({
objectStore: store
});
var ds = new dojox.charting.DataSeries(
storeAdapter/*, { query: { needed if the store contains more than data points } }*/);
var chart = new dojox.charting.Chart("chart");
chart.addPlot("default", { type: "Pie" });
chart.addSeries("default", ds);
chart.connectToPlot("default", function(evt) {
if(evt.type == "onclick"){
var itm = evt.run.source.items[evt.index];
console.dir(itm);
}
});
chart.render();

Resources