jsPlumb: Avoiding multiple connectors between divs - jsplumb

Experts,
I'm using jsPlumb to connect divs with multiple endpoints. I'm trying to prevent multiple connectors between the same two divs. So for example, if I have divs A, B, C, connectors between A and B, A and C are ok but not two connectors between A and B (using different endpoints).
Does anyone know how to do this?
Thanks!

It depends a lot of your code, but you can do something like this:
var exampleEndpoint = {
reattach:true,
scope:"yourScope",
isSource: true,
isTarget:true,
beforeDrop:function(params) { return !checkExistingLinks(params.sourceId,params.targetId); }
};
function checkExistingLinks(sourceId, targetId) {
var flag = false;
jsPlumb.select({source: sourceId}).each(function(connection) {
if(connection.targetId === targetId) {
flag = true;
}
});
return flag;
}
That is, you need to identify a connection attempt and then verify if the elements involved already have a connection. jsPlumb.select() returns for you a list (or map) of this connections.

Related

Should the opaque cursors in connections be stable across different field args?

The RANGE_ADD mutation requires an edgeName so that it can insert the new edge into the client side connection. As part of its query, it also includes the cursor.
The issue is that the server has no way of knowing which args the client might be applying to a connection when it's generating the edge response.
Does this mean that the cursor should be stable?
In general, cursors are not required to be the same when connections are used with different arguments. For example, if I did:
{
namedFriends: friends(orderby:NAME first:5) {
edges { cursor, node { id } }
}
favoriteFriends: friends(orderby:FAVORITE first:5) {
edges { cursor, node { id } }
}
}
Different backends might be use to server those two connections, since we might have different backends for the two orderings; because of that, the cursors might be different for the same friend, because they might need to encode different information for the different backends.
This makes it tricky when performing a mutation, though:
mutation M {
addFriend($input) {
newFriendsEdge {
{ cursor, node { id } } // Which cursor is this?
}
}
}
In cases like this, where the mutation is going to return an edge from a connection, it's useful for the field to accept the same non-pagination arguments that the connection does. So in the above case, we would do:
mutation M {
addFriend($input) {
newNamedFriendsEdge: newFriendsEdge(orderby:NAME) {
{ cursor, node { id } } // Cursor for namedFriends
}
newFavoriteFriendsEdge: newFriendsEdge(orderby:FAVORITE) {
{ cursor, node { id } } // Cursor for favoriteFriends
}
}
}
And ideally, the implementation for newFriendsEdge(orderby:FAVORITE) and favoriteFriends: friends(orderby:FAVORITE first:5) share common code to generate cursors.
Note that while the cursors are not required to be the same, it's fine if they are, as an implementation detail of the server. Often, the cursor is just the ID of the node, which is a common way for this to happen. In practice, in these situations, if a argument on the connections doesn't affect the cursor, we would omit it from the mutation's edge field; so if orderby didn't affect the cursor, then:
mutation M {
addFriend($input) {
newFriendsEdge {
{ cursor, node { id } } // orderby didn't exist on newFriendsEdge, so this cursor must apply to both.
}
}
}
This is the common pattern in our mutations. Let me know if you run into any issues; we thought through the "arguments change cursors" case when developing the pattern of returning edges on mutations to make sure there was a possible solution to it (which is when we came up with the arguments on edge fields idea), but it hasn't come up in practice all that much, so if you run into trickiness definitely let me know, and we can and should revisit these assumptions / requirements!

Get room/rooms of client [duplicate]

I can get room's clients list with this code in socket.io 0.9.
io.sockets.clients(roomName)
How can I do this in socket.io 1.0?
Consider this rather more complete answer linked in a comment above on the question: https://stackoverflow.com/a/24425207/1449799
The clients in a room can be found at
io.nsps[yourNamespace].adapter.rooms[roomName]
This is an associative array with keys that are socket ids. In our case, we wanted to know the number of clients in a room, so we did Object.keys(io.nsps[yourNamespace].adapter.rooms[roomName]).length
In case you haven't seen/used namespaces (like this guy[me]), you can learn about them here http://socket.io/docs/rooms-and-namespaces/ (importantly: the default namespace is '/')
Updated (esp. for #Zettam):
checkout this repo to see this working: https://github.com/thegreatmichael/socket-io-clients
Using #ryan_Hdot link, I made a small temporary function in my code, which avoids maintaining a patch. Here it is :
function getClient(roomId) {
var res = [],
room = io.sockets.adapter.rooms[roomId];
if (room) {
for (var id in room) {
res.push(io.sockets.adapter.nsp.connected[id]);
}
}
return res;
}
If using a namespace :
function getClient (ns, id) {
return io.nsps[ns].adapter.rooms[id]
}
Which I use as a temporary fix for io.sockets.clients(roomId) which becomes findClientsSocketByRoomId(roomId).
EDIT :
Most of the time it is worth considering avoiding using this method if possible.
What I do now is that I usually put a client in it's own room (ie. in a room whose name is it's clientID). I found the code more readable that way, and I don't have to rely on this workaround anymore.
Also, I haven't tested this with a Redis adapter.
If you have to, also see this related question if you are using namespaces.
For those of you using namespaces I made a function too that can handle different namespaces. It's quite the same as the answer of nha.
function get_users_by_room(nsp, room) {
var users = []
for (var id in io.of(nsp).adapter.rooms[room]) {
users.push(io.of(nsp).adapter.nsp.connected[id]);
};
return users;
};
As of at least 1.4.5 nha’s method doesn’t work anymore either, and there is still no public api for getting clients in a room. Here is what works for me.
io.sockets.adapter.rooms[roomId] returns an object that has two properties, sockets, and length. The first is another object that has socketId’s for keys, and boolean’s as the values:
Room {
sockets:
{ '/#vQh0q0gVKgtLGIQGAAAB': true,
'/#p9Z7l6UeYwhBQkdoAAAD': true },
length: 2 }
So my code to get clients looks like this:
var sioRoom = io.sockets.adapter.rooms[roomId];
if( sioRoom ) {
Object.keys(sioRoom.sockets).forEach( function(socketId){
console.log("sioRoom client socket Id: " + socketId );
});
}
You can see this github pull request for discussion on the topic, however, it seems as though that functionality has been stripped from the 1.0 pre release candidate for SocketIO.

Sequelize correctly executing multiple creates + updates

I have a cron job that scrapes a list of items on a website and then inserts or updates records in a database. When I scrape the page, I want to create records for new ones that haven't been created yet, otherwise update any existing ones. Currently I'm doing something like this:
// pretend there is a "Widget" model defined
function createOrUpdateWidget(widgetConfig) {
return Widget.find(widgetConfig.id)
.then(function(widget) {
if (widget === null) {
return Widget.create(widgetConfig);
}
else {
widget.updateAttributes(widgetConfig);
}
});
}
function createOrUpdateWidgets(widgetConfigObjects) {
var promises = [];
widgetConfigObjects.forEach(function(widgetConfig) {
promises.push(createOrUpdateWidget(widgetConfig));
});
return Sequelize.Promise.all(promises);
}
createOrUpdateWidgets([...])
.done(function() {
console.log('Done!');
});
This seems to work fine, but I'm not sure if I'm doing this "correctly" or not. Do all promises that perform DB interactions need to run serially, or is how I have them defined ok? Is there a better way to do this kind of thing?
What you're doing is pretty idiomatic and perfectly fine, the only room for improvement is to utilize the fact Sequelize uses Bluebird for promises so you get .map for free, which lets you convert:
function createOrUpdateWidgets(widgetConfigObjects) {
var promises = [];
widgetConfigObjects.forEach(function(widgetConfig) {
promises.push(createOrUpdateWidget(widgetConfig));
});
return Sequelize.Promise.all(promises);
}
Into:
function createOrUpdateWidgets(widgetConfigObjects) {
return Sequelize.Promise.map(widgetConfig, createOrUpdateWidget)
}
Other than that minor improvement - you're chaining promises correctly and seem to have the correct hang of it.

GEF Shapes Example: How can I change the TargetDecoration for Connections?

In the GEF shapes example, there are two types of connections, they differ by line style only (int Graphics.LINE_DASH or int Graphics.LINE_SOLID).
Now I wanna change the TargetDecoration (or rather set it to null) for one of them. How do I do this?
I've tried an if in the below method (below is the original), but it didn't have any effect. I tried if (getCastedModel().getLineStyle() != Graphics.LINE_DASH) connections.setTargetDecoration(null) and left he rest as it was.
protected IFigure createFigure() {
PolylineConnection connection = (PolylineConnection) super
.createFigure();
connection.setTargetDecoration(new PolygonDecoration()); // arrow at
// target
// endpoint
connection.setLineStyle(getCastedModel().getLineStyle()); // line
// style
return connection;
}
Thanks!
The following code worked (changed the last two lines before the return):
protected IFigure createFigure() {
PolylineConnection connection = (PolylineConnection) super.createFigure();
connection.setTargetDecoration(new PolygonDecoration());
connection.setLineStyle(getCastedModel().getLineStyle());
if(getCastedModel().getLineStyle() == Graphics.LINE_DASH) {
connection.setTargetDecoration(null);
}
return connection;
}
This is the result:
Strange that this didn't work for you...

YUI onDragDrop multiple listeners

I was wondering if anyone was aware of an example that shows multiple listeners to the YUI DDProxy, DD, or DDTarget onDragDrop event. Currently I have a hacked up example going. I'm using the http://developer.yahoo.com/yui/examples/datatable/dt_ddrows.html'>DataTable Reorder Rows example. I've modified it into several js classes. One object they override DDProxy for handles the onDragDrop event but it seems that if it's handled here then nothing else catches the event. Check the snip:
YAHOO.extend(YAHOO.example.DDRows, YAHOO.util.DDProxy, {
//snip
onDragDrop: function(e, id) {
if (id == "right-function-pane") {
alert("In the right drop place");
YAHOO.util.DragDropMgr.refreshCache();
}
}
In a class that does the creation of DDRows, I want to listen to the event here as well. Some actions are more appropriate in some places. I want to listen to the event in both places. Here's what I'm doing in class that builds the above:
onDropHandler: function(e, id) {
DragDropTable.myDataTable.deleteRow(YAHOO.util.DragDropMgr.dragCurrent.id);
},
rowInitializer: function() {
var i, id, myDDRow,
allRows = DragDropTable.myDataTable.getTbodyEl().rows;
for (i = 0; i < allRows.length; i++) {
id = allRows[i].id;
// Clean up any existing Drag instances
if (DragDropTable.myDTDrags[id]) {
DragDropTable.myDTDrags[id].unreg();
delete DragDropTable.myDTDrags[id];
}
// Create a Drag instance for each row
myDDRow = new YAHOO.example.DDRows(id);
myDDRow.srcData = DragDropTable.myDataTable.getRecord(id).getData();
myDDRow.onDragDrop = DragDropTable.onDropHandler;
DragDropTable.myDTDrags[id] = myDDRow;
}
}
It seems like if one is listening the other isn't working. I haven't found the syntax for allowing events to continue to bubble or to have multiple subscriptions to onDragDrop. Does anyone have the correct syntax?
Thanks
Josh Robinson

Resources