DISPATCH-745 Remove symlinks from hawtio to stand-alone

Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/6a1e6632
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/6a1e6632
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/6a1e6632

Branch: refs/heads/master
Commit: 6a1e66322cf8b1298acc76bf3ed9d5bfeade2c3f
Parents: 4c14b0a
Author: Ernest Allen <eal...@redhat.com>
Authored: Wed Apr 19 08:16:53 2017 -0400
Committer: Ernest Allen <eal...@redhat.com>
Committed: Wed Apr 19 08:16:53 2017 -0400

----------------------------------------------------------------------
 .../hawtio/src/main/webapp/plugin/js/navbar.js  |  302 ++-
 .../hawtio/src/main/webapp/plugin/js/qdrList.js |  788 ++++++-
 .../src/main/webapp/plugin/js/qdrListChart.js   |  142 +-
 .../src/main/webapp/plugin/js/qdrNewNode.js     |  446 +++-
 .../src/main/webapp/plugin/js/qdrOverview.js    | 1767 +++++++++++++-
 .../plugin/js/qdrOverviewLogsController.js      |   68 +-
 .../src/main/webapp/plugin/js/qdrTopology.js    | 2161 +++++++++++++++++-
 console/stand-alone/plugin/js/navbar.js         |   43 +-
 console/stand-alone/plugin/js/qdrList.js        |   13 +-
 console/stand-alone/plugin/js/qdrListChart.js   |  835 ++++++-
 console/stand-alone/plugin/js/qdrNewNode.js     |   10 +-
 console/stand-alone/plugin/js/qdrOverview.js    |  309 ++-
 .../plugin/js/qdrOverviewLogsController.js      |    4 +-
 console/stand-alone/plugin/js/qdrTopology.js    |   88 +-
 14 files changed, 6709 insertions(+), 267 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6a1e6632/console/hawtio/src/main/webapp/plugin/js/navbar.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/navbar.js 
b/console/hawtio/src/main/webapp/plugin/js/navbar.js
deleted file mode 120000
index e88b618..0000000
--- a/console/hawtio/src/main/webapp/plugin/js/navbar.js
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../stand-alone/plugin/js/navbar.js
\ No newline at end of file
diff --git a/console/hawtio/src/main/webapp/plugin/js/navbar.js 
b/console/hawtio/src/main/webapp/plugin/js/navbar.js
new file mode 100644
index 0000000..5125748
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/navbar.js
@@ -0,0 +1,301 @@
+/*
+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) {
+
+  /**
+   * @property breadcrumbs
+   * @type {{content: string, title: string, isValid: isValid, href: string}[]}
+   *
+   * Data structure that defines the sub-level tabs for
+   * our plugin, used by the navbar controller to show
+   * or hide tabs based on some criteria
+   */
+  QDR.breadcrumbs = [
+    {
+        content: '<i class="icon-cogs"></i> Connect',
+        title: "Connect to a router",
+        isValid: function () { return true; },
+        href: "#" + QDR.pluginRoot + "/connect"
+    },
+    {
+        content: '<i class="icon-home"></i> Overview',
+        title: "View router overview",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#" + QDR.pluginRoot + "/overview"
+      },
+    {
+        content: '<i class="icon-list "></i> Entities',
+        title: "View the attributes of the router entities",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#" + QDR.pluginRoot + "/list"
+      },
+    {
+        content: '<i class="icon-star-empty"></i> Topology',
+        title: "View router network topology",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#" + QDR.pluginRoot + "/topology"
+      },
+    {
+        content: '<i class="icon-bar-chart"></i> Charts',
+        title: "View charts",
+        isValid: function (QDRService, $location) { return 
QDRService.isConnected() && QDR.isStandalone; },
+        href: "#/charts"
+    },
+    {
+        content: '<i class="icon-align-left"></i> Schema',
+        title: "View dispatch schema",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#" + QDR.pluginRoot + "/schema",
+        right: true
+      }
+  ];
+  /**
+   * @function NavBarController
+   *
+   * @param $scope
+   * @param workspace
+   *
+   * The controller for this plugin's navigation bar
+   *
+   */
+  QDR.module.controller("QDR.NavBarController", ['$scope', 'QDRService', 
'QDRChartService', '$routeParams', '$location', function($scope, QDRService, 
QDRChartService, $routeParams, $location) {
+    $scope.breadcrumbs = QDR.breadcrumbs;
+    $scope.isValid = function(link) {
+      return link.isValid(QDRService, $location);
+    };
+
+    $scope.isActive = function(href) {
+    // highlight the connect tab if we are on the root page
+    if (($location.path() === QDR.pluginRoot) && (href.split("#")[1] === 
QDR.pluginRoot + "/connect"))
+      return true
+        return href.split("#")[1] == $location.path();
+    };
+
+    $scope.isRight = function (link) {
+        return angular.isDefined(link.right);
+    };
+
+    $scope.hasChart = function (link) {
+        if (link.href == "#/charts") {
+            return QDRChartService.charts.some(function (c) { return 
c.dashboard });
+        }
+    }
+
+  $scope.isDashboardable = function () {
+    return  ($location.path().indexOf("schema") < 0 && 
$location.path().indexOf("connect") < 0);
+  }
+
+  $scope.addToDashboardLink = function () {
+    var href = "#" + $location.path();
+    var size = angular.toJson({
+                size_x: 2,
+                size_y: 2
+              });
+
+        var routeParams = angular.toJson($routeParams);
+        var title = "Dispatch Router";
+      return "/hawtio/#/dashboard/add?tab=dashboard" +
+            "&href=" + encodeURIComponent(href) +
+            "&routeParams=" + encodeURIComponent(routeParams) +
+            "&title=" + encodeURIComponent(title) +
+            "&size=" + encodeURIComponent(size);
+    };
+
+  }]);
+
+  // controller for the edit/configure chart dialog
+  QDR.module.controller("QDR.ChartDialogController", function($scope, 
QDRChartService, $location, dialog, chart, updateTick, dashboard, adding) {
+    var dialogSvgChart = null;
+    $scope.svgDivId = "dialogEditChart";    // the div id for the svg chart
+
+    var updateTimer = null;
+    $scope.chart = chart;  // the underlying chart object from the dashboard
+    $scope.dialogChart = $scope.chart.copy(); // the chart object for this 
dialog
+    $scope.userTitle = $scope.chart.title();
+
+    $scope.$watch('userTitle', function(newValue, oldValue) {
+    if (newValue !== oldValue) {
+      $scope.dialogChart.title(newValue);
+      dialogSvgChart.tick($scope.svgDivId);
+    }
+    })
+    $scope.$watch("dialogChart.areaColor", function (newValue, oldValue) {
+      if (newValue !== oldValue) {
+        if (dialogSvgChart)
+         dialogSvgChart.tick($scope.svgDivId);
+      }
+    })
+    $scope.$watch("dialogChart.lineColor", function (newValue, oldValue) {
+      if (newValue !== oldValue) {
+        if (dialogSvgChart)
+          dialogSvgChart.tick($scope.svgDivId);
+      }
+    })
+    $scope.$watch("dialogChart.type", function (newValue, oldValue) {
+      if (newValue !== oldValue) {
+        if (dialogSvgChart)
+          dialogSvgChart.tick($scope.svgDivId);
+      }
+    })
+
+    // the stored rateWindow is in milliseconds, but the slider is in seconds
+    $scope.rateWindow = $scope.chart.rateWindow / 1000;
+
+    $scope.addChartsPage = function () {
+      QDRChartService.addDashboard(dialogSvgChart.chart);
+    };
+
+    $scope.showChartsPage = function () {
+      cleanup();
+      dialog.close(true);
+      $location.path(QDR.pluginRoot + "/charts");
+    };
+
+    var cleanup = function () {
+      if (updateTimer) {
+        clearTimeout(updateTimer);
+        updateTimer = null;
+      }
+      if (!$scope.isOnChartsPage())
+        QDRChartService.unRegisterChart($scope.dialogChart);     // remove the 
chart
+    }
+    $scope.okClick = function () {
+      cleanup();
+      dialog.close(true);
+    };
+
+    var initRateSlider = function () {
+      if (document.getElementById('rateSlider')) {
+        $( "#rateSlider" ).slider({
+          value: $scope.rateWindow,
+          min: 1,
+          max: 10,
+          step: 1,
+          slide: function( event, ui ) {
+            $scope.rateWindow = ui.value;
+            $scope.dialogChart.rateWindow = ui.value * 1000;
+            $scope.$apply();
+            if (dialogSvgChart)
+              dialogSvgChart.tick($scope.svgDivId);
+          }
+        });
+      } else {
+        setTimeout(initRateSlider, 100)
+      }
+    }
+    initRateSlider();
+
+    var initDurationSlider = function () {
+      if (document.getElementById('durationSlider')) {
+        $( "#durationSlider" ).slider({
+          value: $scope.dialogChart.visibleDuration,
+          min: 1,
+          max: 10,
+          step: 1,
+          slide: function( event, ui ) {
+            $scope.visibleDuration = $scope.dialogChart.visibleDuration = 
ui.value;
+            $scope.$apply();
+            if (dialogSvgChart)
+              dialogSvgChart.tick($scope.svgDivId);
+          }
+        });
+      } else {
+        setTimeout(initDurationSlider, 100)
+      }
+    }
+    initDurationSlider();
+
+    $scope.adding = function () {
+      return adding
+    }
+
+    $scope.isOnChartsPage = function () {
+      if (adding)
+        return dialogSvgChart ? dialogSvgChart.chart.dashboard : false;
+      else
+        return $scope.chart.dashboard
+    }
+
+    // handle the Apply button click
+    // update the dashboard chart's properties
+    $scope.apply = function () {
+      $scope.chart.areaColor = $scope.dialogChart.areaColor;
+      $scope.chart.lineColor = $scope.dialogChart.lineColor;
+      $scope.chart.type = $scope.dialogChart.type;
+      $scope.chart.rateWindow = $scope.rateWindow * 1000;
+      $scope.chart.title($scope.dialogChart.title());
+      $scope.chart.visibleDuration = $scope.dialogChart.visibleDuration;
+      QDRChartService.saveCharts();
+      if (typeof updateTick === "function")
+        updateTick();
+    }
+
+    // add a new chart to the dashboard based on the current dialog settings
+    $scope.copyToDashboard = function () {
+        var chart = $scope.dialogChart.copy();
+        // set the new chart's dashboard state
+        QDRChartService.addDashboard(chart);
+        // notify the chart controller that it needs to display a new chart
+        dashboard.addChart(chart);
+    }
+
+    // update the chart on the popup dialog
+    var updateDialogChart = function () {
+      // draw the chart using the current data
+      if (dialogSvgChart)
+          dialogSvgChart.tick($scope.svgDivId);
+
+      // draw the chart again in 1 second
+      var updateRate = localStorage['updateRate'] ? localStorage['updateRate'] 
: 5000;
+      if (updateTimer)
+      clearTimeout(updateTimer);
+        updateTimer = setTimeout(updateDialogChart, updateRate);
+    }
+
+    var showChart = function () {
+      // ensure the div for our chart is loaded in the dom
+      var div = angular.element("#" + $scope.svgDivId);
+      if (!div.width()) {
+        setTimeout(showChart, 100);
+        return;
+      }
+      dialogSvgChart = new QDRChartService.AreaChart($scope.dialogChart);
+      $('input[name=lineColor]').val($scope.dialogChart.lineColor);
+      $('input[name=areaColor]').val($scope.dialogChart.areaColor);
+      $('input[name=areaColor]').on('input', function (e) {
+        $scope.dialogChart.areaColor = $(this).val();
+        updateDialogChart()
+      })
+      $('input[name=lineColor]').on('input', function (e) {
+        $scope.dialogChart.lineColor = $(this).val();
+        updateDialogChart()
+      })
+      if (updateTimer)
+        clearTimeout(updateTimer);
+          updateDialogChart();
+    }
+    showChart();
+  });
+
+  return QDR;
+
+} (QDR || {}));

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6a1e6632/console/hawtio/src/main/webapp/plugin/js/qdrList.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrList.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrList.js
deleted file mode 120000
index 3955db5..0000000
--- a/console/hawtio/src/main/webapp/plugin/js/qdrList.js
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../stand-alone/plugin/js/qdrList.js
\ No newline at end of file
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrList.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrList.js
new file mode 100644
index 0000000..ec2efb2
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrList.js
@@ -0,0 +1,787 @@
+/*
+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 ListController
+   * @param $scope
+   * @param QDRService
+   *
+   * Controller for the main interface
+   */
+  QDR.module.controller("QDR.ListController", ['$scope', '$location', 
'$dialog', '$filter', '$timeout', 'QDRService', 'QDRChartService',
+    function ($scope, $location, $dialog, $filter, $timeout, QDRService, 
QDRChartService) {
+
+    var updateIntervalHandle = undefined;
+    var updateInterval = 5000;
+    var ListExpandedKey = "QDRListExpanded";
+    $scope.details = {};
+
+    $scope.tmplListTree = QDR.templatePath + 'tmplListTree.html';
+    $scope.selectedEntity = localStorage['QDRSelectedEntity'] || "address";
+    $scope.selectedNode = localStorage['QDRSelectedNode'];
+    $scope.selectedNodeId = localStorage['QDRSelectedNodeId'];
+    $scope.selectedRecordName = localStorage['QDRSelectedRecordName'];
+    $scope.nodes = []
+    $scope.currentNode = undefined;
+    $scope.modes = [
+      {
+        content: '<a><i class="icon-list"></i> Attributes</a>',
+        id: 'attributes',
+        op: 'READ',
+        title: "View router attributes",
+        isValid: function () { return true; }
+      },
+      {
+        content: '<a><i class="icon-edit"></i> Update</a>',
+        id: 'operations',
+        op: 'UPDATE',
+        title: "Update this attribute",
+        isValid: function () {
+          //QDR.log.debug("isValid UPDAATE? " + this.op)
+          //console.dump($scope.operations)
+          return $scope.operations.indexOf(this.op) > -1
+        }
+      },
+      {
+        content: '<a><i class="icon-plus"></i> Create</a>',
+        id: 'operations',
+        op: 'CREATE',
+        title: "Create a new attribute",
+        isValid: function () { return $scope.operations.indexOf(this.op) > -1 }
+      },
+      {
+        content: '<a><i class="icon-remove"></i> Delete</a>',
+        id: 'delete',
+        op: 'DELETE',
+        title: "Delete",
+        isValid: function () { return $scope.operations.indexOf(this.op) > -1 }
+      },
+      {
+        content: '<a><i class="icon-eye-open"></i> Fetch</a>',
+        id: 'log',
+        op: 'GET-LOG',
+        title: "Fetch recent log entries",
+        isValid: function () { return ($scope.selectedEntity === 'log') }
+      }
+    ];
+    $scope.operations = []
+      $scope.currentMode = $scope.modes[0];
+      $scope.isModeSelected = function (mode) {
+      return mode === $scope.currentMode;
+    }
+    $scope.fetchingLog = false;
+    $scope.selectMode = function (mode) {
+      $scope.currentMode = mode;
+      if (mode.id === 'log') {
+        $scope.logResults = [];
+        $scope.fetchingLog = true;
+        var entity; // undefined since it is not supported in the GET-LOG call
+        QDRService.sendMethod($scope.currentNode.id, entity, {}, 
$scope.currentMode.op, {}, function (nodeName, entity, response, context) {
+          $scope.fetchingLog = false;
+          var statusCode = context.message.application_properties.statusCode;
+          if (statusCode < 200 || statusCode >= 300) {
+            Core.notification('error', 
context.message.application_properties.statusDescription);
+            
//QDR.log.debug(context.message.application_properties.statusDescription)
+            return;
+          }
+          $scope.logResults = response.filter( function (entry) {
+            return entry[0] === $scope.detailsObject.module
+          }).sort( function (a, b) {
+            return b[5] - a[5]
+          }).map( function (entry) {
+            return {
+              type: entry[1],
+              message: entry[2],
+              source: entry[3],
+              line: entry[4],
+              time: Date(entry[5]).toString()
+            }
+          })
+          $scope.$apply();
+        })
+      }
+    }
+    $scope.isValid = function (mode) {
+      return mode.isValid()
+    }
+
+    $scope.expandAll = function () {
+      $("#entityTree").dynatree("getRoot").visit(function(node){
+                node.expand(true);
+            });
+    }
+    $scope.contractAll = function () {
+      $("#entityTree").dynatree("getRoot").visit(function(node){
+                node.expand(false);
+            });
+    }
+
+    if (!QDRService.connected) {
+      // we are not connected. we probably got here from a bookmark or manual 
page reload
+      QDRService.redirectWhenConnected("list");
+      return;
+    }
+    // we are currently connected. setup a handler to get notified if we are 
ever disconnected
+    QDRService.addDisconnectAction( function () {
+      QDRService.redirectWhenConnected("list")
+      $scope.$apply();
+    })
+
+    $scope.nodes = []
+    var excludedEntities = ["management", "org.amqp.management", 
"operationalEntity", "entity", "configurationEntity", "dummy", "console"];
+    var aggregateEntities = ["router.address"];
+    var classOverrides = {
+      "connection": function (row, nodeId) {
+        var isConsole = QDRService.isAConsole (row.properties.value, 
row.identity.value, row.role.value, nodeId)
+        return isConsole ? "console" : row.role.value === "inter-router" ? 
"inter-router" : "external";
+      },
+      "router.link": function (row, nodeId) {
+        var link = {nodeId: nodeId, connectionId: row.connectionId.value}
+        var isConsole = QDRService.isConsoleLink(link)
+        return isConsole ? "console" : row.linkType.value;
+      },
+      "router.address": function (row) {
+        var identity = QDRService.identity_clean(row.identity.value)
+        var address = QDRService.addr_text(identity)
+        var cls = QDRService.addr_class(identity)
+        if (address === "$management")
+          cls = "internal " + cls
+        return cls
+      }
+    }
+
+    var lookupOperations = function () {
+      var ops = 
QDRService.schema.entityTypes[$scope.selectedEntity].operations.filter( 
function (op) { return op !== 'READ'});
+      $scope.operation = ops.length ? ops[0] : "";
+      return ops;
+    }
+
+    var entityTreeChildren = [];
+    var expandedList = angular.fromJson(localStorage[ListExpandedKey]) || [];
+    var onTreeNodeExpanded = function (expanded, node) {
+      // save the list of entities that are expanded
+      var tree = $("#entityTree").dynatree("getTree");
+      var list = [];
+      tree.visit( function (tnode) {
+        if (tnode.isExpanded()) {
+          list.push(tnode.data.key)
+        }
+      })
+      localStorage[ListExpandedKey] = JSON.stringify(list)
+
+      if (expanded)
+        onTreeSelected(node);
+    }
+    // a tree node was selected
+    var onTreeSelected = function (selectedNode) {
+      $timeout( function () {
+        if ($scope.currentMode.id === 'operations')
+          $scope.currentMode = $scope.modes[0];
+        else if ($scope.currentMode.id === 'log')
+          $scope.selectMode($scope.currentMode)
+        else if ($scope.currentMode.id === 'delete') {
+          // clicked on a tree node while on the delete screen -> switch to 
attribute screen
+          $scope.currentMode = $scope.modes[0];
+        }
+        if (selectedNode.data.typeName === "entity") {
+          $scope.selectedEntity = selectedNode.data.key;
+          $scope.operations = lookupOperations()
+        } else if (selectedNode.data.typeName === 'attribute') {
+          $scope.selectedEntity = selectedNode.parent.data.key;
+          $scope.operations = lookupOperations()
+          $scope.selectedRecordName = selectedNode.data.key;
+          updateDetails(selectedNode.data.details);   // update the table on 
the right
+          $("#entityTree").dynatree("getRoot").visit(function(node){
+             node.select(false);
+          });
+          selectedNode.select();
+        }
+      })
+    }
+
+    // fill in an empty results recoord based on the entities schema
+    var fromSchema = function (entityName) {
+      var row = {}
+      var schemaEntity = QDRService.schema.entityTypes[entityName]
+      for (attr in schemaEntity.attributes) {
+        var entity = schemaEntity.attributes[attr]
+        var value = ""
+        if (angular.isDefined(entity['default']))
+          value = entity['default']
+        row[attr] = {
+          value: value,
+          type: entity.type,
+          graph: false,
+          title: entity.description,
+          aggregate: false,
+          aggregateTip: '',
+          'default': entity['default']
+        }
+      }
+      return row;
+    }
+    $scope.hasCreate = function () {
+      var schemaEntity = QDRService.schema.entityTypes[$scope.selectedEntity]
+      return (schemaEntity.operations.indexOf("CREATE") > -1)
+    }
+
+    var stopUpdating = function () {
+      if (angular.isDefined(updateIntervalHandle)) {
+        clearInterval(updateIntervalHandle);
+      }
+      updateIntervalHandle = undefined;
+    }
+
+    // the data for the selected entity is available, populate the tree
+    var updateEntityChildren = function (entity, tableRows, expand) {
+      var tree = $("#entityTree").dynatree("getTree");
+      if (!tree.getNodeByKey) {
+        return stopUpdating()
+      }
+      var node = tree.getNodeByKey(entity)
+      var updatedDetails = false;
+      var scrollTreeDiv = $('.qdr-attributes.pane.left .pane-viewport')
+      var scrollTop = scrollTreeDiv.scrollTop();
+      node.removeChildren();
+      if (tableRows.length == 0) {
+          node.addChild({
+          addClass:   "no-data",
+              typeName:   "none",
+              title:      "no data",
+          key:        node.data.key + ".1"
+          })
+          if (expand) {
+              updateDetails(fromSchema(entity));
+                 $scope.selectedRecordName = entity;
+        }
+      } else {
+        tableRows.forEach( function (row) {
+          var addClass = entity;
+          if (classOverrides[entity]) {
+            addClass += " " + classOverrides[entity](row, 
$scope.currentNode.id);
+          }
+          var child = {
+                        typeName:   "attribute",
+                        addClass:   addClass,
+                        tooltip:    addClass,
+                        key:        row.name.value,
+                        title:      row.name.value,
+                        details:    row
+                    }
+          if (row.name.value === $scope.selectedRecordName) {
+            if (expand)
+              updateDetails(row); // update the table on the right
+            child.select = true;
+            updatedDetails = true;
+          }
+          node.addChild(child)
+        })
+      }
+      // if the selectedRecordName was not found, select the 1st one
+      if (expand && !updatedDetails && tableRows.length > 0) {
+        var row = tableRows[0];
+        $scope.selectedRecordName = row.name.value;
+        var node = tree.getNodeByKey($scope.selectedRecordName);
+        node.select(true);
+        updateDetails(row)  // update the table on the right
+      }
+      scrollTreeDiv.scrollTop(scrollTop)
+    }
+
+    var schemaProps = function (entityName, key, currentNode) {
+         var typeMap = {integer: 'number', string: 'text', path: 'text', 
boolean: 'boolean', map: 'textarea'};
+
+      var entity = QDRService.schema.entityTypes[entityName]
+      var value = entity.attributes[key]
+      // skip identity and depricated fields
+      if (!value)
+        return {input: 'input', type: 'disabled', required: false, selected: 
"", rawtype: 'string', disabled: true, 'default': ''}
+      var description = value.description || ""
+      var val = value['default'];
+      var disabled = (key == 'identity' || 
description.startsWith('Deprecated'))
+      // special cases
+      if (entityName == 'log' && key == 'module') {
+        return {input: 'input', type: 'disabled', required: false, selected: 
"", rawtype: 'string', disabled: true, 'default': ''}
+      }
+      if (entityName === 'linkRoutePattern' && key === 'connector') {
+        // turn input into a select. the values will be populated later
+        value.type = []
+        // find all the connector names and populate the select
+        QDRService.fetchEntity(currentNode.id, '.connector', ['name'], 
function (nodeName, dotentity, response) {
+          $scope.detailFields.some( function (field) {
+            if (field.name === 'connector') {
+              field.rawtype = response.results.map (function (result) {return 
result[0]})
+              return true;
+            }
+          })
+        });
+      }
+      return {    name:       key,
+            humanName:  QDRService.humanify(key),
+                        description:value.description,
+                        type:       disabled ? 'disabled' : 
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,
+                        disabled:   disabled
+            };
+    }
+    $scope.getAttributeValue = function (attribute) {
+      var value = attribute.attributeValue;
+      if ($scope.currentMode.op === "CREATE" && attribute.name === 'identity')
+        value = "<assigned by system>"
+      return value;
+    }
+
+    // update the table on the right
+    var updateDetails = function (row) {
+      var details = [];
+      $scope.detailsObject = {};
+      var attrs = Object.keys(row).sort();
+      attrs.forEach( function (attr) {
+        var changed = $scope.detailFields.filter(function (old) {
+          return (old.name === attr) ? old.graph && old.rawValue != 
row[attr].value : false;
+        })
+        var schemaEntity = schemaProps($scope.selectedEntity, attr, 
$scope.currentNode)
+        details.push( {
+          attributeName:  QDRService.humanify(attr),
+          attributeValue: attr === 'port' ? row[attr].value : 
QDRService.pretty(row[attr].value),
+          name:           attr,
+          changed:        changed.length,
+          rawValue:       row[attr].value,
+          graph:          row[attr].graph,
+          title:          row[attr].title,
+          aggregateValue: QDRService.pretty(row[attr].aggregate),
+          aggregateTip:   row[attr].aggregateTip,
+
+          input:          schemaEntity.input,
+          type:           schemaEntity.type,
+          required:       schemaEntity.required,
+          selected:       schemaEntity.selected,
+          rawtype:        schemaEntity.rawtype,
+          disabled:       schemaEntity.disabled,
+          'default':      schemaEntity['default']
+        })
+        $scope.detailsObject[attr] = row[attr].value;
+      })
+      setTimeout(applyDetails, 1, details)
+    }
+
+    var applyDetails = function (details) {
+      $scope.detailFields = details;
+      aggregateColumn();
+      $scope.$apply();
+      // ng-grid bug? the entire table doesn't always draw unless a reflow is 
triggered;
+      $(window).trigger('resize');
+    }
+
+    var restartUpdate = function () {
+      stopUpdating();
+      updateTableData($scope.selectedEntity, true);
+      updateIntervalHandle = setInterval(updateExpandedEntities, 
updateInterval);
+    }
+    var updateExpandedEntities = function () {
+      var tree = $("#entityTree").dynatree("getTree");
+      if (tree.visit) {
+        tree.visit( function (node) {
+          if (node.isExpanded()) {
+            updateTableData(node.data.key, node.data.key === 
$scope.selectedEntity)
+          }
+        })
+      } else {
+        stopUpdating();
+      }
+    }
+
+    $scope.selectNode = function(node) {
+      $scope.selectedNode = node.name;
+      $scope.selectedNodeId = node.id;
+      setCurrentNode();
+      restartUpdate();
+    };
+    $scope.$watch('selectedEntity', function(newValue, oldValue) {
+      if (newValue !== oldValue) {
+        localStorage['QDRSelectedEntity'] = $scope.selectedEntity;
+        restartUpdate();
+        $scope.operations = lookupOperations()
+      }
+    })
+    $scope.$watch('selectedNode', function(newValue, oldValue) {
+        if (newValue !== oldValue) {
+        localStorage['QDRSelectedNode'] = $scope.selectedNode;
+        localStorage['QDRSelectedNodeId'] = $scope.selectedNodeId;
+      }
+    })
+    $scope.$watch('selectedRecordName', function(newValue, oldValue) {
+      if (newValue != oldValue) {
+        localStorage['QDRSelectedRecordName'] = $scope.selectedRecordName;
+      }
+    })
+
+    /* Called periodically to refresh the data on the page */
+    var updateTableData = function (entity, expand) {
+      if (!QDRService.connected) {
+        // we are no longer connected. bail back to the connect page
+        $location.path("/" + QDR.pluginName + "/connect")
+        $location.search('org', "list");
+        return;
+      }
+      // don't update the data when on the operations tab
+      if ($scope.currentMode.id === 'operations') {
+        return;
+      }
+
+      var gotNodeInfo = function (nodeName, dotentity, response) {
+        var tableRows = [];
+        var records = response.results;
+        var aggregates = response.aggregates;
+        var attributeNames = response.attributeNames;
+        // If !attributeNmes then  there was an error getting the records for 
this entity
+        if (attributeNames) {
+          var nameIndex = attributeNames.indexOf("name");
+          var identityIndex = attributeNames.indexOf("identity");
+          var ent = QDRService.schema.entityTypes[entity];
+          for (var i=0; i<records.length; ++i) {
+            var record = records[i];
+            var aggregate = aggregates ? aggregates[i] : undefined;
+            var row = {};
+            var rowName;
+            if (nameIndex > -1) {
+              rowName = record[nameIndex];
+              if (!rowName && identityIndex > -1) {
+                rowName = record[nameIndex] = (dotentity + '/' + 
record[identityIndex])
+              }
+            }
+            if (!rowName) {
+              QDR.log.error("response attributeNames did not contain a name 
field");
+              console.dump(response.attributeNames);
+              return;
+            }
+            for (var j=0; j<attributeNames.length; ++j) {
+              var col = attributeNames[j];
+              row[col] = {value: record[j], type: undefined, graph: false, 
title: '', aggregate: '', aggregateTip: ''};
+              if (ent) {
+                var att = ent.attributes[col];
+                if (att) {
+                  row[col].type = att.type;
+                  row[col].graph = att.graph;
+                  row[col].title = att.description;
+
+                  if (aggregate) {
+                    if (att.graph) {
+                      row[col].aggregate = att.graph ? aggregate[j].sum : '';
+                      var tip = [];
+                      aggregate[j].detail.forEach( function (line) {
+                        tip.push(line);
+                      })
+                      row[col].aggregateTip = angular.toJson(tip);
+                    }
+                  }
+                }
+              }
+            }
+            tableRows.push(row);
+          }
+        }
+
+        tableRows.sort( function (a, b) { return 
a.name.value.localeCompare(b.name.value) })
+        setTimeout(selectRow, 0, {entity: dotentity, rows: tableRows, expand: 
expand});
+      }
+      // if this entity should show an aggregate column, send the request to 
get the info for this entity from all the nedes
+      if (aggregateEntities.indexOf(entity) > -1) {
+        var nodeInfo = QDRService.topology.nodeInfo();
+        QDRService.getMultipleNodeInfo(Object.keys(nodeInfo), entity, [], 
gotNodeInfo, $scope.selectedNodeId);
+      } else {
+        QDRService.fetchEntity($scope.selectedNodeId, entity, [], gotNodeInfo);
+      }
+    };
+
+    // tableRows are the records that were returned, this populates the left 
hand table on the page
+    var selectRow = function (info) {
+      updateEntityChildren(info.entity, info.rows, info.expand);
+      fixTooltips();
+    }
+
+    var titleFromAlt = function (alt) {
+      if (alt && alt.length) {
+        var data = angular.fromJson(alt);
+        var table = "<table class='tiptable'><tbody>";
+        data.forEach (function (row) {
+          table += "<tr>";
+          table += "<td>" + row.node + "</td><td align='right'>" + 
QDRService.pretty(row.val) + "</td>";
+          table += "</tr>"
+        })
+        table += "</tbody></table>"
+        return table;
+      }
+      return '';
+    }
+
+    var fixTooltips = function () {
+      if ($('.hastip').length == 0) {
+        setTimeout(fixTooltips, 100);
+        return;
+      }
+      $('.hastip').each( function (i, tip) {
+        var tipset = tip.getAttribute('tipset')
+        if (!tipset) {
+          $(tip).tipsy({html: true, className: 'subTip', opacity: 1, title: 
function () {
+            return titleFromAlt(this.getAttribute('alt'))
+          } });
+          tip.setAttribute('tipset', true)
+        } else {
+          var title = titleFromAlt(tip.getAttribute('alt'))
+          tip.setAttribute('original-title', title)
+        }
+      })
+    }
+
+    $scope.detailFields = [];
+
+    $scope.addToGraph = function(rowEntity) {
+      var chart = QDRChartService.registerChart(
+        {nodeId: $scope.selectedNodeId,
+         entity: "." + $scope.selectedEntity,
+         name:   $scope.selectedRecordName,
+         attr:    rowEntity.name,
+         forceCreate: true});
+      doDialog('tmplListChart.html', chart);
+    }
+
+    $scope.addAllToGraph = function(rowEntity) {
+      var chart = QDRChartService.registerChart({
+        nodeId:     $scope.selectedNodeId,
+        entity:     $scope.selectedEntity,
+        name:       $scope.selectedRecordName,
+        attr:       rowEntity.name,
+        type:       "rate",
+        rateWindow: updateInterval,
+        visibleDuration: 1,
+        forceCreate: true,
+        aggregate:   true});
+      doDialog('tmplListChart.html', chart);
+    }
+
+    $scope.detailCols = [];
+    var aggregateColumn = function () {
+      if ((aggregateEntities.indexOf($scope.selectedEntity) > -1 && 
$scope.detailCols.length != 3) ||
+        (aggregateEntities.indexOf($scope.selectedEntity) == -1 && 
$scope.detailCols.length != 2)) {
+        // column defs have to be reassigned and not spliced, so no push/pop
+         $scope.detailCols = [
+         {
+           field: 'attributeName',
+           displayName: 'Attribute',
+           cellTemplate: '<div title="{{row.entity.title}}" 
class="listAttrName">{{row.entity[col.field]}}<i ng-if="row.entity.graph" 
ng-click="addToGraph(row.entity)" ng-class="{\'icon-bar-chart\': 
row.entity.graph == true }"></i></div>'
+         },
+         {
+           field: 'attributeValue',
+           displayName: 'Value',
+           cellTemplate: '<div class="ngCellText" ng-class="{\'changed\': 
row.entity.changed == 1}"><span>{{row.getProperty(col.field)}}</span></div>'
+         }
+         ]
+        if (aggregateEntities.indexOf($scope.selectedEntity) > -1) {
+          $scope.detailCols.push(
+           {
+             width: '10%',
+             field: 'aggregateValue',
+             displayName: 'Aggregate',
+             cellTemplate: '<div class="hastip" 
alt="{{row.entity.aggregateTip}}"><span ng-class="{\'changed\': 
row.entity.changed == 1}">{{row.entity[col.field]}}</span><i 
ng-if="row.entity.graph" ng-click="addAllToGraph(row.entity)" 
ng-class="{\'icon-bar-chart\': row.entity.graph == true }"></i></div>',
+             cellClass: 'aggregate'
+           }
+          )
+        }
+      }
+      if ($scope.selectedRecordName === "")
+        $scope.detailCols = [];
+    }
+
+    // the table on the right of the page contains a row for each field in the 
selected record in the table on the left
+    $scope.details = {
+      data: 'detailFields',
+      columnDefs: "detailCols",
+      enableColumnResize: true,
+      multiSelect: false,
+      beforeSelectionChange: function() {
+          return false;
+      }
+    };
+    $scope.$on("$destroy", function( event ) {
+      //QDR.log.debug("scope destroyed for qdrList");
+      stopUpdating();
+    });
+
+    function gotMethodResponse (nodeName, entity, response, context) {
+      var statusCode = context.message.application_properties.statusCode;
+      if (statusCode < 200 || statusCode >= 300) {
+        Core.notification('error', 
context.message.application_properties.statusDescription);
+        
//QDR.log.debug(context.message.application_properties.statusDescription)
+      } else {
+        var note = entity + " " + $filter('Pascalcase')($scope.currentMode.op) 
+ "d"
+        Core.notification('success', note);
+        $scope.selectMode($scope.modes[0]);
+        restartUpdate();
+      }
+    }
+    $scope.ok = function () {
+      var attributes = {}
+      $scope.detailFields.forEach( function (field) {
+        var value = field.rawValue;
+        if (field.input === 'input') {
+          if (field.type === 'text' || field.type === 'disabled')
+            value = field.attributeValue;
+        } else if (field.input === 'select') {
+          value = field.selected;
+        } else if (field.input === 'boolean') {
+          value = field.rawValue
+        }
+        if (value === "")
+          value = undefined;
+
+        if ((value && value != field['default']) || field.required || 
(field.name === 'role')) {
+          if (field.name !== 'identity')
+            attributes[field.name] = value
+        }
+      })
+      QDRService.sendMethod($scope.currentNode.id, $scope.selectedEntity, 
attributes, $scope.currentMode.op, undefined, gotMethodResponse)
+    }
+    $scope.remove = function () {
+      var attributes = {type: $scope.selectedEntity, name: 
$scope.selectedRecordName}
+      QDRService.sendMethod($scope.currentNode.id, $scope.selectedEntity, 
attributes, $scope.currentMode.op, undefined, gotMethodResponse)
+    }
+
+    function doDialog(tmpl, chart) {
+        var d = $dialog.dialog({
+          backdrop: true,
+          keyboard: true,
+          backdropClick: true,
+          templateUrl: QDR.templatePath + tmpl,
+          controller: "QDR.ListChartController",
+          resolve: {
+                 chart: function() {
+                   return chart
+                 },
+                 nodeName: function () {
+                    return $scope.selectedNode
+                 }
+              }
+        });
+
+        d.open().then(function(result) { console.log("d.open().then"); });
+
+    };
+
+    var setCurrentNode = function () {
+      $scope.nodes.some( function (node, i) {
+        if (node.name === $scope.selectedNode) {
+          $scope.currentNode = $scope.nodes[i]
+          return true;
+        }
+      })
+    }
+
+    var treeReady = false;
+    var serviceReady = false;
+    $scope.largeNetwork = QDRService.isLargeNetwork()
+    // called after we know for sure the schema is fetched and the routers are 
all ready
+    QDRService.addUpdatedAction("initList", function () {
+      QDRService.stopUpdating();
+      QDRService.delUpdatedAction("initList")
+
+      $scope.nodes = QDRService.nodeList().sort(function (a, b) { return 
a.name.toLowerCase() > b.name.toLowerCase()});
+      // unable to get node list? Bail.
+      if ($scope.nodes.length == 0) {
+        $location.path("/" + QDR.pluginName + "/connect")
+        $location.search('org', "list");
+      }
+      if (!angular.isDefined($scope.selectedNode)) {
+        //QDR.log.debug("selectedNode was " + $scope.selectedNode);
+        if ($scope.nodes.length > 0) {
+          $scope.selectedNode = $scope.nodes[0].name;
+          $scope.selectedNodeId = $scope.nodes[0].id;
+          //QDR.log.debug("forcing selectedNode to " + $scope.selectedNode);
+        }
+      }
+      setCurrentNode();
+      if ($scope.currentNode == undefined) {
+        if ($scope.nodes.length > 0) {
+          $scope.selectedNode = $scope.nodes[0].name;
+          $scope.selectedNodeId = $scope.nodes[0].id;
+          $scope.currentNode = $scope.nodes[0];
+        }
+      }
+      var sortedEntities = Object.keys(QDRService.schema.entityTypes).sort();
+      sortedEntities.forEach( function (entity) {
+        if (excludedEntities.indexOf(entity) == -1) {
+          if (!angular.isDefined($scope.selectedEntity)) {
+            $scope.selectedEntity = entity;
+            $scope.operations = lookupOperations()
+          }
+          var e = new Folder(entity)
+          e.typeName = "entity"
+          e.key = entity
+          e.expand = (expandedList.indexOf(entity) > -1)
+          var placeHolder = new Folder("loading...")
+          placeHolder.addClass = "loading"
+          e.children = [placeHolder]
+          entityTreeChildren.push(e)
+        }
+      })
+      serviceReady = true;
+      initTree();
+    })
+    $scope.treeReady = function () {
+      treeReady = true;
+      initTree();
+    }
+
+    var initTree = function () {
+      if (!treeReady || !serviceReady)
+        return;
+      $('#entityTree').dynatree({
+        onActivate: onTreeSelected,
+        onExpand: onTreeNodeExpanded,
+        selectMode: 1,
+        autoCollapse: $scope.largeNetwork,
+        activeVisible: !$scope.largeNetwork,
+        debugLevel: 0,
+        children: entityTreeChildren
+      })
+      restartUpdate()
+      updateExpandedEntities();
+    };
+    QDRService.ensureAllEntities({entity: ".connection"}, function () {
+      QDRService.setUpdateEntities([".connection"])
+      QDRService.startUpdating();
+    })
+
+
+  }]);
+
+    return QDR;
+
+} (QDR || {}));

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6a1e6632/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js
deleted file mode 120000
index d1fcf4a..0000000
--- a/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../stand-alone/plugin/js/qdrListChart.js
\ No newline at end of file
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js
new file mode 100644
index 0000000..93391f1
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrListChart.js
@@ -0,0 +1,141 @@
+/*
+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.ListChartController', function ($scope, dialog, 
$dialog, $location, QDRChartService, chart, nodeName) {
+    $scope.chart = chart;
+    $scope.dialogSvgChart = null;
+    var updateTimer = null;
+    $scope.svgDivId = "dialogChart";    // the div id for the svg chart
+
+    $scope.showChartsPage = function () {
+      cleanup();
+      dialog.close(true);
+      $location.path(QDR.pluginRoot + "/charts");
+    };
+
+    $scope.addHChart = function () {
+      QDRChartService.addHDash($scope.chart);
+      cleanup();
+      dialog.close(true);
+    }
+
+    $scope.addToDashboardLink = function () {
+      var href = "#/" + QDR.pluginName + "/charts";
+      var size = angular.toJson({
+                  size_x: 2,
+                  size_y: 2
+                });
+
+      var params = angular.toJson({chid: $scope.chart.id()});
+      var title = "Dispatch - " + nodeName;
+      return "/hawtio/#/dashboard/add?tab=dashboard" +
+        "&href=" + encodeURIComponent(href) +
+        "&routeParams=" + encodeURIComponent(params) +
+        "&title=" + encodeURIComponent(title) +
+        "&size=" + encodeURIComponent(size);
+    };
+
+
+    $scope.addChartsPage = function () {
+      QDRChartService.addDashboard($scope.chart);
+    };
+
+    $scope.delChartsPage = function () {
+      QDRChartService.delDashboard($scope.chart);
+    };
+
+    $scope.isOnChartsPage = function () {
+      return $scope.chart.dashboard;
+    }
+
+    var showChart = function () {
+      // the chart divs are generated by angular and aren't available 
immediately
+      var div = angular.element("#" + $scope.svgDivId);
+      if (!div.width()) {
+        setTimeout(showChart, 100);
+        return;
+      }
+      dialogSvgChart = new QDRChartService.AreaChart($scope.chart);
+      $scope.dialogSvgChart = dialogSvgChart;
+      updateDialogChart();
+    }
+    showChart();
+
+    var updateDialogChart = function () {
+      if ($scope.dialogSvgChart)
+        $scope.dialogSvgChart.tick($scope.svgDivId);
+      if (updateTimer)
+        clearTimeout(updateTimer)
+      updateTimer = setTimeout(updateDialogChart, 1000);
+    }
+
+    var cleanup = function () {
+      if (updateTimer) {
+        clearTimeout(updateTimer);
+        updateTimer = null;
+      }
+      if (!$scope.chart.hdash && !$scope.chart.dashboard)
+        QDRChartService.unRegisterChart($scope.chart);     // remove the chart
+
+    }
+    $scope.ok = function () {
+      cleanup();
+      dialog.close(true);
+      };
+
+    $scope.editChart = function () {
+      doDialog('tmplChartConfig.html', chart)
+    }
+
+    function doDialog(template, chart) {
+
+      $dialog.dialog({
+      backdrop: true,
+      keyboard: true,
+      backdropClick: true,
+      templateUrl: QDR.templatePath + template,
+      controller: "QDR.ChartDialogController",
+      resolve: {
+        chart: function() {
+          return chart;
+        },
+        updateTick: function () {
+          return function () {};
+        },
+        dashboard: function () {
+          return $scope;
+        },
+        adding: function () {
+          return true
+        }
+      }
+      }).open().then(function(result) {
+        $scope.ok()
+      });
+    };
+
+  });
+
+  return QDR;
+
+} (QDR || {}));

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6a1e6632/console/hawtio/src/main/webapp/plugin/js/qdrNewNode.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrNewNode.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrNewNode.js
deleted file mode 120000
index 16b0e42..0000000
--- a/console/hawtio/src/main/webapp/plugin/js/qdrNewNode.js
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../stand-alone/plugin/js/qdrNewNode.js
\ No newline at end of file
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrNewNode.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrNewNode.js
new file mode 100644
index 0000000..f6d035a
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrNewNode.js
@@ -0,0 +1,445 @@
+/*
+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.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 || {}));


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

Reply via email to