diff --git a/Kieker.WebGUI/bin/Kieker.WebGUI.bat b/Kieker.WebGUI/bin/Kieker.WebGUI.bat index 62e25701269f32f1ad785be5b8730369045a2133..0867192faac2b401faf96f2e3de8d52e45ae84a5 100644 --- a/Kieker.WebGUI/bin/Kieker.WebGUI.bat +++ b/Kieker.WebGUI/bin/Kieker.WebGUI.bat @@ -1,7 +1,7 @@ @echo off REM @author Nils Christian Ehmke -java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled -jar ..\lib\jetty-runner-*.jar --path /Kieker.WebGUI ..\target\Kieker.WebGUI-*.war +java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled -jar ..\lib\jetty-runner-8.1.7.v20120910.jar --path /Kieker.WebGUI ..\target\Kieker.WebGUI-*.war @echo on diff --git a/Kieker.WebGUI/bin/Kieker.WebGUI.sh b/Kieker.WebGUI/bin/Kieker.WebGUI.sh index fc2ebe5e286818c4a27f2098094ecd5fc1e32a4f..e13101c9ead8481494dbc1ee380e44465f2f59a6 100644 --- a/Kieker.WebGUI/bin/Kieker.WebGUI.sh +++ b/Kieker.WebGUI/bin/Kieker.WebGUI.sh @@ -2,4 +2,4 @@ # @author Nils Christian Ehmke -java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled -jar ../lib/jetty-runner-*.jar --path /Kieker.WebGUI ../target/Kieker.WebGUI-*.war \ No newline at end of file +java -XX:PermSize=256M -XX:MaxPermSize=512M -Xms128M -Xmx256M -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSClassUnloadingEnabled -jar ../lib/jetty-runner-8.1.7.v20120910.jar --path /Kieker.WebGUI ../target/Kieker.WebGUI-*.war \ No newline at end of file diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java index 4375516a39aab5b27c89615c8eaa3084522ad8df..1f230ff3d3490d83d0f0655d15fb90a62a51074e 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentAnalysisEditorGraphBean.java @@ -150,7 +150,7 @@ public final class CurrentAnalysisEditorGraphBean { } /** - * This method initializes the auto layout of the graph, resulting ing a javascript event, delivering the necessary information about the graph. + * This method initializes the auto layout of the graph, resulting in a javascript event, delivering the necessary information about the graph. */ public synchronized void startAutoLayout() { RequestContext.getCurrentInstance().execute(CurrentAnalysisEditorGraphBean.JS_CMD_START_AUTO_LAYOUT); diff --git a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js index 5d5515f8af5642df29985621729dcee2b92047b6..20f468e342589dbe5aa650658478891981e68ea9 100644 --- a/Kieker.WebGUI/src/main/webapp/js/flowEditor.js +++ b/Kieker.WebGUI/src/main/webapp/js/flowEditor.js @@ -47,7 +47,9 @@ var Log = { } }; - + +var testVar = "no"; + function GraphFlow(){ ///////////////////////////////////////////// // VARIABLES // @@ -72,7 +74,7 @@ function GraphFlow(){ /** Icon Images */ var nodeIcons = []; - + /** Overall size modifier: the width of node labels in pixels */ var vertexLabelSize = 12; @@ -160,6 +162,111 @@ function GraphFlow(){ // FUNCTIONS // ///////////////////////////////////////////// + /** + * Measures the width of a text on the canvas + * + * @param text the text that is measured + * @param font the size and font of the text + * @returns the measured width + */ + this.getTextWidth = function(text, font){ + + // validate arguments + if(!validArg("getTextWidth()", text, "string") || + !validArg("getTextWidth()", font, "string")){ + return; + } + + // get canvas context + var ctx = fd.canvas.getCtx(), + width = 0; + + // measure width + ctx.save(); + ctx.font = font; + width = ctx.measureText(text).width; + ctx.restore(); + + return width; + } + + /** + * @returns the size modifier that scales the whole graph. + */ + this.getSizeModifier = function(){ + return vertexLabelSize; + } + + /** + * Adds a node (that is NOT a Nodefamily) to the graph. + * + * @param xPosition - the horizontal position of the node + * @param yPosition - the vertical position of the node + * @param node - an object which at least contains an 'id' and may have a 'data' field + * @param type - the type of the defined custom node (basic types are: inputPort, nodeFamily, ...) + * @param noPlot - if false, a refresh is required to apply the changes + * @param forced - if true, does not throw a listener event + */ + this.addCustomNode = function(xPosition, yPosition, node, type, noPlot, forced){ + + // validate arguments + if(!validArg("addCustomNode()", xPosition, "number") || + !validArg("addCustomNode()", yPosition, "number") || + !validArg("addCustomNode()", type, "string") || + !validArgNode("addCustomNode()", node)){ + return; + } + + var nodeType = 'Custom'; + + if(node.data && node.data.$nodeType){ + nodeType = node.data.$nodeType; + } + + // define node + var newNode = { + "adjacencies": [], + "data": { + "$dim": vertexLabelSize, + "$type": type, + "$icon": nodeIcons[nodeType], + "$nodeType": nodeType, + "$xPos": xPosition, + "$yPos": yPosition, + "$custom": true + }, + "id": node.id, + "name": node.name}; + + // add colors if they exist + var strokeColor = nodeStrokeColor[type], + nodeColor = nodeFillColor[type]; + if(strokeColor){ newNode.data.$color = strokeColor;} + if(nodeColor){ newNode.data.$fillColor = nodeColor;} + + // (over-)write data if it was specified in node.data + if(node.data){ + for(var field in node.data){ + newNode.data[field] = node.data[field]; + } + } + + // add node to json array + json.push(newNode); + + // apply changes immediately + if(!noPlot){ + fd.graph.addNode(newNode); + fd.graph.getNode(newNode.id).pos.setc(xPosition, yPosition); + fd.plot(); + } + + // call listener + if(!forced){ + callListener("onCreateNode", [newNode]); + } + } + /** * Zooms in or out, depending on the factor * @@ -238,6 +345,65 @@ function GraphFlow(){ return positions; } + /** + Changes one or more properties of an edge immediately. + @param sourceID - the ID of the node where the edge starts + @param targetID - the ID of the node where the edge ends + @param properties - a data object that may contain any field from + data as well as "id". Data fields must start with a $. + @param noPlot - if true, the graph will not be plotted anew + */ + this.setEdgeData = function(sourceID, targetID, properties, noPlot){ + + // check arguments + if(!validArg("setEdgeData()", sourceID, "string") || + !validArg("setEdgeData()", targetID, "string")){ + return; + } + + var node = getNode(sourceID), + domEdge = fd.graph.getAdjacence(sourceID, targetID), + edge; + + // find edge in json-array + for(var e in node.adjacencies){ + if(e.nodeTo == targetID){ + edge = e; + break; + } + } + + // iterate through properties object + var value; + for(var field in properties){ + value = properties[field]; + + // make changes to data + if(field[0] == '$'){ + + if(field == '$type'){ + // apply new value to data + edge.Edge[field] = value; + if(!noPlot){ domEdge.Edge[field] = value;} + } + else{ + // apply new value to data + edge.data[field] = value; + if(!noPlot){ domEdge.data[field] = value;} + } + } + + // make changes to name or id + else{ + edge[field] = value; + if(!noPlot){ domEdge[field] = value;} + } + } + + // apply visual changes + if(!noPlot){ fd.plot();} + } + /** Changes one or more properties of a node immediately. @nodeID - the ID of the node that needs changing @@ -284,7 +450,9 @@ function GraphFlow(){ } // recalculate node dimensions in case we changed the font or name - updateNodeWidth(node, domNode); + if(!node.data.$custom){ + updateNodeWidth(node, domNode); + } // apply visual changes if(!noPlot){ fd.plot();} @@ -573,7 +741,7 @@ function GraphFlow(){ @param color - the color argument which is checked @returns - false if the string is not a well formed color string */ - function validArgColor(callFunction, color){ + this.validArgColor = function(callFunction, color){ var valid = false; // is it a string at all ? @@ -599,7 +767,7 @@ function GraphFlow(){ @param node - the object argument which is checked @returns - false if the object does not have an id */ - function validArgNode(callFunction, node){ + this.validArgNode = function(callFunction, node){ var valid = false; // is it an object at all ? @@ -612,7 +780,34 @@ function GraphFlow(){ callListener("onError", ["Error in function "+ callFunction + ": Invalid Node '"+ node + "'. Must have a field 'id'."]); + return valid; + } + + var id = node.id; + /* + findDuplicate:{ + for(var n = 0, l = jsonCapacity.occupied; n < l; n++){ + if(json[n].id == id){ + valid = false; + break findDuplicate; + } + } + + for(var n = jsonCapacity.max, l = json.length; n < l; n++){ + if(json[n].id == id){ + valid = false; + break findDuplicate; + } + } } + + + if(!valid){ + callListener("onError", ["Error in function "+ callFunction + + ": Invalid Node '"+ node + + "'. The ID '"+ id +"' is already taken."]); + } + */ return valid; } @@ -625,7 +820,7 @@ function GraphFlow(){ @param type - the type of which the argument must be @returns - false if the string is not a well formed color string */ - function validArg(callFunction, arg, type){ + this.validArg = function(callFunction, arg, type){ var valid = (typeof arg == type); if(!valid){ callListener("onError", ["Error in function "+ callFunction @@ -707,7 +902,6 @@ function GraphFlow(){ in which it is drawn. */ this.scaleToFit = function(){ - // do nothing if the graph is empty if(jsonCapacity.occupied == 0){ return; @@ -729,7 +923,7 @@ function GraphFlow(){ topMost = top, bottomMost = bottom; - // calculate the outer bounds of the displayed graph + // check nodeFamilies for position for(var n = 1, l= jsonCapacity.occupied; n < l; n++){ data = json[n].data, width = data.$width/2, @@ -754,6 +948,34 @@ function GraphFlow(){ bottomMost = bottom; } } + // check custom nodes for position + for(var n = jsonCapacity.max+1, l= json.length; n < l; n++){ + data = json[n].data; + + if(data.$custom){ + width = data.$width/2, + height = data.$height/2; + + left = data.$xPos - width, + right = data.$xPos + width, + top = data.$yPos - height, + bottom = data.$yPos + height; + + // update bounds + if(left < leftMost){ + leftMost = left; + } + if(right > rightMost){ + rightMost = right; + } + if(top < topMost){ + topMost = top; + } + if(bottom > bottomMost){ + bottomMost = bottom; + } + } + } // scaling values var canvas = fd.canvas, @@ -793,7 +1015,25 @@ function GraphFlow(){ if(path == null){ delete nodeIcons[nodeType]; - //TODO: adjust shrink width + + // remove icons from displayed graph and shrink nodes + var jsonNode, domNode, data; + for(var n = 0, l = jsonCapacity.occupied; n < l; n++){ + + // get data + jsonNode = json[n]; + data = jsonNode.data; + + // update node width and port positions + if(data.$nodeType == nodeType){ + + domNode = fd.graph.getNode(jsonNode.id); + delete data.$icon; + delete domNode.data.$icon; + updateNodeWidth(jsonNode, domNode); + } + } + fd.plot(); } else{ if(!validArg('setNodeIcon()', path, 'string')){ @@ -801,53 +1041,49 @@ function GraphFlow(){ } var icon; icon = new Image(); - icon.src = path; - icon.onload = function(){}; - // this may happen on IE, when linking to images via web - if(!icon.complete){ - callListener("onError", ["Error in function setNodeIcon(): Timeout while loading '"+ path +"'. Please try again!"]); - return; - } - // if the image was loaded successfully, its width must be greater 0 - if(icon.width != 0){ + // load error + icon.onerror = function(){ + callListener("onError", ["Error in function setNodeIcon(): Could not load '"+ path +"'."]); + }; + + // load success + icon.onload = function(){ + // this may happen on IE, when linking to images via web + if(!icon.complete){ + callListener("onError", ["Error in function setNodeIcon(): Timeout while loading '"+ path +"'. Please try again!"]); + return; + } + // if nodes were already created, we need to // adjust their width and iconPath - if(nodeIcons[nodeType] == undefined){ - var addSize = 6* vertexLabelSize, - addPortSize = addSize / 2, - max = jsonCapacity.max; + var prevImage = nodeIcons[nodeType]; + nodeIcons[nodeType] = icon; - var data, portData, portCount, addPortSizeRel; + if(!prevImage){ + + var jsonNode, domNode, data; for(var n = 0, l = jsonCapacity.occupied; n < l; n++){ - // adjust node width - data = json[n].data; + + // get data + jsonNode = json[n]; + data = jsonNode.data; + + // update node width and port positions if(data.$nodeType == nodeType){ - data.$icon = icon; - data.$width += addSize; - portCount = data.$portCount; - // adjust port positions - for(var p = data.$portIndex+max, s = p+portCount; p < s; p++){ - portData = json[p].data; - if(portData.$type == "inputPort"){ - addPortSizeRel = -addPortSize; - }else{ - addPortSizeRel = addPortSize; - } - if(portData.$relativeX){ - portData.$relativeX += addPortSizeRel; - } - portData.$xPos += addPortSizeRel; - } + domNode = fd.graph.getNode(jsonNode.id); + data.$icon = icon; + domNode.data.$icon = icon; + updateNodeWidth(jsonNode, domNode); } } } - nodeIcons[nodeType] = icon; - } - else{ - callListener("onError", ["Error in function setNodeIcon(): Could not load '"+ path +"'."]); - } + fd.plot(); + }; + + // attempt to load the image + icon.src = path; } } @@ -938,36 +1174,11 @@ function GraphFlow(){ var testval; // iterate through all listener this event concerns - - // mouse listener functions with 3 arguments (node, eventInfo, e) - if(arguments.length == 3){ - for(var l=0; l < lArr.length; l++){ - lArr[l](arguments[0], arguments[1], arguments[2]); - } - } - - // auto layout listener - else if(arguments.length == 2){ - for(var l=0; l < lArr.length; l++){ - lArr[l](arguments[0], arguments[1]); - } + for(var e in lArr){ + testval = lArr[e].apply(lArr[e], arguments); - // node listener functions with 1 argument (node) - }else if(arguments.length == 1){ - for(var l=0; l < lArr.length; l++){ - testval = lArr[l](arguments[0]); - if(testval == false){ - permitted = false; - } - } - - // edge listener functions with 4 arguments (sourceNode, targetNode, sourcePort, targetPort) - }else if(arguments.length == 4){ - for(var l=0; l < lArr.length; l++){ - testval = lArr[l](arguments[0], arguments[1], arguments[2], arguments[3]); - if(testval == false){ - permitted = false; - } + if(testval == false){ + permitted = false; } } return permitted; @@ -1043,57 +1254,59 @@ function GraphFlow(){ /** Changes the colors of the entire graph. Requires a refresh(). + @param nodeType - the name of the nodeType, whose fill and stroke color are changed @param fillColor - the fill color of nodes. If null, restore Node Color from global Array. @param strokeColor - the stroke color of nodes. If null, restore Stroke Color from global Array. - @param portColor - the color of all ports. If null, restore Port Color from global Array. @param edgeColor - the color of edges. If null, restore Edge Color from global Array. @param highlightColor - the of highlighted objects. If null, restore Edge Color from global Array. */ - this.setNodeStyle = function(fillColor, strokeColor, portColor, edColor, fcColor){ + this.setNodeStyle = function(nodeType, fillColor, strokeColor, edgeColor, focusColor){ + if(validArg('setNodeStyle()', nodeType, 'string')){ - if(fillColor && validArgColor('setNodeStyle()', fillColor)){ - nodeFillColor['nodeFamily'] = fillColor; - } - if(strokeColor && validArgColor('setNodeStyle()', strokeColor)){ - nodeStrokeColor['nodeFamily'] = strokeColor; + if(fillColor && validArgColor('setNodeStyle()', fillColor)){ + nodeFillColor[nodeType] = fillColor; + } + if(strokeColor && validArgColor('setNodeStyle()', strokeColor)){ + nodeStrokeColor[nodeType] = strokeColor; + } } - if(portColor && validArgColor('setNodeStyle()', portColor)){ - nodeStrokeColor['inputPort'] = portColor; - nodeStrokeColor['outputPort'] = portColor; - nodeStrokeColor['repositoryPort'] = portColor; - } - if(edColor && validArgColor('setNodeStyle()', edColor)){ - edgeColor = edColor; + if(edgeColor && validArgColor('setNodeStyle()', edgeColor)){ + edgeColor = edgeColor; } - if(fcColor && validArgColor('setNodeStyle()', fcColor)){ - edgeColorFocus = fcColor; - nodeColorFocus = fcColor; + if(focusColor && validArgColor('setNodeStyle()', focusColor)){ + edgeColorFocus = focusColor; + nodeColorFocus = focusColor; } var data, a, al, adja; // change family colors - for(var n = 0, l = jsonCapacity.occupied; n < l; n++){ - data = json[n].data; - // change only if node is not marked - if(json[n] != markedNode){ - data.$fillColor = nodeFillColor["nodeFamily"]; - data.$color = nodeStrokeColor["nodeFamily"]; + if(nodeType == "nodeFamily"){ + for(var n = 0, l = jsonCapacity.occupied; n < l; n++){ + data = json[n].data; + // change only if node is not marked + if(json[n] != markedNode){ + data.$fillColor = nodeFillColor["nodeFamily"]; + data.$color = nodeStrokeColor["nodeFamily"]; + } } } - // change port colors - for(var n = jsonCapacity.max+1, l = json.length; n < l; n++){ - data = json[n].data; - data.$color = nodeStrokeColor[data.$type]; - if(data.$type == "crossBox"){ - data.$fillColor = nodeFillColor["nodeFamily"]; - } - // change edge colors - if(edColor){ - adja = data.adjacencies; - for(a = 0, al = adja.length; a < al; a++){ - adja[a].data.$color = edgeColor; + else{ + // change colors of ports or custom nodes + for(var n = jsonCapacity.max+1, l = json.length; n < l; n++){ + data = json[n].data; + + if(data.$type == nodeType){ + data.$fillColor = nodeFillColor[nodeType]; + data.$color = nodeStrokeColor[nodeType]; + } + // change edge colors + if(edgeColor){ + adja = json[n].adjacencies; // TODO: data.adjacencies? + for(a = 0, al = adja.length; a < al; a++){ + adja[a].data.$color = edgeColor; + } } } } @@ -1214,7 +1427,7 @@ function GraphFlow(){ Math.max( 2*size, size/2 + maxPorts * size); // the classname may be longer than the diplayed name - if( nodeFamily.nodeClass != undefined){ + if( nodeFamily.nodeClass){ width = Math.max(width, (nodeFamily.nodeClass.length+6+iconSpace)*(vertexLabelSize-2)); } @@ -1242,6 +1455,7 @@ function GraphFlow(){ "$xPos": xPosition, "$yPos": yPosition, "$font": "px "+font, + "$movable" : true, "$tooltip": nodeFamily.tooltip }, "id": nodeFamily.id, @@ -1251,7 +1465,6 @@ function GraphFlow(){ // add closeButton var closeButton ={ - "adjacencies": [], "data": { "$dim": size, "$type": "crossBox", @@ -1376,6 +1589,14 @@ function GraphFlow(){ markedNode = null; } + // removalFunction for custom nodes + if(nodeFamily.data.$custom){ + var index = nodeFamily.data.$jsonIndex; + delete json[index]; + json.splice(index, 1); + return; + } + var deleteFrom = nodeFamily.data.$portIndex + jsonCapacity.max, deleteUntil = deleteFrom+ nodeFamily.data.$portCount, familyIndex = nodeFamily.data.$jsonIndex, @@ -1390,9 +1611,9 @@ function GraphFlow(){ var portID = json[n].id; // remove all incoming edges - for(var e = 0; e < json.length; e++){ + for(var e = jsonCapacity.max+1, l = json.length; e < l; e++){ var incNode = json[e]; - if(incNode != undefined){ + if(incNode){ removeEdge(incNode.id, portID, true); } } @@ -1438,19 +1659,21 @@ function GraphFlow(){ function prepareNodeMove(node){ var nodes = []; - var familyIndex = node.data.$jsonIndex, - moveFrom = node.data.$portIndex+ jsonCapacity.max, - moveUntil = moveFrom+ node.data.$portCount; // memorize the nodeFamily nodes.push(node); - // memorize all ports and the crossbox - var subnode; - for(var i= moveFrom; i< moveUntil; i++){ - subnode = fd.graph.getNode(json[i].id); - nodes.push(subnode); + // memorize all ports and the crossbox if it is a nodefamily + if(!node.data.$custom){ + var familyIndex = node.data.$jsonIndex, + moveFrom = node.data.$portIndex+ jsonCapacity.max, + moveUntil = moveFrom+ node.data.$portCount, + subnode; + for(var i= moveFrom; i< moveUntil; i++){ + subnode = fd.graph.getNode(json[i].id); + nodes.push(subnode); + } } navi.draggedNodes = nodes; @@ -1550,27 +1773,39 @@ function GraphFlow(){ when a node drag operation has finished. */ function saveNodeMove(){ - var node = navi.draggedNodes[0], - familyIndex = node.data.$jsonIndex, - moveFrom = node.data.$portIndex+ jsonCapacity.max-1, - nodes = navi.draggedNodes, - pos = node.pos.getc(true); - - - // set pos of family box - var data= json[familyIndex].data; - data.$xPos = pos.x; - data.$yPos = pos.y; - - // set pos of ports - for(var i= 1, l = nodes.length; i < l; i++){ - data = json[moveFrom+i].data; - pos = nodes[i].pos.getc(true); + if(navi.draggedNodes){ + var node = navi.draggedNodes[0], + familyIndex = node.data.$jsonIndex, + pos = node.pos.getc(true); + + var data; + if(familyIndex){ + data = json[familyIndex].data; + } + else{ + data = getNode(node.id).data; + } + // set pos of family box data.$xPos = pos.x; data.$yPos = pos.y; + + // move ports if it is a nodefamily + if(!data.$custom){ + var moveFrom = node.data.$portIndex+ jsonCapacity.max-1, + nodes = navi.draggedNodes; + + // set pos of ports + for(var i= 1, l = nodes.length; i < l; i++){ + data = json[moveFrom+i].data; + pos = nodes[i].pos.getc(true); + + data.$xPos = pos.x; + data.$yPos = pos.y; + } + } + navi.draggedNodes = null; } - navi.draggedNodes = null; } /** @@ -1628,7 +1863,6 @@ function GraphFlow(){ * @return true - if the edge was successfully added */ this.addEdge = function(sourceID, targetID, edgeLabel, forced, noPlot){ - // check for valid arguments if(!validArg("addEdge()", sourceID, "string")){ return false; @@ -1671,20 +1905,23 @@ function GraphFlow(){ } } + // the standard data set, which may be manipulated by event listeners + var data = {}; + // call listener if the edge is not connected to the dummy node and check for permission if(!forced && sourceID != mouseNode.id && targetID != mouseNode.id){ // check if ports and families were found - if(!(sourceFamily && source)){ + if(!((sourceFamily || source.data.$custom) && source)){ callListener("onError", ["Error in function addEdge(): The source port '" + sourceID + "' does not exist."]); return false; } - if(!(targetFamily && target)){ + if(!((targetFamily || target.data.$custom) && target)){ callListener("onError", ["Error in function addEdge(): The target port '" + targetID + "' does not exist."]); return false; } - if (!callListener("onCreateEdge", [sourceFamily, targetFamily, source, target])){ + if (!callListener("onCreateEdge", [sourceFamily, targetFamily, source, target, data])){ return false; } } @@ -1709,6 +1946,11 @@ function GraphFlow(){ "$color" : edgeColor} }; + // change data if it was specified by the event listener + for(var d in data){ + edge.data[d] = data[d]; + } + // add label if(edgeLabel){ edge.data.$label = edgeLabel; @@ -1795,7 +2037,7 @@ function GraphFlow(){ // look for ports for(var n = jsonCapacity.max, l = json.length; n < l; n++){ loopNode = json[n]; - if(loopNode != undefined){ + if(loopNode){ if(loopNode.id == sourceID){ source = loopNode; } @@ -1819,8 +2061,8 @@ function GraphFlow(){ } } - // call listener and ask for permission to delete - if(sourceID != mouseNode.id && targetID != mouseNode.id){ + // call listener and ask for permission to delete + if(sourceID != mouseNode.id && targetID != mouseNode.id){ if(!callListener("onRemoveEdge", [sourceFamily, targetFamily, source, target])){ return; } @@ -1828,7 +2070,7 @@ function GraphFlow(){ } // nothing to delete? - if(source == undefined){ + if(!source || !source.adjacencies){ return; } var adja = source.adjacencies; @@ -1895,7 +2137,7 @@ function GraphFlow(){ hover.setData('color', nodeStrokeColor[type], 'end'); } // decrease size of ports and cross button - if(type != 'nodeFamily'){ + if(type == 'inputPort' || type == 'outputPort' || type == 'repositoryPort'){ hover.setData('dim', vertexLabelSize*2, 'end'); } @@ -1912,7 +2154,8 @@ function GraphFlow(){ node.setData('color', nodeColorFocus, 'end'); // increase size of ports and cross button - if(node.data.$type != 'nodeFamily'){ + var type = node.data.$type; + if(type == 'inputPort' || type == 'outputPort' || type == 'repositoryPort'){ node.setData('dim', vertexLabelSize*3, 'end'); } } @@ -2000,6 +2243,61 @@ function GraphFlow(){ markedNode = jsonNode; } } + + /** + * @returns true if a mouse edge exists + */ + this.isEdgeDragged = function(){ + return !!selectedNode; + } + + /** + * Creates a dangling edge between a node and the mouse pointer. + * If such an edge exists already, create an edge between both nodes + * that were passed to this function. + * + * @param node - the node which is to be connected to the mouse pointer + * @param isIncoming - if true, the edge will point towards the node + * @param label - an optional edge label + */ + this.connectEdge = function(nodeID, isIncoming, label){ + + // second node clicked? + if(selectedNode){ + var label = selectedNode.label; + + // do we create an edge or simply discard it + if(nodeID){ + // determine direction of edge + if(selectedNode.isIncoming){ + addEdge(nodeID, selectedNode.id, label); + } + else{ + addEdge(selectedNode.id, nodeID, label); + } + } + + // remove mouse edge + if(selectedNode.isIncoming){ + removeEdge(mouseNode.id, selectedNode.id, true); + }else{ + removeEdge(selectedNode.id, mouseNode.id, true); + } + + selectedNode = null; + } + // first node clicked + else{ + selectedNode = {"id": nodeID, "isIncoming" : isIncoming, "label" : label}; + + // determine direction of mouse edge + if(isIncoming){ + addEdge(mouseNode.id, nodeID, label); + }else{ + addEdge(nodeID, mouseNode.id, label); + } + } + } /** Initializes the Graph, enabling canvas navigation and tooltips. @@ -2112,9 +2410,8 @@ function GraphFlow(){ // ignore some highlights if we are dragging an edge if(selectedNode != null && - (node.data.$type != "inputPort" - && node.data.$type != "outputPort" - && node.data.$type != "repositoryPort")){ + (node.data.$type == "nodeFamily" + || node.data.$type == "crossBox")){ return; } @@ -2145,7 +2442,7 @@ function GraphFlow(){ } */ // are we dragging an edge with the mouse ? - if(selectedNode != null){ + if(selectedNode){ var pos = eventInfo.getPos(); mouseNode.pos.setc(pos.x, pos.y-2); fd.plot(); @@ -2174,7 +2471,8 @@ function GraphFlow(){ // dragging the canvas if(node == false || node.id == mouseNode.id || - node.data.$type != "nodeFamily"){ + !node.data.$movable){ + navi.dragX = e.layerX//e.pageX; navi.dragY = e.layerX//e.pageY; navi.isDragging = true; @@ -2212,8 +2510,8 @@ function GraphFlow(){ * EVENT: OnDragMove */ onDragMove: function(node, eventInfo, e) { - if(!readOnly.move || !node || - node.id == mouseNode.id || node.data.$type != "nodeFamily"){ + if(selectedNode || !readOnly.move || !node || + node.id == mouseNode.id || !node.data.$movable){ return; } @@ -2249,7 +2547,7 @@ function GraphFlow(){ * EVENT: OnDragEnd */ onDragEnd: function(node, eventInfo, e) { - if(node.data.$type == "nodeFamily"){ + if(node.data.$movable){ saveNodeMove(); } @@ -2277,28 +2575,14 @@ function GraphFlow(){ navi.centerX -= deltaX; navi.centerY -= deltaY; - /* - // deblur edges by moving the canvas to a half pixel - var scale = fd.canvas.scaleOffsetX; - halfX = fd.canvas.translateOffsetX, - halfY = fd.canvas.translateOffsetY; - if(halfX < 0){ - halfX = -(halfX - Math.ceil(halfX) + 0.0)/scale; - } else{ - halfX = -(halfX - Math.floor(halfX) + 0.0)/scale; - } - if(halfY < 0){ - halfY = -(halfY - Math.ceil(halfY) + 0.0)/scale; - } else{ - halfY = -(halfY - Math.floor(halfY) + 0.0)/scale; - } - fd.canvas.translate(halfX, halfY); - */ - - //fd.canvas.translate(deltaX, deltaY); + // remove selection and mouse-edge upon clicking anywhere + if(selectedNode){ + connectEdge(null); + } } else if(mouseOverNode && readOnly.deleteCreate){ - + + var incomingEdge = false; switch(node.data.$type){ case "crossBox": removeNode(node.data.$family); @@ -2308,38 +2592,17 @@ function GraphFlow(){ case "nodeFamily": break; - case "outputPort": case "inputPort": + incomingEdge = true; + case "outputPort": case "repositoryPort": - // no selection yet - if(selectedNode == null){ - selectedNode = {"id": node.id, "from": node.data.$type}; - - // add edge to mouseNode - if(selectedNode.from == "inputPort"){ - addEdge(mouseNode.id, node.id); - } - else{ - addEdge(node.id, mouseNode.id); - } + connectEdge(node.id, incomingEdge); + // set mouse node position + if(selectedNode){ var pos = node.pos.getc(true); - // set mouse position mouseNode.pos.setc(pos.x, pos.y); fd.plot(); - return; - } - // click outside the edge drag box - else{ - // add Edge if the selectedNode differs from the clickedNode - if(selectedNode.from == "inputPort"){ - var label = selectedNode.label; - addEdge(node.id, selectedNode.id, label); - } - else{ - var label = selectedNode.label; - addEdge(selectedNode.id, node.id, label); - } } break; } @@ -2348,34 +2611,25 @@ function GraphFlow(){ else if(mouseOverEdge && readOnly.deleteCreate){ // no selection yet if(selectedNode == null){ - selectedNode = {"id":node.data.$direction[0], "from":"outputPort", "label":node.data.$label};//nodeTo + + var source = node.data.$direction[0], + label = node.data.$label; // remove selectedEdge - var label = node.data.$label; - removeEdge(selectedNode.id, node.data.$direction[1]); + removeEdge(source, node.data.$direction[1]); - // add edge to mouseNode - addEdge(selectedNode.id, mouseNode.id, label); - var pos = eventInfo.getPos(); + // create dangling edge + connectEdge(source, false, label); // set mouse position + var pos = eventInfo.getPos(); mouseNode.pos.setc(pos.x, pos.y-2); fd.plot(); + return; } } - // remove selection and mouse-edge upon clicking anywhere - if(selectedNode != null){ - - if(selectedNode.from == "inputPort"){ - removeEdge(mouseNode.id, selectedNode.id, true); - }else{ - removeEdge(selectedNode.id, mouseNode.id, true); - } - - selectedNode = null; - } callListener("onClick", [node, eventInfo, e]); navi.isDragging = false; }, diff --git a/Kieker.WebGUI/src/main/webapp/js/jit.js b/Kieker.WebGUI/src/main/webapp/js/jit.js index 98284eb01a7ab884d6e1367cd78736409cdf18f3..6712e46afc73a4420d67bb74df05ed9da40d2a4a 100644 --- a/Kieker.WebGUI/src/main/webapp/js/jit.js +++ b/Kieker.WebGUI/src/main/webapp/js/jit.js @@ -17750,17 +17750,17 @@ $jit.FlowGraph.$extend = true; 'render': function(node, canvas){ //Log.write("render "+Math.random()); - var pos = node.pos.getc(true), - width = node.getData('width'), - height = node.getData('height'), - dim = node.getData('dim'), + var data = node.data, + pos = node.pos.getc(true), + width = data.$width, + height = data.$height, + dim = data.$dim, rounded = dim/4, strokeWidth = 2, - fillColor = node.getData('fillColor'), + fillColor = data.$fillColor, posX = pos.x , posY = pos.y; var ctx = canvas.getCtx(); - // draw Stroke ( in the color, specified in node properties) this.nodeHelper.roundRect.render('fill', {x: posX, y: posY}, width+4, height+4, rounded, canvas); @@ -17771,7 +17771,7 @@ $jit.FlowGraph.$extend = true; var tX = posX; - var img = node.data.$icon; + var img = data.$icon; var imgLoaded = (img && img.width != undefined); // we need this for the crazy Internet Explorer if(imgLoaded){ @@ -17786,7 +17786,7 @@ $jit.FlowGraph.$extend = true; if(canvas.showLabels){ if(name != undefined){ - ctx.fillStyle = node.getData('color'); + ctx.fillStyle = data.$color; ctx.font = textSize + node.data.$font; textWidth = ctx.measureText(name).width/2; midX = tX - textWidth; @@ -17797,10 +17797,10 @@ $jit.FlowGraph.$extend = true; // paint class - var nodeClass = node.data.$nodeClass; + var nodeClass = data.$nodeClass; if(nodeClass != undefined){ nodeClass = "<"+nodeClass+">"; - ctx.font = (textSize - 3.22) + node.data.$font; + ctx.font = (textSize - 3.22) + data.$font; var classWidth = ctx.measureText(nodeClass).width/2; midX = tX - classWidth; @@ -17826,9 +17826,9 @@ $jit.FlowGraph.$extend = true; 'contains': function(node, pos){ var npos = node.pos.getc(true), - dim = node.getData('dim')/2, - width = node.getData('width')/2 -dim, - height = node.getData('height')/2 -dim, + dim = node.data.$dim/2, + width = node.data.$width/2 -dim, + height = node.data.$height/2 -dim, checkPosX = pos.x - npos.x, checkPosY = pos.y - npos.y; var notInCloseButton = (checkPosX < width-dim || checkPosY > dim-height); @@ -17846,7 +17846,7 @@ $jit.FlowGraph.$extend = true; return; } - var size = node.getData('dim'); + var size = node.data.$dim; if(canvas.showLabels){ var pos = node.pos.getc(true), @@ -17866,7 +17866,7 @@ $jit.FlowGraph.$extend = true; } var npos = node.pos.getc(true), - size = node.getData('dim')/2, + size = node.data.$dim/2, xN = npos.x - size/2, yN = npos.y - size; return Math.abs(pos.x - xN) <= size/2 && Math.abs(pos.y - yN) <= size; @@ -18078,8 +18078,7 @@ $jit.FlowGraph.$extend = true; 'flowArrow': { 'render': function(adj, canvas) { - - var dim = adj.getData('dim'), + var dim = adj.Edge.dim, inverse = adj.data.$direction[0] != adj.nodeFrom.id, posFrom = adj.nodeFrom.pos.getc(true), posTo = adj.nodeTo.pos.getc(true); @@ -18106,7 +18105,6 @@ $jit.FlowGraph.$extend = true; from = bend[b]; } } - this.edgeHelper.flowarrow.render(from, to, dim, false, canvas); // add the label