Added dh-make to Dockerfile
[osm/LW-UI.git] / static / topology3D / js / 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.GraphEditor = (function (global) {
22 'use strict';
23
24 var DEBUG = true;
25 var SHIFT_BUTTON = 16;
26 var CANC_BUTTON = 46;
27 var default_link_color = "#888";
28 var nominal_text_size = 15;
29 var nominal_stroke = 1.5;
30 var EventHandler = dreamer.Event;
31 // var IMAGE_PATH = "/static/assets/img/";
32
33
34
35 /**
36 * Constructor
37 */
38 function GraphEditor(args) {
39 log("Constructor");
40 this.eventHandler = new EventHandler();
41 this.lastKeyDown = -1;
42 this._selected_node = undefined;
43 this._selected_link = undefined;
44 this._edit_mode = true;
45 this.filter_parameters = {
46 node: {
47 type: [],
48 group: [],
49 },
50 link: {
51 group: [],
52 view: [],
53 }
54 };
55 this.current_view_id = '';
56 // graph data initailization
57 this.d3_graph = {
58 nodes: [],
59 links: [],
60 graph_parameters: {}
61
62 };
63
64
65 }
66
67
68
69 GraphEditor.prototype.init = function (args) {
70 args = args || {}
71 var self = this;
72 this.width = 550//args.width || 500;
73 this.height = 550// args.height || 500;
74 this.forceSimulationActive = false;
75
76 //FixMe
77 this.width = this.width - this.width * 0.007;
78 this.height = this.height - this.height * 0.07;
79
80 //console.log("this.width", this.width, "this.height", this.height);
81 var min_zoom = 0.1;
82 var max_zoom = 7;
83 this._setupBehaviorsOnEvents();
84 this._setupFiltersBehaviors(args);
85
86 this.type_property = {
87 "unrecognized": {
88 "shape": d3.symbolCircle,
89 "color": "white",
90 "node_label_color": "black",
91 "size": 15
92 },
93 };
94
95 this.type_property_link = {
96 "unrecognized": {
97 "color": "#888",
98 //"color": "red",
99 },
100 };
101
102 this.force = d3.forceSimulation()
103 .force("collide", d3.forceCollide().radius(40))
104 .force("link", d3.forceLink().distance(80).iterations(1).id(function (d) {
105 return d.id;
106 }))
107 .force("center", d3.forceCenter(this.width / 2, this.height / 2));
108
109 var zoom = d3.zoom().scaleExtent([min_zoom, max_zoom])
110
111 var size = d3.scalePow().exponent(2)
112 .domain([1, 100])
113 .range([8, 24]);
114
115 this.svg = d3.select("#graph_ed_container").append("svg")
116 .attr("id", "graph_svg")
117 .attr("perserveAspectRatio", "xMinYMid")
118 .attr("width", this.width)
119 .attr("height", this.height);
120
121 //End Arrow style
122 this.defs = this.svg.append("svg:defs");
123
124 this.defs.selectAll("marker")
125 .data(["unrecognized"]) // Different link/path types can be defined here
126 .enter().append("svg:marker") // This section adds in the arrows
127 .attr("id", String)
128 .attr("viewBox", "-5 -5 10 10")
129 .attr("refX", 13) //must be smarter way to calculate shift
130 .attr("refY", 0)
131 .attr("markerUnits", "userSpaceOnUse")
132 .attr("markerWidth", 12)
133 .attr("markerHeight", 12)
134 .attr("orient", "auto")
135 .append("path")
136 .attr("d", "M 0,0 m -5,-5 L 5,0 L -5,5 Z")
137 .attr('fill', this.type_property_link['unrecognized']['color']);
138
139 d3.select(window)
140 .on('keydown', function () {
141 log('keydown ' + d3.event.keyCode);
142 //d3.event.preventDefault();
143 if (self.lastKeyDown !== -1) return;
144 self.lastKeyDown = d3.event.keyCode;
145 if (self.lastKeyDown === CANC_BUTTON && self._selected_node != undefined) {
146 self.removeNode(self._selected_node, null, showAlert);
147 } else if (self.lastKeyDown === CANC_BUTTON && self._selected_link != undefined) {
148 self.removeLink(self._selected_link, null, showAlert);
149 }
150
151 })
152 .on('keyup', function () {
153 log('keyup' + self.lastKeyDown);
154 self.lastKeyDown = -1;
155 });
156 var popup = this.svg.append("g")
157 .attr("id", "popup")
158 .attr("class", "popup")
159 .attr("opacity", "0")
160 .attr("transform", "translate(1 1)")
161 .call(d3.drag()
162 .on("start", dragstarted)
163 .on("drag", dragged)
164 .on("end", dragended));
165
166 function dragstarted(d) {
167 //d3.select(this).raise().classed("active", true);
168 }
169
170 function dragged(d) {
171 //console.log(JSON.stringify(d))
172 d3.select(this).attr("transform", function () {
173 return "translate("+d3.event.x+","+d3.event.y+")";
174
175 })
176 }
177
178 function dragended(d) {
179 //d3.select(this).classed("active", false);
180 }
181
182 var chart = $("#graph_svg");
183 this.aspect = chart.width() / chart.height();
184 this.container = chart.parent();
185 $(window).on("resize", function() {
186
187 var palette_width = ($("#palette").length > 0) ? $("#palette").width() : 0;
188 var working_width = self.container.width() - palette_width;
189 self.width = (working_width < 0) ? 0 : working_width;
190 self.height = self.container.height();
191 chart.attr("width", self.width);
192 chart.attr("height", self.height);
193 }).trigger("resize");
194
195 }
196
197
198 GraphEditor.prototype.get_d3_symbol =
199 function (myString) {
200
201 switch (myString) {
202 case "circle":
203 return d3.symbolCircle;
204 break;
205 case "square":
206 return d3.symbolSquare;
207 break;
208 case "diamond":
209 return d3.symbolDiamond;
210 break;
211 case "triangle":
212 return d3.symbolTriangle;
213 break;
214 case "star":
215 return d3.symbolStar;
216 break;
217 case "cross":
218 return d3.symbolCross;
219 break;
220 default:
221 // if the string is not recognized
222 return d3.symbolCross;
223 //return d3.symbolCircleUnknown;
224 }
225
226 }
227
228 GraphEditor.prototype.get_name_from_d3_symbol =
229 function (mySymbol) {
230 switch (mySymbol) {
231 case d3.symbolCircle:
232 return "circle";
233 break;
234 case d3.symbolSquare:
235 return "square";
236 break;
237 case d3.symbolDiamond:
238 return "diamond";
239 break;
240 case d3.symbolTriangle:
241 return "triangle";
242 break;
243 case d3.symbolStar:
244 return "star";
245 break;
246 case d3.symbolCross:
247 return "cross";
248 break;
249 default:
250 // if the string is not recognized
251 return "unknown";
252 //return d3.symbolCircleUnknown;
253 }
254
255 }
256
257 /**
258 * Start or Stop force layout
259 * @param {boolean} Required. Value true: start, false: stop
260 * @returns {boolean}
261 */
262 GraphEditor.prototype.handleForce = function (start) {
263 if (start)
264 this.force.stop();
265 this.forceSimulationActive = start;
266 this.node.each(function (d) {
267 d.fx = (start) ? null : d.x;
268 d.fy = (start) ? null : d.y;
269 });
270
271 if (start)
272 this.force.restart();
273
274 this.eventHandler.fire("force_status_changed_on", start);
275 };
276
277 /**
278 * Handle the parameters of basic filters: node type, view, group
279 * @param {Object} Required.
280 *
281 */
282 GraphEditor.prototype.handleFiltersParams = function (filtersParams, notFireEvent) {
283 console.log("handleFiltersParams", filtersParams)
284 this.filter_parameters = (filtersParams != undefined) ? filtersParams : this.filter_parameters;
285 this.current_view_id = (this.filter_parameters != undefined && this.filter_parameters.link.view[0] != undefined) ? this.filter_parameters.link.view[0] : this.current_view_id
286 this.cleanAll();
287 this.refresh();
288 this.startForce();
289 this.force.restart();
290 this._deselectAllNodes();
291 this.handleForce(this.forceSimulationActive);
292 if (!notFireEvent)
293 this.eventHandler.fire("filters_changed", filtersParams);
294
295 };
296
297 /**
298 * Add a new node to the graph.
299 * @param {Object} Required. An object that specifies tha data of the new node.
300 * @returns {boolean}
301 */
302 GraphEditor.prototype.addNode = function (args) {
303 if (args.id && args.info && args.info.type) {
304 args.fixed = true;
305 this.force.stop();
306 this.cleanAll();
307 this.d3_graph.nodes.push(args);
308 this.refresh();
309 this.startForce();
310 this.force.restart();
311 this.handleForce(this.forceSimulationActive);
312 return true;
313 }
314
315 return false;
316
317 };
318
319 /**
320 * Update the data properties of the node
321 * @param {Object} Required. An object that specifies tha data of the node.
322 * @returns {boolean}
323 */
324 GraphEditor.prototype.updateDataNode = function (args) {
325
326 };
327
328 /**
329 * Remove a node from graph and related links.
330 * @param {String} Required. Id of node to remove.
331 * @returns {boolean}
332 */
333 GraphEditor.prototype.removeNode = function (node) {
334 if (node != undefined) {
335 var node_id = node.id;
336 this.d3_graph['nodes'].forEach(function (n, index, object) {
337 if (n.id == node_id) {
338 object.splice(index, 1);
339
340 }
341
342 });
343 //TODO trovare una metodo piu efficace
344 var self = this;
345 var links_to_remove = [];
346 this.d3_graph['links'].forEach(function (l, index, object) {
347 if (node_id === l.source.id || node_id === l.target.id) {
348 links_to_remove.push(index);
349 }
350
351 });
352 var links_removed = 0;
353 links_to_remove.forEach(function (l_index) {
354 self.d3_graph['links'].splice(l_index - links_removed, 1);
355 links_removed++;
356 });
357 this.cleanAll();
358 this.refresh();
359 this.startForce();
360 this.force.restart();
361
362 return true;
363 }
364 return false;
365 };
366
367
368 /**
369 * Add a new link to graph.
370 * @param {Object} Required. An object that specifies tha data of the new Link.
371 * @returns {boolean}
372 */
373 GraphEditor.prototype.addLink = function (link) {
374 console.log("addLink" + JSON.stringify(link))
375 if (link.source && link.target) {
376 this.force.stop();
377 this.cleanAll();
378 this.d3_graph.links.push(link);
379 this.refresh();
380 this.startForce();
381 this.force.restart();
382 return true;
383 }
384
385 return false;
386 };
387
388 /**
389 * Remove a link from graph.
390 * @param {String} Required. The identifier of link to remove.
391 * @returns {boolean}
392 */
393 GraphEditor.prototype.removeLink = function (link_id) {
394 var self = this;
395 if (link_id !== 'undefined') {
396 this.d3_graph['links'].forEach(function (l, index, object) {
397 if (link_id === l.index) {
398 object.splice(index, 1);
399
400 self.cleanAll();
401 self.refresh();
402 self.startForce();
403 self.force.restart();
404 return true;
405 }
406
407 });
408 }
409
410 return false;
411 };
412
413
414 /**
415 * Force a refresh of GraphView
416 * @returns {}
417 */
418 GraphEditor.prototype.refresh = function () {
419
420 //log(data)
421 var self = this;
422
423 this.link = this.svg
424 .selectAll()
425 .data(self.d3_graph.links
426 .filter(this.link_filter_cb)
427 )
428 .enter().append("g")
429 .attr("class", "link cleanable")
430 .append("path")
431 .attr("class", "link")
432 .attr("class", "cleanable")
433 .style("stroke-width", nominal_stroke)
434 .style("stroke", function (d) {
435 return self._link_property_by_type((d.type_link) ? d.type_link : "unrecognized", "color");
436 })
437 .attr("marker-end", function (d) {
438 if (!d.directed_edge)
439 return '';
440
441 var marker_url = (d.type_link) ? d.type_link : "unrecognized"
442 return (d.directed_edge ? "url(#" + marker_url + ")" : '');
443 });
444
445 this.nodeContainer = this.svg
446 .selectAll()
447 .data(self.d3_graph.nodes
448 .filter(this.node_filter_cb))
449 .enter()
450 .append("g")
451 // .attr("class", "nodosdads")
452 .attr("class", "node cleanable");
453
454 this.svg.selectAll('.node')
455 .data(self.d3_graph.nodes
456 .filter(this.node_filter_cb))
457
458 .filter(function (d) {
459 return (d.info.type == undefined) || (self._node_property_by_type(d.info.type, 'image', d) == undefined)
460 })
461
462 .append("svg:path")
463 .attr("d", d3.symbol()
464 .size(function (d) {
465 return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d), 2) / 4;
466 })
467 .type(function (d) {
468 // console.log(d.info.type, 'shape', self.current_view_id)
469 return (self._node_property_by_type(d.info.type, 'shape', d));
470 })
471 )
472 .style("fill", function (d) {
473 return self._node_property_by_type(d.info.type, 'color', d);
474 })
475 .attr("transform", function () {
476 return "rotate(-45)";
477
478 })
479 .attr("stroke-width", 2.4)
480
481 .attr("class", "node_path")
482 .attr("id", function (d) {
483 return "path_" + d.id;
484 })
485
486 .call(d3.drag()
487 .on("start", dragstarted)
488 .on("drag", dragged)
489 .on("end", dragended));
490
491 var figure_node = this.svg.selectAll('.node')
492 .data(self.d3_graph.nodes
493 .filter(this.node_filter_cb))
494
495 .filter(function (d) {
496 return self._node_property_by_type(d.info.type, 'image', d) != undefined
497 });
498
499 figure_node.append("svg:image")
500 .attr("xlink:href", function (d) {
501 return self._node_property_by_type(d.info.type, 'image', d)
502 })
503 .attr("x", function (d) {
504 return -self._node_property_by_type(d.info.type, 'size', d) / 2
505 })
506 .attr("y", function (d) {
507 return -self._node_property_by_type(d.info.type, 'size', d) / 2
508 })
509 .attr("width", function (d) {
510 return self._node_property_by_type(d.info.type, 'size', d)
511 })
512 .attr("height", function (d) {
513 return self._node_property_by_type(d.info.type, 'size', d)
514 })
515 .style("stroke", "black")
516 .style("stroke-width", "1px")
517
518 .attr("class", "node_path")
519 .attr("id", function (d) {
520 return "path_" + d.id;
521 })
522 .call(d3.drag()
523 .on("start", dragstarted)
524 .on("drag", dragged)
525 .on("end", dragended));
526
527 figure_node.append("svg:path")
528 .attr("d", d3.symbol()
529 .size(function (d) {
530 return Math.PI * Math.pow(self._node_property_by_type(d.info.type, 'size', d) + 7, 2) / 4;
531 })
532 .type(function (d) {
533 return (self.get_d3_symbol('circle'));
534 })
535 )
536 .style("fill", 'transparent')
537 .attr("transform", function () {
538 return "rotate(-45)";
539
540 })
541 .attr("stroke-width", 2.4)
542
543 .attr("class", "hidden_circle")
544 .attr("id", function (d) {
545 return "path_" + d.id;
546 })
547
548 .call(d3.drag()
549 .on("start", dragstarted)
550 .on("drag", dragged)
551 .on("end", dragended));
552
553
554
555 this.node = this.svg.selectAll('.node')
556 .data(self.d3_graph.nodes
557 .filter(this.node_filter_cb)).selectAll("image, path, circle");
558
559
560
561 this.node.on("contextmenu", self.behavioursOnEvents.nodes["contextmenu"])
562 .on("mouseover", self.behavioursOnEvents.nodes["mouseover"])
563 .on("mouseout", self.behavioursOnEvents.nodes["mouseout"])
564 .on('click', self.behavioursOnEvents.nodes["click"])
565 .on('dblclick', self.behavioursOnEvents.nodes["dblclick"]);
566
567 this.link
568 .on("contextmenu", self.behavioursOnEvents.links["contextmenu"])
569 .on("mouseover", self.behavioursOnEvents.links["mouseover"])
570 .on('click', self.behavioursOnEvents.links["click"])
571 .on("mouseout", self.behavioursOnEvents.links["mouseout"]);
572
573
574
575 this.text = this.svg.selectAll(".node")
576 .data(self.d3_graph.nodes
577 .filter(this.node_filter_cb))
578 .append("svg:text")
579 .attr("class", "nodetext")
580 .attr("class", "cleanable")
581 .attr("dy", function(d) {
582 if (self._node_property_by_type(d.info.type, 'image', d) == undefined) {
583 //shape
584 return "-5"
585 }
586 else {
587 //image
588 return (-self._node_property_by_type(d.info.type, 'size', d)/2).toString()
589 }
590 })
591 .attr("pointer-events", "none")
592 .style("font-size", nominal_text_size + "px")
593 .style("font-family", "Lucida Console")
594 .style("fill", function (d) {
595 return self._node_property_by_type(d.info.type, 'node_label_color', d);
596 })
597 .style("text-anchor", "middle")
598 .text(function (d) {
599 return d.id;
600 });
601
602
603
604 function dragstarted(d) {
605 d.draggednode = true;
606 if (!d3.event.active) self.force.alphaTarget(0.3).restart();
607 d.fx = d.x;
608 d.fy = d.y;
609
610 }
611
612 function dragged(d) {
613 d.fx = d3.event.x;
614 d.fy = d3.event.y;
615 }
616
617 function dragended(d) {
618 d.draggednode = false;
619 if (!d3.event.active) self.force.alphaTarget(0);
620 if (self.forceSimulationActive) {
621 d.fx = null;
622 d.fy = null;
623 } else {
624 d.fx = d.x;
625 d.fy = d.y;
626 self.force.stop();
627 self.forceSimulationActive = false;
628 }
629 }
630
631
632 };
633
634 /**
635 * Start force layout on Graph.
636 *
637 */
638 GraphEditor.prototype.startForce = function () {
639 //this.force.stop();
640 var self = this
641 this.force
642 .nodes(this.d3_graph.nodes)
643 .on("tick", ticked);
644
645
646 this.force
647 .force("link")
648 .links(this.d3_graph.links);
649
650 function ticked() {
651 self.node.attr("cx", function (d) {
652 return d.x = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.width - self._node_property_by_type(d.info.type, 'size', d), d.x));
653 })
654 .attr("cy", function (d) {
655 return d.y = Math.max(self._node_property_by_type(d.info.type, 'size', d), Math.min(self.height - self._node_property_by_type(d.info.type, 'size', d), d.y));
656 });
657
658 self.link.attr("d", function (d) {
659 var dx = d.target.x - d.source.x,
660 dy = d.target.y - d.source.y,
661 dr = Math.sqrt(dx * dx + dy * dy);
662 return "M" + d.source.x + "," + d.source.y + "," + d.target.x + "," + d.target.y;
663 });
664
665 self.node.attr("transform", function (d) {
666 return "translate(" + d.x + "," + d.y + ")";
667 });
668 self.text.attr("transform", function (d) {
669 var label_pos_y = d.y + self._node_property_by_type(d.info.type, 'size', d) + 10;
670 return "translate(" + d.x + "," + label_pos_y + ")";
671 });
672 };
673
674
675
676 };
677
678 /**
679 * This method attaches an event handler.
680 * @param {String} Required. A String that specifies the name of the event.
681 * @param {Function} Required. Specifies the function to run when the event occurs.
682 * @returns {}
683 */
684 GraphEditor.prototype.addListener = function (event_name, cb) {
685 this.eventHandler.addL(event_name, cb);
686 }
687
688 /**
689 * This method removes an event handler that has been attached with the addListener() method.
690 * @param {String} Required. A String that specifies the name of the event to remove.
691 * @param {Function} Required. Specifies the function to remove.
692 * @returns {}
693 */
694 GraphEditor.prototype.removeListener = function (event_name, cb) {
695
696 }
697
698
699 GraphEditor.prototype.setNodeClass = function (class_name, filter_cb) {
700 log("setNodeClass");
701 var self = this;
702 this.svg.selectAll('.node').classed(class_name, false);
703 this.svg.selectAll('.node')
704 .classed(class_name, filter_cb);
705 }
706
707 GraphEditor.prototype.setLinkClass = function (class_name, filter_cb) {
708 log("setLinkClass");
709 var self = this;
710 this.svg.selectAll('.link').classed(class_name, false);
711 this.svg.selectAll('.link')
712 .classed(class_name, filter_cb);
713 }
714
715 GraphEditor.prototype.showNodeInfo = function(args){
716 this.addLinesToPopup(args['node_info'], "Info about node selected")
717 this.handlePopupVisibility(true, 'right')
718 }
719 GraphEditor.prototype.addLinesToPopup = function(data, title) {
720 var self = this;
721 var index = 1;
722 var translate_y = 0;
723 var width_popup = 400;
724 var height_popup = 0;
725
726 d3.selectAll(".popupcleanable").remove(); // clean
727
728 var popupbg = d3.select(".popup").append("rect")
729 .attr("id", "popupbg")
730 .attr("class", "popup bg popupcleanable cleanable")
731 .attr("width", "400")
732 .attr("height", "0")
733 .attr("rx", 10) // set the x corner curve radius
734 .attr("ry", 10); // set the y corner curve radius
735
736
737 d3.select(".popup").append("svg:path")
738 .attr("d", d3.symbol()
739 .size(function (d) {
740 return 80
741 })
742 .type(function (d) {
743 console.log("popiup")
744 return (self.get_d3_symbol());
745 })
746 )
747 .style("fill", 'red')
748 .attr("transform", function () {
749 return "translate(380,15) rotate(-45)";
750
751 })
752 .attr("stroke-width", 2.4)
753 .attr("id", "close_popup")
754 .attr("class", "popupcleanable cleanable")
755 .on("click", function(d) {
756 self.handlePopupVisibility(false);
757 });
758
759 d3.select(".popup").append("text")
760 .attr("class", "popup title popupcleanable cleanable")
761 .attr("x", "10")
762 .attr("y", "20")
763 .text(title);
764
765 for (var i in data) {
766 //console.log(i, data, data[i])
767 //var typeofvalue = typeof data[i];
768 var record = data[i];
769 index = this._addRecordToPopup(i, record,index)
770
771 }
772
773 };
774
775 GraphEditor.prototype._addRecordToPopup = function (key, record, index, tab) {
776 //console.log("_addRecordToPopup", key, record, index)
777 var translate_y = 23 * index;
778 var summary = d3.select(".popup").append("g")
779 .attr("class", "popup summary d popupcleanable cleanable")
780 .attr("transform", "translate(10 " + translate_y + ")");
781 if(Object.prototype.toString.call( record ) !== '[object Array]'){ //is a record simple key:value
782 //console.log(key, record)
783 var summary_g = summary.append("g");
784 summary_g.append("rect")
785 .attr("class", "popup summary bg popupcleanable cleanable")
786 .attr("width", "380")
787 .attr("height", "20");
788
789 summary_g.append("text")
790 .attr("class", "popup summary popupcleanable cleanable")
791 .attr("x", (tab)? tab: 10)
792 .attr("y", "17")
793 .attr("width", "100")
794 .text(function(d){
795 return key.toUpperCase() + ":";
796 });
797
798 summary_g.append("text")
799 .attr("class", "popup summary popupcleanable cleanable")
800 .attr("x", "370")
801 .attr("y", "17")
802 .attr("text-anchor", "end")
803 .text(function(d){return record});
804 }
805 else {//is a record simple complex: have a list of sub record key:value
806 //index ++;
807 this._addRecordToPopup(key, "", index)
808 for(var r in record){
809 //console.log(i, r, record, record[r])
810 for(var k in record[r]){
811 //console.log(i, r, k, record[r][k])
812 var curr_key = k;
813 var recordValue = record[r][k]
814
815 index ++;
816 this._addRecordToPopup(curr_key, recordValue, index, 20)
817 }
818 }
819
820 }
821
822 translate_y = 30 * index++;
823 d3.select('#popupbg').attr("height", translate_y);
824 return index;
825 };
826
827
828
829 /**
830 * Remove all the graph objects from the view
831 */
832 GraphEditor.prototype.cleanAll = function () {
833 this.svg.selectAll('.cleanable').remove();
834 };
835
836 /**
837 * Internal functions
838 */
839
840 GraphEditor.prototype._node_property_by_type = function (type, property, node) {
841 //console.log(type, property, layer, group)
842 var unrecognized = function (ui_prop, property) {
843 return ui_prop['unrecognized'][property]
844 };
845
846 //type recognized
847 if (this.type_property[type]) {
848
849 if (this.type_property[type]['property']) {
850 var filt_property = this.type_property[type]['property']
851 return this.type_property[type][node.info[filt_property]][property]
852 } else { // type without property spec
853
854 return this.type_property[type][property]
855
856 }
857
858 } else { //type unrecognized
859 return unrecognized(this.type_property, property)
860 }
861
862 };
863
864 GraphEditor.prototype._link_property_by_type = function (type, property) {
865 //log(type + "-" + property)
866 if (this.type_property_link[type] != undefined && this.type_property_link[type][property] != undefined) {
867 //if(property == "shape")
868 // log("dentro" + this.type_property[type][property])
869 return this.type_property_link[type][property];
870 } else {
871 return this.type_property_link['unrecognized'][property];
872 }
873
874 }
875
876
877 /**
878 *
879 *
880 *
881 */
882 GraphEditor.prototype._setupFiltersBehaviors = function (args) {
883
884 var self = this;
885
886 this.node_filter_cb = args.node_filter_cb || function (d) {
887
888 var cond_view = true,
889 cond_group = true;
890 //log(d.info.type + " " + self.filter_parameters.node.type + " group: " + self.filter_parameters.node.group + "- " + d.info.group)
891 // check filter by node type
892 if (self.filter_parameters.node.type.length > 0) {
893
894 if (self.filter_parameters.node.type.indexOf(d.info.type) < 0)
895 cond_view = false;
896 }
897
898 // check filter by group
899 if (self.filter_parameters.node.group.length > 0) {
900 self.filter_parameters.node.group.forEach(function (group) {
901 if (d.info.group.indexOf(group) < 0)
902 cond_group = false;
903 });
904
905
906 }
907
908
909 return cond_view && cond_group;
910 };
911
912 this.link_filter_cb = args.link_filter_cb || function (d) {
913 var cond_view = true,
914 cond_group = true;
915
916 // check filter by view
917 if (self.filter_parameters.link.view.length > 0) {
918 self.filter_parameters.link.view.forEach(function (view) {
919 if (d.view.indexOf(view) < 0)
920 cond_view = false;
921 });
922 }
923
924 // check filter by group
925 if (self.filter_parameters.link.group.length > 0) {
926 self.filter_parameters.link.group.forEach(function (group) {
927 if (d.group.indexOf(group) < 0)
928 cond_group = false;
929 });
930 }
931 return cond_view && cond_group;
932 };
933
934 };
935
936 /**
937 *
938 *
939 */
940 GraphEditor.prototype._setupBehaviorsOnEvents = function () {
941 log("_setupBehaviorsOnEvents");
942 var self = this;
943 this.behavioursOnEvents = {
944 'nodes': {
945 'click': function (d) {
946 d3.event.preventDefault();
947 log('click', d);
948 if (self.lastKeyDown == SHIFT_BUTTON && self._selected_node != undefined) {
949 var source_id = self._selected_node.id;
950 var target_id = d.id;
951 log("--" + JSON.stringify(self.filter_parameters.link.view));
952 var new_link = {
953 source: source_id,
954 target: target_id,
955 view: self.filter_parameters.link.view[0],
956 group: self.filter_parameters.link.group[0],
957 };
958 self.addLink(new_link);
959 self._deselectAllNodes();
960 } else {
961 self._selectNodeExclusive(this, d);
962 }
963
964 },
965 'mouseover': function (d) {
966
967 },
968 'mouseout': function (d) {},
969 'dblclick': function (d) {
970 d3.event.preventDefault();
971 log('dblclick');
972 },
973 'contextmenu': function (d, i) {
974 d3.event.preventDefault();
975 log("contextmenu node");
976 self.eventHandler.fire("right_click_node", d);
977 }
978 },
979 'links': {
980 'click': function (event) {
981
982 },
983 'dblclick': function (event) {
984
985 }
986 }
987 };
988 };
989
990 /**
991 * Deselect previously selected nodes
992 *
993 */
994 GraphEditor.prototype._deselectAllNodes = function () {
995 log("_deselectAllNodes");
996 this.node.classed("node_selected", false);
997 this._selected_node = undefined;
998 };
999
1000 GraphEditor.prototype._deselectAllLinks = function () {
1001 log("_deselectAllLinks");
1002 this.link.classed("link_selected", false).style('stroke-width', 2);
1003 this._selected_link = undefined;
1004 };
1005 /**
1006 * Select node in exclusive mode
1007 * @param {Object} Required. Element selected on click event
1008 */
1009 GraphEditor.prototype._selectNodeExclusive = function (node_instance, node_id) {
1010 log("_selectNodeExclusive ");
1011 var activeClass = "node_selected";
1012 var alreadyIsActive = d3.select(node_instance).classed(activeClass);
1013 this._deselectAllNodes();
1014 this._deselectAllLinks();
1015 d3.select(node_instance).classed(activeClass, !alreadyIsActive);
1016 this._selected_node = (alreadyIsActive) ? undefined : node_instance.__data__;
1017 };
1018
1019 /**
1020 * Select node in exclusive mode
1021 * @param {Object} Required. Element selected on click event
1022 */
1023 GraphEditor.prototype._selectLinkExclusive = function (link_instance, link_id) {
1024 log("_selectLinkExclusive ");
1025 var activeClass = "link_selected";
1026 var alreadyIsActive = d3.select(link_instance).classed(activeClass);
1027 this._deselectAllNodes();
1028 this._deselectAllLinks();
1029 d3.select(link_instance).classed(activeClass, !alreadyIsActive);
1030 d3.select(link_instance).style('stroke-width', 4)
1031 this._selected_link = link_instance.__data__;
1032 };
1033
1034 /**
1035 * Callback to resize SVG element on window resize
1036 */
1037 GraphEditor.prototype.resizeSvg = function (width, height) {
1038 log("resizeSvg");
1039 //log(event);
1040 this.width = width || this.width;
1041 this.height = height || this.height;
1042 this.svg.attr('width', width);
1043 this.svg.attr('height', height);
1044
1045 }
1046
1047 GraphEditor.prototype.handlePopupVisibility = function(visible, side) {
1048 var opacity = (visible) ? 1 : 0;
1049
1050 var translate_op = (side == "left") ? "translate(50 50)" : "translate("+(this.width - 450).toString()+" 50)";
1051
1052 if (!visible) {
1053 d3.selectAll(".popupcleanable").remove();
1054 d3.select(".popup")
1055 .attr("transform", "translate(-1 -1)");
1056 } else {
1057 d3.select(".popup")
1058 .attr("transform", translate_op);
1059 }
1060 d3.select(".popup").attr("opacity", opacity);
1061 };
1062
1063 GraphEditor.prototype.refreshGraphParameters = function (graphParameters) {
1064 this.eventHandler.fire("refresh_graph_parameters", graphParameters);
1065 }
1066
1067 /**
1068 * Log utility
1069 */
1070 function log(text) {
1071 if (DEBUG)
1072 console.log("::GraphEditor::", text);
1073 }
1074
1075
1076
1077 return GraphEditor;
1078
1079
1080 }(this));
1081
1082 if (typeof module === 'object') {
1083 module.exports = dreamer.GraphEditor;
1084 }