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