// Pandora FMS - http://pandorafms.com // ================================================== // Copyright (c) 2005-2010 Artica Soluciones Tecnologicas // Please see http://pandorafms.org for full contribution list // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License // as published by the Free Software Foundation; version 2 // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. /*-----------------------------------------------*/ /*-------------------Constants-------------------*/ /*-----------------------------------------------*/ var MAX_ZOOM_LEVEL = 50; var RELATION_MINIMAP = 4; var CONTROL_KEY = 17; /*-----------------------------------------------*/ /*------------------Constructor------------------*/ /*-----------------------------------------------*/ var MapController = function(target, refresh_time) { this._target = target; this._refresh_time = refresh_time * 1000; // 1000 for the javascript this._id = $(target).data('id'); } /*-----------------------------------------------*/ /*------------------Atributes--------------------*/ /*-----------------------------------------------*/ MapController.prototype._id = null; MapController.prototype._viewport = null; MapController.prototype._viewport_nodes = null; MapController.prototype._viewport_edges = null; MapController.prototype._minimap_viewport = null; MapController.prototype._minimap = null; MapController.prototype._zoomManager = null; MapController.prototype._slider = null; MapController.prototype._relation = null; MapController.prototype._dragging = false; MapController.prototype._start_flag_multiple_selection = false; MapController.prototype._flag_multiple_selection = false; MapController.prototype._cache_files = {}; MapController.prototype._last_event = null; MapController.prototype._keys_pressed = []; MapController.prototype._last_mouse_position = null; MapController.prototype._relationship_in_progress = false; MapController.prototype._relationship_in_progress_type = null; MapController.prototype._over = null MapController.prototype._dragged_nodes = {}; MapController.prototype._enabled_minimap = true; /*-----------------------------------------------*/ /*--------------------Methods--------------------*/ /*-----------------------------------------------*/ /** * Function init_map * Return void * This function init the map */ MapController.prototype.init_map = function() { var self = this; width_svg = $("#map_" + self._id).width(); if ($("#main").height()) { height_svg = $("#main").height(); } else { //Set the height in the pure view (fullscreen). height_svg = $(window).height() - $("#menu_tab_frame_view").height() - 20; // 20 of margin } var svg = d3.select(self._target + " svg"); svg .attr("width", width_svg) .attr("height", height_svg) self._zoomManager = d3.behavior.zoom() .scaleExtent([1/MAX_ZOOM_LEVEL, MAX_ZOOM_LEVEL]).on("zoom", zoom); self._viewport = svg .call(self._zoomManager) .append("g") .attr("class", "viewport"); self._viewport_edges = self._viewport.append("g") .attr("class", "viewport_edges"); self._viewport_nodes = self._viewport.append("g") .attr("class", "viewport_nodes"); self._minimap = svg .append("g") .attr("class", "minimap"); /** * Function zoom * Return void * This function manages the zoom */ function zoom() { self.close_all_tooltips(); self.remove_resize_square(); if (!self._flag_multiple_selection) { self.last_event = "zoom"; var zoom_level = d3.event.scale; self._slider.property("value", Math.log(zoom_level)); self._viewport .attr("transform", "translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")"); self.zoom_minimap(); } else { self.last_event = null; // Reset the zoom and panning actual var viewport_transform = d3.transform( d3.select(self._target + " .viewport").attr("transform")); self._zoomManager .scale(viewport_transform.scale[0]) .translate(viewport_transform.translate); } } /** * Function zoom_in * Return void * This function zoom with "+" button */ function zoom_in(d) { var step = parseFloat(self._slider.property("step")); var slider_value = parseFloat(self._slider.property("value")); slider_value += step; var zoom_level = Math.exp(slider_value); self._slider.property("value", Math.log(zoom_level)); self._slider.on("input")(); } /** * Function zoom_out * Return void * This function zoom with "-" button */ function zoom_out(d) { var step = parseFloat(self._slider.property("step")); var slider_value = parseFloat(self._slider.property("value")); slider_value -= step; var zoom_level = Math.exp(slider_value); self._slider.property("value", Math.log(zoom_level)); self._slider.on("input")(); } /** * Function home_zoom * Return void * This function zoom with "H" button (reset zoom) */ function home_zoom(d) { self._zoomManager.scale(1).translate([0, 0]).event(self._viewport); } /** * Function slided * Return void * This function manages the slide (zoom system) */ function slided(d) { var slider_value = parseFloat(self._slider.property("value")) var zoom_level = Math.exp(slider_value); /*----------------------------------------------------------------*/ /*-Code to translate the map with the zoom for to hold the center-*/ /*----------------------------------------------------------------*/ var center = [ parseFloat(d3.select(self._target).style('width')) / 2, parseFloat(d3.select(self._target).style('height')) / 2]; var old_translate = self._zoomManager.translate(); var old_scale = self._zoomManager.scale(); var temp1 = [(center[0] - old_translate[0]) / old_scale, (center[1] - old_translate[1]) / old_scale]; var temp2 = [temp1[0] * zoom_level + old_translate[0], temp1[1] * zoom_level + old_translate[1]]; var new_translation = [ old_translate[0] + center[0] - temp2[0], old_translate[1] + center[1] - temp2[1]]; self._zoomManager.scale(zoom_level) .translate(new_translation) .event(self._viewport); } self._slider = d3.select(self._target + " .zoom_controller .vertical_range") .property("value", 0) .property("min", -Math.log(MAX_ZOOM_LEVEL)) .property("max", Math.log(MAX_ZOOM_LEVEL)) .property("step", Math.log(MAX_ZOOM_LEVEL) * 2 / MAX_ZOOM_LEVEL) .on("input", slided); d3.select(self._target + " .zoom_box .home_zoom") .on("click", home_zoom); d3.select(self._target + " .zoom_box .zoom_in") .on("click", zoom_in); d3.select(self._target + " .zoom_box .zoom_out") .on("click", zoom_out); self.paint_nodes(); self.paint_arrows(); if (self._enabled_minimap) self.init_minimap(); self.ini_selection_rectangle(); self.events(); } MapController.prototype.set_nodes_map = function(nodes) { var self = this; window["nodes_" + self._id] = nodes; } MapController.prototype.get_nodes_map = function() { var self = this; return window["nodes_" + self._id]; } MapController.prototype.get_edges_map = function() { var self = this; return window["edges_" + self._id]; } MapController.prototype.set_edges_map = function(edges) { var self = this; window["edges_" + self._id] = edges; } MapController.prototype.get_subtype_map = function() { var self = this; return window["subtype_" + self._id]; } MapController.prototype.get_filter_map = function() { var self = this; return window["filter_" + self._id]; } /** * Function ini_selection_rectangle * Return void * This function init the rectangle selection */ MapController.prototype.ini_selection_rectangle = function() { var self = this; d3.select(self._target + " svg") .append("g") .attr("id", "layer_selection_rectangle"); } /** * Function minimap_get_size * Return [width, height] * This function returns the minimap size */ MapController.prototype.minimap_get_size = function() { var self = this; var minimap_size = []; minimap_size[0] = d3.selectAll(self._target + " .minimap rect").attr("width"); minimap_size[1] = d3.selectAll(self._target + " .minimap rect").attr("height"); return minimap_size; } /** * Function get_node * Return node * This function returns a node */ MapController.prototype.get_node = function(id_graph) { var self = this; var return_node = null; $.each(self.get_nodes_map(), function(i, node) { if (node['graph_id'] == id_graph) { node['index_node'] = i; return_node = node; return false; } }); return return_node; } /** * Function get_node_filter * Return void * This function returns the node filter */ MapController.prototype.get_node_filter = function(field, value) { var self = this; var return_node = null; $.each(self.get_nodes_map(), function(i, node) { if (node[field] == value) { return_node = node; return false; } }); return return_node; } /** * Function get_node_type * Return node type * This function returns a node type (module, agent or edge) */ MapController.prototype.get_node_type = function(id_graph) { var self = this; var node = self.get_node(id_graph); if (node !== null) { return node['type']; } return null; } /** * Function get_edges_from_node * Return array[edge] * This function returns the edges of a node */ MapController.prototype.get_edges_from_node = function(id_graph) { var self = this; var return_edges = []; $.each(self.get_edges_map(), function(i, edge) { if ((edge['to'] == id_graph) || (edge['from'] == id_graph)) { return_edges.push(edge); } }); return return_edges; } /** * Function update_edges_from_clean_arrows * Return void * This function updates the graph id of the edges */ MapController.prototype.update_edges_from_clean_arrows = function(clean_arrows) { var self = this; newEdges = []; clean_arrows.forEach(function(arrow, index) { newEdges[index] = arrow; }); self.set_edges_map(newEdges); } /** * Function get_arrow_from_id * Return void * This function return an arrow from a specific id */ MapController.prototype.get_arrow_from_id = function(id) { var self = this; var arrow = $.grep(self.get_edges_map(), function(e) { if (e['graph_id'] == id) { return true; } }); if (arrow.length == 0) { return null; } else { return arrow[0]; } } /** * Function get_arrows_from_edges * Return array[] * This function returns a collection of arrows from edges (array) */ MapController.prototype.get_arrows_from_edges = function() { var self = this; var return_var = []; $.each(self.get_edges_map(), function(i, e) { return_var.push(self.get_arrow_from_id(e['graph_id'])); }); return return_var; } /** * Function get_arrow * Return void * This function return a specific arrow */ MapController.prototype.get_arrow = function(id_to, id_from) { var self = this; var arrow = {}; arrow['nodes'] = {}; var count_nodes = 0; $.each(self.get_nodes_map(), function(i, node) { if (parseInt(node['graph_id']) == parseInt(id_to)) { arrow['nodes']['to'] = node; count_nodes++; } else if (parseInt(node['graph_id']) == parseInt(id_from)) { arrow['nodes']['from'] = node; count_nodes++; } }); if (count_nodes == 2) { $.each(self.get_edges_map(), function(i, edge) { if (edge['to'] == arrow['nodes']['to']['graph_id'] && edge['from'] == arrow['nodes']['from']['graph_id']) { arrow['arrow'] = edge; return false; // Break } }); return arrow; } else { return null; } } /** * Function paint_toggle_button * Return void * This function paints the hide/show button (minimap) */ MapController.prototype.paint_toggle_button = function(wait) { var self = this; if (typeof(wait) === "undefined") wait = 1; var count_files = 2; function wait_load(callback) { count_files--; if (count_files == 0) { callback(); } } var map = d3.select(self._target + " svg"); var minimap = d3.select(self._target + " .minimap"); var transform = d3.transform(); var minimap_transform = d3.transform(minimap.attr("transform")); switch (wait) { case 1: var toggle_minimap_button_layer = map.append("g") .attr("id", "toggle_minimap_button"); if (is_buggy_firefox) { toggle_minimap_button_layer.append("g") .attr("class", "toggle_minimap_on") .append("use") .attr("xlink:href", "#toggle_minimap_on"); arrow_layout.append("g") .attr("class", "toggle_minimap_off") .style("opacity", 0) .append("use") .attr("xlink:href", "#toggle_minimap_off"); self.paint_toggle_button(0); } else { toggle_minimap_button_layer.append("g") .attr("class", "toggle_minimap_on") .append("use") .attr("xlink:href", "images/maps/toggle_minimap_on.svg#toggle_minimap_on") .on("load", function() { wait_load(function() { self.paint_toggle_button(0); }); }); toggle_minimap_button_layer.append("g") .attr("class", "toggle_minimap_off") .style("opacity", 0) .append("use") .attr("xlink:href", "images/maps/toggle_minimap_off.svg#toggle_minimap_off") .on("load", function() { wait_load(function() { self.paint_toggle_button(0); }); }); } break; case 0: var toggle_minimap_button_layer = d3.select(self._target + " #toggle_minimap_button"); var toggle_minimap_button_layer_bbox = toggle_minimap_button_layer.node().getBBox(); transform.translate[0] = minimap_transform.translate[0]; transform.translate[1] = self.minimap_get_size()[1] - toggle_minimap_button_layer_bbox.height; toggle_minimap_button_layer.attr("transform", transform.toString()); toggle_minimap_button_layer.on("click", function() { self.event_toggle_minimap(); }); break; } } /** * Function event_toggle_minimap * Return void * This function captures the minimap events */ MapController.prototype.event_toggle_minimap = function() { var self = this; var map_size = d3.select(self._target).node().getBoundingClientRect(); var toggle_minimap_on = parseInt(d3 .select(self._target + " .toggle_minimap_on").style("opacity")); var toggle_minimap_button_layer = d3.select(self._target + " #toggle_minimap_button"); var transform_toggle_minimap_button = d3 .transform(toggle_minimap_button_layer.attr("transform")); var toggle_minimap_button_layer_bbox = toggle_minimap_button_layer.node().getBBox(); var minimap = d3.select(self._target + " .minimap"); var minimap_transform = d3.transform(minimap.attr("transform")); switch (toggle_minimap_on) { case 0: transform_toggle_minimap_button .translate[0] = minimap_transform.translate[0]; transform_toggle_minimap_button .translate[1] = self.minimap_get_size()[1] - toggle_minimap_button_layer_bbox.height; toggle_minimap_button_layer.attr("transform", transform_toggle_minimap_button); d3.select(self._target + " .minimap") .style("opacity", 1); d3.select(self._target + " .toggle_minimap_off") .style("opacity", 0); d3.select(self._target + " .toggle_minimap_on") .style("opacity", 1); break; case 1: transform_toggle_minimap_button.translate[0] = map_size.width - toggle_minimap_button_layer_bbox.width; transform_toggle_minimap_button.translate[1] = 0; toggle_minimap_button_layer.attr("transform", transform_toggle_minimap_button); d3.select(self._target + " .minimap") .style("opacity", 0); d3.select(self._target + " .toggle_minimap_off") .style("opacity", 1); d3.select(self._target + " .toggle_minimap_on") .style("opacity", 0); break; } } /** * Function get_real_size_map * Return [width, height] * This function returns the real map size */ MapController.prototype.get_real_size_map = function() { var self = this; var map_size = d3.select(self._target + " .viewport").node().getBBox(); if (map_size.width == 0) { map_size.width = 100; } if (map_size.height == 0) { map_size.height = 100; } return map_size; } MapController.prototype.init_minimap = function() { var self = this; var map_size = self.get_real_size_map(); var real_width = map_size.width + map_size.x; var real_height = map_size.height + map_size.y; self.paint_minimap(real_width, real_height); self.paint_toggle_button(); self.paint_items_minimap(); self.zoom_minimap(); self.events_for_minimap(); } /** * Function paint_minimap * Return void * This function paints the minimap */ MapController.prototype.paint_minimap = function(map_width, map_height) { var self = this; var screen_size = d3.select(self._target).node().getBoundingClientRect(); var max_map = map_height; if (map_width > map_height) max_map = map_width; var max_screen = screen_size.height; if (screen_size.width > screen_size.height) max_screen = screen_size.width; self._relation = RELATION_MINIMAP * max_map / max_screen; if (self._relation < 1) self._relation = 1; var minimap_map_width = map_width / self._relation; var minimap_map_height = map_height / self._relation; var minimap = d3.select(self._target + " .minimap"); var svg = d3.select(self._target + " svg"); var transform = d3.transform(); // Move the minimap to the right upper corner transform.translate[0] = screen_size.width - minimap_map_width; transform.translate[1] = 0; minimap.attr("transform", transform.toString()); svg.append("defs") .append("clipPath") .attr("id", "clip_minimap") .append("rect") .attr("x", 0) .attr("y", 0) .attr("width", minimap_map_width) .attr("height", minimap_map_height); minimap .append("rect") .attr("x", 0) .attr("y", 0) .attr("width", minimap_map_width) .attr("height", minimap_map_height) .attr("style", "fill: #ffffff; stroke: #000000; stroke-width: 1;"); transform = d3.transform(); transform.scale[0] = 1 / self._relation; transform.scale[1] = 1 / self._relation; self._minimap_viewport = minimap .append("g") .attr("class", "clip_minimap") .attr("clip-path", "url(#clip_minimap)") .append("g") .attr("class", "map") .attr("transform", transform.toString()); self._minimap_viewport.append("rect") .attr("class", "viewport") .attr("style", "fill: #dddddd; stroke: #aaaaaa; stroke-width: 1;") .attr("x", 0) .attr("y", 0) .attr("height", screen_size.height) .attr("width", screen_size.width); } /** * Function paint_items_minimap * Return void * This function paints the minimap items */ MapController.prototype.paint_items_minimap = function() { var self = this; self._minimap_viewport.selectAll(".node") .data( self.get_nodes_map() .filter(function(d, i) { return self.filter_only_agents(d); })) .enter() .append("g") .attr("transform", function(d) { var x = d['x']; var y = d['y']; return "translate(" + x + " " + y + ")"; }) .attr("class", "node") .attr("id", function(d) { return "node_" + d['graph_id'];}) .attr("data-id", function(d) { return d['id'];}) .attr("data-graph_id", function(d) { return d['graph_id'];}) .attr("data-type", function(d) { return d['type'];}) .append("circle") .attr("style", "fill: rgb(50, 50, 128);") .attr("x", 0) .attr("y", 0) .attr("r", 30); } /** * Function zoom_minimap * Return void * This function apply zoom in minimap */ MapController.prototype.zoom_minimap = function() { var self = this; var viewport_transform = d3.transform( d3.select(self._target + " .viewport").attr("transform")); var transform = d3.transform(); var minimap_viewport = d3.select(self._target + " .minimap .viewport"); transform.translate[0] = -viewport_transform.translate[0]; transform.translate[1] = -viewport_transform.translate[1]; transform.scale[0] = 1 / viewport_transform.scale[0]; transform.scale[1] = 1 / viewport_transform.scale[1]; minimap_viewport .attr("transform", transform.toString()); } /** * Function node_from_edge * Return node * This function returns the node with the specific id_graph */ MapController.prototype.node_from_edge = function(id_graph) { var self = this; var exists = null; $.each(self.get_edges_map(), function(i, e) { if (e.graph_id == id_graph) { exists = i; return false; // jquery.each break; } }); if (exists !== null) return self.get_edges_map()[exists]; else return null; } /** * Function exists_edge * Return bool * This function returns if the node exist */ MapController.prototype.exists_edge = function(id_graph) { var self = this; var exists = false; $.each(self.get_edges_map(), function(i, e) { if (e.graph_id == id_graph) { exists = true; return false; // jquery.each break; } }); return exists; } /** * Function paint_arrows * Return void * This function paints the arrows */ MapController.prototype.paint_arrows = function() { var self = this; var arrow_layouts = self._viewport.selectAll(".arrow") .data( self.get_nodes_map() .filter(function(d, i) { if (d.type == ITEM_TYPE_EDGE_NETWORKMAP) { if (self.exists_edge(d['graph_id'])) return true; else return false; } else return false; })) .enter() .append("g") .attr("class", "arrow") .attr("id", function(d) { return "arrow_" + d['graph_id'];}) .attr("data-id", function(d) { return d['id'];}) .attr("data-to", function(d) { return self.node_from_edge(d['graph_id'])["to"];}) .attr("data-from", function(d) { return self.node_from_edge(d['graph_id'])["from"];}); create_arrow(arrow_layouts); /** * Function create_arrow * Return void * This function creates the arrow */ function create_arrow(arrow_layouts) { arrow_layouts.each(function(d) { var arrow_layout = this; var node_arrow = self.get_edges_map().filter(function(d2) { if (d2['graph_id'] == d['graph_id']) return true; else return false; })[0]; var to_node = self.get_nodes_map().filter(function(d2) { if (d2['graph_id'] == node_arrow['to']) return true; else return false; })[0]; var from_node = self.get_nodes_map().filter(function(d2) { if (d2['graph_id'] == node_arrow['from']) return true; else return false; })[0]; var id_arrow = d3.select(arrow_layout).attr("id"); var id_node_to = "node_" + to_node['graph_id']; var id_node_from = "node_" + from_node['graph_id']; self.arrow_by_pieces(self._target + " svg", id_arrow, id_node_to, id_node_from); }); } } /** * Function paint_nodes * Return void * This function paint the nodes */ MapController.prototype.paint_nodes = function() { var self = this; self._viewport.selectAll(".node") .data( self.get_nodes_map() .filter(function(d, i) { return self.filter_only_agents(d); })) .enter() .append("g") .attr("transform", function(d) { return "translate(" + d['x'] + " " + d['y'] + ")";}) .attr("class", "draggable node") .attr("id", function(d) { return "node_" + d['graph_id'];}) .attr("style", "fill: rgb(50, 50, 128);") .attr("data-id", function(d) { return d['id'];}) .attr("data-graph_id", function(d) { return d['graph_id'];}) .attr("data-type", function(d) { return d['type'];}) .append("rect") .attr("style", "") .attr("x", 0) .attr("y", 0) .attr("height", 30) .attr("width", 30); } /** * Function move_arrow * Return void * This function moves the arrow */ MapController.prototype.move_arrow = function (id_from_any_point_arrow) { var self = this; var arrows = d3.selectAll(self._target + " .arrow").filter(function(d2) { if ( (d3.select(this).attr("data-to") == id_from_any_point_arrow) || (d3.select(this).attr("data-from") == id_from_any_point_arrow) ) { return true; } return false; }); arrows.each(function(d) { self.arrow_by_pieces(self._target + " svg", d, 0); }); } /** * Function remove_resize_square * Return void * This function removes squares resize */ MapController.prototype.remove_resize_square = function(item, wait) { var self = this; d3.select(self._target + " svg #resize_square").remove(); } /** * Function positioning_resize_square * Return void * This function positioning the square to resize the element */ MapController.prototype.positioning_resize_square = function(item) { var self = this; var resize_square = d3.select(self._target + " #resize_square"); var item_d3 = d3.select(self._target + " #node_" + item['graph_id']); var bbox_item = item_d3.node().getBBox(); var bbox_square = resize_square.node().getBBox(); var transform_item = d3.transform(item_d3.attr("transform")); var transform_viewport = d3 .transform(d3.select(self._target + " .viewport").attr("transform")); var transform = d3.transform(); var x = (bbox_item.x + transform_item.translate[0] + transform_viewport.translate[0] ) * transform_viewport.scale[0]; var y = (bbox_item.y + transform_item.translate[1] + transform_viewport.translate[1] ) * transform_viewport.scale[1]; x = (bbox_item.x + transform_item.translate[0]) * transform_viewport.scale[0] + transform_viewport.translate[0]; y = (bbox_item.y + transform_item.translate[1]) * transform_viewport.scale[1] + transform_viewport.translate[1]; transform.translate[0] = x; transform.translate[1] = y; resize_square .attr("transform", transform.toString()); var real_item_width = (bbox_item.width * transform_item.scale[0]); var real_item_height = (bbox_item.height * transform_item.scale[1]); d3.select("#resize_square .square rect") .attr("width", (real_item_width * transform_viewport.scale[0])); d3.select("#resize_square .square rect") .attr("height", (real_item_height * transform_viewport.scale[1])); // Set the handlers var bbox_handler = d3 .select(self._target + " .handler").node().getBBox(); var handler_positions = {}; handler_positions['N'] = []; handler_positions['N'][0] = (real_item_width * transform_viewport.scale[0] / 2) - (bbox_handler.width / 2); handler_positions['N'][1] = 0 - (bbox_handler.height / 2); handler_positions['NE'] = []; handler_positions['NE'][0] = (real_item_width * transform_viewport.scale[0]) - (bbox_handler.width / 2); handler_positions['NE'][1] = handler_positions['N'][1]; handler_positions['E'] = []; handler_positions['E'][0] = handler_positions['NE'][0]; handler_positions['E'][1] = (real_item_height * transform_viewport.scale[1] / 2) - (bbox_handler.height / 2); handler_positions['SE'] = []; handler_positions['SE'][0] = handler_positions['NE'][0]; handler_positions['SE'][1] = (real_item_height * transform_viewport.scale[1]) - (bbox_handler.height / 2); handler_positions['S'] = []; handler_positions['S'][0] = handler_positions['N'][0]; handler_positions['S'][1] = handler_positions['SE'][1]; handler_positions['SW'] = []; handler_positions['SW'][0] = 0 - (bbox_handler.width / 2); handler_positions['SW'][1] = handler_positions['SE'][1]; handler_positions['W'] = []; handler_positions['W'][0] = handler_positions['SW'][0]; handler_positions['W'][1] = handler_positions['E'][1]; handler_positions['NW'] = []; handler_positions['NW'][0] = handler_positions['SW'][0]; handler_positions['NW'][1] = handler_positions['N'][1]; d3.selectAll(" .handler").each(function(d) { var transform = d3.transform(); transform.translate[0] = handler_positions[d][0]; transform.translate[1] = handler_positions[d][1]; d3.select(self._target + " .handler_" + d) .attr("transform", transform.toString()); }); } /** * Function paint_resize_square * Return void * This function paints a square to resize the elements */ MapController.prototype.paint_resize_square = function(item, wait) { var self = this; if (typeof(wait) === "undefined") wait = 1; var count_files = 16; function wait_load(callback) { count_files--; if (count_files == 0) { callback(); } } switch (wait) { /*---------------------------------------------------*/ /*--------------- Prepare the square ----------------*/ /*---------------------------------------------------*/ case 1: var resize_square = d3 .select(self._target + " svg") .append("g").attr("id", "resize_square") .style("opacity", 0); d3.xml("images/maps/square_selection.svg", "application/xml", function(xml) { var nodes = xml .evaluate("//*[@id='square_selection']/*", xml, null, XPathResult.ANY_TYPE, null); var result = nodes.iterateNext(); resize_square .append("g").attr("class", "square_selection") .append(function() {return result}); if (is_buggy_firefox) { resize_square .append("g").attr("class", "handles") .selectAll(".handle") .data(["N", "NE", "E", "SE", "S", "SW", "W", "NW"]) .enter() .append("g") .attr("class", function(d) { return "handler handler_" + d;}) .append("use") .attr("xlink:href", "images/maps/resize_handle.svg#resize_handle") .attr("class", function(d) { return "handler " + d;}); self.paint_resize_square(item, 0); } else { var handles = resize_square .append("g").attr("class", "handles"); handles.selectAll(".handle") .data(["N", "NE", "E", "SE", "S", "SW", "W", "NW"]) .enter() .append("g") .attr("class", function(d) { return "handler handler_" + d;}); handles.selectAll(".handler").each(function(d) { d3.select(this) .append("use") .style("opacity", 1) .attr("class", "default") .attr("xlink:href", "images/maps/resize_handle.svg#resize_handle") .on("load", function() { wait_load(function() { self.paint_resize_square( item, 0); }); }); d3.select(this) .append("use") .style("opacity", 0) .attr("class", "over") .attr("xlink:href", "images/maps/resize_handle.over.svg#resize_handle_over") .on("load", function() { wait_load(function() { self.paint_resize_square( item, 0); }); }); }); } }); break; /*---------------------------------------------------*/ /*-------- Paint and positioning the square ---------*/ /*---------------------------------------------------*/ case 0: self.positioning_resize_square(item); d3.selectAll(" .handler").each(function(d) { var drag = d3.behavior.drag() .origin(function(d) { return d; }) .on("dragstart", function(d) { self.event_resize("dragstart", item, d); }) .on("drag", function(d) { self.event_resize("drag", item, d); }) .on("dragend", function(d) { self.event_resize("dragend", item, d); }); d3.select(this).call(drag); d3.select(this) .on("mouseover", function(d) { self.change_handler_image("mouseover", d); }) .on("mouseout", function(d) { self.change_handler_image("mouseout", d); }); }); var resize_square = d3.select(self._target + " #resize_square"); resize_square.style("opacity", 1); break; } } /** * Function change_handler_image * Return void * This function changes the handler image */ MapController.prototype.change_handler_image = function(action, handler, wait) { var self = this; var handlers_d3 = d3.select(self._target + " .handles"); var handler_d3 = d3.select(self._target + " .handler_" + handler); switch (action) { case "mouseover": handler_d3.select(".default").style("opacity", 0); handler_d3.select(".over").style("opacity", 1); break; case "mouseout": handler_d3.select(".default").style("opacity", 1); handler_d3.select(".over").style("opacity", 0); break; } } /** * Function event_resize * Return void * This function manages the resize event */ MapController.prototype.event_resize = function(action, item, handler) { var self = this; d3.event.sourceEvent.stopPropagation(); d3.event.sourceEvent.preventDefault(); var self = this; var handler_d3 = d3.select(self._target + " .handler_" + handler); switch (action) { case "dragstart": handler_d3.classed("dragging", true); self.save_size_item(item); break; case "drag": var delta_x = d3.event.dx; var delta_y = d3.event.dy; switch (handler) { case "N": case "S": delta_x = 0; break; case "E": case "W": delta_y = 0; break; } self.resize_node(item, handler, delta_x, delta_y); self.positioning_resize_square(item); break; case "dragend": handler_d3.classed("dragging", false); var item_d3 = d3.select(self._target + " #node_" + item['graph_id']); var width = parseFloat(item_d3.attr("data-width")); var height = parseFloat(item_d3.attr("data-height")); var transform_viewport = d3.transform(item_d3.attr("transform")); var x = transform_viewport.translate[0]; var y = transform_viewport.translate[1]; $.each(self.get_nodes_map(), function(i, node) { if (node['graph_id'] != item['graph_id']) return 1; // Continue self.get_nodes_map()[i].x = x; self.get_nodes_map()[i].y = y; self.get_nodes_map()[i].width = width; self.get_nodes_map()[i].height = height; }); self.resize_node_save(item['graph_id']); break; } } MapController.prototype.resize_node_save = function(graph_id) { } /** * Function save_size_item * Return void * This function saves the actual size of the element (node) */ MapController.prototype.save_size_item = function(item) { var self = this; var item_d3 = d3.select(self._target + " #node_" + item['graph_id']); var item_transform = d3.transform(item_d3.attr("transform")); var item_bbox = item_d3.node().getBBox(); var width = item_bbox.width * item_transform.scale[0]; var height = item_bbox.height * item_transform.scale[1]; item_d3.attr("data-original_width", parseFloat(width)); item_d3.attr("data-original_height", parseFloat(height)); } /** * Function resize_node * Return void * This function do the process to resize the element (node) */ MapController.prototype.resize_node = function(item, handler, delta_x, delta_y) { var self = this; var item_d3 = d3.select(self._target + " #node_" + item['graph_id']); var item_transform = d3.transform(item_d3.attr("transform")); var item_bbox = item_d3.node().getBBox(); var transform_viewport = d3.transform(d3.select(self._target + " .viewport").attr("transform")); var inc_w = delta_x * transform_viewport.scale[0]; var inc_h = delta_y * transform_viewport.scale[1]; var width = item_d3.attr("data-width"); var height = item_d3.attr("data-height"); if (width == null) { width = old_width = item_d3.attr("data-original_width"); height = old_height = item_d3.attr("data-original_height"); } old_width = parseFloat(old_width); old_height = parseFloat(old_height); width = parseFloat(width); height = parseFloat(height); var new_width; var new_height; var scale_x; var scale_y; switch (handler) { case "SE": case "E": case "S": new_width = width + inc_w; new_height = height + inc_h; if ((new_width < 0) || (new_height < 0)) { if ((new_width < 0) && (new_height < 0)) { new_width = Math.abs(new_width); new_height = Math.abs(new_height); } else if (new_width < 0) { new_width = Math.abs(new_width); } else { new_height = Math.abs(new_height); } } break; case "N": case "NE": new_width = width + inc_w; new_height = height - inc_h; if ((new_width < 0) || (new_height < 0)) { if ((new_width < 0) && (new_height < 0)) { new_width = Math.abs(new_width); new_height = Math.abs(new_height); } else if (new_width < 0) { new_width = Math.abs(new_width); } else { new_height = Math.abs(new_height); } } else { item_transform.translate[1] += inc_h; } break; case "NW": case "W": new_width = width - inc_w; new_height = height - inc_h; if ((new_width < 0) || (new_height < 0)) { if ((new_width < 0) && (new_height < 0)) { new_width = Math.abs(new_width); new_height = Math.abs(new_height); } else if (new_width < 0) { new_width = Math.abs(new_width); } else { new_height = Math.abs(new_height); } } else { item_transform.translate[0] += inc_w; item_transform.translate[1] += inc_h; } break; case "SW": new_width = width - inc_w; new_height = height + inc_h; if ((new_width < 0) || (new_height < 0)) { if ((new_width < 0) && (new_height < 0)) { new_width = Math.abs(new_width); new_height = Math.abs(new_height); } else if (new_width < 0) { new_width = Math.abs(new_width); } else { new_height = Math.abs(new_height); } } else { item_transform.translate[0] += inc_w; } break; } scale_x = new_width / old_width; scale_y = new_height / old_height; item_transform.scale[0] = scale_x; item_transform.scale[1] = scale_y; item_d3.attr("transform", item_transform.toString()); item_d3.attr("data-width", parseFloat(new_width)); item_d3.attr("data-height", parseFloat(new_height)); self.move_arrow(item["graph_id"]); } /** * Function key_is_pressed * Return void * This function checks if the crtl key is pressed */ MapController.prototype.key_is_pressed = function(key) { var self = this; if (typeof(self._keys_pressed[key]) == "undefined") return false; else return self._keys_pressed[key]; } /** * Function get_menu_nodes * Return void * This function returns the node menu */ MapController.prototype.get_menu_nodes = function() { var self = this; var node_menu = [ { title: 'Show details', action: function(elm, d, i) { var nodeTarget = $(elm); var type = parseInt(nodeTarget.data("type")); if (type == 0) { self.nodeGetDetails(self, elm); } } }, { title: 'Resize', action: function(elm, d, i) { self.paint_resize_square(d); } }, { title: 'Edit', action: function(elm, d, i) { self.editNode(elm); } }, { title: 'Set as children', action: function(elm, d, i) { self._last_event = null; self._relationship_in_progress_type = "children"; self.set_as_children(); } }, { title: 'Set as parent', action: function(elm, d, i) { self._last_event = null; self._relationship_in_progress_type = "parent"; self.set_as_parent(); } }, { title: 'Delete', action: function(elm, d, i) { self.deleteNode(self, elm); } } ]; return node_menu; } /** * Function events_for_minimap * Return void * This function init the minimap events */ MapController.prototype.events_for_minimap = function() { var self = this; d3.select(self._target + " .minimap") .on("mouseover", function(d) { self._over = "minimap"; }) .on("mouseout", function(d) { self._over = null; }) //~ .on("mousedown", function(d) { //~ console.log("minimap mousedown"); //~ d3.event.stopPropagation(); //~ d3.event.preventDefault(); //~ }) //~ .on("mouseup", function(d) { //~ console.log("minimap mouseup"); //~ d3.event.stopPropagation(); //~ d3.event.preventDefault(); //~ }) .on("click", function(d) { //~ console.log("minimap click"); self.minimap_panning_map(); }); var drag = d3.behavior.drag() .origin(function(d) { return d; }) .on("dragstart", function(d) { console.log("dragstart minimap"); //~ self.event_resize("dragstart", item, d); }) .on("drag", function(d) { console.log("drag minimap"); }) .on("dragend", function(d) { console.log("dragend minimap"); }); d3.select(self._target + " .minimap .viewport").call(drag); } /** * Function map_move_position * Return void * This function moves map position with minimap click */ MapController.prototype.map_move_position = function(x, y) { var self = this; self._zoomManager .translate([-x, -y]) .event(self._viewport); } /** * Function minimap_panning_map * Return void * This function calculates the position in click event (minimap) */ MapController.prototype.minimap_panning_map = function() { var self = this; var minimap_translate = d3.transform( d3.select(self._target +" .minimap").attr("transform")).translate; var minimap_map_transform = d3.transform( d3.select(self._target +" .minimap .map").attr("transform")); var viewport_minimap = d3.select(self._target +" .minimap .viewport"); var x = self._last_mouse_position[0] - minimap_translate[0]; var y = self._last_mouse_position[1] - minimap_translate[1]; x = x / minimap_map_transform.scale[0] - parseFloat(viewport_minimap.attr("width")) / 2; y = y / minimap_map_transform.scale[0] - parseFloat(viewport_minimap.attr("height")) / 2; self.map_move_position(x, y); } /** * Function events_for_nodes * Return void * This function init the nodes events */ MapController.prototype.events_for_nodes = function(id_node) { var self = this; var selector = ".node"; if (typeof(id_node) != "undefined") { selector = "#node_" + id_node; } var node_menu = self.get_menu_nodes(); d3.selectAll(selector) .on("mouseover", function(d) { if (!self._dragging) { self.select_node(d['graph_id'], "over"); } self.last_event = null; }) .on("mouseout", function(d) { if (!self._dragging) { var status_selection = self.get_status_selection_node(d['graph_id']); self.select_node(d['graph_id'], "off"); if (status_selection.indexOf("select") != -1) { self.select_node(d['graph_id'], "select"); } } }) .on("click", function(d) { if (self.last_event == "relationship") { self.last_event = null; return; } if (d3.event.button != 0) { d3.event.stopPropagation(); d3.event.preventDefault(); } if (d3.event.defaultPrevented) return; if ((d['type'] == ITEM_TYPE_AGENT_NETWORKMAP) || (d['type'] == ITEM_TYPE_MODULE_NETWORKMAP) || (d['type'] == ITEM_TYPE_GROUP_NETWORKMAP) || (d['type'] == ITEM_TYPE_POLICY_NETWORKMAP)) { self.tooltip_map_create(self, this); } }) .on("contextmenu", d3.contextMenu(node_menu, function(node) { self._last_event = "contextmenu"; self.select_node(node['graph_id'], "select"); })); var drag = d3.behavior.drag() .origin(function(d) { return d; }) .on("dragstart", dragstarted) .on("drag", dragged) .on("dragend", dragended); d3.selectAll(selector).call(drag); /** * Function dragstarted * Return void */ function dragstarted(d) { if (d3.event.sourceEvent.button == 0) { d3.event.sourceEvent.stopPropagation(); d3.event.sourceEvent.preventDefault(); } if (self._relationship_in_progress) { return; } if ($("#node_" + d['graph_id']).hasClass("tooltipstered")) { $("#node_" + d['graph_id']).tooltipster('destroy'); } self.remove_resize_square(); var status_selection = self.get_status_selection_node(d['graph_id']); if (status_selection.indexOf("select") == -1) { self.remove_selection_nodes(); } self.select_node(d['graph_id'], "select"); self._dragging = true; self._dragged_nodes = {}; $.each(self.get_nodes_map(), function(i, node) { if (!self.is_draggable(node)) return 1; // Continue var status_selection = self.get_status_selection_node(node.graph_id); if (status_selection.indexOf("select") == -1) { return 1; // Continue } self._dragged_nodes[node.graph_id] = {}; self._dragged_nodes[node.graph_id]['old_x'] = node.x; self._dragged_nodes[node.graph_id]['old_y'] = node.y; }); } /** * Function dragged * Return void */ function dragged(d) { var delta_x = d3.event.dx; var delta_y = d3.event.dy; self._dragging = true; $.each(self.get_nodes_map(), function(i, node) { if (!self.is_draggable(node)) return 1; // Continue var status_selection = self.get_status_selection_node(node.graph_id); if (status_selection.indexOf("select") == -1) { return 1; // Continue } var d3_node = d3.select(self._target + " #node_" + node.graph_id); var transform = d3.transform(d3_node.attr("transform")); transform.translate[0] += delta_x; transform.translate[1] += delta_y; self.get_nodes_map()[i].x = transform.translate[0]; self.get_nodes_map()[i].y = transform.translate[1]; d3.select(self._target + " .minimap #node_" + node.graph_id) .attr("transform", transform.toString()); d3_node.attr("transform", transform.toString()); self.move_arrow(node.graph_id); }); } /** * Function dragended * Return void */ function dragended(d) { if (self._last_event != "contextmenu") { self._last_event = null; $.each(self._dragged_nodes, function(id_node, old_values) { var node = self.get_node(id_node); self.move_node(node); }); self._dragged_nodes = {}; self.select_node(d['graph_id'], "off"); if ($("#node_" + d['graph_id']).hasClass("tooltipstered")) { $("#node_" + d['graph_id']).tooltipster('destroy'); } self.remove_resize_square(); self._dragging = false; } } } MapController.prototype.move_node = function(node) { } /** * Function is_draggable * Return void * This function checks if the node is draggable */ MapController.prototype.is_draggable = function(node) { var self = this; var return_var = false; switch (node.type) { case ITEM_TYPE_MODULE_NETWORKMAP: if (self.get_filter_map()['show_modules']) { return_var = true; } else { return_var = false; } break; case ITEM_TYPE_MODULEGROUP_NETWORKMAP: if (self.get_filter_map()['show_modules']) { if (self.get_filter_map()['show_module_group']) { return_var = true; } else { return_var = false; } } else { return_var = false; } break; case ITEM_TYPE_AGENT_NETWORKMAP: case ITEM_TYPE_GROUP_NETWORKMAP: case ITEM_TYPE_POLICY_NETWORKMAP: case ITEM_TYPE_FICTIONAL_NODE: return_var = true; break; } return return_var; } /** * Function is_relationshipy * Return void * This function checks if the node have a relathion */ MapController.prototype.is_relationshipy = function(node) { var return_var = false; switch (node.type) { case ITEM_TYPE_AGENT_NETWORKMAP: case ITEM_TYPE_FICTIONAL_NODE: return_var = true; break; } return return_var; } /** * Function is_selecty * Return void * This function checks if the node is selecty */ MapController.prototype.is_selecty = function(node) { var self = this; var return_var = false; switch (node.type) { case ITEM_TYPE_AGENT_NETWORKMAP: case ITEM_TYPE_FICTIONAL_NODE: case ITEM_TYPE_GROUP_NETWORKMAP: case ITEM_TYPE_POLICY_NETWORKMAP: return_var = true; break; case ITEM_TYPE_MODULE_NETWORKMAP: if (self.get_filter_map()['show_modules']) { return_var = true; } else { return_var = false; } break; case ITEM_TYPE_MODULEGROUP_NETWORKMAP: if (self.get_filter_map()['show_modules']) { if (self.get_filter_map()['show_module_group']) { return_var = true; } else { return_var = false; } } else { return_var = false; } break; } return return_var; } /** * Function is_delety * Return void * This function checks if the node is delety */ MapController.prototype.is_delety = function(node) { var self = this; var return_var = false; switch (node.type) { case ITEM_TYPE_AGENT_NETWORKMAP: case ITEM_TYPE_FICTIONAL_NODE: case ITEM_TYPE_GROUP_NETWORKMAP: case ITEM_TYPE_POLICY_NETWORKMAP: return_var = true; break; case ITEM_TYPE_MODULE_NETWORKMAP: if (self.get_filter_map()['show_modules']) { return_var = true; } else { return_var = false; } break; case ITEM_TYPE_MODULEGROUP_NETWORKMAP: if (self.get_filter_map()['show_modules']) { if (self.get_filter_map()['show_module_group']) { return_var = true; } else { return_var = false; } } else { return_var = false; } break; } return return_var; } /** * Function init_events * Return boolean * This function init click events in the map */ MapController.prototype.events = function() { var self = this; self.events_for_nodes(); d3.select("body") .on("keydown", function() { // ctrl key if (d3.event.keyCode === CONTROL_KEY) { self._start_flag_multiple_selection = true; self.multiple_selection_start(); self._keys_pressed[CONTROL_KEY] = true; } }) .on("keyup", function() { if (d3.event.keyCode === CONTROL_KEY) { self.multiple_selection_end(); self._last_event = "multiple_selection_end"; self._keys_pressed[CONTROL_KEY] = false; } }); var map_menu = [ { title: 'Add fictional node', action: function(elm, d, i) { self.add_fictional_node(); } } ]; d3.select(self._target).on("contextmenu", d3.contextMenu(map_menu)); d3.select(self._target + " svg").on("mousedown", function() { if (self._start_flag_multiple_selection) { self._start_flag_multiple_selection = false; self._flag_multiple_selection = true; } if (self._flag_multiple_selection) { self.multiple_selection_dragging( d3.event.offsetX, d3.event.offsetY, true); } d3.event.stopPropagation(); d3.event.preventDefault(); }); d3.select(self._target + " svg").on("mouseup", function(d) { if (self._over == "minimap") return; if (self._last_event == "multiple_selection_end") { self._last_event = null; return; } if (!self._flag_multiple_selection) { if (self.last_event != "zoom") { self.last_event = null; if (self._relationship_in_progress) { var found_id = null; for (i in d3.event.path) { var item = d3.event.path[i]; if (item == document) continue; if (typeof(d3.select(item).node().tagName) == "undefined") continue; if (d3.select(item).classed("node")) { found_id = d3.select(item).attr("data-graph_id"); } } if (found_id !== null) { self.apply_temp_arrows(found_id); } else { self.remove_temp_arrows(); } self.last_event = "relationship"; self.hide_help_text_in_pointer(); } self.remove_selection_nodes(); } else { self.last_event = null; } } else { self.multiple_selection_end(); } }); d3.select(self._target + " svg").on("mousemove", function() { if (self._flag_multiple_selection) { self.multiple_selection_dragging( d3.event.offsetX, d3.event.offsetY, false); } }); d3.select(self._target).on("mousemove", function() { var map_pos = d3.select(self._target).node().getBoundingClientRect(); var x = d3.event.pageX - map_pos.left; var y = d3.event.pageY - map_pos.top;; self._last_mouse_position = [x, y]; if (self._relationship_in_progress) { $.each(self.get_nodes_map(), function(i, node) { if (!self.is_relationshipy(node)) return 1; // Continue var status_selection = self.get_status_selection_node(node.graph_id); if (status_selection.indexOf("select") == -1) { return 1; // Continue } self._relationship_in_progress = true; self.move_temp_arrows(node, self._relationship_in_progress_type); self.show_help_text_in_pointer(); }); } }); setTimeout(function() { self.refresh_map();}, self._refresh_time); } /** * Function refresh_map * Return void * This function refresh the map */ MapController.prototype.refresh_map = function() { var self = this; self.refresh_nodes(); self.refresh_arrows(); setTimeout(function() { self.refresh_map();}, self._refresh_time); } /** * Function set_as_parent * Return void * This function sets a node as a parent */ MapController.prototype.set_as_parent = function() { var self = this; self.start_relationship_nodes("parent"); } /** * Function set_as_children * Return void * This function sets a node as a children */ MapController.prototype.set_as_children = function() { var self = this; self.start_relationship_nodes("children"); } /** * Function start_relationship_nodes * Return void * This function starts the relation nodes function */ MapController.prototype.start_relationship_nodes = function(type) { var self = this; $.each(self.get_nodes_map(), function(i, node) { if (!self.is_relationshipy(node)) return 1; // Continue var status_selection = self.get_status_selection_node(node.graph_id); if (status_selection.indexOf("select") == -1) { return 1; // Continue } self._relationship_in_progress = true; self._dragging = false; self.show_temp_arrows(node, type); }); } /** * Function show_temp_arrows * Return void * This function shows temporal arrows to parent-children relation */ MapController.prototype.show_temp_arrows = function(node, type) { var self = this; // Apply the zoom and panning var zoom = d3.transform( d3.select(self._target + " .viewport").attr("transform")); var x = self._last_mouse_position[0]/ zoom.scale[0] - zoom.translate[0] / zoom.scale[0]; var y = self._last_mouse_position[1]/ zoom.scale[1] - zoom.translate[1] / zoom.scale[1]; var temp_arrow = {}; temp_arrow['graph_id'] = "temp_" + node.graph_id; temp_arrow['mouse'] = [x, y]; temp_arrow['temp'] = 1; temp_arrow['type'] = type; temp_arrow['from'] = {}; temp_arrow['to'] = {}; if (d3.select(self._target + " #arrow_temp_" + node.graph_id).empty()) { self._viewport .append("g") .attr("class", "arrow") .attr("id", function(d) { return "arrow_temp_" + node.graph_id;}) .attr("data-id", function(d) { return node.graph_id;}) .attr("data-temp", 1); } switch (type) { case 'parent': temp_arrow['from']['graph_id'] = node.graph_id; temp_arrow['to']['graph_id'] = null; break; case 'children': temp_arrow['from']['graph_id'] = null; temp_arrow['to']['graph_id'] = node.graph_id; break; } self.arrow_by_pieces(self._target + " svg", temp_arrow); } /** * Function move_temp_arrows * Return void * This function move the temporal arrows */ MapController.prototype.move_temp_arrows = function(node, type) { var self = this; // Apply the zoom and panning var zoom = d3.transform( d3.select(self._target + " .viewport").attr("transform")); var x = self._last_mouse_position[0]/ zoom.scale[0] - zoom.translate[0] / zoom.scale[0]; var y = self._last_mouse_position[1]/ zoom.scale[1] - zoom.translate[1] / zoom.scale[1]; var temp_arrow = {}; temp_arrow['graph_id'] = "temp_" + node.graph_id; temp_arrow['mouse'] = [x, y]; temp_arrow['temp'] = 1; temp_arrow['type'] = type; temp_arrow['from'] = {}; temp_arrow['to'] = {}; switch (type) { case 'parent': temp_arrow['from']['graph_id'] = node.graph_id; temp_arrow['to']['graph_id'] = null; break; case 'children': temp_arrow['from']['graph_id'] = null; temp_arrow['to']['graph_id'] = node.graph_id; break; } self.arrow_by_pieces(self._target + " svg", temp_arrow, 0); } MapController.prototype.hide_help_text_in_pointer = function() { var self = this; d3.select(self._target + " .help_text_pointer").remove(); } MapController.prototype.show_help_text_in_pointer = function() { var self = this; var zoom = d3.transform( d3.select(self._target + " .viewport").attr("transform")); var x = self._last_mouse_position[0]/ zoom.scale[0] - zoom.translate[0] / zoom.scale[0]; var y = self._last_mouse_position[1]/ zoom.scale[1] - zoom.translate[1] / zoom.scale[1]; var help_text_pointer = d3.select(self._target + " .help_text_pointer"); if (help_text_pointer.node() == null) { help_text_pointer = d3.select(self._target + " svg").append("g") .attr("class", "help_text_pointer"); var rect_background_help= help_text_pointer.append("rect").attr("class", ".rect_background_help"); help_text_pointer.append("text") .text("Click in anynode to link") .attr("x", 3).attr("y", 12); var bbox = help_text_pointer.node().getBBox() rect_background_help.attr("x", 0).attr("y", 0) .attr("height", bbox.height) .attr("width", bbox.width + 5) .attr("style", "opacity:1;fill:#feffb4;fill-opacity:1;stroke:#808080;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"); } var transform = d3.transform(); transform.translate[0] = x; transform.translate[1] = y + 20; // Under the mouse pointer help_text_pointer.attr("transform", transform.toString()); } /** * Function remove_temp_arrows * Return void * This function remove the temporal arrows */ MapController.prototype.remove_temp_arrows = function() { var self = this; d3.selectAll(self._target + " .arrow") .filter(function(d, i) { if (d3.select(this).attr("data-temp") == 1) return true; else return false; }) .remove(); self._relationship_in_progress = false; self._relationship_in_progress_type = null; } /** * Function apply_temp_arrows * Return void * This function apply the temporal arrows */ MapController.prototype.apply_temp_arrows = function(target_id) { var self = this; $.each(self.get_nodes_map(), function(i, node) { if (!self.is_relationshipy(node)) return 1; // Continue var status_selection = self.get_status_selection_node(node.graph_id); if (status_selection.indexOf("select") == -1) { return 1; // Continue } switch (self._relationship_in_progress_type) { case 'parent': self.make_arrow(node.graph_id, target_id); break; case 'children': self.make_arrow(target_id, node.graph_id); break; } }); self.remove_temp_arrows(); self.paint_arrows(); } /** * Function get_status_selection_node * Return node status * This function returns the status of a node */ MapController.prototype.get_status_selection_node = function(id_node) { var self = this; var status = d3.select(self._target + " #node_" + id_node) .attr("data-select"); if (status === null) { return []; } else { return status.split(" "); } } /** * Function multiple_selection_start * Return void * This function init multiple selection mode */ MapController.prototype.multiple_selection_start = function() { var self = this; if (!self._cache_files.hasOwnProperty("selection_box")) { var selection_box = d3 .select(self._target + " svg") .append("g").attr("id", "selection_box") .style("opacity", 0); d3.xml("images/maps/selection_box.svg", "application/xml", function(xml) { var nodes = xml .evaluate("//*[@id='selection_box']/*", xml, null, XPathResult.ANY_TYPE, null); self._cache_files["selection_box"] = nodes.iterateNext(); self.multiple_selection_start(); }); } else { var selection_box = d3 .select(self._target + " #selection_box"); selection_box .append(function() { return self._cache_files["selection_box"]; }); } } /** * Function add_fictional_node * Return void * This function add a fictional node */ MapController.prototype.add_fictional_node = function() { var self = this; // Apply the zoom and panning var zoom = d3.transform( d3.select(self._target + " .viewport").attr("transform")); var x = self._last_mouse_position[0] / zoom.scale[0] - zoom.translate[0] / zoom.scale[0]; var y = self._last_mouse_position[1] / zoom.scale[1] - zoom.translate[1] / zoom.scale[1]; var new_node = {}; new_node['id'] = 0; new_node['id_agent'] = 0 new_node['graph_id'] = self.get_last_graph_id() + 1; new_node['height'] = 30; new_node['width'] = 30; new_node['status'] = AGENT_MODULE_STATUS_NO_DATA; new_node['title'] = "fictional point"; new_node['type'] = ITEM_TYPE_FICTIONAL_NODE; new_node['x'] = x; new_node['y'] = y; self.get_nodes_map().push(new_node); self.paint_nodes(); self.paint_items_minimap(); self.events_for_nodes(new_node['graph_id']); } /** * Function get_last_graph_id * Return id * This function returns the last graph id */ MapController.prototype.get_last_graph_id = function() { var self = this; var return_var = 0; $.each(self.get_nodes_map(), function(i, node) { if (node['graph_id'] > return_var) { return_var = parseInt(node['graph_id']); } }); return return_var; } /** * Function multiple_selection_dragging * Return void * This function init multiple selection drag */ MapController.prototype.multiple_selection_dragging = function(x, y, first) { var self = this; var selection_box = d3 .select(self._target + " #selection_box"); var transform = d3.transform(); if (first) { transform.translate[0] = x; transform.translate[1] = y; selection_box.attr("transform", transform.toString()); selection_box.style("opacity", 1); selection_box.select("rect") .attr("width", 0); selection_box.select("rect") .attr("height", 0); selection_box.attr("data-ini_x", x); selection_box.attr("data-ini_y", y); } else { var delta_x = x - parseInt(selection_box.attr("data-ini_x")); var delta_y = y - parseInt(selection_box.attr("data-ini_y")); if (delta_x < 0) { transform = d3.transform(selection_box.attr("transform")); transform.translate[0] = x; selection_box.attr("transform", transform.toString()); selection_box.select("rect") .attr("width", -delta_x); } else { selection_box.select("rect") .attr("width", delta_x); } if (delta_y < 0) { transform = d3.transform(selection_box.attr("transform")); transform.translate[1] = y; selection_box.attr("transform", transform.toString()); selection_box.select("rect") .attr("height", -delta_y); } else { selection_box.select("rect") .attr("height", delta_y); } self.multiple_selection_select_nodes(); } } /** * Function multiple_selection_end * Return void * This function ends multiple selection mode */ MapController.prototype.multiple_selection_end = function() { var self = this; self._flag_multiple_selection = false; var selection_box = d3 .select(self._target + " #selection_box"); selection_box.style("opacity", 0); selection_box.select("rect") .attr("width", 0); selection_box.select("rect") .attr("height", 0); } /** * Function multiple_selection_select_nodes * Return void * This function gets the nodes in the selection zone */ MapController.prototype.multiple_selection_select_nodes = function() { var self = this; var selection_box = d3 .select(self._target + " #selection_box"); var transform = d3.transform(selection_box.attr("transform")); var selection_box_dimensions = {}; selection_box_dimensions["x"] = transform.translate[0]; selection_box_dimensions["y"] = transform.translate[1]; selection_box_dimensions["width"] = selection_box.select("rect") .attr("width"); selection_box_dimensions["height"] = selection_box.select("rect") .attr("height"); // Apply the zoom and panning var zoom = d3.transform( d3.select(self._target + " .viewport").attr("transform")); selection_box_dimensions["x"] = (selection_box_dimensions["x"] / zoom.scale[0] - zoom.translate[0] / zoom.scale[0]); selection_box_dimensions["y"] = (selection_box_dimensions["y"] / zoom.scale[1] - zoom.translate[1] / zoom.scale[1]); selection_box_dimensions["width"] = selection_box_dimensions["width"] / zoom.scale[0]; selection_box_dimensions["height"] = selection_box_dimensions["height"] / zoom.scale[1]; $.each(self.get_nodes_map(), function(i, node) { if (!self.is_selecty(node)) return 1; // Continue var x = parseFloat(node.x); var y = parseFloat(node.y); var node_bbox = null; var width; if (!node.hasOwnProperty("width")) { node_bbox = d3.select(self._target + " #node_" + node.graph_id).node().getBBox() width = node_bbox['x'] + node_bbox['width']; self.get_nodes_map()[i].width = width; } else { width = node.width; } var height; if (!node.hasOwnProperty("height")) { if (node_bbox === null) { node_bbox = d3.select(self._target + " #node_" + node.graph_id).node().getBBox() } height = node_bbox['y'] + node_bbox['height']; self.get_nodes_map()[i].height = height; } else { height = node.height; } if ( (x >= selection_box_dimensions["x"]) && (y >= selection_box_dimensions["y"]) && ((x + width) <= (selection_box_dimensions["x"] + selection_box_dimensions["width"])) && ((y + height) <= (selection_box_dimensions["y"] + selection_box_dimensions["height"])) ) { self.select_node(node.graph_id, "select"); } }); } /** * Function remove_selection_nodes * Return void * This function removes the selection */ MapController.prototype.remove_selection_nodes = function() { var self = this; $.each(self.get_nodes_map(), function(i, node) { if (!self.is_selecty(node)) return 1; // Continue self.select_node(node.graph_id, "off"); }); } /** * Function select_node * Return void * This function gets the node eith selection mode */ MapController.prototype.select_node = function(node_id, type) { var self = this; d3.select(self._target + " #node_" + node_id) .classed("over", false); var data = ""; switch (type) { case 'select': d3.select(self._target + " #node_" + node_id) .classed("select", true); d3.select(self._target + " #node_" + node_id) .attr("style", "fill: rgb(50, 128, 50);"); data = "select"; break; case 'over': d3.select(self._target + " #node_" + node_id) .classed("over", true); var over_color = d3.rgb( d3.select(self._target + " #node_" + node_id) .style("fill")) .brighter(1).toString(); if (d3.select(self._target + " #node_" + node_id) .classed("select")) { d3.select(self._target + " #node_" + node_id) .style("fill", over_color); data = "select over"; } else { d3.select(self._target + " #node_" + node_id) .style("fill", over_color); data = "over"; } break; case 'off': var status_color = d3 .select(self._target + " #node_" + node_id) .attr("data-status_color"); d3.select(self._target + " #node_" + node_id) .classed("select", false); d3.select(self._target + " #node_" + node_id) .style("fill", status_color); break; } d3.select(self._target + " #node_" + node_id) .attr("data-select", data); } /** * Function tooltip_map_create * Return void * This function manages nodes tooltips */ MapController.prototype.tooltip_map_create = function(self, target) { var nodeTarget = $(target); var spinner = $('#spinner_tooltip').html(); var nodeSize = get_size_element("#" + $(nodeTarget).attr("id")); nodeSize[0] = nodeSize[0] * self._zoomManager.scale(); // Apply zoom var node_id = nodeTarget.attr("id"); var type = parseInt(nodeTarget.data("type")); var data_id = parseInt(nodeTarget.data("id")); var data_graph_id = parseInt(nodeTarget.data("graph_id")); nodeTarget.tooltipster({ arrow: true, trigger: 'click', contentAsHTML: true, autoClose: false, offsetX: nodeSize[0] / 2, theme: 'tooltipster-noir', multiple: true, interactive: true, content: spinner, restoration: 'none', functionBefore: function(origin, continueTooltip) { continueTooltip(); self.nodeData(data_id, type, self._id, data_graph_id, origin, node_id); } }); nodeTarget.tooltipster("show"); } /** * Function editMap * Return void * This function prints the map edition table */ MapController.prototype.editMap = function(self, target) { var id_map = self._id; var params = {}; params["printEditMapTable"] = 1; params["id_map"] = id_map; params["page"] = "include/ajax/map.ajax"; jQuery.ajax ({ data: params, dataType: "html", type: "POST", url: "ajax.php", success: function (data) { $(target).append("
"); $("#edit_map_dialog").append(data); $("#edit_map_dialog").dialog({ autoOpen: false }); $("#edit_map_dialog").dialog("open"); } }); } /** * Function nodeGetDetails * Return link * This function returns a link with node details */ MapController.prototype.nodeGetDetails = function(self, target) { var nodeTarget = $(target); var node_id = nodeTarget.attr("id"); var data_id = parseInt(nodeTarget.data("id")); var data_graph_id = parseInt(nodeTarget.data("graph_id")); var params = {}; params["getNodeDetails"] = 1; params["id_node_data"] = data_id; params["data_graph_id"] = data_graph_id; params["node_id"] = node_id; params["page"] = "include/ajax/map.ajax"; jQuery.ajax ({ data: params, dataType: "JSON", type: "POST", url: "ajax.php", success: function (data) { var window_popup = window.open("", "window_" + data_graph_id, 'title=DETAILS, width=300, height=300, toolbar=no, location=no, directories=no, status=no, menubar=no'); $(window_popup.document.body).html(data); } }); } /** * Function editNode * Return void * This function prints the node edition table */ MapController.prototype.editNode = function(self, target) { } /** * Function deleteNode * Return void * This function delete a node and the arrows that use it */ MapController.prototype.deleteNode = function(self, target) { var self = this; $.each(self.get_nodes_map(), function(i, node) { if (!self.is_delety(node)) return 1; // Continue var status_selection = self.get_status_selection_node(node.graph_id); if (status_selection.indexOf("select") == -1) { return 1; // Continue } var id_node = "node_" + node.graph_id; var arrowsToDelete = self.get_edges_to_del(node.graph_id); // Delete edges and nodes in "nodes" and "edges" arrays self.deleteEdgesAndNode(arrowsToDelete, id_node); // Delete visual edges and nodes arrowsToDelete.forEach(function(arrow) { d3.select("#arrow_" + arrow['graph_id']).remove(); }); d3.select(self._target + " #" + id_node).remove(); d3.select(self._target + " .minimap" + " #" + id_node).remove(); }); } /** * Function deleteEdges * Return void * This function delete the edges of a node in the edges array */ MapController.prototype.deleteEdgesAndNode = function(arrowsToDelete, id_node) { var self = this; var temp; arrowsToDelete.forEach(function(arrow) { temp = []; self.get_edges_map().forEach(function(edge) { if (edge["graph_id"] != arrow['graph_id']) { temp.push(edge); } }); self.set_edges_map(temp); }); arrowsToDelete.forEach(function(arrow) { temp = []; self.get_nodes_map().forEach(function(nodeOrEdge) { var nodeToDel = "node_" + nodeOrEdge["graph_id"]; if ((nodeOrEdge["graph_id"] != arrow['graph_id']) && (nodeToDel != id_node)) { temp.push(nodeOrEdge); } }); self.set_nodes_map(temp); }); if (arrowsToDelete.length == 0) { temp = []; self.get_nodes_map().forEach(function(nodeOrEdge) { var nodeToDel = "node_" + nodeOrEdge["graph_id"]; if (nodeToDel != id_node) { temp.push(nodeOrEdge); } }); self.set_nodes_map(temp); } } /** * Function get_edges_to_del * Return array[edge] * This function returns the edges of a node to delete them */ MapController.prototype.get_edges_to_del = function(id_graph) { var self = this; var return_edges = []; $.each(self.get_edges_map(), function(i, edge) { if ((edge['to']['graph_id'] == id_graph) || (edge['from']['graph_id'] == id_graph)) { return_edges.push(edge); } }); return return_edges; } /** * Function getArrows * Return array[id_arrow] * This function returns the arrows of a node */ MapController.prototype.getArrows = function(id_node) { var self = this; var edgesToDel = []; var j = 0; self.get_edges_map().forEach(function(edge, index) { var nodeTo = "node_" + edge["to"]; var nodeFrom = "node_" + edge["from"]; if (nodeTo == id_node || nodeFrom == id_node) { edgesToDel[index] = edge["graph_id"]; } }); return edgesToDel; } /** * Function close_all_tooltips * Return void * This function hide nodes tooltips */ MapController.prototype.close_all_tooltips = function() { $("svg .tooltipstered").tooltipster("destroy"); } /** * Function nodeData * Return array(data) * This function returns the data of the node */ MapController.prototype.nodeData = function(data_id, type, id_map, data_graph_id, origin, node_id) { var params = {}; params["getNodeData"] = 1; params["id_node_data"] = data_id; params["type"] = type; params["id_map"] = id_map; params["data_graph_id"] = data_graph_id; params["node_id"] = node_id; params["page"] = "include/ajax/map.ajax"; jQuery.ajax ({ data: params, dataType: "json", type: "POST", url: "ajax.php", success: function (data) { if ($(origin).hasClass("tooltipstered")) { origin.tooltipster('content', data); } } }); } /** * Function arrow_by_pieces * Return void * This function print the arrow by pieces (3 steps) */ //~ MapController.prototype.arrow_by_pieces = function(target, id_arrow, id_node_to, id_node_from, wait) { MapController.prototype.arrow_by_pieces = function(target, arrow_data, wait) { var self = this; if (typeof(wait) === "undefined") wait = 1; var count_files = 2; function wait_load(callback) { count_files--; if (count_files == 0) { callback(); } } var arrow_layout = d3 .select(target + " #arrow_" + arrow_data['graph_id']); switch (wait) { /*---------------------------------------------*/ /*-------- Preload head and body arrow --------*/ /*---------------------------------------------*/ case 1: arrow_layout = arrow_layout.append("g") .attr("class", "arrow_position_rotation") .append("g") .attr("class", "arrow_translation") .append("g") .attr("class", "arrow_container"); if (is_buggy_firefox) { arrow_layout.append("g") .attr("class", "body") .append("use") .attr("xlink:href", "#body_arrow"); arrow_layout.append("g") .attr("class", "head") .append("use") .attr("xlink:href", "#head_arrow"); self.arrow_by_pieces(target, arrow_data, 0); } else { arrow_layout.append("g") .attr("class", "body") .append("use") .attr("xlink:href", "images/maps/body_arrow.svg#body_arrow") .on("load", function() { wait_load(function() { self.arrow_by_pieces(target, arrow_data, 0); }); }); arrow_layout.append("g") .attr("class", "head") .append("use") .attr("xlink:href", "images/maps/head_arrow.svg#head_arrow") .on("load", function() { wait_load(function() { self.arrow_by_pieces(target, arrow_data, 0); }); }); } break; /*---------------------------------------------*/ /*---- Print head and body arrow by steps -----*/ /*---------------------------------------------*/ case 0: if (arrow_data['temp']) { switch (arrow_data['type']) { case 'parent': var id_node_to = null; var id_node_from = "node_" + arrow_data['from']['graph_id']; var c_elem2 = arrow_data['mouse']; var c_elem1 = get_center_element(self._target +" #" + id_node_from); var radius_to = 5; var radius_from = parseFloat(get_radius_element("#" + id_node_from)); break; case 'children': var id_node_to = "node_" + arrow_data['to']['graph_id']; var id_node_from = null; var c_elem2 = get_center_element(self._target +" #" + id_node_to); var c_elem1 = arrow_data['mouse']; var radius_to = parseFloat(get_radius_element("#" + id_node_to)); var radius_from = 5; break; } } else { var id_node_to = "node_" + arrow_data['to']['graph_id']; var id_node_from = "node_" + arrow_data['from']['graph_id']; var c_elem2 = get_center_element(self._target +" #" + id_node_to); var c_elem1 = get_center_element(self._target +" #" + id_node_from); var radius_to = parseFloat(get_radius_element("#" + id_node_to)); var radius_from = parseFloat(get_radius_element("#" + id_node_from)); } var distance = get_distance_between_point(c_elem1, c_elem2); var transform = d3.transform(); /*---------------------------------------------*/ /*--- Position of layer arrow (body + head) ---*/ /*---------------------------------------------*/ var arrow_body = arrow_layout.select(".body"); var arrow_body_b = arrow_body.node().getBBox(); var arrow_body_height = (arrow_body_b['height'] + arrow_body_b['y']); var arrow_body_width = (arrow_body_b['width'] + arrow_body_b['x']); transform.translate[0] = c_elem1[0]; transform.translate[1] = c_elem1[1]; transform.rotate = get_angle_of_line(c_elem1, c_elem2); arrow_layout.select(".arrow_position_rotation").attr("transform", transform.toString()); transform = d3.transform(); transform.translate[0] = radius_from; transform.translate[1] = - (arrow_body_height / 2); arrow_layout.select(".arrow_translation").attr("transform", transform.toString()); /*---------------------------------------------*/ /*-------- Resize the body arrow width --------*/ /*---------------------------------------------*/ var arrow_head = arrow_layout.select(".head"); var arrow_head_b = arrow_head.node().getBBox(); var arrow_head_height = (arrow_head_b['height'] + arrow_head_b['y']); var arrow_head_width = (arrow_head_b['width'] + arrow_head_b['x']); var body_width = distance - arrow_head_width - radius_to - radius_from; transform = d3.transform(); transform.scale[0] = body_width / arrow_body_width; arrow_body.attr("transform", transform.toString()); /*---------------------------------------------*/ /*---------- Position of head arrow -----------*/ /*---------------------------------------------*/ transform = d3.transform(); var arrow_body_t = d3.transform(arrow_body.attr("transform")); var scale = arrow_body_t.scale[0]; var x = 0 + arrow_body_width * scale; var y = 0 + (arrow_body_height / 2 - arrow_head_height / 2); transform.translate[0] = x; transform.translate[1] = y; arrow_head.attr("transform", transform.toString()); /*---------------------------------------------*/ /*------- Show the result in one time ---------*/ /*---------------------------------------------*/ arrow_layout.attr("style", "opacity: 1"); break; } } /*-----------------------------------------------*/ /*-------------------Functions-------------------*/ /*-----------------------------------------------*/ /** * Function open_in_another_window * Return void * This function open the node in extra window */ function open_in_another_window(link) { window.open(link); } /** * Function close_button_tooltip * Return void * This function manages "close" button from tooltips */ function close_button_tooltip(data_graph_id) { $("#node_" + data_graph_id).tooltipster("destroy"); } /** * Function tooltip_to_new_window * Return void * This function manages "open in new wondow" button from tooltips */ function tooltip_to_new_window(data_graph_id) { var content = $("#tooltip_" + data_graph_id + " .body").html(); $("#node_" + data_graph_id).tooltipster("destroy"); var window_popup = window.open("", "window_" + data_graph_id, 'title=NEW_WINDOW, width=300, height=300, toolbar=no, location=no, directories=no, status=no, menubar=no'); $(window_popup.document.body).html(content); } /** * Function get_distance_between_point * Return float * This function returns the distance between two nodes */ function get_distance_between_point(point1, point2) { var delta_x = Math.abs(point1[0] - point2[0]); var delta_y = Math.abs(point1[1] - point2[1]); return Math.sqrt( Math.pow(delta_x, 2) + Math.pow(delta_y, 2)); } /** * Function get_center_element * Return array[x, y] * This function returns the center of the node */ function get_center_element(element) { var element_t; try { element_t = d3.transform(d3.select(element).attr("transform")); } catch(err) { console.log(element, err); } //~ var element_t = d3.transform(d3.select(element).attr("transform")); var element_t_scale = parseFloat(element_t['scale']); var element_b = d3.select(element).node().getBBox(); var box_x = parseFloat(element_t.translate[0]) + parseFloat(element_b['x']) * element_t_scale; var box_y = parseFloat(element_t.translate[1]) + parseFloat(element_b['y']) * element_t_scale; var width = (element_t_scale * element_b['width']); var height = (element_t_scale * element_b['height']); var c_x = box_x + (width / 2); var c_y = box_y + (height / 2); return [c_x, c_y]; } /** * Function get_angle_of_line * Return float * This function returns the angle between two lines (center node to another node) */ function get_angle_of_line(point1, point2) { return Math.atan2(point2[1] - point1[1], point2[0] - point1[0]) * 180 / Math.PI; } /** * Function getBBox_Symbol * Return BBox * This function returns bbox of the symbol */ function getBBox_Symbol(target, symbol) { var base64symbol = btoa(symbol).replace(/=/g, ""); return d3.select(target + " #" + base64symbol).node().getBBox(); } /** * Function get_size_element * Return array[width, height] * This function returns the size of the element */ function get_size_element(element) { var transform = d3.transform(d3.select(element).attr("transform")); var element_b = d3.select(element).node().getBBox(); return [ element_b['width'] * transform.scale[0], element_b['height'] * transform.scale[1]]; } /** * Function get_radius_element * Return void * This function returns the element radius */ function get_radius_element(element) { // Hack for the circle items if (!d3.select(element + " .layout_size_node circle").empty()) { return parseFloat( d3.select(element + " .layout_size_node circle").attr("r")); } else { //~ var size = get_size_element(element); var size = get_size_element(element + " .layout_size_node"); return Math.sqrt( Math.pow(size[0] / 2, 2) + Math.pow(size[1] / 2, 2)); } }