How to expand node with Sigmajs and Neo4j database (doubleclick Mouse event)? - javascript

I'm using the Sigma.js library to create a webapp on top of a Neo4j graph database.
So, I have my graph with nodes and edges and I would like to expand nodes by double click event with javascript.
Can you help me to do this?
Thank you so much
sigma.neo4j.cypher(
{url :'http://localhost:7474', user: 'neo4j', password: 'neo4j'},
'MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 100' ,
{container:'graph-container'},
function (s) {
s.graph.nodes().forEach(function (node){
if (node.neo4j_labels[0] === "Movies"){
node.label = node.neo4j_data['movie'];
node.color = '#68BDF6';
node.size = 26;
node.type = "star";
}
else if (node.neo4j_labels[0] === "Personn"){
node.label = node.neo4j_data['personn'];
node.type = "diamond";
node.color = '#303A2B';
node.size = 12;
}
console.log(s.graph.nodes());
s.settings('touchEnabled', true);
s.settings('mouseEnabled', true);
s.settings('defaultEdgeLabelSize', 18);
s.settings('defaultNodeLabelSize', 18);
s.settings('animationsTime', 1000);
s.graph.edges().forEach(function(e) {
//e.type = 'curvedArrow';
//e.color='#F00';
e.originalColor = e.color;
e.type = "curvedArrow";
e.size=2;
// e.label = neo4j_type;
//console.log(s.graph.edges())
});
s.refresh();
});

You have to bind the sigma event doubleClickNode like this :
s.bind("doubleClickNode", function(e) {
var node = e.data.node;
var query = 'MATCH (n)-[r]-(m) WHERE id(n)=' + node.id+ ' RETURN n,r,m';
sigma.neo4j.cypher(
neo4jConfig,
query,
function(graph) {
// adding node if not existing
graph.nodes.forEach(function (node){
if(s.graph.nodes(node.id) === undefined) {
s.graph.addNode(node);
}
});
// adding edge if not existing
graph.edges.forEach(function (edge){
if(s.graph.edges(edge.id) === undefined) {
s.graph.addEdge(edge);
}
});
// apply style
applyStyle(s);
}
);
});
My complete example is available here : https://jsfiddle.net/sim51/qkc0g58o/34/
Before to use it, you need to accept Neo4j SSL certificate, by opening this link https://localhost:7473/browser and adding a security exception

Related

Paste from clipboard in overwrite mode - input cursor moves to the end of text node

Our target browser is IE8 as the application is a legacy one and has got some com dependency.We are showing content inside a content-editable div. One of the requirements is to be able to replace texts inside the div when the browser is in "overwrite" mode. Paste is working fine but the input cursor is always moving to the end after the paste. We are using rangy-core, version: 1.3.1 for range/selection related logic. Unable to figure out what is going wrong here. Need help.
The following code is called when the document is loaded:
$("#info").on("paste", function (e) {
var clipboardData = window.clipboardData.getData("Text");
clipboardData = clipboardData.replace(/(^ *)|(\r\n|\n|\r)/gm, "");
if (isOverwriteEnabled()) {
e.preventDefault ? e.preventDefault() : e.returnValue = false;
pasteCopiedData(clipboardData);
}
});
Related code snippets for reference:
function isOverwriteEnabled() {
try {
// try the MSIE way
return document.queryCommandValue("OverWrite");
} catch (ex) {
// not MSIE => not supported
return false;
}
}
function pasteCopiedData(clipboardData) {
var json = getCurrentNodeWithOffset();
handleTextOverwrite(json, clipboardData);
}
function getCurrentNodeWithOffset() {
var json = {};
var selectedObj = rangy.getSelection();
var range = selectedObj.getRangeAt(0);
json.node = selectedObj.anchorNode.nodeType === 3 ? selectedObj.anchorNode : findImmediateTextNode(selectedObj.anchorNode, range.startOffset);
json.offset = selectedObj.anchorNode.nodeType === 3 ? range.startOffset : json.node.nodeValue.length - 1;
return json;
}
function handleTextOverwrite(json, textToReplace) {
var lenToCopy = textToReplace.length;
var offsetPos = json.offset;
var jsonNode = json.node;
try {
while (lenToCopy > 0) {
var toCopy = jsonNode.nodeValue.length - offsetPos;
var startPos = textToReplace.length - lenToCopy;
if (lenToCopy <= toCopy) {
json.node.nodeValue = jsonNode.nodeValue.substr(0, offsetPos) + textToReplace.substr(startPos) + jsonNode.nodeValue.substr(offsetPos + lenToCopy);
lenToCopy = 0;
}
else {
var copiedPos = startPos + toCopy;
jsonNode.nodeValue = jsonNode.nodeValue.substr(0, offsetPos) + textToReplace.substr(startPos, toCopy);
lenToCopy -= copiedPos;
var nextJsonNode = findNextTextNode(jsonNode);
if (!nextJsonNode) {
//do the remaining paste in jsonNode
jsonNode.nodeValue = jsonNode.nodeValue + textToReplace.substr(copiedPos);
lenToCopy = 0;
}
else {
jsonNode = nextJsonNode;
}
}
offsetPos = 0;
}
setCaret(json.node, json.offset);
}
catch (ex) {
}
}
function setCaret(node, pos) {
var el = document.getElementById("info");
var rangyRange = rangy.createRange(el);
var sel = rangy.getSelection();
rangyRange.setStart(node, pos + 1);
rangyRange.setEnd(node, pos + 1);
rangyRange.collapse(true);
sel.removeAllRanges();
sel.addRange(rangyRange);
}
Please let me know if more information is required.

object:removed Event Not Fired - Fabric JS

I am facing a weird issue with fabric events. Take a look at this snippet
canvas.on('object:added', function(e) {
console.log(e.target.type);
console.log("Something was Added");
});
canvas.on('object:removed', function(e) {
console.log(e.target.type);
console.log("Something was removed");
});
Given this code base I am experimenting on an undo / redo functionality. Given both undo & redo can add , modify or remove an object I would like to be notified if something was added or removed in the canvas (I am not much worried about object modified at this stage).
But strange enough no matter if an object is added or removed from the canvas using the undo / redo functionality. I always get the output - Something was Added
Undo / Redo Routines:
// Undo Redo Clear
canvas.counter = 0;
var newleft = 0;
canvas.selection = false;
var state = [];
var mods = 0;
canvas.on(
'object:modified', function () {
updateModifications(true);
},
'object:added', function () {
updateModifications(true);
},
'object:removed' , function(e){
updateModifications(true);
console.log('test me');
});
function updateModifications(savehistory) {
if (savehistory === true) {
myjson = JSON.stringify(canvas);
state.push(myjson);
console.log(myjson);
}
}
undo = function undo() {
if (mods < state.length) {
canvas.clear().renderAll();
canvas.loadFromJSON(state[state.length - 1 - mods - 1]);
canvas.renderAll();
mods += 1;
//check_team();
//compare_states(state[state.length - 1 - mods - 1] , state[state.length - 1 - mods + 1])
}
//make_objects_selectable();
}
redo = function redo() {
if (mods > 0) {
canvas.clear().renderAll();
canvas.loadFromJSON(state[state.length - 1 - mods + 1]);
canvas.renderAll();
mods -= 1;
//check_team();
}
//make_objects_selectable();
}
clearcan = function clearcan() {
canvas.clear().renderAll();
newleft = 0;
}
Fabric version:"1.6.0-rc.1"
Update: The Event is working fine in case of a normal delete action. Hence I added the Undo and Redo Routines.
Regards
Both your undo and redo functions do basically the same thing, erase canvas, load a new state and render it. When you clear the canvas, there is no object:removed event, but another event is fired, called canvas:cleared. That is why you never see your object:removed event fired when doing undo/redo. On the other hand, you do see object:added fired on both undo and redo, because I am guessing that canvas.renderAll adds every object on the current state into the canvas (since it was previously removed with canvas.clear()).
EDIT
A better solution is to store every action that happens on canvas, like add, modify or remove, and have each action associated with some object data. For example, you could have an object_added action associated with a serialization of the added object, or an object_removed action associated with a serialization of the removed object. For object_modified you would need two associated object serializations, one prior modification and one after modification. In case of a canvas_cleared action, you would have to store the whole canvas state as associative data.
A simple stack structure can work great for the purpose of action storage.
function SimpleStackException(msg) {
this.message = msg;
this.name = 'SimpleStackException';
}
function SimpleStack() {
var MAX_ENTRIES = 2048;
var self = this;
self.sp = -1; // stack pointer
self.entries = []; // stack heap
self.push = function(newEntry) {
if (self.sp > MAX_ENTRIES - 1) {
throw new SimpleStackException('Can not push on a full stack.');
}
self.sp++;
self.entries[self.sp] = newEntry;
// make sure to clear the "future" stack after a push occurs
self.entries.splice(self.sp + 1, self.entries.length);
};
self.pop = function() {
if (self.sp < 0) {
throw new SimpleStackException('Can not pop from an empty stack.');
}
var entry = self.entries[self.sp];
self.sp--;
return entry;
};
self.reversePop = function() {
self.sp++;
if (!self.entries[self.sp]) {
self.sp--;
throw new SimpleStackException('Can not reverse pop an entry that has never been created.');
}
return self.entries[self.sp];
}
}
Go ahead and create such a structure:
var actionHistory = new SimpleStack();
Another feature you will need for the action-based undo/redo to work, is the ability to "reference" objects in the canvas. In fabric.js you can reference objects from canvas.getObjects(), but that is a plain js array and does not help much. I have added object IDs, in the form of UUID.
Here is a function (taken somewhere in SO, dont have the link now) tha generates UUIDs
var lut = [];
for (var i = 0; i < 256; i++) {
lut[i] = (i < 16 ? '0' : '') + (i).toString(16);
}
function generateUuid() {
var d0 = Math.random() * 0xffffffff | 0;
var d1 = Math.random() * 0xffffffff | 0;
var d2 = Math.random() * 0xffffffff | 0;
var d3 = Math.random() * 0xffffffff | 0;
return lut[d0 & 0xff] + lut[d0 >> 8 & 0xff] + lut[d0 >> 16 & 0xff] + lut[d0 >> 24 & 0xff] + '-' +
lut[d1 & 0xff] + lut[d1 >> 8 & 0xff] + '-' + lut[d1 >> 16 & 0x0f | 0x40] + lut[d1 >> 24 & 0xff] + '-' +
lut[d2 & 0x3f | 0x80] + lut[d2 >> 8 & 0xff] + '-' + lut[d2 >> 16 & 0xff] + lut[d2 >> 24 & 0xff] +
lut[d3 & 0xff] + lut[d3 >> 8 & 0xff] + lut[d3 >> 16 & 0xff] + lut[d3 >> 24 & 0xff];
}
In order for fabric objects to have a new uuid property you need to add it to the object prototype, and to the object serialization method as well
fabric.Object.prototype.uuid = "";
fabric.Object.prototype.toObject = (function(toObject) {
return function() {
return fabric.util.object.extend(toObject.call(this), {
uuid: this.uuid,
});
};
})(fabric.Object.prototype.toObject);
Finally you need a function to "reference" objects via this uuid property.
function getFabricObjectByUuid(uuid) {
var fabricObject = null;
canvas.getObjects().forEach(function(object) {
if (object.uuid === uuid) {
fabricObject = object;
}
});
return fabricObject;
}
Now you need to listen for events on the canvas, and update the actionHistory accordingly:
canvas.on('path:created', function(path) {
var object = path.path;
object.uuid = generateUuid();
actionHistory.push({
type: 'object_added',
object: JSON.stringify(object)
});
});
canvas.on('object:added', function(e) {
var object = e.target;
// bypass the event for path objects, as they are handled by `path:created`
if (object.type === 'path') {
return;
}
// if the object has not been given an uuid, that means it is a fresh object created by this client
if (!object.uuid) {
object.uuid = generateUuid();
}
if (!object.bypassHistory) {
actionHistory.push({
type: 'object_added',
object: JSON.stringify(object)
});
}
});
canvas.on('object:modified', function(e) {
var object = e.target;
actionHistory.push({
type: 'object_modified',
objectOld: JSON.stringify(latestTouchedObject),
objectNew: JSON.stringify(object)
});
});
canvas.on('text:changed', function(e) {
var object = e.target;
actionHistory.push({
type: 'text_changed',
objectOld: JSON.stringify(latestTouchedObject),
objectNew: JSON.stringify(object)
});
});
canvas.on('object:removed', function(e) {
var object = e.target;
if (!object.bypassHistory) {
actionHistory.push({
type: 'object_removed',
object: JSON.stringify(object)
});
}
});
canvas.on('canvas:cleared', function(e) {
if (!canvas.bypassHistory) {
actionHistory.push({
type: 'canvas_cleared',
canvas: JSON.stringify(canvas)
});
}
});
Check out each event handler carefully to understand the actual data that will be stored on actionHistory. Also be mindful when the uuid property is actually added to an object. There are two things to note about the above snippet.
bypassHistory is a custom property of canvas objects and the canvas itself. You only want to store actions that are willingly performed by a user onto the canvas. If a user hand-draws a line, you want to save that action, and you do so by listening to path:cleared. However, in the case of a programmatically drawn line (eg. when performing a redo), you may not want to store the action. To add this custom property do as follows:
fabric.Object.prototype.bypassHistory = false; // default value false
object_modified is a special action because it needs to store two object representations: before and after modification. While the "after" version is obtained easily via event.target of the object:modified event, the "before" version has to be tracked programmatically. In my solution i have a high level latestTouchedObject variable that keeps track of the latest modified object on the canvas.
canvas.on('mouse:down', function(options) {
if (options.target) {
latestTouchedObject = fabric.util.object.clone(options.target);
}
});
Now that the action storage and all listeners have been setup, it's time to implement the undo and redo functions
function undoAction() {
var action, objectCandidate;
try {
action = actionHistory.pop();
} catch (e) {
console.log(e.message);
return;
}
if (action.type === 'object_added') {
objectCandidate = JSON.parse(action.object);
var object = getFabricObjectByUuid(objectCandidate.uuid);
object.bypassHistory = true;
canvas.remove(object);
} else if (action.type === 'object_removed') {
objectCandidate = JSON.parse(action.object);
fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
actualObjects[0].uuid = objectCandidate.uuid;
var object = actualObjects[0];
object.bypassHistory = true;
canvas.add(object);
object.bypassHistory = false;
});
} else if (action.type === 'object_modified' || action.type === 'text_changed') {
objectCandidate = JSON.parse(action.objectOld);
fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
actualObjects[0].uuid = objectCandidate.uuid;
var object = actualObjects[0];
var existingObject = getFabricObjectByUuid(objectCandidate.uuid);
if (existingObject) {
existingObject.bypassRemoveEvent = true;
existingObject.bypassHistory = true;
canvas.remove(existingObject);
}
object.bypassHistory = true;
canvas.add(object);
object.bypassHistory = false;
});
} else if (action.type === 'canvas_cleared') {
var canvasPresentation = JSON.parse(action.canvas);
canvas.bypassHistory = true;
canvas.loadFromJSON(canvasPresentation);
canvas.renderAll();
canvas.bypassHistory = false;
}
}
function redoAction() {
var action, objectCandidate;
try {
action = actionHistory.reversePop();
} catch (e) {
console.log(e.message);
return;
}
if (action.type === 'object_added') {
objectCandidate = JSON.parse(action.object);
fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
actualObjects[0].uuid = objectCandidate.uuid;
var object = actualObjects[0];
object.bypassHistory = true;
canvas.add(object);
object.bypassHistory = false;
});
} else if (action.type === 'object_removed') {
objectCandidate = JSON.parse(action.object);
var object = getFabricObjectByUuid(objectCandidate.uuid);
object.bypassHistory = true;
canvas.remove(object);
object.bypassHistory = false;
} else if (action.type === 'object_modified' || action.type === 'text_changed') {
objectCandidate = JSON.parse(action.objectNew);
fabric.util.enlivenObjects([objectCandidate], function(actualObjects) {
actualObjects[0].uuid = objectCandidate.uuid;
var object = actualObjects[0];
var existingObject = getFabricObjectByUuid(objectCandidate.uuid);
if (existingObject) {
existingObject.bypassRemoveEvent = true;
existingObject.bypassHistory = true;
canvas.remove(existingObject);
}
object.bypassHistory = true;
canvas.add(object);
object.bypassHistory = false;
});
} else if (action.type === 'canvas_cleared') {
canvas.clear();
}
}
I dont know if this solution (and code) fits your needs out-of-the-box. Maybe it is at some degree coupled to my specific application. I hope you manage to understand what i propose and make use of it.

D3.js force layout : show root path of any node

I am using force layout of d3.js and I need to show the path to the root in graph. Ex- On click of "Task 10" node it should show the path (Task 10->Task 6-> Task 4-> Task 1). Root will be recognize as same source and target but this info I am passing in data only. My data contain "RootPath" having path info for the node like for "Task 10" RootPath is ["Task 6","Task 4","Task 1"] .
My complete code for the graph can be seen here http://plnkr.co/edit/EvpNC6B5DBWczNXKiL82?p=preview.
I am writing below method so that on click to the particular node it will show the root path. But currently it only show for 1 node. I don't know how to iterate for all other lists of node.
function rootPath(d){
var curNodeDetail = d.details.RootPath;
var source=[],target=[],i=0,j=0;
source[i] = d.name;
target[i] = curNodeDetail[i];
links.style("stroke-opacity", function(o) {
if(source[i] === o.source.name && target[i] === o.target.name){
source[i+1] = curNodeDetail[i];
target[i+1] = curNodeDetail[i+1];
i++;
return 1;
}
else
return 0.1;
})
.attr("marker-end", function(o){
if(source[j] === o.source.name && target[j] === o.target.name){
j++;
return "url(#arrowhead)";
}
else
return "url(#arrowhead2)";
});
}
Turn d.details.rootpath into a d3.set and add the current node, then interrogate each link to see if both ends are part of the set.
This works for your links, but you'll have to do likewise for the nodes -->
var curNodeDetail = d.details.RootPath;
var rootMap = d3.set (curNodeDetail);
rootMap.add(d.name);
links.style("stroke-opacity", function(o) {
return (rootMap.has(o.source.name) && rootMap.has (o.target.name)) ? 1 : 0.1;
})
.attr("marker-end", function(o){
return (rootMap.has(o.source.name) && rootMap.has (o.target.name)) ? "url(#arrowhead)" : "url(#arrowhead2)";
});
}
Below function can be useful :-
function rootPath(d){
var curNodeDetail = d.details.RootPath;
var rootMap = rootPathItinerary(d.name,curNodeDetail);
links.style("stroke-opacity", function(o) {
return (rootMap.has(o.source.name) && (rootMap.get(o.source.name) === o.target.name)) ? 1 : 0.1;
})
.attr("marker-end", function(o){
return (rootMap.has(o.source.name) && (rootMap.get(o.source.name) === o.target.name)) ? "url(#arrowhead)" : "url(#arrowhead2)";
});
}
function rootPathItinerary(node,rootPathDet){
var i=0;
var map = d3.map();
map.set(node,rootPathDet[i]);//10,6
while(i+1 <rootPathDet.length){ //6,4,1
map.set(rootPathDet[i],rootPathDet[i+1]) ; //6,4 ; 4,1
i++;
}
return map;
}

Jquery function implementation

I write this function for getting xpath location:
JQuery (this function get xpath):
function getXPath(node, path) {
path = path || [];
if(node.parentNode) {
path = getXPath(node.parentNode, path);
}
if(node.previousSibling) {
var count = 1;
var sibling = node.previousSibling
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {count++;}
sibling = sibling.previousSibling;
} while(sibling);
if(count == 1) {count = null;}
} else if(node.nextSibling) {
var sibling = node.nextSibling;
do {
if(sibling.nodeType == 1 && sibling.nodeName == node.nodeName) {
var count = 1;
sibling = null;
} else {
var count = null;
sibling = sibling.previousSibling;
}
} while(sibling);
}
if(node.nodeType == 1) {
path.push(node.nodeName.toLowerCase() + (node.id ? "[#id='"+node.id+"']" : count > 0 ? "["+count+"]" : ''));
}
return path;
};
and I have an iframe:
<iframe id="frameID" width="100%" src="http://www.example.com"></iframe>
But Now I have a problem how to integrate my jquery function and where to when i click somewhere in Iframe get xpath result?
You wrote that function? -- That code was written nearly 5 years ago on DZone: http://snippets.dzone.com/posts/show/4349 -- This includes instructions for usage for the function.
You can only do that when the iframe location is in the same domain as the page embedding it.
If so, get the Document from the iframe (document.getElementById('frameId').contentDocument, see MDN) and add a click event listener to it. The event you're receiving will contain a reference to the target node.

Remove All id Attributes from nodes in a Range of Fragment

Is there a way to remove the id attribute of every node in a range or fragment?
Update: I finally found out that the bug I'm struggling with is based on a <[script]> being included in a range, and therefore unexpectedly cloned, when a chrome user does a ctrl+a. My goal would be to remove any instance of <[script]> from the range (or doc fragment), such that it is not replicated when cloned.
You may be able to use a TreeWalker, which works in pretty much all the browers that Range works in.
function actOnElementsInRange(range, func) {
function isContainedInRange(el, range) {
var elRange = range.cloneRange();
elRange.selectNode(el);
return range.compareBoundaryPoints(Range.START_TO_START, elRange) <= 0
&& range.compareBoundaryPoints(Range.END_TO_END, elRange) >= 0;
}
var rangeStartElement = range.startContainer;
if (rangeStartElement.nodeType == 3) {
rangeStartElement = rangeStartElement.parentNode;
}
var rangeEndElement = range.endContainer;
if (rangeEndElement.nodeType == 3) {
rangeEndElement = rangeEndElement.parentNode;
}
var isInRange = function(el) {
return (el === rangeStartElement || el === rangeEndElement ||
isContainedInRange(el, range))
? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};
var container = range.commonAncestorContainer;
if (container.nodeType != 1) {
container = container.parentNode;
}
var walker = document.createTreeWalker(document,
NodeFilter.SHOW_ELEMENT, isInRange, false);
while (walker.nextNode()) {
func(walker.currentNode);
}
}
actOnElementsInRange(range, function(el) {
el.removeAttribute("id");
});
yes: http://api.jquery.com/removeAttr/

Categories

Resources