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 console
.log("updateData")
122 this.d3_graph
.nodes
= args
.graph_data
.vertices
;
123 this.d3_graph
.links
= args
.graph_data
.edges
;
124 this.d3_graph
.graph_parameters
= args
.graph_parameters
;
125 this.model
= args
.model
;
126 this.refreshGraphParameters(this.d3_graph
.graph_parameters
);
129 //if(args.filter_base != undefined)
131 //if(args.filter_base){
133 setTimeout(function () {
134 self
.handleForce(true);
135 self
.handleFiltersParams(args
.filter_base
);
141 * Add a new node to the graph.
142 * @param {Object} Required. An object that specifies tha data of the new node.
145 ModelGraphEditor
.prototype.addNode = function (node
, success
, error
) {
147 var current_layer
= self
.getCurrentView();
148 var node_type
= node
.info
.type
;
150 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].addable
) {
151 if (self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
) {
152 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
].class;
153 var controller
= new TCD3
[c
]();
154 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
](self
, node
, function () {
155 self
.parent
.addNode
.call(self
, node
);
156 success
&& success();
161 log('addNode: callback undefined in model spec.');
162 error
&& error("You can't add a " + node
.info
.type
+ ", callback undefined.");
165 //FIXME Error handling????
166 log("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
167 error
&& error("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
173 * Update the data properties of the node
174 * @param {Object} Required. An object that specifies tha data of the node.
177 ModelGraphEditor
.prototype.updateDataNode = function (args
) {
178 //FIXME updating a node properties need commit to server side!
179 this.parent
.updateDataNode
.call(this, args
);
183 * Remove a node from graph and related links.
184 * @param {String} Required. Id of node to remove.
187 ModelGraphEditor
.prototype.removeNode = function (node
, success
, error
) {
188 console
.log('removeNode', JSON
.stringify(node
))
190 var current_layer
= self
.getCurrentView();
191 var node_type
= node
.info
.type
;
192 if (node
.info
.desc_id
== undefined) {
193 node
.info
.desc_id
= self
.desc_id
;
195 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].removable
) {
196 if (self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
) {
197 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
].class;
198 var controller
= new TCD3
[c
]();
199 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
](self
, node
, function () {
200 self
.parent
.removeNode
.call(self
, node
);
201 success
&& success();
205 log('removeNode: callback undefined in model spec.');
206 error
&& error("You can't remove a " + node
.info
.type
+ ", callback undefined.");
209 //FIXME we need to manage alert in a different way: FAILBACK
210 log("You can't remove a " + node
.info
.type
);
211 error
&& error("You can't remove a " + node
.info
.type
);
216 * Add a new link to graph.
217 * @param {Object} Required. An object that specifies tha data of the new Link.
220 ModelGraphEditor
.prototype.addLink = function (s
, d
, success
, error
) {
222 var source_id
= s
.id
;
223 var target_id
= d
.id
;
224 var source_type
= s
.info
.type
;
225 var destination_type
= d
.info
.type
;
229 view
: this.filter_parameters
.link
.view
[0],
230 group
: this.filter_parameters
.link
.group
,
231 desc_id
: this.desc_id
233 log("addLink: " + JSON
.stringify(link
))
234 var current_layer
= self
.getCurrentView()
235 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
]) {
237 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
) {
238 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
;
239 console
.log(callback
, self
.model
.callback
)
240 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;
241 link
.directed_edge
= direct_edge
;
242 var c
= self
.model
.callback
[callback
].class;
243 var controller
= new TCD3
[c
]();
244 controller
[callback
](self
, link
, function () {
245 self
._deselectAllNodes();
246 self
.parent
.addLink
.call(self
, link
);
251 log('addLink: callback undefined in model spec.');
252 error
&& error("You can't add a link, callback undefined.");
256 //FIXME we need to manage alert in a different way: FAILBACK
257 log("You can't link a " + source_type
+ " with a " + destination_type
);
259 error
&& error("You can't link a " + source_type
+ " with a " + destination_type
);
264 * Remove a link from graph.
265 * @param {String} Required. The identifier of link to remove.
268 ModelGraphEditor
.prototype.removeLink = function (link
, success
, error
) {
272 var source_type
= s
.info
.type
;
273 var destination_type
= d
.info
.type
;
274 var current_layer
= self
.getCurrentView()
275 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
] &&
276 self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
278 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
) {
279 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
;
280 var c
= self
.model
.callback
[callback
].class;
281 var controller
= new TCD3
[c
]();
282 controller
[callback
](self
, link
, function () {
283 self
._deselectAllNodes();
284 self
._deselectAllLinks();
285 self
.parent
.removeLink
.call(self
, link
.index
);
286 success
&& success();
289 log('removeLink: callback undefined in model spec.');
290 error
&& error("You can't remove a link, callback undefined.");
294 //FIXME we need to manage alert in a different way: FAILBACK
295 log("You can't delete the link");
296 error
&& error("You can't delete the link");
303 ModelGraphEditor
.prototype.savePositions = function (data
) {
305 this.node
.each(function (d
) {
307 vertices
[d
.id
]['x'] = d
.x
;
308 vertices
[d
.id
]['y'] = d
.y
;
310 new TCD3
.GraphRequests().savePositions({
320 ModelGraphEditor
.prototype._setupBehaviorsOnEvents = function (layer
) {
323 var contextMenuLinksAction
= [{
324 title
: 'Delete Link',
325 action: function (elm
, link
, i
) {
326 self
.removeLink(link
, null, showAlert
);
330 var contextMenuNodesAction
= [{
332 action: function (elm
, d
, i
) {
333 if (d
.info
.type
!= undefined) {
334 self
.eventHandler
.fire("edit_descriptor", self
.project_id
, d
);
342 action: function (elm
, d
, i
) {
343 self
.removeNode(d
, null, showAlert
);
349 if (this.customBehavioursOnEvents
) {
350 contextMenuNodesAction
= contextMenuNodesAction
.concat(this.customBehavioursOnEvents
['behaviors'].nodes
);
354 if (self
.model
&& self
.model
.layer
&& self
.model
.layer
[layer
] && self
.model
.layer
[layer
].action
&& self
.model
.layer
[layer
].action
.node
) {
355 for (var i
in self
.model
.layer
[layer
].action
.node
) {
356 var action
= self
.model
.layer
[layer
].action
.node
[i
]
357 contextMenuNodesAction
.push({
359 action: function (elm
, d
, i
) {
360 var callback
= action
.callback
;
361 var c
= self
.model
.callback
[callback
].class;
362 var controller
= new TCD3
[c
]();
369 controller
[callback
](self
, args
);
371 edit_mode
: (action
.edit_mode
!== undefined) ? action
.edit_mode
: undefined
376 this.behavioursOnEvents
= {
378 'click': function (d
) {
380 d3
.event
.preventDefault();
382 if (self
._edit_mode
&& self
.lastKeyDown
=== SHIFT_BUTTON
&& self
._selected_node
!== undefined) {
383 self
.addLink(self
._selected_node
, d
, null, showAlert
);
385 self
._selectNodeExclusive(this, d
);
389 'mouseover': function (d
) {
390 self
.link
.style('stroke-width', function (l
) {
391 if (d
=== l
.source
|| d
=== l
.target
)
397 'mouseout': function (d
) {
398 self
.link
.style('stroke-width', 2);
400 'contextmenu': d3
.contextMenu(contextMenuNodesAction
, {
401 'edit_mode': self
._edit_mode
,
403 'type_object': 'node'
407 'click': function (d
) {
408 self
._selectLinkExclusive(this, d
);
411 'dblclick': function (event
) {
414 'mouseover': function (d
) {
415 d3
.select(this).style('stroke-width', 4);
417 'mouseout': function (d
) {
418 if (d
!== self
._selected_link
)
419 d3
.select(this).style('stroke-width', 2);
421 'contextmenu': d3
.contextMenu(contextMenuLinksAction
, {
422 'edit_mode': self
._edit_mode
,
424 'type_object': 'link'
430 ModelGraphEditor
.prototype.handleFiltersParams = function (filtersParams
, notFireEvent
) {
432 this.parent
.handleFiltersParams
.call(this, filtersParams
, notFireEvent
);
433 if (filtersParams
&& filtersParams
.link
&& filtersParams
.link
.view
)
434 this._setupBehaviorsOnEvents(filtersParams
.link
.view
[0]);
437 ModelGraphEditor
.prototype.getAvailableNodes = function () {
438 log('getAvailableNodes');
440 if (this.model
&& this.model
.layer
[this.getCurrentView()] !== undefined)
441 return this.model
.layer
[this.getCurrentView()].nodes
;
446 ModelGraphEditor
.prototype.getTypeProperty = function () {
447 return this.type_property
;
450 ModelGraphEditor
.prototype.getCurrentGroup = function () {
451 return this.filter_parameters
.node
.group
[0];
455 ModelGraphEditor
.prototype.getCurrentView = function () {
456 return this.filter_parameters
.link
.view
[0];
458 ModelGraphEditor
.prototype.getCurrentFilters = function () {
459 return this.filter_parameters
;
462 ModelGraphEditor
.prototype.getGraphParams = function () {
463 return this.d3_graph
.graph_parameters
;
471 console
.log("::ModelGraphEditor::", text
);
475 return ModelGraphEditor
;
480 if (typeof module
=== 'object') {
481 module
.exports
= TCD3
.ModelGraphEditor
;