3c275ddb5db30edd64bae36fd6968ebb4b34c293
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 this.type_property
[key
] = args
.gui_properties
["nodes"][key
];
60 if ( this.type_property
[key
]['property'] != undefined){
61 for(var c_prop
in this.type_property
[key
]){
62 if(c_prop
!= 'property'){
64 this.type_property
[key
][c_prop
]['shape'] = this.parent
.get_d3_symbol(this.type_property
[key
][c_prop
]['shape']);
65 if(this.type_property
[key
][c_prop
]["image"] != undefined){
66 this.type_property
[key
][c_prop
]["image"] = IMAGE_PATH
+ this.type_property
[key
][c_prop
]["image"]
72 this.type_property
[key
]["shape"] = this.parent
.get_d3_symbol(this.type_property
[key
]["shape"]);
73 if (this.type_property
[key
]["image"] != undefined) {
74 this.type_property
[key
]["image"] = IMAGE_PATH
+ this.type_property
[key
]["image"];
81 if(args
.gui_properties
["edges"]){
82 this.type_property_link
= args
.gui_properties
["edges"];
84 var link_types
= ['unrecognized'].concat(Object
.keys(self
.type_property_link
))
85 this.defs
.selectAll("marker")
88 .append("svg:marker") // This section adds in the arrows
89 .attr("id", function(d
){
92 .attr("viewBox", "-5 -5 10 10")
93 .attr("refX", 13) /*must be smarter way to calculate shift*/
95 .attr("markerUnits", "userSpaceOnUse")
96 .attr("markerWidth", 12)
97 .attr("markerHeight", 12)
98 .attr("orient", "auto")
100 .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
101 .attr('fill', function(d
){
102 return self
.type_property_link
[d
].color
;
106 this.customBehavioursOnEvents
= args
.behaviorsOnEvents
|| undefined;
109 var data_url
= (args
.data_url
) ? args
.data_url
: "graph_data/";
110 if (!args
.graph_data
) {
111 d3
.json(data_url
, function (error
, data
) {
112 //console.log(JSON.stringify(data))
113 self
.d3_graph
.nodes
= data
.vertices
;
114 self
.d3_graph
.links
= data
.edges
;
115 self
.d3_graph
.graph_parameters
= data
.graph_parameters
;
116 self
.model
= data
.model
;
117 self
.refreshGraphParameters(self
.d3_graph
.graph_parameters
);
120 //if(args.filter_base != undefined)
122 setTimeout(function () {
123 //self.handleForce(self.forceSimulationActive);
124 //var f_t = {"node":{"type":[],"group":["vlan_r3u0"]},"link":{"group":["vlan_r3u0"],"view":[""]}}
125 //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"]}}
126 self
.handleFiltersParams(args
.filter_base
);
127 //self.handleFiltersParams(f_t);
128 //console.log(JSON.stringify(args.filter_base))
129 //console.log(self.d3_graph.nodes.length)
130 //console.log(JSON.stringify(self.d3_graph.nodes))
131 //self.d3_graph.nodes.forEach(function(key, index){
132 //console.log(key, index);
138 this.updateData(args
)
143 * Update data of the graph.
144 * @param {Object} Required. An object that specifies tha data of the new node.
147 ModelGraphEditor
.prototype.updateData = function (args
) {
148 console
.log("updateData")
149 this.d3_graph
.nodes
= args
.graph_data
.vertices
;
150 this.d3_graph
.links
= args
.graph_data
.edges
;
151 this.d3_graph
.graph_parameters
= args
.graph_parameters
;
152 this.model
= args
.model
;
153 this.refreshGraphParameters(this.d3_graph
.graph_parameters
);
156 //if(args.filter_base != undefined)
158 //if(args.filter_base){
160 setTimeout(function () {
161 self
.handleForce(true);
162 self
.handleFiltersParams(args
.filter_base
);
168 * Add a new node to the graph.
169 * @param {Object} Required. An object that specifies tha data of the new node.
172 ModelGraphEditor
.prototype.addNode = function (node
, success
, error
) {
174 var current_layer
= self
.getCurrentView();
175 var node_type
= node
.info
.type
;
177 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].addable
) {
178 if (self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
) {
179 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
].class;
180 var controller
= new dreamer
[c
]();
181 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].addable
.callback
](self
, node
, function () {
182 self
.parent
.addNode
.call(self
, node
);
183 success
&& success();
188 log('addNode: callback undefined in model spec.');
189 error
&& error("You can't add a " + node
.info
.type
+ ", callback undefined.");
192 //FIXME Error handling????
193 log("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
194 error
&& error("You can't add a " + node
.info
.type
+ " in a current layer " + current_layer
);
201 * Update the data properties of the node
202 * @param {Object} Required. An object that specifies tha data of the node.
205 ModelGraphEditor
.prototype.updateDataNode = function (args
) {
206 //FIXME updating a node properties need commit to server side!
207 this.parent
.updateDataNode
.call(this, args
);
211 * Remove a node from graph and related links.
212 * @param {String} Required. Id of node to remove.
215 ModelGraphEditor
.prototype.removeNode = function (node
, success
, error
) {
216 console
.log('removeNode', JSON
.stringify(node
))
218 var current_layer
= self
.getCurrentView();
219 var node_type
= node
.info
.type
;
220 if (node
.info
.desc_id
== undefined){
221 node
.info
.desc_id
= self
.desc_id
;
223 if (self
.model
.layer
[current_layer
] && self
.model
.layer
[current_layer
].nodes
[node_type
] && self
.model
.layer
[current_layer
].nodes
[node_type
].removable
) {
224 if (self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
) {
225 var c
= self
.model
.callback
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
].class;
226 var controller
= new dreamer
[c
]();
227 controller
[self
.model
.layer
[current_layer
].nodes
[node_type
].removable
.callback
](self
, node
, function () {
228 self
.parent
.removeNode
.call(self
, node
);
229 success
&& success();
233 log('removeNode: callback undefined in model spec.');
234 error
&& error("You can't remove a " + node
.info
.type
+ ", callback undefined.");
237 //FIXME we need to manage alert in a different way: FAILBACK
238 log("You can't remove a " + node
.info
.type
);
239 error
&& error("You can't remove a " + node
.info
.type
);
244 * Add a new link to graph.
245 * @param {Object} Required. An object that specifies tha data of the new Link.
248 ModelGraphEditor
.prototype.addLink = function (s
, d
, success
, error
) {
250 var source_id
= s
.id
;
251 var target_id
= d
.id
;
252 var source_type
= s
.info
.type
;
253 var destination_type
= d
.info
.type
;
257 view
: this.filter_parameters
.link
.view
[0],
258 group
: this.filter_parameters
.link
.group
,
259 desc_id
: this.desc_id
261 log("addLink: " + JSON
.stringify(link
))
262 var current_layer
= self
.getCurrentView()
263 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
]) {
265 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
) {
266 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].callback
;
267 console
.log(callback
, self
.model
.callback
)
268 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;
269 link
.directed_edge
= direct_edge
;
270 var c
= self
.model
.callback
[callback
].class;
271 var controller
= new dreamer
[c
]();
272 controller
[callback
](self
, link
, function () {
273 self
._deselectAllNodes();
274 self
.parent
.addLink
.call(self
, link
);
279 log('addLink: callback undefined in model spec.');
280 error
&& error("You can't add a link, callback undefined.");
284 //FIXME we need to manage alert in a different way: FAILBACK
285 log("You can't link a " + source_type
+ " with a " + destination_type
);
287 error
&& error("You can't link a " + source_type
+ " with a " + destination_type
);
292 * Remove a link from graph.
293 * @param {String} Required. The identifier of link to remove.
296 ModelGraphEditor
.prototype.removeLink = function (link
, success
, error
) {
300 var source_type
= s
.info
.type
;
301 var destination_type
= d
.info
.type
;
302 var current_layer
= self
.getCurrentView()
303 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
] &&
304 self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
306 if (self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
) {
307 var callback
= self
.model
.layer
[current_layer
].allowed_edges
[source_type
].destination
[destination_type
].removable
.callback
;
308 var c
= self
.model
.callback
[callback
].class;
309 var controller
= new dreamer
[c
]();
310 controller
[callback
](self
, link
, function () {
311 self
._deselectAllNodes();
312 self
._deselectAllLinks();
313 self
.parent
.removeLink
.call(self
, link
.index
);
314 success
&& success();
317 log('removeLink: callback undefined in model spec.');
318 error
&& error("You can't remove a link, callback undefined.");
322 //FIXME we need to manage alert in a different way: FAILBACK
323 log("You can't delete the link");
324 error
&& error("You can't delete the link");
331 ModelGraphEditor
.prototype.savePositions = function (data
) {
333 this.node
.each(function (d
) {
335 vertices
[d
.id
]['x'] = d
.x
;
336 vertices
[d
.id
]['y'] = d
.y
;
338 new dreamer
.GraphRequests().savePositions({
353 ModelGraphEditor
.prototype._setupBehaviorsOnEvents = function (layer
) {
356 var contextMenuLinksAction
= [{
357 title
: 'Delete Link',
358 action: function (elm
, link
, i
) {
359 self
.removeLink(link
, null, showAlert
);
363 var contextMenuNodesAction
= [{
365 action: function (elm
, d
, i
) {
366 if (d
.info
.type
!= undefined) {
367 self
.eventHandler
.fire("edit_descriptor", self
.project_id
, d
);
375 action: function (elm
, d
, i
) {
376 self
.removeNode(d
, null, showAlert
);
382 if(this.customBehavioursOnEvents
){
383 contextMenuNodesAction
= contextMenuNodesAction
.concat(this.customBehavioursOnEvents
['behaviors'].nodes
);
387 if ( self
.model
&& self
.model
.layer
&& self
.model
.layer
[layer
] && self
.model
.layer
[layer
].action
&& self
.model
.layer
[layer
].action
.node
) {
388 for (var i
in self
.model
.layer
[layer
].action
.node
) {
389 var action
= self
.model
.layer
[layer
].action
.node
[i
]
390 contextMenuNodesAction
.push({
392 action: function (elm
, d
, i
) {
393 var callback
= action
.callback
;
394 var c
= self
.model
.callback
[callback
].class;
395 var controller
= new dreamer
[c
]();
402 controller
[callback
](self
, args
);
404 edit_mode
: (action
.edit_mode
!= undefined) ? action
.edit_mode
: undefined
409 this.behavioursOnEvents
= {
411 'click': function (d
) {
413 d3
.event
.preventDefault();
415 if (self
._edit_mode
&& self
.lastKeyDown
== SHIFT_BUTTON
&& self
._selected_node
!= undefined) {
416 self
.addLink(self
._selected_node
, d
, null, showAlert
);
418 self
._selectNodeExclusive(this, d
);
422 'mouseover': function (d
) {
423 self
.link
.style('stroke-width', function (l
) {
424 if (d
=== l
.source
|| d
=== l
.target
)
430 'mouseout': function (d
) {
431 self
.link
.style('stroke-width', 2);
433 'contextmenu': d3
.contextMenu(contextMenuNodesAction
, {
434 'edit_mode': self
._edit_mode
,
436 'type_object': 'node'
440 'click': function (d
) {
441 self
._selectLinkExclusive(this, d
);
444 'dblclick': function (event
) {
447 'mouseover': function (d
) {
448 d3
.select(this).style('stroke-width', 4);
450 'mouseout': function (d
) {
451 if (d
!= self
._selected_link
)
452 d3
.select(this).style('stroke-width', 2);
454 'contextmenu': d3
.contextMenu(contextMenuLinksAction
, {
455 'edit_mode': self
._edit_mode
,
457 'type_object': 'link'
463 ModelGraphEditor
.prototype.handleFiltersParams = function (filtersParams
, notFireEvent
) {
465 this.parent
.handleFiltersParams
.call(this, filtersParams
, notFireEvent
);
466 this._setupBehaviorsOnEvents(filtersParams
.link
.view
[0]);
469 ModelGraphEditor
.prototype.getAvailableNodes = function () {
470 log('getAvailableNodes');
472 if (this.model
&& this.model
.layer
[this.getCurrentView()] != undefined)
473 return this.model
.layer
[this.getCurrentView()].nodes
;
478 ModelGraphEditor
.prototype.exploreLayer = function (args
) {
482 ModelGraphEditor
.prototype.getTypeProperty = function () {
483 return this.type_property
;
486 ModelGraphEditor
.prototype.getCurrentGroup = function () {
487 return this.filter_parameters
.node
.group
[0];
491 ModelGraphEditor
.prototype.getCurrentView = function () {
492 return this.filter_parameters
.link
.view
[0];
500 console
.log("::ModelGraphEditor::", text
);
505 return ModelGraphEditor
;
510 if (typeof module
=== 'object') {
511 module
.exports
= dreamer
.ModelGraphEditor
;