|
|
@ -5,7 +5,7 @@ |
|
|
|
* A dynamic, browser-based visualization library. |
|
|
|
* |
|
|
|
* @version 0.4.0-SNAPSHOT |
|
|
|
* @date 2014-01-29 |
|
|
|
* @date 2014-01-30 |
|
|
|
* |
|
|
|
* @license |
|
|
|
* Copyright (C) 2011-2014 Almende B.V, http://almende.com
|
|
|
@ -9780,6 +9780,7 @@ function Edge (properties, graph, constants) { |
|
|
|
this.width = constants.edges.width; |
|
|
|
this.value = undefined; |
|
|
|
this.length = constants.edges.length; |
|
|
|
this.selected = false; |
|
|
|
|
|
|
|
this.from = null; // a node
|
|
|
|
this.to = null; // a node
|
|
|
@ -10016,7 +10017,7 @@ Edge.prototype._drawLine = function(ctx) { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
Edge.prototype._getLineWidth = function() { |
|
|
|
if (this.from.selected || this.to.selected) { |
|
|
|
if (this.selected == true) { |
|
|
|
return Math.min(this.width * 2, this.widthMax)*this.graphScaleInv; |
|
|
|
} |
|
|
|
else { |
|
|
@ -10366,6 +10367,15 @@ Edge._dist = function (x1,y1, x2,y2, x3,y3) { // x3,y3 is the point |
|
|
|
Edge.prototype.setScale = function(scale) { |
|
|
|
this.graphScaleInv = 1.0/scale; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Edge.prototype.select = function() { |
|
|
|
this.selected = true; |
|
|
|
} |
|
|
|
|
|
|
|
Edge.prototype.unselect = function() { |
|
|
|
this.selected = false; |
|
|
|
} |
|
|
|
/** |
|
|
|
* Popup is a class to create a popup window with some text |
|
|
|
* @param {Element} container The container object. |
|
|
@ -11479,6 +11489,10 @@ var ClusterMixin = { |
|
|
|
|
|
|
|
// if child node has been added on smaller scale than current, kick out
|
|
|
|
if (childNode.formationScale < this.scale || force == true) { |
|
|
|
// remove the selection, first remove the selection from the connected edges
|
|
|
|
this._unselectConnectedEdges(parentNode); |
|
|
|
parentNode.unselect(); |
|
|
|
|
|
|
|
// put the child node back in the global nodes object
|
|
|
|
this.nodes[containedNodeId] = childNode; |
|
|
|
|
|
|
@ -11527,6 +11541,9 @@ var ClusterMixin = { |
|
|
|
|
|
|
|
// recalculate the size of the node on the next time the node is rendered
|
|
|
|
parentNode.clearSizeCache(); |
|
|
|
|
|
|
|
// this unselects the rest of the edges
|
|
|
|
this._unselectConnectedEdges(parentNode); |
|
|
|
} |
|
|
|
|
|
|
|
// check if a further expansion step is possible if recursivity is enabled
|
|
|
@ -12272,7 +12289,7 @@ var SelectionMixin = { |
|
|
|
_getNodeAt : function (pointer) { |
|
|
|
// we first check if this is an navigationUI element
|
|
|
|
var positionObject = this._pointerToPositionObject(pointer); |
|
|
|
overlappingNodes = this._getAllNodesOverlappingWith(positionObject); |
|
|
|
var overlappingNodes = this._getAllNodesOverlappingWith(positionObject); |
|
|
|
|
|
|
|
// if there are overlapping nodes, select the last one, this is the
|
|
|
|
// one which is drawn on top of the others
|
|
|
@ -12285,6 +12302,36 @@ var SelectionMixin = { |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* retrieve all edges overlapping with given object, selector is around center |
|
|
|
* @param {Object} object An object with parameters left, top, right, bottom |
|
|
|
* @return {Number[]} An array with id's of the overlapping nodes |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getEdgesOverlappingWith : function (object, overlappingEdges) { |
|
|
|
var edges = this.edges; |
|
|
|
for (var edgeId in edges) { |
|
|
|
if (edges.hasOwnProperty(edgeId)) { |
|
|
|
if (edges[edgeId].isOverlappingWith(object)) { |
|
|
|
overlappingEdges.push(edgeId); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* retrieve all nodes overlapping with given object |
|
|
|
* @param {Object} object An object with parameters left, top, right, bottom |
|
|
|
* @return {Number[]} An array with id's of the overlapping nodes |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getAllEdgesOverlappingWith : function (object) { |
|
|
|
var overlappingEdges = []; |
|
|
|
this._doInAllActiveSectors("_getEdgesOverlappingWith",object,overlappingEdges); |
|
|
|
return overlappingEdges; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* Place holder. To implement change the _getNodeAt to a _getObjectAt. Have the _getObjectAt call |
|
|
|
* _getNodeAt and _getEdgesAt, then priortize the selection to user preferences. |
|
|
@ -12294,18 +12341,25 @@ var SelectionMixin = { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getEdgeAt : function(pointer) { |
|
|
|
return null; |
|
|
|
var positionObject = this._pointerToPositionObject(pointer); |
|
|
|
var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); |
|
|
|
|
|
|
|
if (overlappingEdges.length > 0) { |
|
|
|
return this.edges[overlappingEdges[overlappingEdges.length - 1]]; |
|
|
|
} |
|
|
|
else { |
|
|
|
return null; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Add object to the selection array. The this.selection id array may not be needed. |
|
|
|
* Add object to the selection array. |
|
|
|
* |
|
|
|
* @param obj |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_addToSelection : function(obj) { |
|
|
|
this.selection.push(obj.id); |
|
|
|
this.selectionObj[obj.id] = obj; |
|
|
|
}, |
|
|
|
|
|
|
@ -12317,12 +12371,6 @@ var SelectionMixin = { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_removeFromSelection : function(obj) { |
|
|
|
for (var i = 0; i < this.selection.length; i++) { |
|
|
|
if (obj.id == this.selection[i]) { |
|
|
|
this.selection.splice(i,1); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
delete this.selectionObj[obj.id]; |
|
|
|
}, |
|
|
|
|
|
|
@ -12338,10 +12386,9 @@ var SelectionMixin = { |
|
|
|
doNotTrigger = false; |
|
|
|
} |
|
|
|
|
|
|
|
this.selection = []; |
|
|
|
for (var objId in this.selectionObj) { |
|
|
|
if (this.selectionObj.hasOwnProperty(objId)) { |
|
|
|
this.selectionObj[objId].unselect(); |
|
|
|
for (var objectId in this.selectionObj) { |
|
|
|
if (this.selectionObj.hasOwnProperty(objectId)) { |
|
|
|
this.selectionObj[objectId].unselect(); |
|
|
|
} |
|
|
|
} |
|
|
|
this.selectionObj = {}; |
|
|
@ -12359,25 +12406,55 @@ var SelectionMixin = { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_selectionIsEmpty : function() { |
|
|
|
if (this.selection.length == 0) { |
|
|
|
return true; |
|
|
|
for(var objectId in this.selectionObj) { |
|
|
|
if(this.selectionObj.hasOwnProperty(objectId)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
return false; |
|
|
|
return true; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* select the edges connected to the node that is being selected |
|
|
|
* |
|
|
|
* @param {Node} node |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_selectConnectedEdges : function(node) { |
|
|
|
for (var i = 0; i < node.dynamicEdges.length; i++) { |
|
|
|
var edge = node.dynamicEdges[i]; |
|
|
|
edge.select(); |
|
|
|
this._addToSelection(edge); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* unselect the edges connected to the node that is being selected |
|
|
|
* |
|
|
|
* @param {Node} node |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_unselectConnectedEdges : function(node) { |
|
|
|
for (var i = 0; i < node.dynamicEdges.length; i++) { |
|
|
|
var edge = node.dynamicEdges[i]; |
|
|
|
edge.unselect(); |
|
|
|
this._removeFromSelection(edge); |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* This is called when someone clicks on a node. either select or deselect it. |
|
|
|
* If there is an existing selection and we don't want to append to it, clear the existing selection |
|
|
|
* |
|
|
|
* @param {Node} node |
|
|
|
* @param {Node || Edge} object |
|
|
|
* @param {Boolean} append |
|
|
|
* @param {Boolean} [doNotTrigger] | ignore trigger |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_selectNode : function(node, append, doNotTrigger) { |
|
|
|
_selectObject : function(object, append, doNotTrigger) { |
|
|
|
if (doNotTrigger === undefined) { |
|
|
|
doNotTrigger = false; |
|
|
|
} |
|
|
@ -12386,14 +12463,16 @@ var SelectionMixin = { |
|
|
|
this._unselectAll(true); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (node.selected == false) { |
|
|
|
node.select(); |
|
|
|
this._addToSelection(node); |
|
|
|
if (object.selected == false) { |
|
|
|
object.select(); |
|
|
|
this._addToSelection(object); |
|
|
|
if (object instanceof Node) { |
|
|
|
this._selectConnectedEdges(object); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
node.unselect(); |
|
|
|
this._removeFromSelection(node); |
|
|
|
object.unselect(); |
|
|
|
this._removeFromSelection(object); |
|
|
|
} |
|
|
|
if (doNotTrigger == false) { |
|
|
|
this._trigger('select'); |
|
|
@ -12428,10 +12507,16 @@ var SelectionMixin = { |
|
|
|
_handleTap : function(pointer) { |
|
|
|
var node = this._getNodeAt(pointer); |
|
|
|
if (node != null) { |
|
|
|
this._selectNode(node,false); |
|
|
|
this._selectObject(node,false); |
|
|
|
} |
|
|
|
else { |
|
|
|
this._unselectAll(); |
|
|
|
var edge = this._getEdgeAt(pointer); |
|
|
|
if (edge != null) { |
|
|
|
this._selectObject(edge,false); |
|
|
|
} |
|
|
|
else { |
|
|
|
this._unselectAll(); |
|
|
|
} |
|
|
|
} |
|
|
|
this._redraw(); |
|
|
|
}, |
|
|
@ -12463,7 +12548,13 @@ var SelectionMixin = { |
|
|
|
_handleOnHold : function(pointer) { |
|
|
|
var node = this._getNodeAt(pointer); |
|
|
|
if (node != null) { |
|
|
|
this._selectNode(node,true); |
|
|
|
this._selectObject(node,true); |
|
|
|
} |
|
|
|
else { |
|
|
|
var edge = this._getEdgeAt(pointer); |
|
|
|
if (edge != null) { |
|
|
|
this._selectObject(edge,true); |
|
|
|
} |
|
|
|
} |
|
|
|
this._redraw(); |
|
|
|
}, |
|
|
@ -12485,12 +12576,72 @@ var SelectionMixin = { |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* retrieve the currently selected nodes |
|
|
|
* retrieve the currently selected objects |
|
|
|
* @return {Number[] | String[]} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
|
*/ |
|
|
|
getSelection : function() { |
|
|
|
return this.selection.concat([]); |
|
|
|
var nodeIdString = this.getSelectedNodes(); |
|
|
|
/* |
|
|
|
var edgeIdString = this.getSelectedEdges(); |
|
|
|
|
|
|
|
var idString = ""; |
|
|
|
if (nodeIdString != "") { |
|
|
|
idString = idString.concat("Nodes: ",nodeIdString); |
|
|
|
|
|
|
|
if (edgeIdString != "") { |
|
|
|
idString = idString.concat("; "); |
|
|
|
} |
|
|
|
} |
|
|
|
if (edgeIdString != "") { |
|
|
|
idString = idString.concat("Edges: ",edgeIdString); |
|
|
|
} |
|
|
|
*/ |
|
|
|
return nodeIdString |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* retrieve the currently selected nodes |
|
|
|
* @return {String} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
|
*/ |
|
|
|
getSelectedNodes : function() { |
|
|
|
var idString = "", i = 0; |
|
|
|
for(var objectId in this.selectionObj) { |
|
|
|
if(this.selectionObj.hasOwnProperty(objectId)) { |
|
|
|
if (this.selectionObj[objectId] instanceof Node) { |
|
|
|
if (i != 0) { |
|
|
|
idString = idString.concat(", "); |
|
|
|
} |
|
|
|
idString = idString.concat(String(objectId)); |
|
|
|
i++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return idString |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* retrieve the currently selected edges |
|
|
|
* @return {String} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
|
*/ |
|
|
|
getSelectedEdges : function() { |
|
|
|
var idString = "", i = 0; |
|
|
|
for(var objectId in this.selectionObj) { |
|
|
|
if(this.selectionObj.hasOwnProperty(objectId)) { |
|
|
|
if (this.selectionObj[objectId] instanceof Edge) { |
|
|
|
if (i != 0) { |
|
|
|
idString = idString.concat(", "); |
|
|
|
} |
|
|
|
idString = idString.concat(String(objectId)); |
|
|
|
i++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return idString |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
@ -12504,8 +12655,6 @@ var SelectionMixin = { |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* // TODO: rework this function, it is from the old system
|
|
|
|
* |
|
|
|
* select zero or more nodes |
|
|
|
* @param {Number[] | String[]} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
@ -12526,7 +12675,7 @@ var SelectionMixin = { |
|
|
|
if (!node) { |
|
|
|
throw new RangeError('Node with id "' + id + '" not found'); |
|
|
|
} |
|
|
|
this._selectNode(node,true,true); |
|
|
|
this._selectObject(node,true,true); |
|
|
|
} |
|
|
|
|
|
|
|
this.redraw(); |
|
|
@ -12534,135 +12683,28 @@ var SelectionMixin = { |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* TODO: rework this function, it is from the old system |
|
|
|
* |
|
|
|
* Validate the selection: remove ids of nodes which no longer exist |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_updateSelection : function () { |
|
|
|
var i = 0; |
|
|
|
while (i < this.selection.length) { |
|
|
|
var nodeId = this.selection[i]; |
|
|
|
if (!this.nodes.hasOwnProperty(nodeId)) { |
|
|
|
this.selection.splice(i, 1); |
|
|
|
delete this.selectionObj[nodeId]; |
|
|
|
} |
|
|
|
else { |
|
|
|
i++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Unselect selected nodes. If no selection array is provided, all nodes |
|
|
|
* are unselected |
|
|
|
* @param {Object[]} selection Array with selection objects, each selection |
|
|
|
* object has a parameter row. Optional |
|
|
|
* @param {Boolean} triggerSelect If true (default), the select event |
|
|
|
* is triggered when nodes are unselected |
|
|
|
* @return {Boolean} changed True if the selection is changed |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
/* _unselectNodes : function(selection, triggerSelect) { |
|
|
|
var changed = false; |
|
|
|
var i, iMax, id; |
|
|
|
|
|
|
|
if (selection) { |
|
|
|
// remove provided selections
|
|
|
|
for (i = 0, iMax = selection.length; i < iMax; i++) { |
|
|
|
id = selection[i]; |
|
|
|
if (this.nodes.hasOwnProperty(id)) { |
|
|
|
this.nodes[id].unselect(); |
|
|
|
} |
|
|
|
var j = 0; |
|
|
|
while (j < this.selection.length) { |
|
|
|
if (this.selection[j] == id) { |
|
|
|
this.selection.splice(j, 1); |
|
|
|
changed = true; |
|
|
|
for(var objectId in this.selectionObj) { |
|
|
|
if(this.selectionObj.hasOwnProperty(objectId)) { |
|
|
|
if (this.selectionObj[objectId] instanceof Node) { |
|
|
|
if (!this.nodes.hasOwnProperty(objectId)) { |
|
|
|
delete this.selectionObj[objectId]; |
|
|
|
} |
|
|
|
else { |
|
|
|
j++; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.selection && this.selection.length) { |
|
|
|
// remove all selections
|
|
|
|
for (i = 0, iMax = this.selection.length; i < iMax; i++) { |
|
|
|
id = this.selection[i]; |
|
|
|
if (this.nodes.hasOwnProperty(id)) { |
|
|
|
this.nodes[id].unselect(); |
|
|
|
} |
|
|
|
changed = true; |
|
|
|
} |
|
|
|
this.selection = []; |
|
|
|
} |
|
|
|
|
|
|
|
if (changed && (triggerSelect == true || triggerSelect == undefined)) { |
|
|
|
// fire the select event
|
|
|
|
this._trigger('select'); |
|
|
|
} |
|
|
|
|
|
|
|
return changed; |
|
|
|
}, |
|
|
|
*/ |
|
|
|
/** |
|
|
|
* select all nodes on given location x, y |
|
|
|
* @param {Array} selection an array with node ids |
|
|
|
* @param {boolean} append If true, the new selection will be appended to the |
|
|
|
* current selection (except for duplicate entries) |
|
|
|
* @return {Boolean} changed True if the selection is changed |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
/* _selectNodes : function(selection, append) { |
|
|
|
var changed = false; |
|
|
|
var i, iMax; |
|
|
|
|
|
|
|
// TODO: the selectNodes method is a little messy, rework this
|
|
|
|
|
|
|
|
// check if the current selection equals the desired selection
|
|
|
|
var selectionAlreadyThere = true; |
|
|
|
if (selection.length != this.selection.length) { |
|
|
|
selectionAlreadyThere = false; |
|
|
|
} |
|
|
|
else { |
|
|
|
for (i = 0, iMax = Math.min(selection.length, this.selection.length); i < iMax; i++) { |
|
|
|
if (selection[i] != this.selection[i]) { |
|
|
|
selectionAlreadyThere = false; |
|
|
|
break; |
|
|
|
else { // assuming only edges and nodes are selected
|
|
|
|
if (!this.edges.hasOwnProperty(objectId)) { |
|
|
|
delete this.selectionObj[objectId]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if (selectionAlreadyThere) { |
|
|
|
return changed; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (append == undefined || append == false) { |
|
|
|
// first deselect any selected node
|
|
|
|
var triggerSelect = false; |
|
|
|
changed = this._unselectNodes(undefined, triggerSelect); |
|
|
|
} |
|
|
|
|
|
|
|
for (i = 0, iMax = selection.length; i < iMax; i++) { |
|
|
|
// add each of the new selections, but only when they are not duplicate
|
|
|
|
var id = selection[i]; |
|
|
|
var isDuplicate = (this.selection.indexOf(id) != -1); |
|
|
|
if (!isDuplicate) { |
|
|
|
this.nodes[id].select(); |
|
|
|
this.selection.push(id); |
|
|
|
changed = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (changed) { |
|
|
|
// fire the select event
|
|
|
|
this._trigger('select'); |
|
|
|
} |
|
|
|
|
|
|
|
return changed; |
|
|
|
}, |
|
|
|
*/ |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -13569,27 +13611,28 @@ Graph.prototype._onDragStart = function () { |
|
|
|
} |
|
|
|
|
|
|
|
// create an array with the selected nodes and their original location and status
|
|
|
|
var me = this; |
|
|
|
this.selection.forEach(function (id) { |
|
|
|
var node = me.nodes[id]; |
|
|
|
if (node) { |
|
|
|
var s = { |
|
|
|
id: id, |
|
|
|
node: node, |
|
|
|
|
|
|
|
// store original x, y, xFixed and yFixed, make the node temporarily Fixed
|
|
|
|
x: node.x, |
|
|
|
y: node.y, |
|
|
|
xFixed: node.xFixed, |
|
|
|
yFixed: node.yFixed |
|
|
|
}; |
|
|
|
for (var objectId in this.selectionObj) { |
|
|
|
if (this.selectionObj.hasOwnProperty(objectId)) { |
|
|
|
var object = this.selectionObj[objectId]; |
|
|
|
if (object instanceof Node) { |
|
|
|
var s = { |
|
|
|
id: object.id, |
|
|
|
node: object, |
|
|
|
|
|
|
|
// store original x, y, xFixed and yFixed, make the node temporarily Fixed
|
|
|
|
x: object.x, |
|
|
|
y: object.y, |
|
|
|
xFixed: object.xFixed, |
|
|
|
yFixed: object.yFixed |
|
|
|
}; |
|
|
|
|
|
|
|
node.xFixed = true; |
|
|
|
node.yFixed = true; |
|
|
|
object.xFixed = true; |
|
|
|
object.yFixed = true; |
|
|
|
|
|
|
|
drag.selection.push(s); |
|
|
|
drag.selection.push(s); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
@ -14932,7 +14975,6 @@ Graph.prototype._loadSectorSystem = function() { |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
Graph.prototype._loadSelectionSystem = function() { |
|
|
|
this.selection = []; |
|
|
|
this.selectionObj = {}; |
|
|
|
|
|
|
|
for (var mixinFunction in SelectionMixin) { |
|
|
|