qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From eal...@apache.org
Subject [08/11] qpid-dispatch git commit: DISPATCH-316 Hawtio and stand-alone consoles now use common code
Date Fri, 19 Aug 2016 16:59:15 GMT
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js b/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js
deleted file mode 100644
index e81463a..0000000
--- a/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-/**
- * @module QDR
- */
-var QDR = (function (QDR) {
-
-  /**
-   * @method SettingsController
-   * @param $scope
-   * @param QDRServer
-   *
-   * Controller that handles the QDR settings page
-   */
-
-  QDR.module.controller("QDR.SettingsController", ['$scope', 'QDRService', '$timeout', '$location', function($scope, QDRService, $timeout, $location) {
-
-    $scope.connecting = false;
-    $scope.connectionError = false;
-    $scope.connectionErrorText = undefined;
-    $scope.forms = {};
-
-    $scope.formEntity = angular.fromJson(localStorage[QDR.SETTINGS_KEY]) || {address: '', port: '', username: '', password: '', autostart: false};
-
-    $scope.$watch('formEntity', function(newValue, oldValue) {
-      if (newValue !== oldValue) {
-        localStorage[QDR.SETTINGS_KEY] = angular.toJson(newValue);
-      }
-    }, true);
-
-    $scope.buttonText = function() {
-      if (QDRService.isConnected()) {
-        return "Disconnect";
-      } else {
-        return "Connect";
-      }
-    };
-
-    $scope.connect = function() {
-		if (QDRService.connected) {
-			QDRService.disconnect();
-		return;
-		}
-
-		if ($scope.settings.$valid) {
-			$scope.connectionError = false;
-			$scope.connecting = true;
-			$timeout( doConnect )   // timeout so connecting animation can display
-		}
-	}
-
-	var doConnect = function () {
-        if (!$scope.formEntity.address)
-            $scope.formEntity.address = "localhost"
-
-        console.log("attempting to connect to " + $scope.formEntity.address + ':' + $scope.formEntity.port);
-	    QDRService.addDisconnectAction(function () {
-			$timeout( function () {
-				QDR.log.debug("disconnect action called");
-                $scope.connecting = false;
-                $scope.connectionErrorText = QDRService.errorText;
-                $scope.connectionError = true;
-        	})
-	    });
-        QDRService.addConnectAction(function() {
-          //QDR.log.debug("got connection notification");
-			$timeout( function () {
-				$scope.connecting = false;
-			})
-        });
-        QDRService.connect($scope.formEntity);
-      }
-
-  }]);
-
-
-QDR.module.directive('posint', function (){
-   return {
-	require: 'ngModel',
-
-	link: function(scope, elem, attr, ctrl) {
-		// input type number allows + and - but we don't want them so filter them out
-		elem.bind('keypress', function (event) {
-			var nkey = !event.charCode ? event.which : event.charCode;
-			var skey = String.fromCharCode(nkey);
-			var nono = "-+.,"
-			if (nono.indexOf(skey) >= 0) {
-				event.preventDefault();
-				return false;
-			}
-			// firefox doesn't filter out non-numeric input. it just sets the ctrl to invalid
-			if (/[\!\@\#\$\%^&*\(\)]/.test(skey) && event.shiftKey || // prevent shift numbers
-				!(                                      // prevent all but the following
-					nkey <= 0 ||                            // arrows
-					nkey == 8 ||                            // delete|backspace
-					nkey == 13 ||                           // enter
-					(nkey >= 37 && nkey <=40) ||            // arrows
-					event.ctrlKey || event.altKey ||        // ctrl-v, etc.
-					/[0-9]/.test(skey))                     // numbers
-				) {
-					event.preventDefault();
-					return false;
-			}
-		})
-		// check the current value of input
-		var _isPortInvalid = function (value) {
-			var port = value + ''
-			var isErrRange = false;
-			// empty string is valid
-			if (port.length !== 0) {
-				var n = ~~Number(port);
-				if (n < 1 || n > 65535) {
-					isErrRange = true;
-				}
-			}
-			ctrl.$setValidity('range', !isErrRange)
-			return isErrRange;
-		}
-
-		//For DOM -> model validation
-		ctrl.$parsers.unshift(function(value) {
-			return _isPortInvalid(value) ? undefined : value;
-		});
-
-		//For model -> DOM validation
-		ctrl.$formatters.unshift(function(value) {
-			_isPortInvalid(value);
-			return value;
-		});
-      }
-   };
-});
-
-  return QDR;
-}(QDR || {}));
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js b/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js
new file mode 120000
index 0000000..6861eb2
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrSettings.js
@@ -0,0 +1 @@
+../../../../../../stand-alone/plugin/js/qdrSettings.js
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js b/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
deleted file mode 100644
index 795549a..0000000
--- a/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
+++ /dev/null
@@ -1,2132 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
-*/
-/**
- * @module QDR
- */
-var QDR = (function (QDR) {
-
-
-	QDR.module.controller('QDR.TopologyFormController', function ($scope, QDRService) {
-
-		$scope.attributes = []
-        var nameTemplate = '<div title="{{row.entity.description}}" class="ngCellText"><span>{{row.entity.attributeName}}</span></div>';
-        var valueTemplate = '<div title="{{row.entity.attributeValue}}" class="ngCellText"><span>{{row.entity.attributeValue}}</span></div>';
-        $scope.topoGridOptions = {
-            data: 'attributes',
-			enableColumnResize: true,
-			multiSelect: false,
-            columnDefs: [
-            {
-                field: 'attributeName',
-                displayName: 'Attribute',
-                cellTemplate: nameTemplate
-            },
-            {
-                field: 'attributeValue',
-                displayName: 'Value',
-				cellTemplate: valueTemplate
-            }
-            ]
-        };
-		$scope.form = ''
-		$scope.$on('showEntityForm', function (event, args) {
-			var attributes = args.attributes;
-			var entityTypes = QDRService.schema.entityTypes[args.entity].attributes;
-			attributes.forEach( function (attr) {
-				if (entityTypes[attr.attributeName] && entityTypes[attr.attributeName].description)
-					attr.description = entityTypes[attr.attributeName].description
-			})
-			$scope.attributes = attributes;
-			$scope.form = args.entity;
-		})
-		$scope.$on('showAddForm', function (event) {
-			$scope.form = 'add';
-		})
-	})
-
-
-  /**
-   * @method TopologyController
-   *
-   * Controller that handles the QDR topology page
-   */
-    QDR.module.controller("QDR.TopologyController", ['$scope', '$rootScope', 'QDRService', '$location', '$timeout', '$dialog',
-    function($scope, $rootScope, QDRService, $location, $timeout, $dialog) {
-
-        $scope.multiData = []
-        $scope.selectedClient = [];
-        $scope.quiesceState = {}
-        var dontHide = false;
-		$scope.quiesceConnection = function (row) {
-			var entity = row.entity;
-			var state = $scope.quiesceState[entity.connectionId].state;
-			if (state === 'enabled') {
-				// start quiescing all links
-				$scope.quiesceState[entity.connectionId].state = 'quiescing';
-			} else if (state === 'quiesced') {
-				// start reviving all links
-				$scope.quiesceState[entity.connectionId].state = 'reviving';
-			}
-			$scope.multiDetails.updateState(entity);
-			dontHide = true;
-			$scope.multiDetails.selectRow(row.rowIndex, true);
-			$scope.multiDetails.showLinksList(row)
-		}
-		$scope.quiesceDisabled = function (row) {
-			return $scope.quiesceState[row.entity.connectionId].buttonDisabled;
-		}
-		$scope.quiesceText = function (row) {
-			return $scope.quiesceState[row.entity.connectionId].buttonText;
-		}
-		$scope.quiesceClass = function (row) {
-			var stateClassMap = {
-				enabled: 'btn-primary',
-				quiescing: 'btn-warning',
-				reviving: 'btn-warning',
-				quiesced: 'btn-danger'
-			}
-			return stateClassMap[$scope.quiesceState[row.entity.connectionId].state];
-		}
-        $scope.multiDetails = {
-            data: 'multiData',
-			selectedItems: $scope.selectedClient,
-			multiSelect: false,
-			afterSelectionChange: function (obj) {
-				if (obj.selected && obj.orig) {
-					var detailsDiv = d3.select('#link_details')
-					var isVis = detailsDiv.style('display') === 'block';
-					if (!dontHide && isVis && $scope.connectionId === obj.entity.connectionId) {
-						hideLinkDetails();
-						return;
-					}
-					dontHide = false;
-					$scope.multiDetails.showLinksList(obj)
-				}
-			},
-			showLinksList: function (obj) {
-				$scope.linkData = obj.entity.linkData;
-				$scope.connectionId = obj.entity.connectionId;
-				var visibleLen = Math.min(obj.entity.linkData.length, 10)
-				var left = parseInt(d3.select('#multiple_details').style("left"))
-				var detailsDiv = d3.select('#link_details')
-	            detailsDiv
-	                .style({
-	                    display: 'block',
-	                    opacity: 1,
-	                    left: (left + 20) + "px",
-	                    top:  (mouseY + 20 + $(document).scrollTop()) + "px",
-	                    height: (visibleLen + 1) * 30 + "px", // +1 for the header row
-	                    'overflow-y': obj.entity.linkData > 10 ? 'scroll' : 'hidden'})
-			},
-			updateState: function (entity) {
-				var state = $scope.quiesceState[entity.connectionId].state
-
-				// count enabled and disabled links for this connection
-				var enabled = 0, disabled = 0;
-				entity.linkData.forEach ( function (link) {
-					if (link.adminStatus === 'enabled')
-						++enabled;
-					if (link.adminStatus === 'disabled')
-						++disabled;
-				})
-
-				var linkCount = entity.linkData.length;
-				// if state is quiescing and any links are enabled, button should say 'Quiescing' and be disabled
-				if (state === 'quiescing' && (enabled > 0)) {
-					$scope.quiesceState[entity.connectionId].buttonText = 'Quiescing';
-					$scope.quiesceState[entity.connectionId].buttonDisabled = true;
-				} else
-				// if state is enabled and all links are disabled, button should say Revive and be enabled. set state to quisced
-				// if state is quiescing and all links are disabled, button should say 'Revive' and be enabled. set state to quiesced
-				if ((state === 'quiescing' || state === 'enabled') && (disabled === linkCount)) {
-					$scope.quiesceState[entity.connectionId].buttonText = 'Revive';
-					$scope.quiesceState[entity.connectionId].buttonDisabled = false;
-					$scope.quiesceState[entity.connectionId].state = 'quiesced'
-				} else
-				// if state is reviving and any links are disabled, button should say 'Reviving' and be disabled
-				if (state === 'reviving' && (disabled > 0)) {
-					$scope.quiesceState[entity.connectionId].buttonText = 'Reviving';
-					$scope.quiesceState[entity.connectionId].buttonDisabled = true;
-				} else
-				// if state is reviving or quiesced and all links are enabled, button should say 'Quiesce' and be enabled. set state to enabled
-				if ((state === 'reviving' || state === 'quiesced') && (enabled === linkCount)) {
-					$scope.quiesceState[entity.connectionId].buttonText = 'Quiesce';
-					$scope.quiesceState[entity.connectionId].buttonDisabled = false;
-					$scope.quiesceState[entity.connectionId].state = 'enabled'
-				}
-			},
-            columnDefs: [
-            {
-                field: 'host',
-                cellTemplate: "titleCellTemplate.html",
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-                displayName: 'Connection host'
-            },
-            {
-                field: 'user',
-                cellTemplate: "titleCellTemplate.html",
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-                displayName: 'User'
-            },
-			{
-				field: 'properties',
-                cellTemplate: "titleCellTemplate.html",
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-				displayName: 'Properties'
-			}/*,
-			{
-				cellClass: 'gridCellButton',
-				cellTemplate: '<button title="{{quiesceText(row)}} the links" type="button" ng-class="quiesceClass(row)" class="btn" ng-click="$event.stopPropagation();quiesceConnection(row)" ng-disabled="quiesceDisabled(row)">{{quiesceText(row)}}</button>'
-			}*/
-            ]
-        };
-		$scope.linkData = [];
-		$scope.quiesceLinkClass = function (row) {
-			var stateClassMap = {
-				enabled: 'btn-primary',
-				disabled: 'btn-danger'
-			}
-			return stateClassMap[row.entity.adminStatus]
-			//return stateClassMap[$scope.quiesceState[row.entity.connectionId].linkStates[row.entity.identity]];
-		}
-		$scope.quiesceLink = function (row) {
-			var state = row.entity.adminStatus === 'enabled' ? 'disabled' : 'enabled';
-			$scope.quiesceState[row.entity.connectionId].linkStates[row.entity.identity] = state;
-		}
-		$scope.quiesceLinkDisabled = function (row) {
-			return false;
-		}
-		$scope.quiesceLinkText = function (row) {
-			return row.entity.adminStatus === 'disabled' ? "Revive" : "Quiesce";
-		}
-		$scope.linkDetails = {
-			data: 'linkData',
-            columnDefs: [
-			{
-				field: 'adminStatus',
-                cellTemplate: "titleCellTemplate.html",
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-				displayName: 'Admin stat'
-			},
-			{
-				field: 'dir',
-                cellTemplate: "titleCellTemplate.html",
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-				displayName: 'dir'
-			},
-			{
-				field: 'owningAddr',
-                cellTemplate: "titleCellTemplate.html",
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-				displayName: 'Address'
-			},
-			{
-				field: 'deliveryCount',
-				displayName: 'Delivered',
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-				cellClass: 'grid-values'
-
-			},
-			{
-				field: 'undeliveredCount',
-				displayName: 'Undelivered',
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-				cellClass: 'grid-values'
-			},
-			{
-				field: 'unsettledCount',
-				displayName: 'Unsettled',
-				headerCellTemplate: 'titleHeaderCellTemplate.html',
-				cellClass: 'grid-values'
-			}/*,
-
-			{
-				cellClass: 'gridCellButton',
-				cellTemplate: '<button title="{{quiesceLinkText(row)}} this link" type="button" ng-class="quiesceLinkClass(row)" class="btn" ng-click="quiesceLink(row)" ng-disabled="quiesceLinkDisabled(row)">{{quiesceLinkText(row)}}</button>'
-			}*/
-			]
-		}
-
-		if (!QDRService.connected) {
-			// we are not connected. we probably got here from a bookmark or manual page reload
-			QDRService.redirectWhenConnected("topology");
-			return;
-		}
-		// we are currently connected. setup a handler to get notified if we are ever disconnected
-		QDRService.addDisconnectAction( function () {
-			QDRService.redirectWhenConnected("topology");
-			$scope.$apply();
-		})
-
-		QDR.log.debug("started QDR.TopologyController with urlPrefix: " + $location.absUrl());
-		var urlPrefix = $location.absUrl();
-
-		$scope.addingNode = {
-			step: 0,
-			hasLink: false,
-			trigger: ''
-		};
-
-        $scope.cancel = function () {
-            $scope.addingNode.step = 0;
-        }
-		$scope.editNewRouter = function () {
-			$scope.addingNode.trigger = 'editNode';
-		}
-
-		var NewRouterName = "__NEW__";
-	    // mouse event vars
-	    var selected_node = null,
-	        selected_link = null,
-	        mousedown_link = null,
-	        mousedown_node = null,
-	        mouseup_node = null,
-	        initial_mouse_down_position = null;
-
-        $scope.schema = "Not connected";
-
-	    $scope.modes = [
-	    	{title: 'Topology view', name: 'Diagram', right: false},
-	    	/* {title: 'Add a new router node', name: 'Add Router', right: true} */
-	    	];
-		$scope.mode = "Diagram";
-		$scope.contextNode = null; // node that is associated with the current context menu
-
-		$scope.isModeActive = function (name) {
-			if ((name == 'Add Router' || name == 'Diagram') && $scope.addingNode.step > 0)
-				return true;
-			return ($scope.mode == name);
-		}
-		$scope.selectMode = function (name) {
-			if (name == "Add Router") {
-				name = 'Diagram';
-				if ($scope.addingNode.step > 0) {
-					$scope.addingNode.step = 0;
-				} else {
-					// start adding node mode
-					$scope.addingNode.step = 1;
-				}
-			} else {
-				$scope.addingNode.step = 0;
-			}
-
-			$scope.mode = name;
-		}
-		$scope.$watch(function () {return $scope.addingNode.step}, function (newValue, oldValue) {
-			if (newValue == 0 && oldValue != 0) {
-				// we are cancelling the add
-
-				// find the New node
-				nodes.every(function (n, i) {
-					// for the placeholder node, the key will be __internal__
-					if (QDRService.nameFromId(n.key) == '__internal__') {
-						var newLinks = links.filter(function (e, i) {
-							return e.source.id == n.id || e.target.id == n.id;
-						})
-						// newLinks is an array of links to remove
-						newLinks.map(function (e) {
-							links.splice(links.indexOf(e), 1);
-						})
-						// i is the index of the node to remove
-						nodes.splice(i, 1);
-						force.nodes(nodes).links(links).start();
-		                restart(false);
-						return false; // stop looping
-					}
-					return true;
-				})
-				updateForm(Object.keys(QDRService.topology.nodeInfo())[0], 'router', 0);
-
-			} else if (newValue > 0) {
-				// we are starting the add mode
-				$scope.$broadcast('showAddForm')
-
-				resetMouseVars();
-                selected_node = null;
-                selected_link = null;
-                // add a new node
-                var id = "amqp:/_topo/0/__internal__/$management";
-                var x = radiusNormal * 4;
-                var y = x;;
-                if (newValue > 1) {   // add at current mouse position
-                    var offset = jQuery('#topology').offset();
-                    x = mouseX - offset.left + $(document).scrollLeft();
-                    y = mouseY - offset.top + $(document).scrollTop();;
-                }
-                NewRouterName = genNewName();
-                nodes.push( aNode(id, NewRouterName, "inter-router", undefined, nodes.length, x, y, undefined, true) );
-                force.nodes(nodes).links(links).start();
-                restart(false);
-			}
-
-		})
-		$scope.isRight = function (mode) {
-			return mode.right;
-		}
-
-		// for ng-grid that shows details for multiple consoles/clients
-		// generate unique name for router and containerName
-		var genNewName = function () {
-			var nodeInfo = QDRService.topology.nodeInfo();
-			var nameIndex = 1;
-			var newName = "R." + nameIndex;
-
-			var names = [];
-			for (key in nodeInfo) {
-				var node = nodeInfo[key];
-				var router = node['.router'];
-				var attrNames = router.attributeNames;
-				var name = QDRService.valFor(attrNames, router.results[0], 'routerId')
-				if (!name)
-					name = QDRService.valFor(attrNames, router.results[0], 'name')
-				names.push(name);
-			}
-
-			while (names.indexOf(newName) >= 0) {
-				newName = "R." + nameIndex++;
-			}
-			return newName;
-		}
-
-		$scope.$watch(function () {return $scope.addingNode.trigger}, function (newValue, oldValue) {
-			if (newValue == 'editNode') {
-				$scope.addingNode.trigger = "";
-				editNode();
-			}
-		})
-
-	    function editNode() {
-	        doAddDialog(NewRouterName);
-	    };
-		$scope.reverseLink = function () {
-			if (!mousedown_link)
-				return;
-			var d = mousedown_link;
-			var tmp = d.left;
-			d.left = d.right;;
-			d.right = tmp;
-		    restart(false);
-		    tick();
-		}
-		$scope.removeLink = function () {
-			if (!mousedown_link)
-				return;
-			var d = mousedown_link;
-			 links.every( function (l, i) {
-				if (l.source.id == d.source.id && l.target.id == d.target.id) {
-			        links.splice(i, 1);
-					force.links(links).start();
-					return false; // exit the 'every' loop
-				}
-				return true;
-			});
-		    restart(false);
-		    tick();
-		}
-		$scope.setFixed = function (b) {
-			if ($scope.contextNode) {
-				$scope.contextNode.fixed = b;
-			}
-			restart();
-		}
-		$scope.isFixed = function () {
-			if (!$scope.contextNode)
-				return false;
-			return ($scope.contextNode.fixed & 0b1);
-		}
-
-		var mouseX, mouseY;
-		// event handlers for popup context menu
-		$(document).mousemove(function (e) {
-		    mouseX = e.clientX;
-		    mouseY = e.clientY;
-		});
-		$(document).mousemove();
-		$(document).click(function (e) {
-			$scope.contextNode = null;
-            $(".contextMenu").fadeOut(200);
-        });
-
-		// set up SVG for D3
-	    var width, height;
-	    var tpdiv = $('#topology');
-	    var colors = {'inter-router': "#EAEAEA", 'normal': "#F0F000", 'on-demand': '#00F000'};
-	    var gap = 5;
-	    var radii = {'inter-router': 25, 'normal': 15, 'on-demand': 15};
-	    var radius = 25;
-	    var radiusNormal = 15;
-	    width = tpdiv.width() - gap;
-	    height = $('#main').height() - $('#topology').position().top - gap;
-		if (width < 10 && height < 10) {
-			QDR.log.info("page width and height are abynormal: returning before very bad things happen")
-			return;
-		}
-
-	    var svg, lsvg;
-		var force;
-		var animate = false; // should the force graph organize itself when it is displayed
-		var path, circle;
-		var savedKeys = {};
-		var dblckickPos = [0,0];
-
-	    // set up initial nodes and links
-	    //  - nodes are known by 'id', not by index in array.
-	    //  - selected edges are indicated on the node (as a bold red circle).
-	    //  - links are always source < target; edge directions are set by 'left' and 'right'.
-		var nodes = [];
-		var links = [];
-
-		var aNode = function (id, name, nodeType, nodeInfo, nodeIndex, x, y, resultIndex, fixed, properties) {
-			properties = properties || {};
-			var routerId;
-			if (nodeInfo) {
-				var node = nodeInfo[id];
-				if (node) {
-					var router = node['.router'];
-					routerId = QDRService.valFor(router.attributeNames, router.results[0], 'id')
-					if (!routerId)
-						routerId = QDRService.valFor(router.attributeNames, router.results[0], 'routerId')
-				}
-			}
-			return {   key: id,
-				name: name,
-				nodeType: nodeType,
-				properties: properties,
-				routerId: routerId,
-				x: x,
-				y: y,
-				id: nodeIndex,
-				resultIndex: resultIndex,
-				fixed: fixed,
-				cls: name == NewRouterName ? 'temp' : ''
-			};
-		};
-
-
-        var initForm = function (attributes, results, entityType, formFields) {
-        
-            while(formFields.length > 0) {
-                // remove all existing attributes
-                    formFields.pop();
-            }
-
-            for (var i=0; i<attributes.length; ++i) {
-                var name = attributes[i];
-                var val = results[i];
-                var desc = "";
-                if (entityType.attributes[name])
-                    if (entityType.attributes[name].description)
-                        desc = entityType.attributes[name].description;
-
-                formFields.push({'attributeName': name, 'attributeValue': val, 'description': desc});
-            }
-        }
-
-		// initialize the nodes and links array from the QDRService.topology._nodeInfo object
-		var initForceGraph = function () {
-			nodes = [];
-			links = [];
-
-			svg = d3.select('#topology')
-				.append('svg')
-				.attr("id", "SVG_ID")
-				.attr('width', width)
-				.attr('height', height)
-	            .on("contextmenu", function(d) {
-	                if (d3.event.defaultPrevented)
-	                    return;
-                    d3.event.preventDefault();
-					if ($scope.addingNode.step != 0)
-						return;
-					if (d3.select('#svg_context_menu').style('display') !== 'block')
-	                    $(document).click();
-                    d3.select('#svg_context_menu')
-                      .style('left', (mouseX + $(document).scrollLeft()) + "px")
-                      .style('top', (mouseY + $(document).scrollTop()) + "px")
-                      .style('display', 'block');
-                })
-                .on('click', function (d) {
-                    removeCrosssection()
-                });
-
-                $(document).keyup(function(e) {
-                  if (e.keyCode === 27) {
-                    removeCrosssection()
-                  }
-                });
-
-			// the legend
-			lsvg = d3.select("#svg_legend")
-			 	.append('svg')
-				.attr('id', 'svglegend')
-			lsvg = lsvg.append('svg:g')
-				.attr('transform', 'translate('+(radii['inter-router']+2)+','+(radii['inter-router']+2)+')')
-				.selectAll('g');
-
-			// mouse event vars
-			selected_node = null;
-			selected_link = null;
-			mousedown_link = null;
-			mousedown_node = null;
-			mouseup_node = null;
-
-			// initialize the list of nodes
-			var yInit = 10;
-			var nodeInfo = QDRService.topology.nodeInfo();
-			var nodeCount = Object.keys(nodeInfo).length;
-			for (var id in nodeInfo) {
-				var name = QDRService.nameFromId(id);
-                // if we have any new nodes, animate the force graph to position them
-				var position = angular.fromJson(localStorage[name]);
-				if (!angular.isDefined(position)) {
-				    animate = true;
-				    position = {x: width / 4 + ((width / 2)/nodeCount) * nodes.length,
-                				y: 200 + yInit,
-                				fixed: false};
-				}
-				if (position.y > height)
-					position.y = 200 - yInit;
-				nodes.push( aNode(id, name, "inter-router", nodeInfo, nodes.length, position.x, position.y, undefined, position.fixed) );
-				yInit *= -1;
-				//QDR.log.debug("adding node " + nodes.length-1);
-			}
-
-			// initialize the list of links
-			var source = 0;
-			var client = 1;
-			for (var id in nodeInfo) {
-				var onode = nodeInfo[id];
-				var conns = onode['.connection'].results;
-				var attrs = onode['.connection'].attributeNames;
-				var parent = getNodeIndex(QDRService.nameFromId(id));
-				//QDR.log.debug("external client parent is " + parent);
-				var normalsParent = {console: undefined, client: undefined}; // 1st normal node for this parent
-
-				for (var j = 0; j < conns.length; j++) {
-                    var role = QDRService.valFor(attrs, conns[j], "role");
-                    var properties = QDRService.valFor(attrs, conns[j], "properties") || {};
-                    var dir = QDRService.valFor(attrs, conns[j], "dir");
-					if (role == "inter-router") {
-						var connId = QDRService.valFor(attrs, conns[j], "container");
-						var target = getContainerIndex(connId);
-						if (target >= 0)
-							getLink(source, target, dir);
-					} else if (role == "normal" || role == "on-demand") {
-						// not a router, but an external client
-						//QDR.log.debug("found an external client for " + id);
-						var name = QDRService.nameFromId(id) + "." + client;
-						//QDR.log.debug("external client name is  " + name + " and the role is " + role);
-
-                        // if we have any new clients, animate the force graph to position them
-                        var position = angular.fromJson(localStorage[name]);
-                        if (!angular.isDefined(position)) {
-                            animate = true;
-                            position = {x: nodes[parent].x + 40 + Math.sin(Math.PI/2 * client),
-                                        y: nodes[parent].y + 40 + Math.cos(Math.PI/2 * client),
-                                        fixed: false};
-                        }
-						if (position.y > height)
-							position.y = nodes[parent].y + 40 + Math.cos(Math.PI/2 * client)
-						var node = aNode(id, name, role, nodeInfo, nodes.length, position.x, position.y, j, position.fixed, properties)
-						var nodeType = QDRService.isAConsole(properties, QDRService.valFor(attrs, conns[j], "identity"), role, node.key)
-
-						if (role === 'normal') {
-							node.user = QDRService.valFor(attrs, conns[j], "user")
-							node.isEncrypted = QDRService.valFor(attrs, conns[j], "isEncrypted")
-							node.host = QDRService.valFor(attrs, conns[j], "host")
-							node.connectionId = QDRService.valFor(attrs, conns[j], "identity")
-
-							if (!normalsParent[nodeType]) {
-								normalsParent[nodeType] = node;
-								nodes.push(	node );
-								node.normals = [node];
-								// now add a link
-								getLink(parent, nodes.length-1, dir);
-								client++;
-							} else {
-								normalsParent[nodeType].normals.push(node)
-							}
-						} else {
-							nodes.push( node)
-							// now add a link
-							getLink(parent, nodes.length-1, dir);
-							client++;
-						}
-					}
-				}
-				source++;
-			}
-
-            $scope.schema = QDRService.schema;
-			// init D3 force layout
-			force = d3.layout.force()
-				.nodes(nodes)
-				.links(links)
-				.size([width, height])
-				.linkDistance(function(d) { return d.target.nodeType === 'inter-router' ? 150 : 65 })
-				.charge(-1800)
-				.friction(.10)
-				.gravity(0.0001)
-				.on('tick', tick)
-				.start()
-
-			svg.append("svg:defs").selectAll('marker')
-				.data(["end-arrow", "end-arrow-selected"])      // Different link/path types can be defined here
-				.enter().append("svg:marker")    // This section adds in the arrows
-				.attr("id", String)
-				.attr("viewBox", "0 -5 10 10")
-				//.attr("refX", 25)
-				.attr("markerWidth", 4)
-				.attr("markerHeight", 4)
-				.attr("orient", "auto")
-				.append("svg:path")
-				.attr('d', 'M 0 -5 L 10 0 L 0 5 z')
-
-			svg.append("svg:defs").selectAll('marker')
-				.data(["start-arrow", "start-arrow-selected"])      // Different link/path types can be defined here
-				.enter().append("svg:marker")    // This section adds in the arrows
-				.attr("id", String)
-				.attr("viewBox", "0 -5 10 10")
-				.attr("refX", 5)
-				.attr("markerWidth", 4)
-				.attr("markerHeight", 4)
-				.attr("orient", "auto")
-				.append("svg:path")
-				.attr('d', 'M 10 -5 L 0 0 L 10 5 z');
-
-			// handles to link and node element groups
-			path = svg.append('svg:g').selectAll('path'),
-			circle = svg.append('svg:g').selectAll('g');
-            
-			force.on('end', function() {
-				//QDR.log.debug("force end called");
-				circle
-					.attr('cx', function(d) {
-						localStorage[d.name] = angular.toJson({x: d.x, y: d.y, fixed: d.fixed});
-						return d.x; });
-			});
-
-			// app starts here
-			restart(false);
-    	    force.start();
-			setTimeout(function () {
-	    	    updateForm(Object.keys(QDRService.topology.nodeInfo())[0], 'router', 0);
-			}, 10)
-
-		}
-
-		function updateForm (key, entity, resultIndex) {
-			var nodeInfo = QDRService.topology.nodeInfo();
-			var onode = nodeInfo[key]
-			if (onode) {
-				var nodeResults = onode['.' + entity].results[resultIndex]
-				var nodeAttributes = onode['.' + entity].attributeNames
-				var attributes = nodeResults.map( function (row, i) {
-					return {
-						attributeName: nodeAttributes[i],
-						attributeValue: row
-					}
-				})
-				// sort by attributeName
-				attributes.sort( function (a, b) { return a.attributeName.localeCompare(b.attributeName) })
-
-				// move the Name first
-				var nameIndex = attributes.findIndex ( function (attr) {
-					return attr.attributeName === 'name'
-				})
-				if (nameIndex >= 0)
-					attributes.splice(0, 0, attributes.splice(nameIndex, 1)[0]);
-				// get the list of ports this router is listening on
-				if (entity === 'router') {
-					var listeners = onode['.listener'].results;
-					var listenerAttributes = onode['.listener'].attributeNames;
-					var normals = listeners.filter ( function (listener) {
-						return QDRService.valFor( listenerAttributes, listener, 'role') === 'normal';
-					})
-					var ports = []
-					normals.forEach (function (normalListener) {
-						ports.push(QDRService.valFor( listenerAttributes, normalListener, 'port'))
-					})
-					// add as 2nd row
-					if (ports.length)
-						attributes.splice(1, 0, {attributeName: 'Listening on', attributeValue: ports, description: 'The port on which this router is listening for connections'});
-				}
-
-				$scope.$broadcast('showEntityForm', {entity: entity, attributes: attributes})
-			}
-			if (!$scope.$$phase) $scope.$apply()
-		}
-
-        function getContainerIndex(_id) {
-            var nodeIndex = 0;
-            var nodeInfo = QDRService.topology.nodeInfo();
-            for (var id in nodeInfo) {
-                var node = nodeInfo[id]['.router'];
-                // there should be only one router entity for each node, so using results[0] should be fine
-                if (QDRService.valFor( node.attributeNames, node.results[0], "id") === _id)
-                    return nodeIndex;
-                if (QDRService.valFor( node.attributeNames, node.results[0], "routerId") === _id)
-                    return nodeIndex;
-                nodeIndex++
-            }
-			// there was no router.id that matched, check deprecated router.routerId
-            nodeIndex = 0;
-            for (var id in nodeInfo) {
-                var node = nodeInfo[id]['.container'];
-				if (node) {
-					if (QDRService.valFor ( node.attributeNames, node.results[0], "containerName") === _id)
-						return nodeIndex;
-				}
-				nodeIndex++
-			}
-            //QDR.log.warn("unable to find containerIndex for " + _id);
-            return -1;
-        }
-
-        function getNodeIndex (_id) {
-            var nodeIndex = 0;
-            var nodeInfo = QDRService.topology.nodeInfo();
-            for (var id in nodeInfo) {
-                if (QDRService.nameFromId(id) == _id) return nodeIndex;
-                nodeIndex++
-            }
-            QDR.log.warn("unable to find nodeIndex for " + _id);
-            return -1;
-        }
-
-        function getLink (_source, _target, dir, cls) {
-            for (var i=0; i < links.length; i++) {
-                var s = links[i].source, t = links[i].target;
-                if (typeof links[i].source == "object") {
-                    s = s.id;
-                    t = t.id;
-				}
-                if (s == _source && t == _target) {
-                    return i;
-                }
-				// same link, just reversed
-                if (s == _target && t == _source) {
-                    return -i;
-				}
-            }
-
-            //QDR.log.debug("creating new link (" + (links.length) + ") between " + nodes[_source].name + " and " + nodes[_target].name);
-            var link = {
-                source: _source,
-                target: _target,
-                left: dir != "out",
-                right: dir == "out",
-                cls: cls
-            };
-            return links.push(link) - 1;
-        }
-
-
-	    function resetMouseVars() {
-	        mousedown_node = null;
-	        mouseup_node = null;
-	        mousedown_link = null;
-	    }
-
-	    // update force layout (called automatically each iteration)
-	    function tick() {
-	        circle.attr('transform', function (d) {
-                var cradius;
-                if (d.nodeType == "inter-router") {
-					cradius = d.left ? radius + 8  : radius;
-                } else {
-					cradius = d.left ? radiusNormal + 18  : radiusNormal;
-                }
-	            d.x = Math.max(d.x, radiusNormal * 2);
-	            d.y = Math.max(d.y, radiusNormal * 2);
-				d.x = Math.max(0, Math.min(width-cradius, d.x))
-				d.y = Math.max(0, Math.min(height-cradius, d.y))
-	            return 'translate(' + d.x + ',' + d.y + ')';
-	        });
-
-	        // draw directed edges with proper padding from node centers
-	        path.attr('d', function (d) {
-				//QDR.log.debug("in tick for d");
-				//console.dump(d);
-                var sourcePadding, targetPadding, r;
-
-                if (d.target.nodeType == "inter-router") {
-					r = radius;
-					//                       right arrow  left line start
-					sourcePadding = d.left ? radius + 8  : radius;
-					//                      left arrow      right line start
-					targetPadding = d.right ? radius + 16 : radius;
-                } else {
-					r = radiusNormal - 18;
-					sourcePadding = d.left ? radiusNormal + 18  : radiusNormal;
-					targetPadding = d.right ? radiusNormal + 16 : radiusNormal;
-                }
-				var dtx = Math.max(targetPadding, Math.min(width-r, d.target.x)),
-				    dty = Math.max(targetPadding, Math.min(height-r, d.target.y)),
-				    dsx = Math.max(sourcePadding, Math.min(width-r, d.source.x)),
-					dsy = Math.max(sourcePadding, Math.min(height-r, d.source.y));
-
-	            var deltaX = dtx - dsx,
-	                deltaY = dty - dsy,
-	                dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
-	                normX = deltaX / dist,
-	                normY = deltaY / dist;
-	                var sourceX = dsx + (sourcePadding * normX),
-	                sourceY = dsy + (sourcePadding * normY),
-	                targetX = dtx - (targetPadding * normX),
-	                targetY = dty - (targetPadding * normY);
-					sourceX = Math.max(0, Math.min(width, sourceX))
-					sourceY = Math.max(0, Math.min(width, sourceY))
-					targetX = Math.max(0, Math.min(width, targetX))
-					targetY = Math.max(0, Math.min(width, targetY))
-
-	            return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
-	        });
-
-	        if (!animate) {
-	            animate = true;
-	            force.stop();
-	        }
-	    }
-
-        // highlight the paths between the selected node and the hovered node
-        function findNextHopNode(from, d) {
-            // d is the node that the mouse is over
-            // from is the selected_node ....
-            if (!from)
-                return null;
-
-            if (from == d)
-                return selected_node;
-
-            //QDR.log.debug("finding nextHop from: " + from.name + " to " + d.name);
-            var sInfo = QDRService.topology.nodeInfo()[from.key];
-
-            if (!sInfo) {
-                QDR.log.warn("unable to find topology node info for " + from.key);
-                return null;
-            }
-
-            // find the hovered name in the selected name's .router.node results
-            if (!sInfo['.router.node'])
-                return null;
-            var aAr = sInfo['.router.node'].attributeNames;
-            var vAr = sInfo['.router.node'].results;
-            for (var hIdx=0; hIdx<vAr.length; ++hIdx) {
-                var addrT = QDRService.valFor(aAr, vAr[hIdx], "id" );
-                if (addrT == d.name) {
-                    //QDR.log.debug("found " + d.name + " at " + hIdx);
-                    var nextHop = QDRService.valFor(aAr, vAr[hIdx], "nextHop");
-                    //QDR.log.debug("nextHop was " + nextHop);
-                    return (nextHop == null) ? nodeFor(addrT) : nodeFor(nextHop);
-                }
-            }
-            return null;
-        }
-
-        function nodeFor(name) {
-            for (var i=0; i<nodes.length; ++i) {
-                if (nodes[i].name == name)
-                    return nodes[i];
-            }
-            return null;
-        }
-
-        function linkFor(source, target) {
-            for (var i=0; i<links.length; ++i) {
-                if ((links[i].source == source) && (links[i].target == target))
-                    return links[i];
-                if ((links[i].source == target) && (links[i].target == source))
-                    return links[i];
-            }
-            // the selected node was a client/broker
-            //QDR.log.debug("failed to find a link between ");
-            //console.dump(source);
-            //QDR.log.debug(" and ");
-            //console.dump(target);
-            return null;
-        }
-
-		function clearPopups() {
-            d3.select("#crosssection").style("display", "none");
-			$('.hastip').empty();
-            d3.select("#multiple_details").style("display", "none")
-            d3.select("#link_details").style("display", "none")
-			d3.select('#node_context_menu').style('display', 'none');
-
-		}
-		function removeCrosssection() {
-			setTimeout(function () {
-				d3.select("[id^=tooltipsy]").remove()
-				$('.hastip').empty();
-			}, 1010);
-			d3.select("#crosssection svg g").transition()
-                .duration(1000)
-				.attr("transform", "translate("+(dblckickPos[0]-140) + "," + (dblckickPos[1]-100) + ") scale(0)")
-                .style("opacity", 0)
-                .each("end", function (d) {
-                    d3.select("#crosssection svg").remove();
-                    d3.select("#crosssection").style("display","none");
-                });
-            d3.select("#multiple_details").transition()
-                .duration(500)
-                .style("opacity", 0)
-                .each("end", function (d) {
-                    d3.select("#multiple_details").style("display", "none")
-                    stopUpdateConnectionsGrid();
-                })
-			hideLinkDetails();
-		}
-		function hideLinkDetails() {
-            d3.select("#link_details").transition()
-                .duration(500)
-                .style("opacity", 0)
-                .each("end", function (d) {
-                    d3.select("#link_details").style("display", "none")
-                })
-		}
-
-	    // takes the nodes and links array of objects and adds svg elements for everything that hasn't already
-	    // been added
-	    function restart(start) {
-	        circle.call(force.drag);
-
-	        // path (link) group
-	        path = path.data(links);
-
-			// update existing links
-  			path.classed('selected', function(d) { return d === selected_link; })
-  			    .classed('highlighted', function(d) { return d.highlighted; } )
-  			    .classed('temp', function(d) { return d.cls == 'temp'; } )
-                .attr('marker-start', function(d) {
-                    var sel = d===selected_link ? '-selected' : '';
-                    return d.left ? 'url('+urlPrefix+'#start-arrow' + sel + ')' : ''; })
-                .attr('marker-end', function(d) {
-                    var sel = d===selected_link ? '-selected' : '';
-                    return d.right ? 'url('+urlPrefix+'#end-arrow' + sel +')' : ''; })
-
-
-			// add new links. if links[] is longer than the existing paths, add a new path for each new element
-			path.enter().append('svg:path')
-				.attr('class', 'link')
-                .attr('marker-start', function(d) {
-                        var sel = d===selected_link ? '-selected' : '';
-						return d.left ? 'url('+urlPrefix+'#start-arrow' + sel + ')' : ''; })
-                .attr('marker-end', function(d) {
-					var sel = d===selected_link ? '-selected' : '';
-                    return d.right ? 'url('+urlPrefix+'#end-arrow' + sel + ')' : ''; })
-  			    .classed('temp', function(d) { return d.cls == 'temp'; } )
-	            .on('mouseover', function (d) {
-				  if($scope.addingNode.step > 0) {
-				    if (d.cls == 'temp') {
-				        d3.select(this).classed('over', true);
-				    }
-				    return;
-				  }
-			        //QDR.log.debug("showing connections form");
-					var resultIndex = 0; // the connection to use
-                    var left = d.left ? d.target : d.source;
-					// right is the node that the arrow points to, left is the other node
-					var right = d.left ? d.source : d.target;
-					var onode = QDRService.topology.nodeInfo()[left.key];
-					// loop through all the connections for left, and find the one for right
-					if (!onode || !onode['.connection'])
-						return;
-                    // update the info dialog for the link the mouse is over
-                    if (!selected_node && !selected_link) {
-                        for (resultIndex=0; resultIndex < onode['.connection'].results.length; ++resultIndex) {
-                            var conn = onode['.connection'].results[resultIndex];
-                            /// find the connection whose container is the right's name
-                            var name = QDRService.valFor(onode['.connection'].attributeNames, conn, "container");
-                            if (name == right.routerId) {
-                                break;
-                            }
-                        }
-                        // did not find connection. this is a connection to a non-interrouter node
-                        if (resultIndex === onode['.connection'].results.length) {
-                            // use the non-interrouter node's connection info
-                            left = d.target;
-                            resultIndex = left.resultIndex;
-                        }
-						if (resultIndex)
-                            updateForm(left.key, 'connection', resultIndex);
-                    }
-
-					mousedown_link = d;
-					selected_link = mousedown_link;
-					restart();
-				})
-	            .on('mouseout', function (d) {
-				  if($scope.addingNode.step > 0) {
-				    if (d.cls == 'temp') {
-				        d3.select(this).classed('over', false);
-				    }
-				    return;
-				  }
-			        //QDR.log.debug("showing connections form");
-					selected_link = null;
-					restart();
-				})
-	            .on("contextmenu", function(d) {
-	                $(document).click();
-                    d3.event.preventDefault();
-	                if (d.cls !== "temp")
-	                    return;
-
-					mousedown_link = d;
-                    d3.select('#link_context_menu')
-                      .style('left', (mouseX + $(document).scrollLeft()) + "px")
-                      .style('top', (mouseY + $(document).scrollTop()) + "px")
-                      .style('display', 'block');
-                })
-                .on("click", function (d) {
-                    dblckickPos = d3.mouse(this);
-                    QDR.log.debug("dblckickPos is [" + dblckickPos[0] + ", " + dblckickPos[1] + "]")
-                    d3.event.stopPropagation();
-                    clearPopups();
-                    var diameter = 400;
-                    var format = d3.format(",d");
-                    var pack = d3.layout.pack()
-                        .size([diameter - 4, diameter - 4])
-                        .padding(-10)
-                        .value(function(d) { return d.size; });
-
-                    d3.select("#crosssection svg").remove();
-                    var svg = d3.select("#crosssection").append("svg")
-                        .attr("width", diameter)
-                        .attr("height", diameter)
-                    var svgg = svg.append("g")
-                        .attr("transform", "translate(2,2)");
-
-					var root = {
-						name: "links between " + d.source.name + " and " + d.target.name,
-						children: []
-					}
-					var nodeInfo = QDRService.topology.nodeInfo();
-					var connections = nodeInfo[d.source.key]['.connection'];
-					var containerIndex = connections.attributeNames.indexOf('container');
-					connections.results.some ( function (connection) {
-                        if (connection[containerIndex] == d.target.routerId) {
-                            root.attributeNames = connections.attributeNames;
-                            root.obj = connection;
-                            root.desc = "Connection";
-                            return true;    // stop looping after 1 match
-                        }
-                        return false;
-                    })
-
-					// find router.links where link.remoteContainer is d.source.name
-					var links = nodeInfo[d.source.key]['.router.link'];
-					var identityIndex = connections.attributeNames.indexOf('identity')
-					var roleIndex = connections.attributeNames.indexOf('role')
-					var connectionIdIndex = links.attributeNames.indexOf('connectionId');
-					var linkTypeIndex = links.attributeNames.indexOf('linkType');
-					var nameIndex = links.attributeNames.indexOf('name');
-					var linkDirIndex = links.attributeNames.indexOf('linkDir');
-
-					if (roleIndex < 0 || identityIndex < 0 || connectionIdIndex < 0
-						|| linkTypeIndex < 0 || nameIndex < 0 || linkDirIndex < 0)
-						return;
-					links.results.forEach ( function (link) {
-						if (root.obj && link[connectionIdIndex] == root.obj[identityIndex] && link[linkTypeIndex] == root.obj[roleIndex])
-							root.children.push (
-								{ name: "(" + link[linkDirIndex] + ") " + link[nameIndex],
-								size: 100,
-								obj: link,
-	                            desc: "Link",
-								attributeNames: links.attributeNames
-							})
-					})
-					if (root.children.length == 0)
-						return;
-	                var node = svgg.datum(root).selectAll(".node")
-	                      .data(pack.nodes)
-	                    .enter().append("g")
-	                      .attr("class", function(d) { return d.children ? "parent node hastip" : "leaf node hastip"; })
-	                      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" + (!d.children ? "scale(0.9)" : ""); })
-	                      .attr("title", function (d) {
-	                          var title = "<h4>" + d.desc + "</h4><table class='tiptable'><tbody>";
-	                          if (d.attributeNames)
-		                            d.attributeNames.forEach( function (n, i) {
-		                                title += "<tr><td>" + n + "</td><td>";
-		                                title += d.obj[i] != null ? d.obj[i] : '';
-		                                title += '</td></tr>';
-		                            })
-		                      title += "</tbody></table>"
-	                          return title
-	                      })
-
-	                node.append("circle")
-	                      .attr("r", function(d) { return d.r; });
-
-//	                node.filter(function(d) { return !d.children; }).append("text")
-	                node.append("text")
-	                      .attr("dy", function (d) { return d.children ? "-10em" : ".3em"})
-	                      .style("text-anchor", "middle")
-	                      .text(function(d) {
-	                          return d.name.substring(0, d.r / 3);
-	                      });
-
-					$('.hastip').tooltipsy({ alignTo: 'cursor'});
-					svgg.attr("transform", "translate("+(dblckickPos[0]-140) + "," + (dblckickPos[1]-100) + ") scale(0.01)")
-	                d3.select("#crosssection").style("display","block");
-
-					svgg.transition().attr("transform", "translate(2,2) scale(1)")
-                })
-
-	        // remove old links
-	        path.exit().remove();
-
-
-	        // circle (node) group
-	        // nodes are known by id
-	        circle = circle.data(nodes, function (d) {
-	            return d.id;
-	        });
-
-	        // update existing nodes visual states
-	        circle.selectAll('circle')
-	            .classed('selected', function (d) { return (d === selected_node) })
-	            .classed('fixed', function (d) { return (d.fixed & 0b1) })
-
-			// add new circle nodes. if nodes[] is longer than the existing paths, add a new path for each new element
-	        var g = circle.enter().append('svg:g')
-  			    .classed('multiple', function(d) { return (d.normals && d.normals.length > 1)  } )
-
-			var appendCircle = function (g) {
-				// add new circles and set their attr/class/behavior
-		        return g.append('svg:circle')
-		            .attr('class', 'node')
-		            .attr('r', function (d) { return radii[d.nodeType] } )
-		            .classed('fixed', function (d) {return d.fixed})
-	                .classed('temp', function(d) { return QDRService.nameFromId(d.key) == '__internal__'; } )
-	                .classed('normal', function(d) { return d.nodeType == 'normal' } )
-	                .classed('inter-router', function(d) { return d.nodeType == 'inter-router' } )
-	                .classed('on-demand', function(d) { return d.nodeType == 'on-demand' } )
-	                .classed('console', function(d) { return QDRService.isConsole(d) } )
-	                .classed('artemis', function(d) { return QDRService.isArtemis(d) } )
-	                .classed('qpid-cpp', function(d) { return QDRService.isQpid(d) } )
-	                .classed('client', function(d) { return d.nodeType === 'normal' && !d.properties.console_identifier } )
-			}
-			appendCircle(g).on('mouseover', function (d) {
-	                if ($scope.addingNode.step > 0) {
-		                d3.select(this).attr('transform', 'scale(1.1)');
-						return;
-	                }
-					if (!selected_node) {
-                        if (d.nodeType === 'inter-router') {
-                            //QDR.log.debug("showing general form");
-                            updateForm(d.key, 'router', 0);
-                        } else if (d.nodeType === 'normal' || d.nodeType === 'on-demand') {
-                            //QDR.log.debug("showing connections form");
-                            updateForm(d.key, 'connection', d.resultIndex);
-                        }
-					}
-
-	                if (d === mousedown_node) 
-	                    return;
-	                //if (d === selected_node)
-	                //    return;
-	                // enlarge target node
-	                d3.select(this).attr('transform', 'scale(1.1)');
-                    // highlight the next-hop route from the selected node to this node
-                    mousedown_node = null;
-
-	                if (!selected_node) {
-	                    return;
-	                }
-                    setTimeout(nextHop, 1, selected_node, d);
-	            })
-	            .on('mouseout', function (d) {
-	                // unenlarge target node
-	                d3.select(this).attr('transform', '');
-                    for (var i=0; i<links.length; ++i) {
-                        links[i]['highlighted'] = false;
-                    }
-                    restart();
-	            })
-	            .on('mousedown', function (d) {
-	                if (d3.event.button !== 0) {   // ignore all but left button
-	                    return;
-	                }
-	                mousedown_node = d;
-	                // mouse position relative to svg
-	                initial_mouse_down_position = d3.mouse(this.parentElement.parentElement.parentElement).slice();
-	            })
-	            .on('mouseup', function (d) {
-	                if (!mousedown_node)
-	                    return;
-
-                    selected_link = null;
-	                // unenlarge target node
-	                d3.select(this).attr('transform', '');
-
-	                // check for drag
-	                mouseup_node = d;
-	                var mySvg = this.parentElement.parentElement.parentElement;
-                    // if we dragged the node, make it fixed
-                    var cur_mouse = d3.mouse(mySvg);
-                    if (cur_mouse[0] != initial_mouse_down_position[0] ||
-                        cur_mouse[1] != initial_mouse_down_position[1]) {
-						console.log("mouse pos changed. making this node fixed")
-						d3.select(this).classed("fixed", d.fixed = true);
-                        resetMouseVars();
-                        return;
-	                }
-
-					// we didn't drag, we just clicked on the node
-	                if ($scope.addingNode.step > 0) {
-                        if (d.nodeType !== 'inter-router')
-                            return;
-						if (QDRService.nameFromId(d.key) == '__internal__')
-							return;
-
-						// add a link from the clicked node to the new node
-						getLink(d.id, nodes.length-1, "in", "temp");
-						$scope.addingNode.hasLink = true;
-						if (!$scope.$$phase) $scope.$apply()
-						// add new elements to the svg
-						force.links(links).start();
-						restart();
-						return;
-
-	                }
-
-					// if this node was selected, unselect it
-                    if (mousedown_node === selected_node) {
-                        selected_node = null;
-                    }
-                    else {
-                        if (d.nodeType !== 'normal' && d.nodeType !== 'on-demand')
-                            selected_node = mousedown_node;
-                    }
-                    for (var i=0; i<links.length; ++i) {
-                        links[i]['highlighted'] = false;
-                    }
-	                mousedown_node = null;
-					if (!$scope.$$phase) $scope.$apply()
-                    restart(false);
-
-	            })
-	            .on("dblclick", function (d) {
-	                if (d.fixed) {
-						d3.select(this).classed("fixed", d.fixed = false);
-						force.start();  // let the nodes move to a new position
-	                }
-	                if (QDRService.nameFromId(d.key) == '__internal__') {
-	                    editNode();
-						if (!$scope.$$phase) $scope.$apply()
-	                }
-	            })
-	            .on("contextmenu", function(d) {
-	                $(document).click();
-                    d3.event.preventDefault();
-	                $scope.contextNode = d;
-					if (!$scope.$$phase) $scope.$apply()     // we just changed a scope valiable during an async event
-                    d3.select('#node_context_menu')
-                      .style('left', (mouseX + $(document).scrollLeft()) + "px")
-                      .style('top', (mouseY + $(document).scrollTop()) + "px")
-                      .style('display', 'block');
-
-                })
-                .on("click", function (d) {
-                    // clicked on a circle
-                    clearPopups();
-					if (!d.normals) {
-						// circle was a router or a broker
-			            if ( QDRService.isArtemis(d) && Core.ConnectionName === 'Artemis' ) {
-							$location.path('/jmx/attributes?tab=artemis&con=Artemis')
-						}
-						return;
-					}
-                    clickPos = d3.mouse(this);
-                    d3.event.stopPropagation();
-					startUpdateConnectionsGrid(d);
-				})
-
-			var appendContent = function (g) {
-		        // show node IDs
-		        g.append('svg:text')
-		            .attr('x', 0)
-		            .attr('y', function (d) {
-		                var y = 6;
-		                if (QDRService.isArtemis(d))
-		                    y = 8;
-		                else if (QDRService.isQpid(d))
-		                    y = 9;
-		                else if (d.nodeType === 'inter-router')
-		                    y = 4;
-		                return y;})
-		            .attr('class', 'id')
-   	                .classed('console', function(d) { return QDRService.isConsole(d) } )
-	                .classed('normal', function(d) { return d.nodeType === 'normal' } )
-	                .classed('on-demand', function(d) { return d.nodeType === 'on-demand' } )
-	                .classed('artemis', function(d) { return QDRService.isArtemis(d) } )
-	                .classed('qpid-cpp', function(d) { return QDRService.isQpid(d) } )
-		            .text(function (d) {
-		                if (QDRService.isConsole(d)) {
-		                    return '\uf108'; // icon-desktop for this console
-		                }
-						if (QDRService.isArtemis(d)) {
-							return '\ue900'
-						}
-		                if (QDRService.isQpid(d)) {
-		                    return '\ue901';
-		                }
-						if (d.nodeType === 'normal')
-							return '\uf109'; // icon-laptop for clients
-		                return d.name.length>7 ? d.name.substr(0,6)+'...' : d.name;
-		        });
-			}
-
-			appendContent(g)
-
-			var appendTitle = function (g) {
-		        g.append("svg:title").text(function (d) {
-	                var x = '';
-	                if (d.normals && d.normals.length > 1)
-	                    x = " x " + d.normals.length;
-		            if (QDRService.isConsole(d)) {
-	                    return 'Dispatch console' + x
-	                }
-		            if (d.properties.product == 'qpid-cpp') {
-	                    return 'Broker - qpid-cpp' + x
-	                }
-		            if ( QDRService.isArtemis(d) ) {
-	                    return 'Broker - Artemis' + x
-	                }
-		            return d.nodeType == 'normal' ? 'client' + x : (d.nodeType == 'on-demand' ? 'broker' : 'Router ' + d.name)
-		        })
-			}
-			appendTitle(g);
-
-	        // remove old nodes
-	        circle.exit().remove();
-
-			// add subcircles
-			svg.selectAll('.subcircle').remove();
-			var multiples = svg.selectAll('.multiple')
-			multiples.each( function (d) {
-				d.normals.forEach( function (n, i) {
-					if (i<d.normals.length-1 && i<3) // only show a few shadow circles
-						this.insert('svg:circle', ":first-child")
-						.attr('class', 'subcircle node')
-						.attr('r', 15 - i)
-						.attr('transform', "translate("+ 4 * (i+1) +", 0)")
-				}, d3.select(this))
-			})
-
-			// dynamically create the legend based on which node types are present
-			var legendNodes = [];
-			legendNodes.push(aNode("Router", "", "inter-router", undefined, 0, 0, 0, 0, false, {}))
-
-			if (!svg.selectAll('circle.console').empty()) {
-				legendNodes.push(aNode("Dispatch console", "", "normal", undefined, 1, 0, 0, 0, false, {console_identifier: 'Dispatch console'}))
-			}
-			if (!svg.selectAll('circle.client').empty()) {
-				legendNodes.push(aNode("Client", "", "normal", undefined, 2, 0, 0, 0, false, {}))
-			}
-			if (!svg.selectAll('circle.qpid-cpp').empty()) {
-				legendNodes.push(aNode("Qpid cpp broker", "", "on-demand", undefined, 3, 0, 0, 0, false, {product: 'qpid-cpp'}))
-			}
-			if (!svg.selectAll('circle.artemis').empty()) {
-				legendNodes.push(aNode("Artemis broker", "", "on-demand", undefined, 4, 0, 0, 0, false, {}))
-			}
-		    lsvg = lsvg.data(legendNodes, function (d) {
-	            return d.id;
-            });
-	        var lg = lsvg.enter().append('svg:g')
-				.attr('transform', function (d, i) {
-					// 45px between lines and add 10px space after 1st line
-					return "translate(0, "+(45*i+(i>0?10:0))+")"
-				})
-
-			appendCircle(lg)
-			appendContent(lg)
-			appendTitle(lg)
-			lg.append('svg:text')
-				.attr('x', 35)
-				.attr('y', 6)
-				.attr('class', "label")
-				.text(function (d) {return d.key })
-			lsvg.exit().remove();
-			var svgEl = document.getElementById('svglegend')
-			if (svgEl) {
-				var bb;
-				// firefox can throw an exception on getBBox on an svg element
-				try {
-					bb = svgEl.getBBox();
-				} catch (e) {
-					bb = {y: 0, height: 200, x: 0, width: 200}
-				}
-				svgEl.style.height = (bb.y + bb.height) + 'px';
-				svgEl.style.width = (bb.x + bb.width) + 'px';
-			}
-
-	        if (!mousedown_node || !selected_node)
-	            return;
-
-            if (!start)
-                return;
-	        // set the graph in motion
-	        //QDR.log.debug("mousedown_node is " + mousedown_node);
-	        force.start();
-
-	    }
-
-		var startUpdateConnectionsGrid = function (d) {
-			var extendConnections = function () {
-				$scope.multiData = []
-				var normals = d.normals;
-				// find updated normals for d
-		        d3.selectAll('.normal')
-                    .each(function(newd) {
-                        if (newd.id == d.id && newd.name == d.name) {
-                            normals = newd.normals;
-                        }
-                    });
-				if (normals) {
-					normals.forEach( function (n) {
-						var nodeInfo = QDRService.topology.nodeInfo();
-						var links = nodeInfo[n.key]['.router.link'];
-						var linkTypeIndex = links.attributeNames.indexOf('linkType');
-						var connectionIdIndex = links.attributeNames.indexOf('connectionId');
-						n.linkData = [];
-						links.results.forEach( function (link) {
-							if (link[linkTypeIndex] === 'endpoint' && link[connectionIdIndex] === n.connectionId) {
-								var l = {};
-								l.owningAddr = QDRService.valFor(links.attributeNames, link, 'owningAddr');
-								l.dir = QDRService.valFor(links.attributeNames, link, 'linkDir');
-								if (l.owningAddr && l.owningAddr.length > 2)
-									if (l.owningAddr[0] === 'M')
-										l.owningAddr = l.owningAddr.substr(2)
-									else
-										l.owningAddr = l.owningAddr.substr(1)
-
-								l.deliveryCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'deliveryCount'));
-								l.undeliveredCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'undeliveredCount'));
-								l.unsettledCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'unsettledCount'));
-								l.adminStatus = QDRService.valFor(links.attributeNames, link, 'adminStatus');
-								l.identity = QDRService.valFor(links.attributeNames, link, 'identity')
-								l.connectionId = QDRService.valFor(links.attributeNames, link, 'connectionId')
-
-								// TODO: remove this fake quiescing/reviving logic when the routers do the work
-								initConnState(n.connectionId)
-								if ($scope.quiesceState[n.connectionId].linkStates[l.identity])
-									l.adminStatus = $scope.quiesceState[n.connectionId].linkStates[l.identity];
-								if ($scope.quiesceState[n.connectionId].state == 'quiescing') {
-									if (l.adminStatus === 'enabled') {
-										// 25% chance of switching
-										var chance = Math.floor(Math.random() * 2);
-										if (chance == 1) {
-											l.adminStatus = 'disabled';
-											$scope.quiesceState[n.connectionId].linkStates[l.identity] = 'disabled';
-										}
-									}
-								}
-								if ($scope.quiesceState[n.connectionId].state == 'reviving') {
-									if (l.adminStatus === 'disabled') {
-										// 25% chance of switching
-										var chance = Math.floor(Math.random() * 2);
-										if (chance == 1) {
-											l.adminStatus = 'enabled';
-											$scope.quiesceState[n.connectionId].linkStates[l.identity] = 'enabled';
-										}
-									}
-								}
-								QDR.log.debug("pushing link state for " + l.owningAddr + " status: "+ l.adminStatus)
-
-
-								n.linkData.push(l)
-							}
-						})
-						$scope.multiData.push(n)
-						if (n.connectionId == $scope.connectionId)
-							$scope.linkData = n.linkData;
-						initConnState(n.connectionId)
-						$scope.multiDetails.updateState(n)
-					})
-				}
-				$scope.$apply();
-
-	            d3.select('#multiple_details')
-	                .style({
-	                    height: (normals.length + 1) * 30 + "px",
-	                    'overflow-y': normals.length > 10 ? 'scroll' : 'hidden'
-					})
-			}
-
-	        QDRService.addUpdatedAction("normalsStats", extendConnections)
-			extendConnections();
-			clearPopups();
-			var display = 'block'
-			var left = mouseX + $(document).scrollLeft()
-			if (d.normals.length === 1) {
-				display = 'none'
-				left = left - 30;
-				mouseY = mouseY - 20
-			}
-            d3.select('#multiple_details')
-                .style({
-                    display: display,
-                    opacity: 1,
-                    left: (mouseX + $(document).scrollLeft()) + "px",
-                    top:  (mouseY + $(document).scrollTop()) + "px"})
-            if (d.normals.length === 1) {
-				// simulate a click on the connection to popup the link details
-                $scope.multiDetails.showLinksList( {entity: d} )
-            }
-		}
-		var stopUpdateConnectionsGrid = function () {
-	        QDRService.delUpdatedAction("normalsStats");
-		}
-
-		var initConnState = function (id) {
-			if (!angular.isDefined($scope.quiesceState[id])) {
-				$scope.quiesceState[id] = {
-					state:          'enabled',
-					buttonText:     'Quiesce',
-					buttonDisabled: false,
-					linkStates:     {}
-				}
-			}
-		}
-
-        function nextHop(thisNode, d) {
-            if ((thisNode) && (thisNode != d)) {
-                var target = findNextHopNode(thisNode, d);
-                //QDR.log.debug("highlight link from node ");
-                 //console.dump(nodeFor(selected_node.name));
-                 //console.dump(target);
-                if (target) {
-                    var hlLink = linkFor(nodeFor(thisNode.name), target);
-                    //QDR.log.debug("need to highlight");
-                    //console.dump(hlLink);
-                    if (hlLink)
-                        hlLink['highlighted'] = true;
-                    else
-                        target = null;
-                }
-                setTimeout(nextHop, 1, target, d);
-            }
-            restart();
-        }
-
-
-	    function mousedown() {
-	        // prevent I-bar on drag
-	        //d3.event.preventDefault();
-
-	        // because :active only works in WebKit?
-	        svg.classed('active', true);
-	    }
-
-        QDRService.addUpdatedAction("topology", function() {
-            //QDR.log.debug("Topology controller was notified that the model was updated");
-            if (hasChanged()) {
-                QDR.log.info("svg graph changed")
-                saveChanged();
-                // TODO: update graph nodes instead of rebuilding entire graph
-                d3.select("#SVG_ID").remove();
-                d3.select("#svg_legend svg").remove();
-                animate = true;
-                initForceGraph();
-                //if ($location.path().startsWith("/topology"))
-                //    Core.notification('info', "Qpid dispatch router topology changed");
-
-            } else {
-                //QDR.log.debug("no changes")
-            }
-        });
-
-		function hasChanged () {
-			// Don't update the underlying topology diagram if we are adding a new node.
-			// Once adding is completed, the topology will update automatically if it has changed
-			if ($scope.addingNode.step > 0)
-				return false;
-			var nodeInfo = QDRService.topology.nodeInfo();
-			if (Object.keys(nodeInfo).length != Object.keys(savedKeys).length)
-				return true;
-			for (var key in nodeInfo) {
-                // if this node isn't in the saved node list
-                if (!savedKeys.hasOwnProperty(key))
-                    return true;
-                // if the number of connections for this node chaanged
-                if (nodeInfo[key]['.connection'].results.length != savedKeys[key]) {
-					/*
-					QDR.log.debug("number of connections changed for " + key);
-					QDR.log.debug("QDRService.topology._nodeInfo[key]['.connection'].results.length");
-					console.dump(QDRService.topology._nodeInfo[key]['.connection'].results.length);
-					QDR.log.debug("savedKeys[key]");
-					console.dump(savedKeys[key]);
-					*/
-                    return true;
-                }
-			}
-			return false;
-		};
-		function saveChanged () {
-            savedKeys = {};
-            var nodeInfo = QDRService.topology.nodeInfo();
-            // save the number of connections per node
-		    for (var key in nodeInfo) {
-		        savedKeys[key] = nodeInfo[key]['.connection'].results.length;
-		    }
-			//QDR.log.debug("saving current keys");
-			console.dump(savedKeys);
-		};
-		// we are about to leave the page, save the node positions
-		$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
-			//QDR.log.debug("locationChangeStart");
-			nodes.forEach( function (d) {
-	           localStorage[d.name] = angular.toJson({x: d.x, y: d.y, fixed: d.fixed});
-			});
-            $scope.addingNode.step = 0;
-
-		});
-		// When the DOM element is removed from the page,
-        // AngularJS will trigger the $destroy event on
-        // the scope
-        $scope.$on("$destroy", function( event ) {
-   			//QDR.log.debug("scope on destroy");
-            QDRService.stopUpdating();
-            QDRService.delUpdatedAction("topology");
-			d3.select("#SVG_ID").remove();
-        });
-
-		initForceGraph();
-		saveChanged();
-        QDRService.startUpdating();
-
-	    function doAddDialog(NewRouterName) {
-		    var d = $dialog.dialog({
-				dialogClass: "modal dlg-large",
-				backdrop: true,
-				keyboard: true,
-				backdropClick: true,
-		        controller: 'QDR.NodeDialogController',
-		        templateUrl: 'node-config-template.html',
-		        resolve: {
-		            newname: function () {
-		                return NewRouterName;
-		            }
-		        }
-		    });
-		    d.open().then(function (result) {
-				if (result)
-					doDownloadDialog(result);
-		    });
-        };
-
-	    function doDownloadDialog(result) {
-		    d = $dialog.dialog({
-				backdrop: true,
-				keyboard: true,
-				backdropClick: true,
-				controller: 'QDR.DownloadDialogController',
-		        templateUrl: 'download-dialog-template.html',
-		        resolve: {
-		            results: function () {
-		                return result;
-		            }
-		        }
-		    });
-		    d.open().then(function (result) {
-		    //QDR.log.debug("download dialog done")
-		    })
-            if (!$scope.$$phase) $scope.$apply()
-        };
-  }]);
-
-  QDR.module.controller("QDR.NodeDialogController", function($scope, QDRService, dialog, newname) {
-   		var schema = QDRService.schema;
-   		var myEntities = ['router', 'log', 'listener' ];
-   		var typeMap = {integer: 'number', string: 'text', path: 'text', boolean: 'boolean'};
-		var newLinks = $('path.temp').toArray();    // jquery array of new links for the added router
-		var nodeInfo = QDRService.topology.nodeInfo();
-		var separatedEntities = []; // additional entities required if a link is reversed
-		var myPort = 0, myAddr = '0.0.0.0'; // port and address for new router
-   		$scope.entities = [];
-
-		// find max port number that is used in all the listeners
-		var getMaxPort = function (nodeInfo) {
-			var maxPort = 5674;
-			for (var key in nodeInfo) {
-				var node = nodeInfo[key];
-				var listeners = node['.listener'];
-				var attrs = listeners.attributeNames;
-				for (var i=0; i<listeners.results.length; ++i) {
-					var res = listeners.results[i];
-					var port = QDRService.valFor(attrs, res, 'port');
-					if (parseInt(port, 10) > maxPort)
-						maxPort = parseInt(port, 10);
-				}
-			}
-			return maxPort;
-		}
-		var maxPort = getMaxPort(nodeInfo);
-
-		// construct an object that contains all the info needed for a single tab's fields
-		var entity = function (actualName, tabName, humanName, ent, icon, link) {
-			var nameIndex = -1; // the index into attributes that the name field was placed
-			var index = 0;
-			var info = {
-			    actualName: actualName,
-				tabName:    tabName,
-				humanName:  humanName,
-				description:ent.description,
-				icon:       angular.isDefined(icon) ? icon : '',
-				references: ent.references,
-				link:       link,
-
-   		        attributes: $.map(ent.attributes, function (value, key) {
-					// skip identity and depricated fields
-   		            if (key == 'identity' || value.description.startsWith('Deprecated'))
-   		                return null;
-					var val = value['default'];
-					if (key == 'name')
-						nameIndex = index;
-					index++;
-					return {    name:       key,
-								humanName:  QDRService.humanify(key),
-                                description:value.description,
-                                type:       typeMap[value.type],
-                                rawtype:    value.type,
-                                input:      typeof value.type == 'string' ? value.type == 'boolean' ? 'boolean' : 'input'
-                                                                          : 'select',
-                                selected:   val ? val : undefined,
-                                'default':  value['default'],
-                                value:      val,
-                                required:   value.required,
-                                unique:     value.unique
-                    };
-                })
-			}
-			// move the 'name' attribute to the 1st position
-			if (nameIndex > -1) {
-				var tmp = info.attributes[0];
-				info.attributes[0] = info.attributes[nameIndex];
-				info.attributes[nameIndex] = tmp;
-			}
-			return info;
-		}
-
-		// remove the annotation fields
-		var stripAnnotations = function (entityName, ent, annotations) {
-			if (ent.references) {
-				var newEnt = {attributes: {}};
-				ent.references.forEach( function (annoKey) {
-					if (!annotations[annoKey])
-						annotations[annoKey] = {};
-					annotations[annoKey][entityName] = true;    // create the key/consolidate duplicates
-					var keys = Object.keys(schema.annotations[annoKey].attributes);
-					for (var attrib in ent.attributes) {
-						if (keys.indexOf(attrib) == -1) {
-							newEnt.attributes[attrib] = ent.attributes[attrib];
-						}
-					}
-					// add a field for the reference name
-					newEnt.attributes[annoKey] = {type: 'string',
-							description: 'Name of the ' + annoKey + ' section.',
-							'default': annoKey, required: true};
-				})
-				newEnt.references = ent.references;
-				newEnt.description = ent.description;
-				return newEnt;
-			}
-			return ent;
-		}
-
-		var annotations = {};
-   		myEntities.forEach(function (entityName) {
-   		    var ent = schema.entityTypes[entityName];
-   		    var hName = QDRService.humanify(entityName);
-   		    if (entityName == 'listener')
-   		        hName = "Listener for clients";
-   		    var noAnnotations = stripAnnotations(entityName, ent, annotations);
-			var ediv = entity(entityName, entityName, hName, noAnnotations, undefined);
-			if (ediv.actualName == 'router') {
-				ediv.attributes.filter(function (attr) { return attr.name == 'name'})[0].value = newname;
-				// if we have any new links (connectors), then the router's mode should be interior
-				if (newLinks.length) {
-					var roleAttr = ediv.attributes.filter(function (attr) { return attr.name == 'mode'})[0];
-					roleAttr.value = roleAttr.selected = "interior";
-				}
-			}
-			if (ediv.actualName == 'container') {
-				ediv.attributes.filter(function (attr) { return attr.name == 'containerName'})[0].value = newname + "-container";
-			}
-			if (ediv.actualName == 'listener') {
-				// find max port number that is used in all the listeners
-				ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = ++maxPort;
-			}
-			// special case for required log.module since it doesn't have a default
-			if (ediv.actualName == 'log') {
-				var moduleAttr = ediv.attributes.filter(function (attr) { return attr.name == 'module'})[0];
-				moduleAttr.value = moduleAttr.selected = "DEFAULT";
-			}
-			$scope.entities.push( ediv );
-   		})
-
-		// add a tab for each annotation that was found
-		var annotationEnts = [];
-		for (var key in annotations) {
-			ent = angular.copy(schema.annotations[key]);
-			ent.attributes.name = {type: "string", unique: true, description: "Unique name that is used to refer to this set of attributes."}
-			var ediv = entity(key, key+'tab', QDRService.humanify(key), ent, undefined);
-			ediv.attributes.filter(function (attr) { return attr.name == 'name'})[0].value = key;
-			$scope.entities.push( ediv );
-			annotationEnts.push( ediv );
-		}
-
-		// add an additional listener tab if any links are reversed
-		ent = schema.entityTypes['listener'];
-		newLinks.some(function (link) {
-			if (link.__data__.right) {
-	   		    var noAnnotations = stripAnnotations('listener', ent, annotations);
-				var ediv = entity("listener", "listener0", "Listener (internal)", noAnnotations, undefined);
-				ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = ++maxPort;
-				// connectors from other routers need to connect to this addr:port
-				myPort = maxPort;
-				myAddr = ediv.attributes.filter(function (attr) { return attr.name == 'host'})[0].value
-
-				// override the role. 'normal' is the default, but we want inter-router
-				ediv.attributes.filter(function( attr ) { return attr.name == 'role'})[0].selected = 'inter-router';
-				separatedEntities.push( ediv );
-				return true; // stop looping
-			}
-			return false;   // continue looping
-		})
-
-		// Add connector tabs for each new link on the topology graph
-		ent = schema.entityTypes['connector'];
-		newLinks.forEach(function (link, i) {
-   		    var noAnnotations = stripAnnotations('connector', ent, annotations);
-			var ediv = entity('connector', 'connector' + i, " " + link.__data__.source.name, noAnnotations, link.__data__.right, link)
-
-			// override the connector role. 'normal' is the default, but we want inter-router
-			ediv.attributes.filter(function( attr ) { return attr.name == 'role'})[0].selected = 'inter-router';
-
-			// find the addr:port of the inter-router listener to use
-			var listener = nodeInfo[link.__data__.source.key]['.listener'];
-			var attrs = listener.attributeNames;
-			for (var i=0; i<listener.results.length; ++i) {
-				var res = listener.results[i];
-				var role = QDRService.valFor(attrs, res, 'role');
-				if (role == 'inter-router') {
-					ediv.attributes.filter(function( attr ) { return attr.name == 'host'})[0].value =
-						QDRService.valFor(attrs, res, 'host')
-					ediv.attributes.filter(function( attr ) { return attr.name == 'port'})[0].value =
-						QDRService.valFor(attrs, res, 'port')
-					break;
-				}
-			}
-			if (link.__data__.right) {
-				// connectors from other nodes need to connect to the new router's listener addr:port
-   				ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = myPort;
-   				ediv.attributes.filter(function (attr) { return attr.name == 'host'})[0].value = myAddr;
-
-				separatedEntities.push(ediv)
-			}
-			else
-				$scope.entities.push( ediv );
-		})
-		Array.prototype.push.apply($scope.entities, separatedEntities);
-
-		// update the description on all the annotation tabs
-		annotationEnts.forEach ( function (ent) {
-			var shared = Object.keys(annotations[ent.actualName]);
-			ent.description += " These fields are shared by " + shared.join(" and ") + ".";
-
-		})
-
-		$scope.testPattern = function (attr) {
-			if (attr.rawtype == 'path')
-				return /^(\/)?([^/\0]+(\/)?)+$/;
-				//return /^(.*\/)([^/]*)$/;
-			return /(.*?)/;
-		}
-
-		$scope.attributeDescription = '';
-		$scope.attributeType = '';
-		$scope.attributeRequired = '';
-		$scope.attributeUnique = '';
-		$scope.active = 'router'
-		$scope.fieldsetDivs = "/fieldsetDivs.html"
-		$scope.setActive = function (tabName) {
-			$scope.active = tabName
-		}
-		$scope.isActive = function (tabName) {
-			return $scope.active === tabName
-		}
-		$scope.showDescription = function (attr, e) {
-			$scope.attributeDescription = attr.description;
-			var offset = jQuery(e.currentTarget).offset()
-			jQuery('.attr-description').offset({top: offset.top})
-
-			$scope.attributeType = "Type: " + JSON.stringify(attr.rawtype);
-			$scope.attributeRequired = attr.required ? 'required' : '';
-			$scope.attributeUnique = attr.unique ? 'Must be unique' : '';
-		}
-        // handle the download button click
-        // copy the dialog's values to the original node
-        $scope.download = function () {
-	        dialog.close({entities: $scope.entities, annotations: annotations});
-        }
-        $scope.cancel = function () {
-            dialog.close()
-        };
-
-		$scope.selectAnnotationTab = function (tabName) {
-            var tabs = $( "#tabs" ).tabs();
-            tabs.tabs("select", tabName);
-		}
-
-        var initTabs = function () {
-            var div = angular.element("#tabs");
-            if (!div.width()) {
-                setTimeout(initTabs, 100);
-                return;
-            }
-            $( "#tabs" )
-                .tabs()
-                .addClass('ui-tabs-vertical ui-helper-clearfix');
-        }
-        // start the update loop
-        initTabs();
-
-  });
-
-QDR.module.controller("QDR.DownloadDialogController", function($scope, QDRService, $templateCache, $window, dialog, results) {
-		var result = results.entities;
-		var annotations = results.annotations;
-		var annotationKeys = Object.keys(annotations);
-		var annotationSections = {};
-
-		// use the router's name as the file name if present
-		$scope.newRouterName = 'router';
-		result.forEach( function (e) {
-			if (e.actualName == 'router') {
-				e.attributes.forEach( function (a) {
-					if (a.name == 'name') {
-						$scope.newRouterName = a.value;
-					}
-				})
-			}
-		})
-		$scope.newRouterName = $scope.newRouterName + ".conf";
-
-		var template = $templateCache.get('config-file-header.html');
-		$scope.verbose = true;
-		$scope.$watch('verbose', function (newVal) {
-			if (newVal !== undefined) {
-				// recreate output using current verbose setting
-				getOutput();
-			}
-		})
-
-		var getOutput = function () {
-			$scope.output = template + '\n';
-			$scope.parts = [];
-			var commentChar = '#'
-			result.forEach(function (entity) {
-				// don't output a section for annotations, they get flattened into the entities
-				var section = "";
-				if (entity.icon) {
-					section += "##\n## Add to " + entity.link.__data__.source.name + "'s configuration file\n##\n";
-				}
-				section += "##\n## " + QDRService.humanify(entity.actualName) + " - " + entity.description + "\n##\n";
-				section += entity.actualName + " {\n";
-				entity.attributes.forEach(function (attribute) {
-					if (attribute.input == 'select')
-						attribute.value = attribute.selected;
-
-					// treat values with all spaces and empty strings as undefined
-					attribute.value = String(attribute.value).trim();
-					if (attribute.value === 'undefined' || attribute.value === '')
-						attribute.value = undefined;
-
-					if ($scope.verbose) {
-						commentChar = attribute.required || attribute.value != attribute['default'] ? ' ' : '#';
-						if (!attribute.value) {
-							commentChar = '#';
-							attribute.value = '';
-						}
-						section += commentChar + "    "
-							+ attribute.name + ":" + Array(Math.max(20 - attribute.name.length, 1)).join(" ")
-							+ attribute.value
-						    + Array(Math.max(20 - ((attribute.value)+"").length, 1)).join(" ")
-							+ '# ' + attribute.description
-						    + "\n";
-					} else {
-						if (attribute.value) {
-							if (attribute.value != attribute['default'] || attribute.required)
-								section += "    "
-									+ attribute.name + ":" + Array(20 - attribute.name.length).join(" ")
-									+ attribute.value + "\n";
-
-						}
-					}
-				})
-				section += "}\n\n";
-				// if entity.icon is true, this is a connector intended for another router
-				if (entity.icon)
-					$scope.parts.push({output: section,
-								link: entity.link,
-								name: entity.link.__data__.source.name,
-								references: entity.references});
-				else
-					$scope.output += section;
-
-				// if this section is actually an annotation
-				if (annotationKeys.indexOf(entity.actualName) > -1) {
-					annotationSections[entity.actualName] = section;
-				}
-			})
-			// go back and add annotation sections to the parts
-			$scope.parts.forEach (function (part) {
-				for (var section in annotationSections) {
-					if (part.references.indexOf(section) > -1) {
-						part.output += annotationSections[section];
-					}
-				}
-			})
-			QDR.log.debug($scope.output);
-		}
-
-        // handle the download button click
-        $scope.download = function () {
-			var output = $scope.output + "\n\n"
-			var blob = new Blob([output], { type: 'text/plain;charset=utf-16' });
-			saveAs(blob, $scope.newRouterName);
-        }
-
-		$scope.downloadPart = function (part) {
-			var linkName = part.link.__data__.source.name + 'additional.conf';
-			var blob = new Blob([part.output], { type: 'text/plain;charset=utf-16' });
-			saveAs(blob, linkName);
-		}
-
-		$scope.done = function () {
-	        dialog.close();
-		}
-});
-
-  return QDR;
-}(QDR || {}));
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js b/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
new file mode 120000
index 0000000..36d5187
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
@@ -0,0 +1 @@
+../../../../../../stand-alone/plugin/js/qdrTopology.js
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


Mime
View raw message