blob: 50e4f44461d6a0323fac71c7cf129a982cad9c17 [file] [log] [blame]
/*
Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
Copyright 2018 EveryUP srl
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
if (typeof TCD3 === 'undefined') {
var TCD3 = {};
}
TCD3.GraphEditor = (function () {
'use strict';
var DEBUG = true;
var SHIFT_BUTTON = 16;
var CANC_BUTTON = 46;
var nominal_text_size = 14;
var nominal_stroke = 1.5;
var EventHandler = TCD3.Event;
/**
* Constructor
*/
function GraphEditor(args) {
log("Constructor");
this.eventHandler = new EventHandler();
this.lastKeyDown = -1;
this._selected_node = undefined;
this._selected_link = undefined;
this._edit_mode = true;
this.filter_parameters = {
node: {
type: [],
group: [],
},
link: {
group: [],
view: [],
}
};
this.current_view_id = '';
// graph data initailization
this.d3_graph = {
nodes: [],
links: [],
graph_parameters: {}
};
}
GraphEditor.prototype.init = function (args) {
args = args || {};
var self = this;
this.width = args.width || 1500;
this.height = args.height || 1500;
this.forceSimulationActive = false;
var min_zoom = 0.1;
var max_zoom = 7;
this._setupBehaviorsOnEvents();
this._setupFiltersBehaviors(args);
this.type_property = {
"unrecognized": {
"shape": d3.symbolCircle,
"color": "#fff",
"node_label_color": "#000",
"size": 15
},
};
this.type_property_link = {
"unrecognized": {
"color": "lightgray"
},
};
this.force = d3.forceSimulation()
.force("charge", d3.forceManyBody())
.force("collide", d3.forceCollide().radius(40))
// .force("link", d3.forceLink().distance(80).iterations(1).id(function (d) {
.force("link", d3.forceLink().distance(function(d){
return d.short ? 1 : 100;
}).id(function (d) {
return d.id;
}))
.force("center", d3.forceCenter(this.width / 2, this.height / 2));
var zoom = d3.zoom().scaleExtent([min_zoom, max_zoom]);
var size = d3.scalePow().exponent(2)
.domain([1, 100])
.range([8, 24]);
this.svg = d3.select("#graph_editor_container").append("svg")
.attr("id", "graph_svg")
.attr("preserveAspectRatio", "xMinYMid")
.attr("width", '100%')
.attr("height", '100%');
//End Arrow style
this.defs = this.svg.append("svg:defs");
this.defs.selectAll("marker")
.data(["unrecognized"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "-5 -5 10 10")
.attr("refX", 13) //must be smarter way to calculate shift
.attr("refY", 0)
.attr("markerUnits", "userSpaceOnUse")
.attr("markerWidth", 12)
.attr("markerHeight", 12)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
.attr('fill', this.type_property_link['unrecognized']['color']);
d3.select(window)
.on('keydown', function () {
log('keydown ' + d3.event.keyCode);
//d3.event.preventDefault();
if (self.lastKeyDown !== -1) return;
self.lastKeyDown = d3.event.keyCode;
if (self.lastKeyDown === CANC_BUTTON && self._selected_node !== undefined) {
self.removeNode(self._selected_node, null, showAlert);
} else if (self.lastKeyDown === CANC_BUTTON && self._selected_link !== undefined) {
self.removeLink(self._selected_link, null, showAlert);
}
})
.on('keyup', function () {
log('keyup' + self.lastKeyDown);
self.lastKeyDown = -1;
});
var popup = this.svg.append("g")
.attr("id", "popup")
.attr("class", "popup")
.attr("opacity", "0")
.attr("transform", "translate(1 1)")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function dragstarted(d) {
//d3.select(this).raise().classed("active", true);
}
function dragged(d) {
//console.log(JSON.stringify(d))
d3.select(this).attr("transform", function () {
return "translate(" + d3.event.x + "," + d3.event.y + ")";
})
}
function dragended(d) {
//d3.select(this).classed("active", false);
}
var chart = $("#graph_svg");
this.aspect = chart.width() / chart.height();
this.container = $("#graph_editor_container");
$(window).on("resize", function () {
self.width = self.container.width();
self.height = self.container.height();
chart.attr("width", self.container.width());
chart.attr("height", self.container.height());
});
}
GraphEditor.prototype.get_d3_symbol =
function (myString) {
switch (myString) {
case "circle":
return d3.symbolCircle;
case "square":
return d3.symbolSquare;
case "diamond":
return d3.symbolDiamond;
case "triangle":
return d3.symbolTriangle;
case "star":
return d3.symbolStar;
case "cross":
return d3.symbolCross;
default:
// if the string is not recognized
return d3.symbolCross;
}
};
GraphEditor.prototype.get_name_from_d3_symbol =
function (mySymbol) {
switch (mySymbol) {
case d3.symbolCircle:
return "circle";
case d3.symbolSquare:
return "square";
case d3.symbolDiamond:
return "diamond";
case d3.symbolTriangle:
return "triangle";
case d3.symbolStar:
return "star";
case d3.symbolCross:
return "cross";
default:
// if the string is not recognized
return "unknown";
//return d3.symbolCircleUnknown;
}
};
/**
* Start or Stop force layout
* @param {boolean} Required. Value true: start, false: stop
* @returns {boolean}
*/
GraphEditor.prototype.handleForce = function (start) {
if (start)
this.force.stop();
this.forceSimulationActive = start;
this.node.each(function (d) {
d.fx = (start) ? null : d.x;
d.fy = (start) ? null : d.y;
});
if (start)
this.force.restart();
this.eventHandler.fire("force_status_changed_on", start);
};
/**
* Handle the parameters of basic filters: node type, view, group
* @param {Object} Required.
*
*/
GraphEditor.prototype.handleFiltersParams = function (filtersParams, notFireEvent) {
this.filter_parameters = (filtersParams !== undefined) ? filtersParams : this.filter_parameters;
this.current_view_id = (this.filter_parameters !== undefined && this.filter_parameters.link.view[0] !== undefined) ? this.filter_parameters.link.view[0] : this.current_view_id
this.cleanAll();
this.refresh();
this.startForce();
this.force.restart();
this._deselectAllNodes();
this.handleForce(this.forceSimulationActive);
if (!notFireEvent)
this.eventHandler.fire("filters_changed", filtersParams);
};
/**
* Add a new node to the graph.
* @param {Object} Required. An object that specifies tha data of the new node.
* @returns {boolean}
*/
GraphEditor.prototype.addNode = function (args) {
if (args.id && args.info && args.info.type) {
args.fixed = true;
this.force.stop();
this.cleanAll();
this.d3_graph.nodes.push(args);
this.refresh();
this.startForce();
this.force.restart();
this.handleForce(this.forceSimulationActive);
return true;
}
return false;
};
/**
* Update the data properties of the node
* @param {Object} Required. An object that specifies tha data of the node.
* @returns {boolean}
*/
GraphEditor.prototype.updateDataNode = function (args) {
};
/**
* Remove a node from graph and related links.
* @param {String} Required. Id of node to remove.
* @returns {boolean}
*/
GraphEditor.prototype.removeNode = function (node) {
if (node != undefined) {
var node_id = node.id;
this.d3_graph['nodes'].forEach(function (n, index, object) {
if (n.id === node_id) {
object.splice(index, 1);
}
});
var self = this;
var links_to_remove = [];
this.d3_graph['links'].forEach(function (l, index, object) {
if (node_id === l.source.id || node_id === l.target.id) {
links_to_remove.push(index);
}
});
var links_removed = 0;
links_to_remove.forEach(function (l_index) {
self.d3_graph['links'].splice(l_index - links_removed, 1);
links_removed++;
});
this.cleanAll();
this.refresh();
this.startForce();
this.force.restart();
return true;
}
return false;
};
/**
* Add a new link to graph.
* @param {Object} Required. An object that specifies tha data of the new Link.
* @returns {boolean}
*/
GraphEditor.prototype.addLink = function (link) {
console.log("addLink" + JSON.stringify(link));
if (link.source && link.target) {
this.force.stop();
this.cleanAll();
this.d3_graph.links.push(link);
this.refresh();
this.startForce();
this.force.restart();
return true;
}
return false;
};
/**
* Remove a link from graph.
* @param {String} Required. The identifier of link to remove.
* @returns {boolean}
*/
GraphEditor.prototype.removeLink = function (link_id) {
var self = this;
if (link_id !== 'undefined') {
this.d3_graph['links'].forEach(function (l, index, object) {
if (link_id === l.index) {
object.splice(index, 1);
self.cleanAll();
self.refresh();
self.startForce();
self.force.restart();
return true;
}
});
}
return false;
};
/**
* Force a refresh of GraphView
* @returns {}
*/
GraphEditor.prototype.refresh = function () {
//log(data)
var self = this;
var link = this.svg
.selectAll()
.data(self.d3_graph.links
.filter(this.link_filter_cb)
);
link.exit().remove();
this.link = link.enter().append("g")
.attr("class", "link cleanable")
.append("path")
.attr("class", "link")
.attr("class", "cleanable")
.style("stroke-width", nominal_stroke)
.style("stroke", function (d) {
return self._link_property_by_type((d.type_link) ? d.type_link : "unrecognized", "color");
})
.attr("marker-end", function (d) {
if (!d.directed_edge)
return '';
var marker_url = (d.type_link) ? d.type_link : "unrecognized"
return (d.directed_edge ? "url(#" + marker_url + ")" : '');
});
var nodeContainer = this.svg
.selectAll()
.data(self.d3_graph.nodes
.filter(this.node_filter_cb));
nodeContainer.exit().remove();
nodeContainer.enter()
.append("g")
// .attr("class", "nodosdads")
.attr("class", "node cleanable");
var nodes_symbols = this.svg.selectAll('.node')
.data(self.d3_graph.nodes
.filter(this.node_filter_cb))
.filter(function (d) {
return (d.info.type === undefined) || (self._node_property_by_type(d.info.type, 'image', d) === undefined)
});
nodes_symbols.exit().remove();
nodes_symbols.append("svg:path")
.attr("d", d3.symbol()
.size(function (d) {
return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d), 2) / 4;
})
.type(function (d) {
// console.log(d.info.type, 'shape', self.current_view_id)
return (self._node_property_by_type(d.info.type, 'shape', d));
})
)
.style("fill", function (d) {
return self._node_property_by_type(d.info.type, 'color', d);
})
.attr("transform", function () {
return "rotate(-45)";
})
.attr("stroke-width", 2.4)
.attr("class", "node_path")
.attr("id", function (d) {
return "path_" + d.id;
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var figure_node = this.svg.selectAll('.node')
.data(self.d3_graph.nodes
.filter(this.node_filter_cb))
.filter(function (d) {
return self._node_property_by_type(d.info.type, 'image', d) != undefined
});
figure_node.exit().remove();
figure_node.append("svg:image")
.attr("xlink:href", function (d) {
return self._node_property_by_type(d.info.type, 'image', d)
})
.attr("x", function (d) {
return -self._node_property_by_type(d.info.type, 'size', d) / 2
})
.attr("y", function (d) {
return -self._node_property_by_type(d.info.type, 'size', d) / 2
})
.attr("width", function (d) {
return self._node_property_by_type(d.info.type, 'size', d)
})
.attr("height", function (d) {
return self._node_property_by_type(d.info.type, 'size', d)
})
.style("stroke", "black")
.style("stroke-width", "1px")
.attr("class", "node_path")
.attr("id", function (d) {
return "path_" + d.id;
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
figure_node.append("svg:path")
.attr("d", d3.symbol()
.size(function (d) {
return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d) + 7, 2) / 4;
})
.type(function (d) {
return (self.get_d3_symbol('circle'));
})
)
.style("fill", 'transparent')
.attr("transform", function () {
return "rotate(-45)";
})
.attr("stroke-width", 2.4)
.attr("class", "hidden_circle")
.attr("id", function (d) {
return "path_" + d.id;
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
this.node = this.svg.selectAll('.node')
.data(self.d3_graph.nodes
.filter(this.node_filter_cb)).selectAll("image, path, circle");
this.node.on("contextmenu", self.behavioursOnEvents.nodes["contextmenu"])
.on("mouseover", self.behavioursOnEvents.nodes["mouseover"])
.on("mouseout", self.behavioursOnEvents.nodes["mouseout"])
.on('click', self.behavioursOnEvents.nodes["click"])
.on('dblclick', self.behavioursOnEvents.nodes["dblclick"]);
this.link
.on("contextmenu", self.behavioursOnEvents.links["contextmenu"])
.on("mouseover", self.behavioursOnEvents.links["mouseover"])
.on('click', self.behavioursOnEvents.links["click"])
.on("mouseout", self.behavioursOnEvents.links["mouseout"]);
this.text = this.svg.selectAll(".node")
.data(self.d3_graph.nodes
.filter(this.node_filter_cb))
.append("svg:text")
.attr("class", "node_text cleanable")
.attr("dy", function (d) {
return "-5";
})
.attr("pointer-events", "none")
.style("font-size", nominal_text_size + "px")
.style("font-family", "'Source Sans Pro','Helvetica Neue',Helvetica,Arial,sans-serif")
.style("fill", function (d) {
return self._node_property_by_type(d.info.type, 'node_label_color', d);
})
//.style("text-anchor", "middle")
.text(function (d) {
if(d.info && d.info.property.custom_label && d.info.property.custom_label !==''){
return d.info.property.custom_label
} else
return d.id;
});
function dragstarted(d) {
d.draggednode = true;
if (!d3.event.active) self.force.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
d.draggednode = false;
if (!d3.event.active) self.force.alphaTarget(0);
if (self.forceSimulationActive) {
d.fx = null;
d.fy = null;
} else {
d.fx = d.x;
d.fy = d.y;
self.force.stop();
self.forceSimulationActive = false;
}
}
};
/**
* Start force layout on Graph.
*
*/
GraphEditor.prototype.startForce = function () {
this.force.stop();
var self = this;
this.force
.nodes(this.d3_graph.nodes)
.on("tick", ticked);
this.force
.force("link")
.links(this.d3_graph.links);
function ticked() {
self.node.attr("cx", function (d) {
return d.x = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.width - self._node_property_by_type(d.info.type, 'size', d), d.x));
})
.attr("cy", function (d) {
return d.y = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.height - self._node_property_by_type(d.info.type, 'size', d), d.y));
});
self.link.attr("d", function (d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "," + d.target.x + "," + d.target.y;
});
self.node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
self.text.attr("transform", function (d) {
var label_pos_y = d.y + self._node_property_by_type(d.info.type, 'size', d)/2 +nominal_text_size;
return "translate(" + d.x + "," + label_pos_y + ")";
});
}
};
/**
* This method attaches an event handler.
* @param {String} Required. A String that specifies the name of the event.
* @param {Function} Required. Specifies the function to run when the event occurs.
* @returns {}
*/
GraphEditor.prototype.addListener = function (event_name, cb) {
this.eventHandler.addL(event_name, cb);
};
/**
* This method removes an event handler that has been attached with the addListener() method.
* @param {String} Required. A String that specifies the name of the event to remove.
* @param {Function} Required. Specifies the function to remove.
* @returns {}
*/
GraphEditor.prototype.removeListener = function (event_name, cb) {
};
GraphEditor.prototype.setNodeClass = function (class_name, filter_cb) {
log("setNodeClass");
var self = this;
this.svg.selectAll('.node').classed(class_name, false);
this.svg.selectAll('.node')
.classed(class_name, filter_cb);
};
GraphEditor.prototype.setLinkClass = function (class_name, filter_cb) {
log("setLinkClass");
var self = this;
this.svg.selectAll('.link').classed(class_name, false);
this.svg.selectAll('.link')
.classed(class_name, filter_cb);
};
GraphEditor.prototype.showNodeInfo = function (args) {
this.addLinesToPopup(args['node_info'], "Info about node selected")
this.handlePopupVisibility(true, 'right')
};
GraphEditor.prototype.addLinesToPopup = function (data, title) {
var self = this;
var index = 1;
var translate_y = 0;
var width_popup = 400;
var height_popup = 0;
d3.selectAll(".popupcleanable").remove(); // clean
var popupbg = d3.select(".popup").append("rect")
.attr("id", "popupbg")
.attr("class", "popup bg popupcleanable cleanable")
.attr("width", "400")
.attr("height", "0")
.attr("rx", 10) // set the x corner curve radius
.attr("ry", 10); // set the y corner curve radius
d3.select(".popup").append("svg:path")
.attr("d", d3.symbol()
.size(function (d) {
return 80
})
.type(function (d) {
console.log("popup")
return (self.get_d3_symbol());
})
)
.style("fill", 'red')
.attr("transform", function () {
return "translate(380,15) rotate(-45)";
})
.attr("stroke-width", 2.4)
.attr("id", "close_popup")
.attr("class", "popupcleanable cleanable")
.on("click", function (d) {
self.handlePopupVisibility(false);
});
d3.select(".popup").append("text")
.attr("class", "popup title popupcleanable cleanable")
.attr("x", "10")
.attr("y", "20")
.text(title);
for (var i in data) {
//console.log(i, data, data[i])
//var typeofvalue = typeof data[i];
var record = data[i];
index = this._addRecordToPopup(i, record, index)
}
};
GraphEditor.prototype._addRecordToPopup = function (key, record, index, tab) {
//console.log("_addRecordToPopup", key, record, index)
var translate_y = 23 * index;
var summary = d3.select(".popup").append("g")
.attr("class", "popup summary d popupcleanable cleanable")
.attr("transform", "translate(10 " + translate_y + ")");
if (Object.prototype.toString.call(record) !== '[object Array]') { //is a record simple key:value
//console.log(key, record)
var summary_g = summary.append("g");
summary_g.append("rect")
.attr("class", "popup summary bg popupcleanable cleanable")
.attr("width", "380")
.attr("height", "20");
summary_g.append("text")
.attr("class", "popup summary popupcleanable cleanable")
.attr("x", (tab) ? tab : 10)
.attr("y", "17")
.attr("width", "100")
.text(function (d) {
return key.toUpperCase() + ":";
});
summary_g.append("text")
.attr("class", "popup summary popupcleanable cleanable")
.attr("x", "370")
.attr("y", "17")
.attr("text-anchor", "end")
.text(function (d) {
return record
});
}
else {//is a record simple complex: have a list of sub record key:value
//index ++;
this._addRecordToPopup(key, "", index)
for (var r in record) {
//console.log(i, r, record, record[r])
for (var k in record[r]) {
//console.log(i, r, k, record[r][k])
var curr_key = k;
var recordValue = record[r][k]
index++;
this._addRecordToPopup(curr_key, recordValue, index, 20)
}
}
}
translate_y = 30 * index++;
d3.select('#popupbg').attr("height", translate_y);
return index;
};
/**
* Remove all the graph objects from the view
*/
GraphEditor.prototype.cleanAll = function () {
this.svg.selectAll('.cleanable').remove();
};
/**
* Internal functions
*/
GraphEditor.prototype._node_property_by_type = function (type, property, node) {
//console.log(type, property, layer, group)
var unrecognized = function (ui_prop, property) {
return ui_prop['unrecognized'][property]
};
//type recognized
if (this.type_property[type]) {
if (this.type_property[type]['property']) {
var filt_property = this.type_property[type]['property']
return this.type_property[type][node.info[filt_property]][property]
} else { // type without property spec
return this.type_property[type][property]
}
} else { //type unrecognized
return unrecognized(this.type_property, property)
}
};
GraphEditor.prototype._link_property_by_type = function (type, property) {
//log(type + "-" + property)
if (this.type_property_link[type] != undefined && this.type_property_link[type][property] != undefined) {
//if(property == "shape")
// log("dentro" + this.type_property[type][property])
return this.type_property_link[type][property];
} else {
return this.type_property_link['unrecognized'][property];
}
}
/**
*
*
*
*/
GraphEditor.prototype._setupFiltersBehaviors = function (args) {
var self = this;
this.node_filter_cb = args.node_filter_cb || function (d) {
var cond_view = true,
cond_group = true;
//log(d.info.type + " " + self.filter_parameters.node.type + " group: " + self.filter_parameters.node.group + "- " + d.info.group)
// check filter by node type
if (self.filter_parameters.node.type.length > 0) {
if (self.filter_parameters.node.type.indexOf(d.info.type) < 0)
cond_view = false;
}
// check filter by group
if (self.filter_parameters.node.group.length > 0) {
self.filter_parameters.node.group.forEach(function (group) {
if (d.info.group.indexOf(group) < 0)
cond_group = false;
});
}
return cond_view && cond_group;
};
this.link_filter_cb = args.link_filter_cb || function (d) {
var cond_view = true,
cond_group = true;
// check filter by view
if (self.filter_parameters.link.view.length > 0) {
self.filter_parameters.link.view.forEach(function (view) {
if (d.view.indexOf(view) < 0)
cond_view = false;
});
}
// check filter by group
if (self.filter_parameters.link.group.length > 0) {
self.filter_parameters.link.group.forEach(function (group) {
if (d.group.indexOf(group) < 0)
cond_group = false;
});
}
return cond_view && cond_group;
};
};
/**
*
*
*/
GraphEditor.prototype._setupBehaviorsOnEvents = function () {
log("_setupBehaviorsOnEvents");
var self = this;
this.behavioursOnEvents = {
'nodes': {
'click': function (d) {
d3.event.preventDefault();
log('click', d);
if (self.lastKeyDown === SHIFT_BUTTON && self._selected_node !== undefined) {
var source_id = self._selected_node.id;
var target_id = d.id;
log("--" + JSON.stringify(self.filter_parameters.link.view));
var new_link = {
source: source_id,
target: target_id,
view: self.filter_parameters.link.view[0],
group: self.filter_parameters.link.group[0],
};
self.addLink(new_link);
self._deselectAllNodes();
} else {
self._selectNodeExclusive(this, d);
}
},
'mouseover': function (d) {
},
'mouseout': function (d) {
},
'dblclick': function (d) {
d3.event.preventDefault();
log('dblclick');
},
'contextmenu': function (d, i) {
d3.event.preventDefault();
log("contextmenu node");
self.eventHandler.fire("right_click_node", d);
}
},
'links': {
'click': function (event) {
},
'dblclick': function (event) {
}
}
};
};
/**
* Deselect previously selected nodes
*
*/
GraphEditor.prototype._deselectAllNodes = function () {
log("_deselectAllNodes");
this.node.classed("node_selected", false);
this._selected_node = undefined;
};
GraphEditor.prototype._deselectAllLinks = function () {
log("_deselectAllLinks");
this.link.classed("link_selected", false).style('stroke-width', 2);
this._selected_link = undefined;
};
/**
* Select node in exclusive mode
* @param {Object} Required. Element selected on click event
*/
GraphEditor.prototype._selectNodeExclusive = function (node_instance, node_id) {
log("_selectNodeExclusive ");
var activeClass = "node_selected";
var alreadyIsActive = d3.select(node_instance).classed(activeClass);
this._deselectAllNodes();
this._deselectAllLinks();
d3.select(node_instance).classed(activeClass, !alreadyIsActive);
this._selected_node = (alreadyIsActive) ? undefined : node_instance.__data__;
if(this._selected_node){
this.eventHandler.fire("node:selected", this._selected_node)
} else {
this.eventHandler.fire("node:deselected", this._selected_node)
}
};
/**
* Select node in exclusive mode
* @param {Object} Required. Element selected on click event
*/
GraphEditor.prototype._selectLinkExclusive = function (link_instance, link_id) {
log("_selectLinkExclusive ");
var activeClass = "link_selected";
var alreadyIsActive = d3.select(link_instance).classed(activeClass);
this._deselectAllNodes();
this._deselectAllLinks();
d3.select(link_instance).classed(activeClass, !alreadyIsActive);
d3.select(link_instance).style('stroke-width', 4)
this._selected_link = link_instance.__data__;
};
/**
* Callback to resize SVG element on window resize
*/
GraphEditor.prototype.resizeSvg = function (width, height) {
log("resizeSvg");
//log(event);
this.width = width || this.width;
this.height = height || this.height;
this.svg.attr('width', width);
this.svg.attr('height', height);
}
GraphEditor.prototype.handlePopupVisibility = function (visible, side) {
var opacity = (visible) ? 1 : 0;
var translate_op = (side === "left") ? "translate(50 50)" : "translate(" + (this.width - 450).toString() + " 50)";
if (!visible) {
d3.selectAll(".popupcleanable").remove();
d3.select(".popup")
.attr("transform", "translate(-1 -1)");
} else {
d3.select(".popup")
.attr("transform", translate_op);
}
d3.select(".popup").attr("opacity", opacity);
};
GraphEditor.prototype.refreshGraphParameters = function (graphParameters) {
this.eventHandler.fire("refresh_graph_parameters", graphParameters);
};
/**
* Log utility
*/
function log(text) {
if (DEBUG)
console.log("::GraphEditor::", text);
}
return GraphEditor;
}(this));
if (typeof module === 'object') {
module.exports = TCD3.GraphEditor;
}