2 Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
16 if (typeof dreamer
=== 'undefined') {
21 dreamer
.ModelGraphEditor
= (function (global
) {
25 var SHIFT_BUTTON
= 16;
26 var IMAGE_PATH
= "/static/assets/img/";
27 var GUI_VERSION
= "v1";
30 ModelGraphEditor
.prototype = new dreamer
.GraphEditor();
31 ModelGraphEditor
.prototype.constructor = ModelGraphEditor
;
32 ModelGraphEditor
.prototype.parent
= dreamer
.GraphEditor
.prototype;
37 function ModelGraphEditor(args
) {
44 ModelGraphEditor
.prototype.init = function (args
) {
45 this.parent
.init
.call(this, args
);
47 if (args
.gui_properties
[GUI_VERSION
] != undefined) {
48 args
.gui_properties
= args
.gui_properties
[GUI_VERSION
];
51 this.desc_id
= args
.desc_id
|| undefined;
52 this.type_property
= {};
53 this.type_property
["unrecognized"] = args
.gui_properties
["default"];
54 this.type_property
["unrecognized"]["default_node_label_color"] = args
.gui_properties
["default"]["label_color"];
55 //this.type_property["unrecognized"]["shape"] = d3.symbolCross;
56 this._edit_mode
= (args
.edit_mode
!= undefined) ? args
.edit_mode
: this._edit_mode
;
58 Object
.keys(args
.gui_properties
["nodes"]).forEach(function (key
, index
) {
59 console
.log(key
+ " ####")
60 this.type_property
[key
] = args
.gui_properties
["nodes"][key
];
61 if ( this.type_property
[key
]['property'] != undefined){
62 for(var c_prop
in this.type_property
[key
]){
63 if(c_prop
!= 'property'){
65 this.type_property
[key
][c_prop
]['shape'] = this.parent
.get_d3_symbol(this.type_property
[key
][c_prop
]['shape']);
66 if(this.type_property
[key
][c_prop
]["image"] != undefined){
67 this.type_property
[key
][c_prop
]["image"] = IMAGE_PATH
+ this.type_property
[key
][c_prop
]["image"]
74 this.type_property
[key
]["shape"] = this.parent
.get_d3_symbol(this.type_property
[key
]["shape"]);
75 if (this.type_property
[key
]["image"] != undefined) {
76 this.type_property
[key
]["image"] = IMAGE_PATH
+ this.type_property
[key
]["image"];
83 if(args
.gui_properties
["edges"]){
84 this.type_property_link
= args
.gui_properties
["edges"];
86 var link_types
= ['unrecognized'].concat(Object
.keys(self
.type_property_link
))
87 this.defs
.selectAll("marker")
90 .append("svg:marker") // This section adds in the arrows
91 .attr("id", function(d
){
94 .attr("viewBox", "-5 -5 10 10")
95 .attr("refX", 13) /*must be smarter way to calculate shift*/
97 .attr("markerUnits", "userSpaceOnUse")
98 .attr("markerWidth", 12)
99 .attr("markerHeight", 12)
100 .attr("orient", "auto")
102 .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
103 .attr('fill', function(d
){
104 return self
.type_property_link
[d
].color
;
108 this.customBehavioursOnEvents
= args
.behaviorsOnEvents
|| undefined;
111 var data_url
= (args
.data_url
) ? args
.data_url
: "graph_data/";
112 if (!args
.graph_data
) {
113 d3
.json(data_url
, function (error
, data
) {
114 //console.log(JSON.stringify(data))
115 self
.d3_graph
.nodes
= data
.vertices
;
116 self
.d3_graph
.links
= data
.edges
;
117 self
.d3_graph
.graph_parameters
= data
.graph_parameters
;
118 self
.model
= data
.model
;
119 self
.refreshGraphParameters(self
.d3_graph
.graph_parameters
);
122 //if(args.filter_base != undefined)
124 setTimeout(function () {
125 //self.handleForce(self.forceSimulationActive);
126 //var f_t = {"node":{"type":[],"group":["vlan_r3u0"]},"link":{"group":["vlan_r3u0"],"view":[""]}}
127 //var f_t ={"node":{"type":["vnf_vl","vnf_ext_cp","vnf_vdu_cp","vnf_vdu","vnf_click_vdu"],"group":["vlan_r3u0"]},"link":{"group":["vlan_r3u0"],"view":["vnf"]}}
128 self
.handleFiltersParams(args
.filter_base
);
129 //self.handleFiltersParams(f_t);
130 //console.log(JSON.stringify(args.filter_base))
131 //console.log(self.d3_graph.nodes.length)
132 //console.log(JSON.stringify(self.d3_graph.nodes))
133 //self.d3_graph.nodes.forEach(function(key, index){
134 //console.log(key, index);
140 this.updateData(args
)
145 * Update data of the graph.
146 * @param {Object} Required. An object that specifies tha data of the new node.
149 ModelGraphEditor
.prototype.updateData = function (args
) {
150 console
.log("updateData")
151 this.d3_graph
.nodes
= args
.graph_data
.vertices
;
152 this.d3_graph
.links
= args
.graph_data
.edges
;
153 this.d3_graph
.graph_parameters
= args
.graph_parameters
;
154 this.model
= args
.model
;
155 this.refreshGraphParameters(this.d3_graph
.graph_parameters
);
158 //if(args.filter_base != undefined)
160 //if(args.filter_base){
162 setTimeout(function () {
163 self
.handleForce(true);
164 self
.handleFiltersParams(args
.filter_base
);
170 * Add a new node to the graph.
171 * @param {Object} Required. An object that specifies tha data of the new node.
174 ModelGraphEditor
.prototype.addNode = function (node
, success
, error
) {
176 var current_layer
= self
.getCurrentView();
177 var node_type
= node
.info
.type
;
179 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].addable
) {
180 if (self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
) {
181 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
].class;
182 var controller
= new dreamer
[c
]();
183 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
](self
, node
, function () {
184 self
.parent
.addNode
.call(self
, node
);
185 success
&& success();
190 log('addNode: callback undefined in model spec.');
191 error
&& error("You can't add a " + node
.info
.type
+ ", callback undefined.");
194 //FIXME Error handling????
195 log("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
196 error
&& error("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
203 * Update the data properties of the node
204 * @param {Object} Required. An object that specifies tha data of the node.
207 ModelGraphEditor
.prototype.updateDataNode = function (args
) {
208 //FIXME updating a node properties need commit to server side!
209 this.parent
.updateDataNode
.call(this, args
);
213 * Remove a node from graph and related links.
214 * @param {String} Required. Id of node to remove.
217 ModelGraphEditor
.prototype.removeNode = function (node
, success
, error
) {
218 console
.log('removeNode', JSON
.stringify(node
))
220 var current_layer
= self
.getCurrentView();
221 var node_type
= node
.info
.type
;
222 if (node
.info
.desc_id
== undefined){
223 node
.info
.desc_id
= self
.desc_id
;
225 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].removable
) {
226 if (self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
) {
227 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
].class;
228 var controller
= new dreamer
[c
]();
229 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
](self
, node
, function () {
230 self
.parent
.removeNode
.call(self
, node
);
231 success
&& success();
235 log('removeNode: callback undefined in model spec.');
236 error
&& error("You can't remove a " + node
.info
.type
+ ", callback undefined.");
239 //FIXME we need to manage alert in a different way: FAILBACK
240 log("You can't remove a " + node
.info
.type
);
241 error
&& error("You can't remove a " + node
.info
.type
);
246 * Add a new link to graph.
247 * @param {Object} Required. An object that specifies tha data of the new Link.
250 ModelGraphEditor
.prototype.addLink = function (s
, d
, success
, error
) {
252 var source_id
= s
.id
;
253 var target_id
= d
.id
;
254 var source_type
= s
.info
.type
;
255 var destination_type
= d
.info
.type
;
259 view
: this.filter_parameters
.link
.view
[0],
260 group
: this.filter_parameters
.link
.group
,
261 desc_id
: this.desc_id
263 log("addLink: " + JSON
.stringify(link
))
264 var current_layer
= self
.getCurrentView()
265 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
]) {
267 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
) {
268 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
;
269 console
.log(callback
, self
.model
.callback
)
270 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;
271 link
.directed_edge
= direct_edge
;
272 var c
= self
.model
.callback
[callback
].class;
273 var controller
= new dreamer
[c
]();
274 controller
[callback
](self
, link
, function () {
275 self
._deselectAllNodes();
276 self
.parent
.addLink
.call(self
, link
);
281 log('addLink: callback undefined in model spec.');
282 error
&& error("You can't add a link, callback undefined.");
286 //FIXME we need to manage alert in a different way: FAILBACK
287 log("You can't link a " + source_type
+ " with a " + destination_type
);
289 error
&& error("You can't link a " + source_type
+ " with a " + destination_type
);
294 * Remove a link from graph.
295 * @param {String} Required. The identifier of link to remove.
298 ModelGraphEditor
.prototype.removeLink = function (link
, success
, error
) {
302 var source_type
= s
.info
.type
;
303 var destination_type
= d
.info
.type
;
304 var current_layer
= self
.getCurrentView()
305 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
] &&
306 self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
308 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
) {
309 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
;
310 var c
= self
.model
.callback
[callback
].class;
311 var controller
= new dreamer
[c
]();
312 controller
[callback
](self
, link
, function () {
313 self
._deselectAllNodes();
314 self
._deselectAllLinks();
315 self
.parent
.removeLink
.call(self
, link
.index
);
316 success
&& success();
319 log('removeLink: callback undefined in model spec.');
320 error
&& error("You can't remove a link, callback undefined.");
324 //FIXME we need to manage alert in a different way: FAILBACK
325 log("You can't delete the link");
326 error
&& error("You can't delete the link");
333 ModelGraphEditor
.prototype.savePositions = function (data
) {
335 this.node
.each(function (d
) {
337 vertices
[d
.id
]['x'] = d
.x
;
338 vertices
[d
.id
]['y'] = d
.y
;
340 new dreamer
.GraphRequests().savePositions({
355 ModelGraphEditor
.prototype._setupBehaviorsOnEvents = function (layer
) {
358 var contextMenuLinksAction
= [{
359 title
: 'Delete Link',
360 action: function (elm
, link
, i
) {
361 self
.removeLink(link
, null, showAlert
);
365 var contextMenuNodesAction
= [{
367 action: function (elm
, d
, i
) {
368 if (d
.info
.type
!= undefined) {
369 self
.eventHandler
.fire("edit_descriptor", self
.project_id
, d
);
377 action: function (elm
, d
, i
) {
378 self
.removeNode(d
, null, showAlert
);
384 if(this.customBehavioursOnEvents
){
385 contextMenuNodesAction
= contextMenuNodesAction
.concat(this.customBehavioursOnEvents
['behaviors'].nodes
);
389 if ( self
.model
&& self
.model
.layer
&& self
.model
.layer
[layer
] && self
.model
.layer
[layer
].action
&& self
.model
.layer
[layer
].action
.node
) {
390 for (var i
in self
.model
.layer
[layer
].action
.node
) {
391 var action
= self
.model
.layer
[layer
].action
.node
[i
]
392 contextMenuNodesAction
.push({
394 action: function (elm
, d
, i
) {
395 var callback
= action
.callback
;
396 var c
= self
.model
.callback
[callback
].class;
397 var controller
= new dreamer
[c
]();
404 controller
[callback
](self
, args
);
406 edit_mode
: (action
.edit_mode
!= undefined) ? action
.edit_mode
: undefined
411 this.behavioursOnEvents
= {
413 'click': function (d
) {
415 d3
.event
.preventDefault();
417 if (self
._edit_mode
&& self
.lastKeyDown
== SHIFT_BUTTON
&& self
._selected_node
!= undefined) {
418 self
.addLink(self
._selected_node
, d
, null, showAlert
);
420 self
._selectNodeExclusive(this, d
);
424 'mouseover': function (d
) {
425 self
.link
.style('stroke-width', function (l
) {
426 if (d
=== l
.source
|| d
=== l
.target
)
432 'mouseout': function (d
) {
433 self
.link
.style('stroke-width', 2);
435 'contextmenu': d3
.contextMenu(contextMenuNodesAction
, {
436 'edit_mode': self
._edit_mode
,
438 'type_object': 'node'
442 'click': function (d
) {
443 self
._selectLinkExclusive(this, d
);
446 'dblclick': function (event
) {
449 'mouseover': function (d
) {
450 d3
.select(this).style('stroke-width', 4);
452 'mouseout': function (d
) {
453 if (d
!= self
._selected_link
)
454 d3
.select(this).style('stroke-width', 2);
456 'contextmenu': d3
.contextMenu(contextMenuLinksAction
, {
457 'edit_mode': self
._edit_mode
,
459 'type_object': 'link'
465 ModelGraphEditor
.prototype.handleFiltersParams = function (filtersParams
, notFireEvent
) {
467 this.parent
.handleFiltersParams
.call(this, filtersParams
, notFireEvent
);
468 this._setupBehaviorsOnEvents(filtersParams
.link
.view
[0]);
471 ModelGraphEditor
.prototype.getAvailableNodes = function () {
472 log('getAvailableNodes');
474 if (this.model
&& this.model
.layer
[this.getCurrentView()] != undefined)
475 return this.model
.layer
[this.getCurrentView()].nodes
;
480 ModelGraphEditor
.prototype.exploreLayer = function (args
) {
484 ModelGraphEditor
.prototype.getTypeProperty = function () {
485 return this.type_property
;
488 ModelGraphEditor
.prototype.getCurrentGroup = function () {
489 return this.filter_parameters
.node
.group
[0];
493 ModelGraphEditor
.prototype.getCurrentView = function () {
494 return this.filter_parameters
.link
.view
[0];
502 console
.log("::ModelGraphEditor::", text
);
507 return ModelGraphEditor
;
512 if (typeof module
=== 'object') {
513 module
.exports
= dreamer
.ModelGraphEditor
;