diff options
Diffstat (limited to 'addons/web_diagram/static/src/js/graph.js')
| -rw-r--r-- | addons/web_diagram/static/src/js/graph.js | 998 |
1 files changed, 0 insertions, 998 deletions
diff --git a/addons/web_diagram/static/src/js/graph.js b/addons/web_diagram/static/src/js/graph.js deleted file mode 100644 index d7e54389340..00000000000 --- a/addons/web_diagram/static/src/js/graph.js +++ /dev/null @@ -1,998 +0,0 @@ - -(function(window){ - - // this serves as the end of an edge when creating a link - function EdgeEnd(pos_x,pos_y){ - this.x = pos_x; - this.y = pos_y; - - this.get_pos = function(){ - return new Vec2(this.x,this.y); - } - } - - // A close button, - // if entity_type == "node": - // GraphNode.destruction_callback(entity) is called where entity is a node. - // If it returns true the node and all connected edges are destroyed. - // if entity_type == "edge": - // GraphEdge.destruction_callback(entity) is called where entity is an edge - // If it returns true the edge is destroyed - // pos_x,pos_y is the relative position of the close button to the entity position (entity.get_pos()) - - function CloseButton(graph, entity, entity_type, pos_x,pos_y){ - var self = this; - var visible = false; - var close_button_radius = graph.style.close_button_radius || 8; - var close_circle = graph.r.circle( entity.get_pos().x + pos_x, - entity.get_pos().y + pos_y, - close_button_radius ); - //the outer gray circle - close_circle.attr({ 'opacity': 0, - 'fill': graph.style.close_button_color || "black", - 'cursor': 'pointer', - 'stroke': 'none' }); - close_circle.transform(graph.get_transform()); - graph.set_scrolling(close_circle); - - //the 'x' inside the circle - var close_label = graph.r.text( entity.get_pos().x + pos_x, entity.get_pos().y + pos_y,"x"); - close_label.attr({ 'fill': graph.style.close_button_x_color || "white", - 'font-size': close_button_radius, - 'cursor': 'pointer' }); - - close_label.transform(graph.get_transform()); - graph.set_scrolling(close_label); - - // the dummy_circle is used to catch events, and avoid hover in/out madness - // between the 'x' and the button - var dummy_circle = graph.r.circle( entity.get_pos().x + pos_x, - entity.get_pos().y + pos_y, - close_button_radius ); - dummy_circle.attr({'opacity':1, 'fill': 'transparent', 'stroke':'none', 'cursor':'pointer'}); - dummy_circle.transform(graph.get_transform()); - graph.set_scrolling(dummy_circle); - - this.get_pos = function(){ - return entity.get_pos().add_xy(pos_x,pos_y); - }; - - this.update_pos = function(){ - var pos = self.get_pos(); - close_circle.attr({'cx':pos.x, 'cy':pos.y}); - dummy_circle.attr({'cx':pos.x, 'cy':pos.y}); - close_label.attr({'x':pos.x, 'y':pos.y}); - }; - - function hover_in(){ - if(!visible){ return; } - close_circle.animate({'r': close_button_radius * 1.5}, 300, 'elastic'); - dummy_circle.animate({'r': close_button_radius * 1.5}, 300, 'elastic'); - } - function hover_out(){ - if(!visible){ return; } - close_circle.animate({'r': close_button_radius},400,'linear'); - dummy_circle.animate({'r': close_button_radius},400,'linear'); - } - dummy_circle.hover(hover_in,hover_out); - close_circle.hover(hover_in,hover_out); - close_label.hover(hover_in,hover_out); - - function click_action(){ - if(!visible){ return; } - - close_circle.attr({'r': close_button_radius * 2 }); - dummy_circle.attr({'r': close_button_radius * 2 }); - close_circle.animate({'r': close_button_radius }, 400, 'linear'); - dummy_circle.animate({'r': close_button_radius }, 400, 'linear'); - - if(entity_type == "node"){ - Promise.resolve(GraphNode.destruction_callback(entity)).then(function () { - entity.remove(); - }); - }else if(entity_type == "edge"){ - Promise.resolve(GraphEdge.destruction_callback(entity)).then(function () { - entity.remove(); - }); - } - } - dummy_circle.click(click_action); - close_circle.click(click_action); - close_label.click(click_action); - - this.show = function(){ - if(!visible){ - close_circle.animate({'opacity':1}, 100, 'linear'); - close_label.animate({'opacity':1}, 100, 'linear'); - visible = true; - } - } - this.hide = function(){ - if(visible){ - close_circle.animate({'opacity':0}, 100, 'linear'); - close_label.animate({'opacity':0}, 100, 'linear'); - visible = false; - } - } - //destroy this object and remove it from the graph - this.remove = function(){ - if(visible){ - visible = false; - close_circle.animate({'opacity':0}, 100, 'linear'); - close_label.animate({'opacity':0}, 100, 'linear',self.remove); - }else{ - close_circle.remove(); - close_label.remove(); - dummy_circle.remove(); - } - } - } - - // connectors are start and end point of edge creation drags. - function Connector(graph,node,pos_x,pos_y){ - var visible = false; - var conn_circle = graph.r.circle(node.get_pos().x + pos_x, node.get_pos().y + pos_y,4); - conn_circle.attr({ 'opacity': 0, - 'fill': graph.style.node_outline_color, - 'stroke': 'none' }); - conn_circle.transform(graph.get_transform()); - graph.set_scrolling(conn_circle); - - var self = this; - - this.update_pos = function(){ - conn_circle.attr({'cx':node.get_pos().x + pos_x, 'cy':node.get_pos().y + pos_y}); - }; - this.get_pos = function(){ - return new node.get_pos().add_xy(pos_x,pos_y); - }; - this.remove = function(){ - conn_circle.remove(); - } - function hover_in(){ - if(!visible){ return;} - conn_circle.animate({'r':8},300,'elastic'); - if(graph.creating_edge){ - graph.target_node = node; - conn_circle.animate({ 'fill': graph.style.connector_active_color, - 'stroke': graph.style.node_outline_color, - 'stroke-width': graph.style.node_selected_width, - },100,'linear'); - } - } - function hover_out(){ - if(!visible){ return;} - conn_circle.animate({ 'r':graph.style.connector_radius, - 'fill':graph.style.node_outline_color, - 'stroke':'none'},400,'linear'); - graph.target_node = null; - } - conn_circle.hover(hover_in,hover_out); - - - var drag_down = function(){ - if(!visible){ return; } - self.ox = conn_circle.attr("cx"); - self.oy = conn_circle.attr("cy"); - self.edge_start = new EdgeEnd(self.ox,self.oy); - self.edge_end = new EdgeEnd(self.ox, self.oy); - self.edge_tmp = new GraphEdge(graph,'',self.edge_start,self.edge_end,true); - graph.creating_edge = true; - }; - var drag_move = function(dx,dy){ - if(!visible){ return; } - self.edge_end.x = self.ox + dx; - self.edge_end.y = self.oy + dy; - self.edge_tmp.update(); - }; - var drag_up = function(){ - if(!visible){ return; } - graph.creating_edge = false; - self.edge_tmp.remove(); - if(graph.target_node){ - var edge_prop = GraphEdge.creation_callback(node,graph.target_node); - if(edge_prop){ - var new_edge = new GraphEdge(graph,edge_prop.label, node,graph.target_node); - GraphEdge.new_edge_callback(new_edge); - } - } - }; - conn_circle.drag(drag_move,drag_down,drag_up); - - function show(){ - if(!visible){ - conn_circle.animate({'opacity':1}, 100, 'linear'); - visible = true; - } - } - function hide(){ - if(visible){ - conn_circle.animate({'opacity':0}, 100, 'linear'); - visible = false; - } - } - this.show = show; - this.hide = hide; - } - - //Creates a new graph on raphael document r. - //style is a dictionary containing the style definitions - //viewport (optional) is the dom element representing the viewport of the graph. It is used - //to prevent scrolling to scroll the graph outside the viewport. - - function Graph(r,style,viewport){ - var self = this; - var nodes = []; // list of all nodes in the graph - var edges = []; // list of all edges in the graph - var graph = {}; // graph[n1.uid][n2.uid] -> list of all edges from n1 to n2 - var links = {}; // links[n.uid] -> list of all edges from or to n - var uid = 1; // all nodes and edges have an uid used to order their display when they are curved - var selected_entity = null; //the selected entity (node or edge) - - self.creating_edge = false; // true if we are dragging a new edge onto a node - self.target_node = null; // this holds the target node when creating an edge and hovering a connector - self.r = r; // the raphael instance - self.style = style; // definition of the colors, spacing, fonts, ... used by the elements - var tr_x = 0, tr_y = 0; // global translation coordinate - - var background = r.rect(0,0,'100%','100%').attr({'fill':'white', 'stroke':'none', 'opacity':0, 'cursor':'move'}); - - // return the global transform of the scene - this.get_transform = function(){ - return "T"+tr_x+","+tr_y - }; - - - // translate every element of the graph except the background. - // elements inserted in the graph after a translate_all() must manually apply transformation - // via get_transform() - var translate_all = function(dx,dy){ - tr_x += dx; - tr_y += dy; - var tstr = self.get_transform(); - - r.forEach(function(el){ - if(el != background){ - el.transform(tstr); - } - }); - }; - //returns {minx, miny, maxx, maxy}, the translated bounds containing all nodes - var get_bounds = function(){ - var minx = Number.MAX_VALUE; - var miny = Number.MAX_VALUE; - var maxx = Number.MIN_VALUE; - var maxy = Number.MIN_VALUE; - - for(var i = 0; i < nodes.length; i++){ - var pos = nodes[i].get_pos(); - minx = Math.min(minx,pos.x); - miny = Math.min(miny,pos.y); - maxx = Math.max(maxx,pos.x); - maxy = Math.max(maxy,pos.y); - } - - minx = minx - style.node_size_x / 2 + tr_x; - miny = miny - style.node_size_y / 2 + tr_y; - maxx = maxx + style.node_size_x / 2 + tr_x; - maxy = maxy + style.node_size_y / 2 + tr_y; - - return { minx:minx, miny:miny, maxx:maxx, maxy:maxy }; - - }; - // returns false if the translation dx,dy of the viewport - // hides the graph (with optional margin) - var translation_respects_viewport = function(dx,dy,margin){ - if(!viewport){ - return true; - } - margin = margin || 0; - var b = get_bounds(); - var width = viewport.offsetWidth; - var height = viewport.offsetHeight; - - if( ( dy < 0 && b.maxy + dy < margin ) || - ( dy > 0 && b.miny + dy > height - margin ) || - ( dx < 0 && b.maxx + dx < margin ) || - ( dx > 0 && b.minx + dx > width - margin ) ){ - return false; - } - - return true; - } - //Adds a mousewheel event callback to raph_element that scrolls the viewport - this.set_scrolling = function(raph_element){ - $(raph_element.node).bind('mousewheel',function(event,delta){ - var dy = delta * 20; - if( translation_respects_viewport(0,dy, style.viewport_margin) ){ - translate_all(0,dy); - } - }); - }; - - var px, py; - // Graph translation when background is dragged - var bg_drag_down = function(){ - px = py = 0; - }; - var bg_drag_move = function(x,y){ - var dx = x - px; - var dy = y - py; - px = x; - py = y; - if( translation_respects_viewport(dx,dy, style.viewport_margin) ){ - translate_all(dx,dy); - } - }; - var bg_drag_up = function(){}; - background.drag( bg_drag_move, bg_drag_down, bg_drag_up); - - this.set_scrolling(background); - - //adds a node to the graph and sets its uid. - this.add_node = function (n){ - nodes.push(n); - n.uid = uid++; - }; - - //return the list of all nodes in the graph - this.get_node_list = function(){ - return nodes; - }; - - //adds an edge to the graph and sets its uid - this.add_edge = function (n1,n2,e){ - edges.push(e); - e.uid = uid++; - if(!graph[n1.uid]) graph[n1.uid] = {}; - if(!graph[n1.uid][n2.uid]) graph[n1.uid][n2.uid] = []; - if(!links[n1.uid]) links[n1.uid] = []; - if(!links[n2.uid]) links[n2.uid] = []; - - graph[n1.uid][n2.uid].push(e); - links[n1.uid].push(e); - if(n1 != n2){ - links[n2.uid].push(e); - } - }; - - //removes an edge from the graph - this.remove_edge = function(edge){ - edges = _.without(edges,edge); - var n1 = edge.get_start(); - var n2 = edge.get_end(); - links[n1.uid] = _.without(links[n1.uid],edge); - links[n2.uid] = _.without(links[n2.uid],edge); - graph[n1.uid][n2.uid] = _.without(graph[n1.uid][n2.uid],edge); - if ( selected_entity == edge ){ - selected_entity = null; - } - }; - //removes a node and all connected edges from the graph - this.remove_node = function(node){ - var linked_edges = self.get_linked_edge_list(node); - for(var i = 0; i < linked_edges.length; i++){ - linked_edges[i].remove(); - } - nodes = _.without(nodes,node); - - if ( selected_entity == node ){ - selected_entity = null; - } - } - - - //return the list of edges from n1 to n2 - this.get_edge_list = function(n1,n2){ - var list = []; - if(!graph[n1.uid]) return list; - if(!graph[n1.uid][n2.uid]) return list; - return graph[n1.uid][n2.uid]; - }; - //returns the list of all edge connected to n - this.get_linked_edge_list = function(n){ - if(!links[n.uid]) return []; - return links[n.uid]; - }; - //return a curvature index so that all edges connecting n1,n2 have different curvatures - this.get_edge_curvature = function(n1,n2,e){ - var el_12 = this.get_edge_list(n1,n2); - var c12 = el_12.length; - var el_21 = this.get_edge_list(n2,n1); - var c21 = el_21.length; - if(c12 + c21 == 1){ // only one edge - return 0; - }else{ - var index = 0; - for(var i = 0; i < c12; i++){ - if (el_12[i].uid < e.uid){ - index++; - } - } - if(c21 == 0){ // all edges in the same direction - return index - (c12-1)/2.0; - }else{ - return index + 0.5; - } - } - }; - - - // Returns the angle in degrees of the edge loop. We do not support more than 8 loops on one node - this.get_loop_angle = function(n,e){ - var loop_list = this.get_edge_list(n,n); - - var slots = []; // the 8 angles where we can put the loops - for(var angle = 0; angle < 360; angle += 45){ - slots.push(Vec2.new_polar_deg(1,angle)); - } - - //we assign to each slot a score. The higher the score, the closer it is to other edges. - var links = this.get_linked_edge_list(n); - for(var i = 0; i < links.length; i++){ - var edge = links[i]; - if(!edge.is_loop || edge.is_loop()){ - continue; - } - var end = edge.get_end(); - if (end == n){ - end = edge.get_start(); - } - var dir = end.get_pos().sub(n.get_pos()).normalize(); - for(var s = 0; s < slots.length; s++){ - var score = slots[s].dot(dir); - if(score < 0){ - score = -0.2*Math.pow(score,2); - }else{ - score = Math.pow(score,2); - } - if(!slots[s].score){ - slots[s].score = score; - }else{ - slots[s].score += score; - } - } - } - //we want the loops with lower uid to get the slots with the lower score - slots.sort(function(a,b){ return a.score < b.score ? -1: 1; }); - - var index = 0; - for(var i = 0; i < links.length; i++){ - var edge = links[i]; - if(!edge.is_loop || !edge.is_loop()){ - continue; - } - if(edge.uid < e.uid){ - index++; - } - } - index %= slots.length; - - return slots[index].angle_deg(); - } - - //selects a node or an edge and deselects everything else - this.select = function(entity){ - if(selected_entity){ - if(selected_entity == entity){ - return; - }else{ - if(selected_entity.set_not_selected){ - selected_entity.set_not_selected(); - } - selected_entity = null; - } - } - selected_entity = entity; - if(entity && entity.set_selected){ - entity.set_selected(); - } - }; - } - - // creates a new Graph Node on Raphael document r, centered on [pos_x,pos_y], with label 'label', - // and of type 'circle' or 'rect', and of color 'color' - function GraphNode(graph,pos_x, pos_y,label,type,color){ - var self = this; - var r = graph.r; - var sy = graph.style.node_size_y; - var sx = graph.style.node_size_x; - var node_fig = null; - var selected = false; - this.connectors = []; - this.close_button = null; - this.uid = 0; - - graph.add_node(this); - - if(type == 'circle'){ - node_fig = r.ellipse(pos_x,pos_y,sx/2,sy/2); - }else{ - node_fig = r.rect(pos_x-sx/2,pos_y-sy/2,sx,sy); - } - node_fig.attr({ 'fill': color, - 'stroke': graph.style.node_outline_color, - 'stroke-width': graph.style.node_outline_width, - 'cursor':'pointer' }); - node_fig.transform(graph.get_transform()); - graph.set_scrolling(node_fig); - - var node_label = r.text(pos_x,pos_y,label); - node_label.attr({ 'fill': graph.style.node_label_color, - 'font-size': graph.style.node_label_font_size, - 'cursor': 'pointer' }); - node_label.transform(graph.get_transform()); - graph.set_scrolling(node_label); - - // redraws all edges linked to this node - var update_linked_edges = function(){ - var edges = graph.get_linked_edge_list(self); - for(var i = 0; i < edges.length; i++){ - edges[i].update(); - } - }; - - // sets the center position of the node - var set_pos = function(pos){ - if(type == 'circle'){ - node_fig.attr({'cx':pos.x,'cy':pos.y}); - }else{ - node_fig.attr({'x':pos.x-sx/2,'y':pos.y-sy/2}); - } - node_label.attr({'x':pos.x,'y':pos.y}); - for(var i = 0; i < self.connectors.length; i++){ - self.connectors[i].update_pos(); - } - if(self.close_button){ - self.close_button.update_pos(); - } - update_linked_edges(); - }; - // returns the figure used to draw the node - var get_fig = function(){ - return node_fig; - }; - // returns the center coordinates - var get_pos = function(){ - if(type == 'circle'){ - return new Vec2(node_fig.attr('cx'), node_fig.attr('cy')); - }else{ - return new Vec2(node_fig.attr('x') + sx/2, node_fig.attr('y') + sy/2); - } - }; - // return the label string - var get_label = function(){ - return node_label.attr("text"); - }; - // sets the label string - var set_label = function(text){ - node_label.attr({'text':text}); - }; - var get_bound = function(){ - if(type == 'circle'){ - return new BEllipse(get_pos().x,get_pos().y,sx/2,sy/2); - }else{ - return BRect.new_centered(get_pos().x,get_pos().y,sx,sy); - } - }; - // selects this node and deselects all other nodes - var set_selected = function(){ - if(!selected){ - selected = true; - node_fig.attr({ 'stroke': graph.style.node_selected_color, - 'stroke-width': graph.style.node_selected_width }); - if(!self.close_button){ - self.close_button = new CloseButton(graph,self, "node" ,sx/2 , - sy/2); - self.close_button.show(); - } - for(var i = 0; i < self.connectors.length; i++){ - self.connectors[i].show(); - } - } - }; - // deselect this node - var set_not_selected = function(){ - if(selected){ - node_fig.animate({ 'stroke': graph.style.node_outline_color, - 'stroke-width': graph.style.node_outline_width }, - 100,'linear'); - if(self.close_button){ - self.close_button.remove(); - self.close_button = null; - } - selected = false; - } - for(var i = 0; i < self.connectors.length; i++){ - self.connectors[i].hide(); - } - }; - var remove = function(){ - if(self.close_button){ - self.close_button.remove(); - } - for(var i = 0; i < self.connectors.length; i++){ - self.connectors[i].remove(); - } - graph.remove_node(self); - node_fig.remove(); - node_label.remove(); - } - - - this.set_pos = set_pos; - this.get_pos = get_pos; - this.set_label = set_label; - this.get_label = get_label; - this.get_bound = get_bound; - this.get_fig = get_fig; - this.set_selected = set_selected; - this.set_not_selected = set_not_selected; - this.update_linked_edges = update_linked_edges; - this.remove = remove; - - - //select the node and play an animation when clicked - var click_action = function(){ - if(type == 'circle'){ - node_fig.attr({'rx':sx/2 + 3, 'ry':sy/2+ 3}); - node_fig.animate({'rx':sx/2, 'ry':sy/2},500,'elastic'); - }else{ - var cx = get_pos().x; - var cy = get_pos().y; - node_fig.attr({'x':cx - (sx/2) - 3, 'y':cy - (sy/2) - 3, 'ẃidth':sx+6, 'height':sy+6}); - node_fig.animate({'x':cx - sx/2, 'y':cy - sy/2, 'ẃidth':sx, 'height':sy},500,'elastic'); - } - graph.select(self); - }; - node_fig.click(click_action); - node_label.click(click_action); - - //move the node when dragged - var drag_down = function(){ - this.opos = get_pos(); - }; - var drag_move = function(dx,dy){ - // we disable labels when moving for performance reasons, - // updating the label position is quite expensive - // we put this here because drag_down is also called on simple clicks ... and this causes unwanted flicker - var edges = graph.get_linked_edge_list(self); - for(var i = 0; i < edges.length; i++){ - edges[i].label_disable(); - } - if(self.close_button){ - self.close_button.hide(); - } - set_pos(this.opos.add_xy(dx,dy)); - }; - var drag_up = function(){ - //we re-enable the - var edges = graph.get_linked_edge_list(self); - for(var i = 0; i < edges.length; i++){ - edges[i].label_enable(); - } - if(self.close_button){ - self.close_button.show(); - } - }; - node_fig.drag(drag_move,drag_down,drag_up); - node_label.drag(drag_move,drag_down,drag_up); - - //allow the user to create edges by dragging onto the node - function hover_in(){ - if(graph.creating_edge){ - graph.target_node = self; - } - } - function hover_out(){ - graph.target_node = null; - } - node_fig.hover(hover_in,hover_out); - node_label.hover(hover_in,hover_out); - - function double_click(){ - GraphNode.double_click_callback(self); - } - node_fig.dblclick(double_click); - node_label.dblclick(double_click); - - this.connectors.push(new Connector(graph,this,-sx/2,0)); - this.connectors.push(new Connector(graph,this,sx/2,0)); - this.connectors.push(new Connector(graph,this,0,-sy/2)); - this.connectors.push(new Connector(graph,this,0,sy/2)); - - this.close_button = new CloseButton(graph,this,"node",sx/2 , - sy/2 ); - } - - GraphNode.double_click_callback = function(node){ - console.log("double click from node:",node); - }; - - // this is the default node destruction callback. It is called before the node is removed from the graph - // and before the connected edges are destroyed - GraphNode.destruction_callback = function(node){ return true; }; - - // creates a new edge with label 'label' from start to end. start and end must implement get_pos_*, - // if tmp is true, the edge is not added to the graph, used for drag edges. - // replace tmp == false by graph == null - function GraphEdge(graph,label,start,end,tmp){ - var self = this; - var r = graph.r; - var curvature = 0; // 0 = straight, != 0 curved - var s,e; // positions of the start and end point of the line between start and end - var mc; // position of the middle of the curve (bezier control point) - var mc1,mc2; // control points of the cubic bezier for the loop edges - var elfs = graph.style.edge_label_font_size || 10 ; - var label_enabled = true; - this.uid = 0; // unique id used to order the curved edges - var edge_path = ""; // svg definition of the edge vector path - var selected = false; - - if(!tmp){ - graph.add_edge(start,end,this); - } - - //Return the position of the label - function get_label_pos(path){ - var cpos = path.getTotalLength() * 0.5; - var cindex = Math.abs(Math.floor(curvature)); - var mod = ((cindex % 3)) * (elfs * 3.1) - (elfs * 0.5); - var verticality = Math.abs(end.get_pos().sub(start.get_pos()).normalize().dot_xy(0,1)); - verticality = Math.max(verticality-0.5,0)*2; - - var lpos = path.getPointAtLength(cpos + mod * verticality); - return new Vec2(lpos.x,lpos.y - elfs *(1-verticality)); - } - - //used by close_button - this.get_pos = function(){ - if(!edge){ - return start.get_pos().lerp(end.get_pos(),0.5); - } - return get_label_pos(edge); - /* - var bbox = edge_label.getBBox(); Does not work... :( - return new Vec2(bbox.x + bbox.width, bbox.y);*/ - } - - //Straight line from s to e - function make_line(){ - return "M" + s.x + "," + s.y + "L" + e.x + "," + e.y ; - } - //Curved line from s to e by mc - function make_curve(){ - return "M" + s.x + "," + s.y + "Q" + mc.x + "," + mc.y + " " + e.x + "," + e.y; - } - //Curved line from s to e by mc1 mc2 - function make_loop(){ - return "M" + s.x + " " + s.y + - "C" + mc1.x + " " + mc1.y + " " + mc2.x + " " + mc2.y + " " + e.x + " " + e.y; - } - - //computes new start and end line coordinates - function update_curve(){ - if(start != end){ - if(!tmp){ - curvature = graph.get_edge_curvature(start,end,self); - }else{ - curvature = 0; - } - s = start.get_pos(); - e = end.get_pos(); - - mc = s.lerp(e,0.5); //middle of the line s->e - var se = e.sub(s); - se = se.normalize(); - se = se.rotate_deg(-90); - se = se.scale(curvature * graph.style.edge_spacing); - mc = mc.add(se); - - if(start.get_bound){ - var col = start.get_bound().collide_segment(s,mc); - if(col.length > 0){ - s = col[0]; - } - } - if(end.get_bound){ - var col = end.get_bound().collide_segment(mc,e); - if(col.length > 0){ - e = col[0]; - } - } - - if(curvature != 0){ - edge_path = make_curve(); - }else{ - edge_path = make_line(); - } - }else{ // start == end - var rad = graph.style.edge_loop_radius || 100; - s = start.get_pos(); - e = end.get_pos(); - - var r = Vec2.new_polar_deg(rad,graph.get_loop_angle(start,self)); - mc = s.add(r); - var p = r.rotate_deg(90); - mc1 = mc.add(p.set_len(rad*0.5)); - mc2 = mc.add(p.set_len(-rad*0.5)); - - if(start.get_bound){ - var col = start.get_bound().collide_segment(s,mc1); - if(col.length > 0){ - s = col[0]; - } - var col = start.get_bound().collide_segment(e,mc2); - if(col.length > 0){ - e = col[0]; - } - } - edge_path = make_loop(); - } - } - - update_curve(); - var edge = r.path(edge_path).attr({ 'stroke': graph.style.edge_color, - 'stroke-width': graph.style.edge_width, - 'arrow-end': 'block-wide-long', - 'cursor':'pointer' }).insertBefore(graph.get_node_list()[0].get_fig()); - var labelpos = get_label_pos(edge); - var edge_label = r.text(labelpos.x, labelpos.y - elfs, label).attr({ - 'fill': graph.style.edge_label_color, - 'cursor': 'pointer', - 'font-size': elfs }); - - edge.transform(graph.get_transform()); - graph.set_scrolling(edge); - - edge_label.transform(graph.get_transform()); - graph.set_scrolling(edge_label); - - - //since we create an edge we need to recompute the edges that have the same start and end positions as this one - if(!tmp){ - var edges_start = graph.get_linked_edge_list(start); - var edges_end = graph.get_linked_edge_list(end); - var edges = edges_start.length < edges_end.length ? edges_start : edges_end; - for(var i = 0; i < edges.length; i ++){ - if(edges[i] != self){ - edges[i].update(); - } - } - } - function label_enable(){ - if(!label_enabled){ - label_enabled = true; - edge_label.animate({'opacity':1},100,'linear'); - if(self.close_button){ - self.close_button.show(); - } - self.update(); - } - } - function label_disable(){ - if(label_enabled){ - label_enabled = false; - edge_label.animate({'opacity':0},100,'linear'); - if(self.close_button){ - self.close_button.hide(); - } - } - } - //update the positions - function update(){ - update_curve(); - edge.attr({'path':edge_path}); - if(label_enabled){ - var labelpos = get_label_pos(edge); - edge_label.attr({'x':labelpos.x, 'y':labelpos.y - 14}); - } - } - // removes the edge from the scene, disconnects it from linked - // nodes, destroy its drawable elements. - function remove(){ - edge.remove(); - edge_label.remove(); - if(!tmp){ - graph.remove_edge(self); - } - if(start.update_linked_edges){ - start.update_linked_edges(); - } - if(start != end && end.update_linked_edges){ - end.update_linked_edges(); - } - if(self.close_button){ - self.close_button.remove(); - } - } - - this.set_selected = function(){ - if(!selected){ - selected = true; - edge.attr({ 'stroke': graph.style.node_selected_color, - 'stroke-width': graph.style.node_selected_width }); - edge_label.attr({ 'fill': graph.style.node_selected_color }); - if(!self.close_button){ - self.close_button = new CloseButton(graph,self,"edge",0,30); - self.close_button.show(); - } - } - }; - - this.set_not_selected = function(){ - if(selected){ - selected = false; - edge.animate({ 'stroke': graph.style.edge_color, - 'stroke-width': graph.style.edge_width }, 100,'linear'); - edge_label.animate({ 'fill': graph.style.edge_label_color}, 100, 'linear'); - if(self.close_button){ - self.close_button.remove(); - self.close_button = null; - } - } - }; - function click_action(){ - graph.select(self); - } - edge.click(click_action); - edge_label.click(click_action); - - function double_click_action(){ - GraphEdge.double_click_callback(self); - } - - edge.dblclick(double_click_action); - edge_label.dblclick(double_click_action); - - - this.label_enable = label_enable; - this.label_disable = label_disable; - this.update = update; - this.remove = remove; - this.is_loop = function(){ return start == end; }; - this.get_start = function(){ return start; }; - this.get_end = function(){ return end; }; - } - - GraphEdge.double_click_callback = function(edge){ - console.log("double click from edge:",edge); - }; - - // this is the default edge creation callback. It is called before an edge is created - // It returns an object containing the properties of the edge. - // If it returns null, the edge is not created. - GraphEdge.creation_callback = function(start,end){ - var edge_prop = {}; - edge_prop.label = 'new edge!'; - return edge_prop; - }; - // This is is called after a new edge is created, with the new edge - // as parameter - GraphEdge.new_edge_callback = function(new_edge){}; - - // this is the default edge destruction callback. It is called before - // an edge is removed from the graph. - GraphEdge.destruction_callback = function(edge){ return true; }; - - - - // returns a new string with the same content as str, but with lines of maximum 'width' characters. - // lines are broken on words, or into words if a word is longer than 'width' - function wordwrap( str, width) { - // http://james.padolsey.com/javascript/wordwrap-for-javascript/ - width = width || 32; - var cut = true; - var brk = '\n'; - if (!str) { return str; } - var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)'); - return str.match(new RegExp(regex, 'g') ).join( brk ); - } - - window.CuteGraph = Graph; - window.CuteNode = GraphNode; - window.CuteEdge = GraphEdge; - - window.CuteGraph.wordwrap = wordwrap; - - -})(window); - |
