2 Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
3 Copyright 2018 EveryUP srl
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
9 http://www.apache.org/licenses/LICENSE-2.0
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
17 if (typeof TCD3
=== 'undefined') {
21 TCD3
.ModelGraphEditor
= (function () {
25 var SHIFT_BUTTON
= 16;
26 var IMAGE_PATH
= "/static/assets/img/";
29 ModelGraphEditor
.prototype = new TCD3
.GraphEditor();
30 ModelGraphEditor
.prototype.constructor = ModelGraphEditor
;
31 ModelGraphEditor
.prototype.parent
= TCD3
.GraphEditor
.prototype;
36 function ModelGraphEditor(args
) {
43 ModelGraphEditor
.prototype.init = function (args
) {
44 this.parent
.init
.call(this, args
);
46 this.desc_id
= args
.desc_id
|| undefined; //TODO remove it
48 this.type_property
= {};
49 this.type_property
["unrecognized"] = args
.gui_properties
["nodes"]["default"];
51 this._edit_mode
= args
.edit_mode
|| false;
53 Object
.keys(args
.gui_properties
["nodes"]).forEach(function (key
, index
) {
55 this.type_property
[key
] = args
.gui_properties
["nodes"][key
];
57 this.type_property
[key
]["shape"] = this.parent
.get_d3_symbol(this.type_property
[key
]["shape"]);
58 if (this.type_property
[key
]["image"] !== undefined) {
59 this.type_property
[key
]["image"] = IMAGE_PATH
+ this.type_property
[key
]["image"];
64 if (args
.gui_properties
["edges"]) {
65 this.type_property_link
= args
.gui_properties
["edges"];
66 var link_types
= ['unrecognized'].concat(Object
.keys(self
.type_property_link
))
67 this.defs
.selectAll("marker")
70 .append("svg:marker") // This section adds in the arrows
71 .attr("id", function (d
) {
74 .attr("viewBox", "-5 -5 10 10")
75 .attr("refX", 13) /*must be smarter way to calculate shift*/
77 .attr("markerUnits", "userSpaceOnUse")
78 .attr("markerWidth", 12)
79 .attr("markerHeight", 12)
80 .attr("orient", "auto")
82 .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
83 .attr('fill', function (d
) {
84 return self
.type_property_link
[d
].color
;
88 this.customBehavioursOnEvents
= args
.behaviorsOnEvents
|| undefined;
91 var data_url
= args
.data_url
|| undefined;
92 if (!args
.graph_data
&& args
.data_url
) {
93 d3
.json(data_url
, function (error
, data
) {
94 //console.log(JSON.stringify(data))
95 self
.d3_graph
.nodes
= data
.vertices
;
96 self
.d3_graph
.links
= data
.edges
;
97 self
.d3_graph
.graph_parameters
= data
.graph_parameters
;
98 self
.model
= data
.model
;
99 self
.refreshGraphParameters(self
.d3_graph
.graph_parameters
);
102 //if(args.filter_base != undefined)
104 setTimeout(function () {
105 self
.handleForce(true);
106 self
.handleFiltersParams(args
.filter_base
);
111 this.updateData(args
)
116 * Update data of the graph.
117 * @param {Object} Required. An object that specifies tha data of the new node.
120 ModelGraphEditor
.prototype.updateData = function (args
) {
121 this.d3_graph
.nodes
= args
.vertices
;
122 this.d3_graph
.links
= args
.edges
;
123 this.d3_graph
.graph_parameters
= args
.graph_parameters
;
124 this.model
= args
.model
;
125 this.refreshGraphParameters(this.d3_graph
.graph_parameters
);
129 this.handleForce(this.forceSimulationActive
);
130 //this.force.restart();
131 //if(args.filter_base != undefined)
133 if(args
.filter_base
){
135 setTimeout(function () {
136 self
.handleForce(true);
137 self
.handleFiltersParams(args
.filter_base
);
143 * Add a new node to the graph.
144 * @param {Object} Required. An object that specifies tha data of the new node.
147 ModelGraphEditor
.prototype.addNode = function (node
, success
, error
) {
149 var current_layer
= self
.getCurrentView();
150 var node_type
= node
.info
.type
;
151 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].addable
) {
152 if (self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
) {
153 console
.log(self
.model
.callback
)
154 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
].class;
155 var controller
= new TCD3
.OsmController();
156 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
](self
, node
, function (result
) {
159 self
.updateData(result
);
160 // self.parent.addNode.call(self, node);
161 success
&& success();
166 log('addNode: callback undefined in model spec.');
167 error
&& error("You can't add a " + node
.info
.type
+ ", callback undefined.");
170 //FIXME Error handling????
171 log("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
172 error
&& error("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
178 * Update the data properties of the node
179 * @param {Object} Required. An object that specifies tha data of the node.
182 ModelGraphEditor
.prototype.updateDataNode = function (node
, args
, success
, error
) {
185 var controller
= new TCD3
.OsmController();
186 controller
.updateNode(this,node
, args
, function(result
){
187 self
.updateData(result
);
188 success
&& success();
193 * Update the data properties of the node
194 * @param {Object} Required. An object that specifies tha data of the node.
197 ModelGraphEditor
.prototype.updateGraphParams = function (args
, success
, error
) {
199 var controller
= new TCD3
.OsmController();
200 controller
.updateGraphParams(args
, function(result
){
201 self
.updateData(result
);
202 success
&& success();
207 * Remove a node from graph and related links.
208 * @param {String} Required. Id of node to remove.
211 ModelGraphEditor
.prototype.removeNode = function (node
, success
, error
) {
212 console
.log('removeNode', JSON
.stringify(node
))
214 var current_layer
= self
.getCurrentView();
215 var node_type
= node
.info
.type
;
216 if (node
.info
.desc_id
== undefined) {
217 node
.info
.desc_id
= self
.desc_id
;
219 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].removable
) {
220 if (self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
) {
221 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
].class;
222 var controller
= new TCD3
.OsmController();
223 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
](self
, node
, function (result
) {
224 self
._deselectAllNodes();
225 self
.updateData(result
);
226 success
&& success();
230 log('removeNode: callback undefined in model spec.');
231 error
&& error("You can't remove a " + node
.info
.type
+ ", callback undefined.");
234 //FIXME we need to manage alert in a different way: FAILBACK
235 log("You can't remove a " + node
.info
.type
);
236 error
&& error("You can't remove a " + node
.info
.type
);
241 * Add a new link to graph.
242 * @param {Object} Required. An object that specifies tha data of the new Link.
245 ModelGraphEditor
.prototype.addLink = function (s
, d
, success
, error
) {
247 var source_id
= s
.id
;
248 var target_id
= d
.id
;
249 var source_type
= s
.info
.type
;
250 var destination_type
= d
.info
.type
;
254 view
: this.filter_parameters
.link
.view
[0],
255 group
: this.filter_parameters
.link
.group
,
256 desc_id
: this.desc_id
258 log("addLink: " + JSON
.stringify(link
))
259 var current_layer
= self
.getCurrentView()
260 if (self
.model
.layer
[current_layer
].allowed_edges
&& self
.model
.layer
[current_layer
].allowed_edges
[source_type
] && self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
]) {
262 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
) {
263 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
;
264 console
.log(callback
, self
.model
.callback
)
265 var direct_edge
= 'direct_edge' in self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
] ? self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
]['direct_edge'] : false;
266 link
.directed_edge
= direct_edge
;
267 var c
= self
.model
.callback
[callback
].class;
268 var controller
= new TCD3
.OsmController();
269 controller
[callback
](self
, link
, function (result
) {
270 self
._deselectAllNodes();
271 self
.updateData(result
);
272 success
&& success();
275 log('addLink: callback undefined in model spec.');
276 error
&& error("You can't add a link, callback undefined.");
280 //FIXME we need to manage alert in a different way: FAILBACK
281 log("You can't link a " + source_type
+ " with a " + destination_type
);
283 error
&& error("You can't link a " + source_type
+ " with a " + destination_type
);
288 * Remove a link from graph.
289 * @param {String} Required. The identifier of link to remove.
292 ModelGraphEditor
.prototype.removeLink = function (link
, success
, error
) {
296 var source_type
= s
.info
.type
;
297 var destination_type
= d
.info
.type
;
298 var current_layer
= self
.getCurrentView();
299 console
.log(self
.model
.layer
[current_layer
])
300 if (self
.model
.layer
[current_layer
].allowed_edges
&& self
.model
.layer
[current_layer
].allowed_edges
[source_type
] && self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
] &&
301 self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
303 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
) {
304 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
;
305 var c
= self
.model
.callback
[callback
].class;
306 var controller
= new TCD3
.OsmController();
307 controller
[callback
](self
, link
, function (result
) {
308 self
._deselectAllNodes();
309 self
._deselectAllLinks();
311 self
.updateData(result
);
312 // success && success();
313 success
&& success();
316 log('removeLink: callback undefined in model spec.');
317 error
&& error("You can't remove a link, callback undefined.");
321 //FIXME we need to manage alert in a different way: FAILBACK
322 log("You can't delete the link");
323 error
&& error("You can't delete the link");
330 ModelGraphEditor
.prototype.savePositions = function (data
) {
332 this.node
.each(function (d
) {
334 vertices
[d
.id
]['x'] = d
.x
;
335 vertices
[d
.id
]['y'] = d
.y
;
337 new TCD3
.GraphRequests().savePositions({
347 ModelGraphEditor
.prototype._setupBehaviorsOnEvents = function (layer
) {
350 var contextMenuLinksAction
= [{
351 title
: 'Delete Link',
352 action: function (elm
, link
, i
) {
353 self
.removeLink(link
, null, showAlert
);
357 var contextMenuNodesAction
= [
360 action: function (elm
, d
, i
) {
361 self
.removeNode(d
, null, showAlert
);
367 if (this.customBehavioursOnEvents
) {
368 contextMenuNodesAction
= contextMenuNodesAction
.concat(this.customBehavioursOnEvents
['behaviors'].nodes
);
372 if (self
.model
&& self
.model
.layer
&& self
.model
.layer
[layer
] && self
.model
.layer
[layer
].action
&& self
.model
.layer
[layer
].action
.node
) {
373 for (var i
in self
.model
.layer
[layer
].action
.node
) {
374 var action
= self
.model
.layer
[layer
].action
.node
[i
]
375 contextMenuNodesAction
.push({
377 action: function (elm
, d
, i
) {
378 var callback
= action
.callback
;
379 var c
= self
.model
.callback
[callback
].class;
380 var controller
= new TCD3
[c
]();
387 controller
[callback
](self
, args
);
389 edit_mode
: (action
.edit_mode
!== undefined) ? action
.edit_mode
: undefined
394 this.behavioursOnEvents
= {
396 'click': function (d
) {
398 d3
.event
.preventDefault();
400 if (self
._edit_mode
&& self
.lastKeyDown
=== SHIFT_BUTTON
&& self
._selected_node
!== undefined) {
401 self
.addLink(self
._selected_node
, d
, null, showAlert
);
403 self
._selectNodeExclusive(this, d
);
407 'mouseover': function (d
) {
408 self
.link
.style('stroke-width', function (l
) {
409 if (d
=== l
.source
|| d
=== l
.target
)
415 'mouseout': function (d
) {
416 self
.link
.style('stroke-width', 2);
418 'contextmenu': d3
.contextMenu(contextMenuNodesAction
, {
419 'edit_mode': self
._edit_mode
,
421 'type_object': 'node'
425 'click': function (d
) {
426 self
._selectLinkExclusive(this, d
);
428 'dblclick': function (event
) {
431 'mouseover': function (d
) {
432 d3
.select(this).style('stroke-width', 4);
434 'mouseout': function (d
) {
435 if (d
!== self
._selected_link
)
436 d3
.select(this).style('stroke-width', 2);
438 'contextmenu': d3
.contextMenu(contextMenuLinksAction
, {
439 'edit_mode': self
._edit_mode
,
441 'type_object': 'link'
447 ModelGraphEditor
.prototype.handleFiltersParams = function (filtersParams
, notFireEvent
) {
449 this.parent
.handleFiltersParams
.call(this, filtersParams
, notFireEvent
);
450 if (filtersParams
&& filtersParams
.link
&& filtersParams
.link
.view
)
451 this._setupBehaviorsOnEvents(filtersParams
.link
.view
[0]);
454 ModelGraphEditor
.prototype.getAvailableNodes = function () {
455 log('getAvailableNodes');
457 if (this.model
&& this.model
.layer
[this.getCurrentView()] !== undefined)
458 return this.model
.layer
[this.getCurrentView()].nodes
;
463 ModelGraphEditor
.prototype.getTypeProperty = function () {
464 return this.type_property
;
467 ModelGraphEditor
.prototype.getCurrentGroup = function () {
468 return this.filter_parameters
.node
.group
[0];
471 ModelGraphEditor
.prototype.getCurrentView = function () {
472 return this.filter_parameters
.link
.view
[0];
474 ModelGraphEditor
.prototype.getCurrentFilters = function () {
475 return this.filter_parameters
;
478 ModelGraphEditor
.prototype.getGraphParams = function () {
479 return this.d3_graph
.graph_parameters
;
487 console
.log("::ModelGraphEditor::", text
);
491 return ModelGraphEditor
;
496 if (typeof module
=== 'object') {
497 module
.exports
= TCD3
.ModelGraphEditor
;