|
|
@ -0,0 +1,640 @@ |
|
|
|
/** |
|
|
|
* Created by Alex on 2/27/2015. |
|
|
|
*/ |
|
|
|
|
|
|
|
var Node = require("../../Node"); |
|
|
|
|
|
|
|
class SelectionHandler { |
|
|
|
constructor(body) { |
|
|
|
this.body = body; |
|
|
|
this.selectionObj = {nodes:[], edges:[]}; |
|
|
|
|
|
|
|
this.options = { |
|
|
|
select: true, |
|
|
|
selectConnectedEdges: true |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
setCanvas(canvas) { |
|
|
|
this.canvas = canvas; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* handles the selection part of the tap; |
|
|
|
* |
|
|
|
* @param {Object} pointer |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
selectOnPoint(pointer) { |
|
|
|
if (this.options.select === true) { |
|
|
|
if (this._getSelectedObjectCount() > 0) {this._unselectAll();} |
|
|
|
|
|
|
|
this.selectObject(pointer); |
|
|
|
this._generateClickEvent(pointer); |
|
|
|
|
|
|
|
this.body.emitter.emit("_requestRedraw"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
_generateClickEvent(pointer) { |
|
|
|
var properties = this.getSelection(); |
|
|
|
properties['pointer'] = { |
|
|
|
DOM: {x: pointer.x, y: pointer.y}, |
|
|
|
canvas: this.canvas.DOMtoCanvas(pointer) |
|
|
|
} |
|
|
|
this.body.emitter.emit("click", properties); |
|
|
|
} |
|
|
|
|
|
|
|
selectObject(pointer) { |
|
|
|
var obj = this._getNodeAt(pointer); |
|
|
|
if (obj != null) { |
|
|
|
if (this.options.selectConnectedEdges === true) { |
|
|
|
this._selectConnectedEdges(obj); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
obj = this._getEdgeAt(pointer); |
|
|
|
} |
|
|
|
|
|
|
|
if (obj !== null) { |
|
|
|
obj.select(); |
|
|
|
this._addToSelection(obj); |
|
|
|
this.body.emitter.emit('selected', this.getSelection()) |
|
|
|
} |
|
|
|
return obj; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* @param object |
|
|
|
* @param overlappingNodes |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getNodesOverlappingWith(object, overlappingNodes) { |
|
|
|
var nodes = this.body.nodes; |
|
|
|
for (var nodeId in nodes) { |
|
|
|
if (nodes.hasOwnProperty(nodeId)) { |
|
|
|
if (nodes[nodeId].isOverlappingWith(object)) { |
|
|
|
overlappingNodes.push(nodeId); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 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 |
|
|
|
*/ |
|
|
|
_getAllNodesOverlappingWith(object) { |
|
|
|
var overlappingNodes = []; |
|
|
|
this._getNodesOverlappingWith(object,overlappingNodes); |
|
|
|
return overlappingNodes; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Return a position object in canvasspace from a single point in screenspace |
|
|
|
* |
|
|
|
* @param pointer |
|
|
|
* @returns {{left: number, top: number, right: number, bottom: number}} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_pointerToPositionObject(pointer) { |
|
|
|
var canvasPos = this.canvas.DOMtoCanvas(pointer); |
|
|
|
return { |
|
|
|
left: canvasPos.x, |
|
|
|
top: canvasPos.y, |
|
|
|
right: canvasPos.x, |
|
|
|
bottom: canvasPos.y |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Get the top node at the a specific point (like a click) |
|
|
|
* |
|
|
|
* @param {{x: Number, y: Number}} pointer |
|
|
|
* @return {Node | null} node |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getNodeAt(pointer) { |
|
|
|
// we first check if this is an navigation controls element
|
|
|
|
var positionObject = this._pointerToPositionObject(pointer); |
|
|
|
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
|
|
|
|
if (overlappingNodes.length > 0) { |
|
|
|
return this.body.nodes[overlappingNodes[overlappingNodes.length - 1]]; |
|
|
|
} |
|
|
|
else { |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 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(object, overlappingEdges) { |
|
|
|
var edges = this.body.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(object) { |
|
|
|
var overlappingEdges = []; |
|
|
|
this._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. |
|
|
|
* |
|
|
|
* @param pointer |
|
|
|
* @returns {null} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getEdgeAt(pointer) { |
|
|
|
var positionObject = this._pointerToPositionObject(pointer); |
|
|
|
var overlappingEdges = this._getAllEdgesOverlappingWith(positionObject); |
|
|
|
|
|
|
|
if (overlappingEdges.length > 0) { |
|
|
|
return this.body.edges[overlappingEdges[overlappingEdges.length - 1]]; |
|
|
|
} |
|
|
|
else { |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Add object to the selection array. |
|
|
|
* |
|
|
|
* @param obj |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_addToSelection(obj) { |
|
|
|
if (obj instanceof Node) { |
|
|
|
this.selectionObj.nodes[obj.id] = obj; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.selectionObj.edges[obj.id] = obj; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Add object to the selection array. |
|
|
|
* |
|
|
|
* @param obj |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_addToHover(obj) { |
|
|
|
if (obj instanceof Node) { |
|
|
|
this.hoverObj.nodes[obj.id] = obj; |
|
|
|
} |
|
|
|
else { |
|
|
|
this.hoverObj.edges[obj.id] = obj; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Remove a single option from selection. |
|
|
|
* |
|
|
|
* @param {Object} obj |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_removeFromSelection(obj) { |
|
|
|
if (obj instanceof Node) { |
|
|
|
delete this.selectionObj.nodes[obj.id]; |
|
|
|
} |
|
|
|
else { |
|
|
|
delete this.selectionObj.edges[obj.id]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Unselect all. The selectionObj is useful for this. |
|
|
|
* |
|
|
|
* @param {Boolean} [doNotTrigger] | ignore trigger |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_unselectAll(doNotTrigger = false) { |
|
|
|
for(var nodeId in this.selectionObj.nodes) { |
|
|
|
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
this.selectionObj.nodes[nodeId].unselect(); |
|
|
|
} |
|
|
|
} |
|
|
|
for(var edgeId in this.selectionObj.edges) { |
|
|
|
if(this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
|
this.selectionObj.edges[edgeId].unselect(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.selectionObj = {nodes:{},edges:{}}; |
|
|
|
|
|
|
|
if (doNotTrigger == false) { |
|
|
|
this.body.emitter.emit('select', this.getSelection()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* return the number of selected nodes |
|
|
|
* |
|
|
|
* @returns {number} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getSelectedNodeCount() { |
|
|
|
var count = 0; |
|
|
|
for (var nodeId in this.selectionObj.nodes) { |
|
|
|
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
return count; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* return the selected node |
|
|
|
* |
|
|
|
* @returns {number} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getSelectedNode() { |
|
|
|
for (var nodeId in this.selectionObj.nodes) { |
|
|
|
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
return this.selectionObj.nodes[nodeId]; |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* return the selected edge |
|
|
|
* |
|
|
|
* @returns {number} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getSelectedEdge() { |
|
|
|
for (var edgeId in this.selectionObj.edges) { |
|
|
|
if (this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
|
return this.selectionObj.edges[edgeId]; |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* return the number of selected edges |
|
|
|
* |
|
|
|
* @returns {number} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getSelectedEdgeCount() { |
|
|
|
var count = 0; |
|
|
|
for (var edgeId in this.selectionObj.edges) { |
|
|
|
if (this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
return count; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* return the number of selected objects. |
|
|
|
* |
|
|
|
* @returns {number} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_getSelectedObjectCount() { |
|
|
|
var count = 0; |
|
|
|
for(var nodeId in this.selectionObj.nodes) { |
|
|
|
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
for(var edgeId in this.selectionObj.edges) { |
|
|
|
if(this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
|
count += 1; |
|
|
|
} |
|
|
|
} |
|
|
|
return count; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Check if anything is selected |
|
|
|
* |
|
|
|
* @returns {boolean} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_selectionIsEmpty() { |
|
|
|
for(var nodeId in this.selectionObj.nodes) { |
|
|
|
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
for(var edgeId in this.selectionObj.edges) { |
|
|
|
if(this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* check if one of the selected nodes is a cluster. |
|
|
|
* |
|
|
|
* @returns {boolean} |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_clusterInSelection() { |
|
|
|
for(var nodeId in this.selectionObj.nodes) { |
|
|
|
if(this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
if (this.selectionObj.nodes[nodeId].clusterSize > 1) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* select the edges connected to the node that is being selected |
|
|
|
* |
|
|
|
* @param {Node} node |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_selectConnectedEdges(node) { |
|
|
|
for (var i = 0; i < node.edges.length; i++) { |
|
|
|
var edge = node.edges[i]; |
|
|
|
edge.select(); |
|
|
|
this._addToSelection(edge); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* select the edges connected to the node that is being selected |
|
|
|
* |
|
|
|
* @param {Node} node |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_hoverConnectedEdges(node) { |
|
|
|
for (var i = 0; i < node.edges.length; i++) { |
|
|
|
var edge = node.edges[i]; |
|
|
|
edge.hover = true; |
|
|
|
this._addToHover(edge); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* unselect the edges connected to the node that is being selected |
|
|
|
* |
|
|
|
* @param {Node} node |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_unselectConnectedEdges(node) { |
|
|
|
for (var i = 0; i < node.edges.length; i++) { |
|
|
|
var edge = node.edges[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 || Edge} object |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_blurObject(object) { |
|
|
|
if (object.hover == true) { |
|
|
|
object.hover = false; |
|
|
|
this.body.emitter.emit("blurNode",{node:object.id}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 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 || Edge} object |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_hoverObject(object) { |
|
|
|
if (object.hover == false) { |
|
|
|
object.hover = true; |
|
|
|
this._addToHover(object); |
|
|
|
if (object instanceof Node) { |
|
|
|
this.body.emitter.emit("hoverNode",{node:object.id}); |
|
|
|
} |
|
|
|
} |
|
|
|
if (object instanceof Node) { |
|
|
|
this._hoverConnectedEdges(object); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* handles the selection part of the double tap and opens a cluster if needed |
|
|
|
* |
|
|
|
* @param {Object} pointer |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_handleDoubleTap(pointer) { |
|
|
|
var node = this._getNodeAt(pointer); |
|
|
|
if (node != null && node !== undefined) { |
|
|
|
// we reset the areaCenter here so the opening of the node will occur
|
|
|
|
this.areaCenter = {"x" : this._XconvertDOMtoCanvas(pointer.x), |
|
|
|
"y" : this._YconvertDOMtoCanvas(pointer.y)}; |
|
|
|
this.openCluster(node); |
|
|
|
} |
|
|
|
var properties = this.getSelection(); |
|
|
|
properties['pointer'] = { |
|
|
|
DOM: {x: pointer.x, y: pointer.y}, |
|
|
|
canvas: {x: this._XconvertDOMtoCanvas(pointer.x), y: this._YconvertDOMtoCanvas(pointer.y)} |
|
|
|
} |
|
|
|
this.body.emitter.emit("doubleClick", properties); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Handle the onHold selection part |
|
|
|
* |
|
|
|
* @param pointer |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_handleOnHold(pointer) { |
|
|
|
var node = this._getNodeAt(pointer); |
|
|
|
if (node != null) { |
|
|
|
this._selectObject(node,true); |
|
|
|
} |
|
|
|
else { |
|
|
|
var edge = this._getEdgeAt(pointer); |
|
|
|
if (edge != null) { |
|
|
|
this._selectObject(edge,true); |
|
|
|
} |
|
|
|
} |
|
|
|
this._requestRedraw(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* retrieve the currently selected objects |
|
|
|
* @return {{nodes: Array.<String>, edges: Array.<String>}} selection |
|
|
|
*/ |
|
|
|
getSelection() { |
|
|
|
var nodeIds = this.getSelectedNodes(); |
|
|
|
var edgeIds = this.getSelectedEdges(); |
|
|
|
return {nodes:nodeIds, edges:edgeIds}; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* retrieve the currently selected nodes |
|
|
|
* @return {String[]} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
|
*/ |
|
|
|
getSelectedNodes() { |
|
|
|
var idArray = []; |
|
|
|
if (this.options.select == true) { |
|
|
|
for (var nodeId in this.selectionObj.nodes) { |
|
|
|
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
idArray.push(nodeId); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return idArray |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* |
|
|
|
* retrieve the currently selected edges |
|
|
|
* @return {Array} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
|
*/ |
|
|
|
getSelectedEdges() { |
|
|
|
var idArray = []; |
|
|
|
if (this.options.select == true) { |
|
|
|
for (var edgeId in this.selectionObj.edges) { |
|
|
|
if (this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
|
idArray.push(edgeId); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return idArray; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* select zero or more nodes with the option to highlight edges |
|
|
|
* @param {Number[] | String[]} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
|
* @param {boolean} [highlightEdges] |
|
|
|
*/ |
|
|
|
selectNodes(selection, highlightEdges) { |
|
|
|
var i, iMax, id; |
|
|
|
|
|
|
|
if (!selection || (selection.length == undefined)) |
|
|
|
throw 'Selection must be an array with ids'; |
|
|
|
|
|
|
|
// first unselect any selected node
|
|
|
|
this._unselectAll(true); |
|
|
|
|
|
|
|
for (i = 0, iMax = selection.length; i < iMax; i++) { |
|
|
|
id = selection[i]; |
|
|
|
|
|
|
|
var node = this.body.nodes[id]; |
|
|
|
if (!node) { |
|
|
|
throw new RangeError('Node with id "' + id + '" not found'); |
|
|
|
} |
|
|
|
this._selectObject(node,true,true,highlightEdges,true); |
|
|
|
} |
|
|
|
this.redraw(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* select zero or more edges |
|
|
|
* @param {Number[] | String[]} selection An array with the ids of the |
|
|
|
* selected nodes. |
|
|
|
*/ |
|
|
|
selectEdges(selection) { |
|
|
|
var i, iMax, id; |
|
|
|
|
|
|
|
if (!selection || (selection.length == undefined)) |
|
|
|
throw 'Selection must be an array with ids'; |
|
|
|
|
|
|
|
// first unselect any selected node
|
|
|
|
this._unselectAll(true); |
|
|
|
|
|
|
|
for (i = 0, iMax = selection.length; i < iMax; i++) { |
|
|
|
id = selection[i]; |
|
|
|
|
|
|
|
var edge = this.body.edges[id]; |
|
|
|
if (!edge) { |
|
|
|
throw new RangeError('Edge with id "' + id + '" not found'); |
|
|
|
} |
|
|
|
this._selectObject(edge,true,true,false,true); |
|
|
|
} |
|
|
|
this.redraw(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Validate the selection: remove ids of nodes which no longer exist |
|
|
|
* @private |
|
|
|
*/ |
|
|
|
_updateSelection() { |
|
|
|
for (var nodeId in this.selectionObj.nodes) { |
|
|
|
if (this.selectionObj.nodes.hasOwnProperty(nodeId)) { |
|
|
|
if (!this.body.nodes.hasOwnProperty(nodeId)) { |
|
|
|
delete this.selectionObj.nodes[nodeId]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
for (var edgeId in this.selectionObj.edges) { |
|
|
|
if (this.selectionObj.edges.hasOwnProperty(edgeId)) { |
|
|
|
if (!this.body.edges.hasOwnProperty(edgeId)) { |
|
|
|
delete this.selectionObj.edges[edgeId]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
export {SelectionHandler}; |