several changes on auth flow
[osm/LW-UI.git] / static / topology3D / js / model_graph_editor.js
1 /*
2 Copyright 2017 CNIT - Consorzio Nazionale Interuniversitario per le Telecomunicazioni
3
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
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
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.
15 */
16 if (typeof dreamer === 'undefined') {
17 var dreamer = {};
18 }
19 var level = {}
20
21 dreamer.ModelGraphEditor = (function (global) {
22 'use strict';
23
24 var DEBUG = true;
25 var SHIFT_BUTTON = 16;
26 var IMAGE_PATH = "/static/assets/img/";
27 var GUI_VERSION = "v1";
28
29
30 ModelGraphEditor.prototype = new dreamer.GraphEditor();
31 ModelGraphEditor.prototype.constructor = ModelGraphEditor;
32 ModelGraphEditor.prototype.parent = dreamer.GraphEditor.prototype;
33
34 /**
35 * Constructor
36 */
37 function ModelGraphEditor(args) {
38
39 log("Constructor");
40
41 }
42
43
44 ModelGraphEditor.prototype.init = function (args) {
45 this.parent.init.call(this, args);
46
47 if (args.gui_properties[GUI_VERSION] != undefined) {
48 args.gui_properties = args.gui_properties[GUI_VERSION];
49 }
50
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;
57
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'){
64
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"]
68 }
69 }
70 }
71 }
72 else{
73
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"];
77 }
78 }
79
80
81
82 }, this);
83 if(args.gui_properties["edges"]){
84 this.type_property_link = args.gui_properties["edges"];
85 var self = this;
86 var link_types = ['unrecognized'].concat(Object.keys(self.type_property_link))
87 this.defs.selectAll("marker")
88 .data(link_types)
89 .enter()
90 .append("svg:marker") // This section adds in the arrows
91 .attr("id", function(d){
92 return d;
93 })
94 .attr("viewBox", "-5 -5 10 10")
95 .attr("refX", 13) /*must be smarter way to calculate shift*/
96 .attr("refY", 0)
97 .attr("markerUnits", "userSpaceOnUse")
98 .attr("markerWidth", 12)
99 .attr("markerHeight", 12)
100 .attr("orient", "auto")
101 .append("path")
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;
105 });
106 }
107
108 this.customBehavioursOnEvents = args.behaviorsOnEvents || undefined;
109
110 var self = this;
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);
120 self.refresh();
121 self.startForce();
122 //if(args.filter_base != undefined)
123
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);
135 //})
136 }, 500);
137
138 });
139 } else {
140 this.updateData(args)
141 }
142 }
143
144 /**
145 * Update data of the graph.
146 * @param {Object} Required. An object that specifies tha data of the new node.
147 * @returns {boolean}
148 */
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);
156 this.refresh();
157 this.startForce();
158 //if(args.filter_base != undefined)
159
160 //if(args.filter_base){
161 var self = this;
162 setTimeout(function () {
163 self.handleForce(true);
164 self.handleFiltersParams(args.filter_base);
165 }, 500);
166 //}
167 }
168
169 /**
170 * Add a new node to the graph.
171 * @param {Object} Required. An object that specifies tha data of the new node.
172 * @returns {boolean}
173 */
174 ModelGraphEditor.prototype.addNode = function (node, success, error) {
175 var self = this;
176 var current_layer = self.getCurrentView();
177 var node_type = node.info.type;
178
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();
186 }, error);
187
188 } else {
189
190 log('addNode: callback undefined in model spec.');
191 error && error("You can't add a " + node.info.type + ", callback undefined.");
192 }
193 } else {
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);
197 }
198 };
199
200
201
202 /**
203 * Update the data properties of the node
204 * @param {Object} Required. An object that specifies tha data of the node.
205 * @returns {boolean}
206 */
207 ModelGraphEditor.prototype.updateDataNode = function (args) {
208 //FIXME updating a node properties need commit to server side!
209 this.parent.updateDataNode.call(this, args);
210 };
211
212 /**
213 * Remove a node from graph and related links.
214 * @param {String} Required. Id of node to remove.
215 * @returns {boolean}
216 */
217 ModelGraphEditor.prototype.removeNode = function (node, success, error) {
218 console.log('removeNode', JSON.stringify(node))
219 var self = this;
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;
224 }
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();
232 }, error);
233 } else {
234
235 log('removeNode: callback undefined in model spec.');
236 error && error("You can't remove a " + node.info.type + ", callback undefined.");
237 }
238 } else {
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);
242 }
243 };
244
245 /**
246 * Add a new link to graph.
247 * @param {Object} Required. An object that specifies tha data of the new Link.
248 * @returns {boolean}
249 */
250 ModelGraphEditor.prototype.addLink = function (s, d, success, error) {
251 var self = this;
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;
256 var link = {
257 source: s,
258 target: d,
259 view: this.filter_parameters.link.view[0],
260 group: this.filter_parameters.link.group,
261 desc_id: this.desc_id
262 };
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]) {
266
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);
277 if (success)
278 success();
279 }, error);
280 } else {
281 log('addLink: callback undefined in model spec.');
282 error && error("You can't add a link, callback undefined.");
283 }
284
285 } else {
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);
288
289 error && error("You can't link a " + source_type + " with a " + destination_type);
290 }
291 };
292
293 /**
294 * Remove a link from graph.
295 * @param {String} Required. The identifier of link to remove.
296 * @returns {boolean}
297 */
298 ModelGraphEditor.prototype.removeLink = function (link, success, error) {
299 var self = this;
300 var s = link.source;
301 var d = link.target;
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
307 ) {
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();
317 }, error);
318 } else {
319 log('removeLink: callback undefined in model spec.');
320 error && error("You can't remove a link, callback undefined.");
321 }
322
323 } else {
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");
327 }
328
329
330 };
331
332
333 ModelGraphEditor.prototype.savePositions = function (data) {
334 var vertices = {}
335 this.node.each(function (d) {
336 vertices[d.id] = {};
337 vertices[d.id]['x'] = d.x;
338 vertices[d.id]['y'] = d.y;
339 });
340 new dreamer.GraphRequests().savePositions({
341 'vertices': vertices
342 });
343
344 };
345
346 /**
347 * Internal functions
348 */
349
350 /**
351 *
352 *
353 */
354
355 ModelGraphEditor.prototype._setupBehaviorsOnEvents = function (layer) {
356
357 var self = this;
358 var contextMenuLinksAction = [{
359 title: 'Delete Link',
360 action: function (elm, link, i) {
361 self.removeLink(link, null, showAlert);
362 },
363 edit_mode: true
364 }];
365 var contextMenuNodesAction = [{
366 title: 'Edit',
367 action: function (elm, d, i) {
368 if (d.info.type != undefined) {
369 self.eventHandler.fire("edit_descriptor", self.project_id, d);
370 }
371 },
372 nodes: [],
373 edit_mode: true
374 },
375 {
376 title: 'Delete',
377 action: function (elm, d, i) {
378 self.removeNode(d, null, showAlert);
379 },
380 edit_mode: true
381 }
382
383 ];
384 if(this.customBehavioursOnEvents){
385 contextMenuNodesAction = contextMenuNodesAction.concat(this.customBehavioursOnEvents['behaviors'].nodes);
386 }
387
388
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({
393 title: action.title,
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]();
398 var args = {
399 elm: elm,
400 d: d,
401 i: i
402 }
403
404 controller[callback](self, args);
405 },
406 edit_mode: (action.edit_mode != undefined) ? action.edit_mode: undefined
407 });
408 }
409 }
410
411 this.behavioursOnEvents = {
412 'nodes': {
413 'click': function (d) {
414
415 d3.event.preventDefault();
416
417 if (self._edit_mode && self.lastKeyDown == SHIFT_BUTTON && self._selected_node != undefined) {
418 self.addLink(self._selected_node, d, null, showAlert);
419 } else {
420 self._selectNodeExclusive(this, d);
421 }
422
423 },
424 'mouseover': function (d) {
425 self.link.style('stroke-width', function (l) {
426 if (d === l.source || d === l.target)
427 return 4;
428 else
429 return 2;
430 });
431 },
432 'mouseout': function (d) {
433 self.link.style('stroke-width', 2);
434 },
435 'contextmenu': d3.contextMenu(contextMenuNodesAction, {
436 'edit_mode': self._edit_mode,
437 'layer': layer,
438 'type_object': 'node'
439 })
440 },
441 'links': {
442 'click': function (d) {
443 self._selectLinkExclusive(this, d);
444
445 },
446 'dblclick': function (event) {
447
448 },
449 'mouseover': function (d) {
450 d3.select(this).style('stroke-width', 4);
451 },
452 'mouseout': function (d) {
453 if (d != self._selected_link)
454 d3.select(this).style('stroke-width', 2);
455 },
456 'contextmenu': d3.contextMenu(contextMenuLinksAction, {
457 'edit_mode': self._edit_mode,
458 'layer': layer,
459 'type_object': 'link'
460 })
461 }
462 }
463 };
464
465 ModelGraphEditor.prototype.handleFiltersParams = function (filtersParams, notFireEvent) {
466
467 this.parent.handleFiltersParams.call(this, filtersParams, notFireEvent);
468 this._setupBehaviorsOnEvents(filtersParams.link.view[0]);
469 };
470
471 ModelGraphEditor.prototype.getAvailableNodes = function () {
472 log('getAvailableNodes');
473 log(this.model)
474 if (this.model && this.model.layer[this.getCurrentView()] != undefined)
475 return this.model.layer[this.getCurrentView()].nodes;
476 return [];
477 }
478
479
480 ModelGraphEditor.prototype.exploreLayer = function (args) {
481
482 };
483
484 ModelGraphEditor.prototype.getTypeProperty = function () {
485 return this.type_property;
486 };
487
488 ModelGraphEditor.prototype.getCurrentGroup = function () {
489 return this.filter_parameters.node.group[0];
490
491 }
492
493 ModelGraphEditor.prototype.getCurrentView = function () {
494 return this.filter_parameters.link.view[0];
495
496 }
497 /**
498 * Log utility
499 */
500 function log(text) {
501 if (DEBUG)
502 console.log("::ModelGraphEditor::", text);
503 }
504
505
506
507 return ModelGraphEditor;
508
509
510 }(this));
511
512 if (typeof module === 'object') {
513 module.exports = dreamer.ModelGraphEditor;
514 }