You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ea...@apache.org on 2016/02/22 22:09:56 UTC

[5/5] qpid-dispatch git commit: DISPATCH-221 - Check in hawtio plugin code

DISPATCH-221 - Check in hawtio plugin code


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

Branch: refs/heads/master
Commit: eb2e027a4eb84d043e01b3e03abf02f0ac7d1b09
Parents: fc4f8e6
Author: Ernest Allen <ea...@redhat.com>
Authored: Mon Feb 22 16:09:25 2016 -0500
Committer: Ernest Allen <ea...@redhat.com>
Committed: Mon Feb 22 16:09:25 2016 -0500

----------------------------------------------------------------------
 LICENSE                                         |    5 +-
 console/hawtio/README.md                        |   86 +
 console/hawtio/pom.xml                          |  240 +++
 .../simpleplugin/PluginContextListener.java     |   37 +
 .../hawtio/src/main/resources/WEB-INF/web.xml   |   41 +
 .../hawtio/src/main/resources/log4j.properties  |    6 +
 .../src/main/webapp/plugin/css/plugin.css       |  725 ++++++++
 .../src/main/webapp/plugin/css/qdrTopology.css  |  495 ++++++
 .../src/main/webapp/plugin/html/qdrCharts.html  |   63 +
 .../src/main/webapp/plugin/html/qdrConnect.html |   38 +
 .../src/main/webapp/plugin/html/qdrGraphs.html  |   15 +
 .../src/main/webapp/plugin/html/qdrLayout.html  |   26 +
 .../src/main/webapp/plugin/html/qdrList.html    |   55 +
 .../main/webapp/plugin/html/qdrOverview.html    |   92 +
 .../src/main/webapp/plugin/html/qdrSchema.html  |   21 +
 .../main/webapp/plugin/html/qdrTopology.html    |  201 +++
 .../src/main/webapp/plugin/js/dispatchPlugin.js |  241 +++
 .../hawtio/src/main/webapp/plugin/js/navbar.js  |  128 ++
 .../main/webapp/plugin/js/qdrChartService.js    | 1074 ++++++++++++
 .../src/main/webapp/plugin/js/qdrCharts.js      |  304 ++++
 .../hawtio/src/main/webapp/plugin/js/qdrList.js |  543 ++++++
 .../src/main/webapp/plugin/js/qdrOverview.js    |  688 ++++++++
 .../src/main/webapp/plugin/js/qdrSchema.js      |   38 +
 .../src/main/webapp/plugin/js/qdrService.js     |  731 ++++++++
 .../src/main/webapp/plugin/js/qdrSettings.js    |  125 ++
 .../src/main/webapp/plugin/js/qdrTopology.js    | 1651 ++++++++++++++++++
 .../src/main/webapp/plugin/lib/rhea-min.js      |   93 +
 27 files changed, 7761 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index 5e0976f..3775103 100644
--- a/LICENSE
+++ b/LICENSE
@@ -205,4 +205,7 @@
 # Thrid Party Dependency Licensing Information:
 ###############################################
 
-console/stand-alone/plugin/lib/rhea-min.js is Licensed under the above Apache License, Version 2.0
+console/stand-alone/plugin/lib/rhea-min.js is licensed under the above Apache License, Version 2.0
+
+console/hawtio/src/main/webapp/plugin/lib/rhea-min.js is licensed under the above Apache License, Version 2.0
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/README.md
----------------------------------------------------------------------
diff --git a/console/hawtio/README.md b/console/hawtio/README.md
new file mode 100644
index 0000000..206400c
--- /dev/null
+++ b/console/hawtio/README.md
@@ -0,0 +1,86 @@
+# hawtio dispatch router plugin
+
+dispatch-plugin.war is a standalone hawtio plugin that can be deployed in a server alongside the main hawtio-web application.
+
+The project creates a war file that can be deployed in various application services and is also OSGi-ified so it deploys nicely into Apache Karaf.
+
+## Docker
+
+The fastest way to use the console is to run the [docker image](https://hub.docker.com/r/ernieallen/dispatch-console/). Follow the installation/run instruction on that page.
+
+## Building
+The dispatch-plugin.war file is pre-built and can be installed alongside the hawtio.war on any system with a modern java installation. If you want to build the dispatch-plugin.war from source:
+
+- clone the hawtio git repo
+
+    $ git clone https://github.com/hawtio/hawtio.git
+
+- build hawtio
+
+    $ cd hawtio
+
+    $ mvn clean install
+
+    If you encounter any errors when building hawtio, visit [the hawtio build page](http://hawt.io/building/index.html) for help.
+
+- create a dispatch dir under the hawtio dir and copy the source code
+
+    $ mkdir dispatch
+
+    $ cp -r {dir where this file is located}/dispatch/* dispatch/
+
+- do a maven build of dispatch
+
+    $ cd dispach
+
+    $ mvn package
+
+The dispatch-plugin-1.4.60.war file should now be in the target directory.
+
+## Apache Tomcat installation
+
+Copy the dispatch-plugin-1.4.60.war file as the following name
+
+    dispatch-plugin.war
+to the deploy directory of Apache Tomcat or similar Java web container. Ensure the hawtio.war file is present in the same directory. Point a browser at http://\<tomcathost:post\>/hawtio
+Dispatch Router should be available as a tab in the console.
+
+## Connecting to a router
+
+On the Dispatch Router's console page, select the Connect sub tab. Enter the address of a dispatch router. Enter the port of a websockets to tcp proxy and click the Connect button.
+
+### Websockts to tcp proxy
+
+The console communicates to a router using Qpid Proton's [rhea](https://github.com/grs/rhea) javascript binding. When run from a browser, it uses websockets. 
+The router communicates using tcp. Therefore a websockts/tcp proxy is required.
+
+#### Manually running a python websockets/tcp proxy
+
+A popular python based proxy is [websockify](https://github.com/kanaka/websockify). To use it:
+
+    $ git clone https://github.com/kanaka/websockify.git
+    $ cd websockify
+    $ ./run 5673 0.0.0.0:20009 &
+    
+In the above, websockify is listening for ws traffic on port 5673 and will proxy it to 0.0.0.0:20009. One of the routers will need a listener on the proxied port. An example router .conf file entry is:
+
+    listener {
+        name: ProxyListener
+        role: normal
+        addr: 0.0.0.0
+        port: 20009
+        sasl-mechanisms: ANONYMOUS
+    }
+
+#### Automatically running a proxy with a router
+
+You can automatically start the proxy program when a router starts. Add the listener above and the following console section to a router's config file.
+
+    console {
+        listener: ProxyListener
+        proxy:    /pathToWebsockify/run
+        wsport:   5673
+    }
+    
+The value for proxy: can be any program that has execute permission. The router will execute it and pass <wsport> <addr:port> as arguments.
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/pom.xml
----------------------------------------------------------------------
diff --git a/console/hawtio/pom.xml b/console/hawtio/pom.xml
new file mode 100644
index 0000000..effdfb4
--- /dev/null
+++ b/console/hawtio/pom.xml
@@ -0,0 +1,240 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>io.hawt</groupId>
+    <artifactId>project</artifactId>
+    <version>1.4.60</version>
+  </parent>
+<!--
+  <parent>
+    <groupId>io.hawt</groupId>
+    <artifactId>hawtio-plugin-examples</artifactId>
+    <version>1.4.60</version>
+    <relativePath>..</relativePath>
+  </parent>
+-->
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>dispatch-plugin</artifactId>
+  <name>${project.artifactId}</name>
+  <description>hawtio :: hawtio Dispatch plugin</description>
+
+  <!-- hawtio plugins are almost always war files -->
+  <packaging>war</packaging>
+
+  <properties>
+    <!-- filtered plugin properties, we don't define plugin-scripts here
+      as we build that dynamically using maven-antrun-plugin below. -->
+    <!-- plugin-context is what context this plugin will handle requests on
+      in the application server -->
+    <plugin-context>/dispatch-plugin</plugin-context>
+
+    <!-- plugin-name is the name of our plugin, affects the name used for
+      the plugin's mbean -->
+    <plugin-name>${project.artifactId}</plugin-name>
+
+    <!-- plugin-domain is currently unused, we just define it to an empty
+      string -->
+    <plugin-domain />
+
+    <!-- this lets this plugin deploy nicely into karaf, these get used
+      for the ImportPackage directive for maven-bundle-plugin -->
+    <fuse.osgi.import>
+      javax.servlet,
+      *;resolution:=optional
+    </fuse.osgi.import>
+  </properties>
+
+  <dependencies>
+
+    <!-- we only need to embed this dependency in the war, this contains
+      a nice helper class that our plugin can use to export it's plugin
+      mbean -->
+    <dependency>
+      <groupId>io.hawt</groupId>
+      <artifactId>hawtio-plugin-mbean</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <!-- servlet API is provided by the container -->
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>servlet-api</artifactId>
+      <version>${servlet-api-version}</version>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- logging -->
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-api</artifactId>
+      <version>${slf4j-version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <version>${slf4j-version}</version>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+
+    <!-- we want to ensure src/main/resources/WEB-INF/web.xml is being filtered
+      so that it picks up all of our javascript files -->
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+        <includes>
+          <include>**/*.xml</include>
+        </includes>
+      </resource>
+    </resources>
+
+    <plugins>
+
+      <!-- We use maven-antrun-plugin to build up a list of
+           javascript files for our plugin mbean, this means
+           it needs to run before the maven-resources-plugin
+           copies and filters the web.xml, since for this
+           example we use contextParam settings to configure
+           our plugin mbean -->
+
+      <plugin>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>${maven-antrun-plugin-version}</version>
+        <executions>
+
+          <execution>
+            <!-- we run this early in the build process before
+              maven-resources-plugin is run.  We're exporting the
+              plugin-scripts property from here, so we need to
+              use maven-antrun-plugin 1.6 or up -->
+            <id>generate-sources</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+            <configuration>
+              <target>
+                <echo>Building plugin javascript file list</echo>
+                <!-- javascript-files will contain all of the javascript in
+                  our project -->
+                <fileset id="javascript-files" dir="${basedir}/src/main/webapp">
+                  <include name="**/*.js" />
+                </fileset>
+                <!-- we need to strip out the top level path which is
+                   our source directory and be sure to change the directory
+                   separators to forward slashes -->
+                <pathconvert pathsep="," dirsep="/" property="plugin-scripts" refid="javascript-files">
+                  <map from="${basedir}/src/main/webapp/" to="" />
+                </pathconvert>
+                <echo>Files: ${plugin-scripts}</echo>
+
+                <echo>Copying log4j.properties</echo>
+                <copy file="src/main/resources/log4j.properties" todir="target/classes" />
+
+              </target>
+              <!-- this exports plugin-scripts to the maven build, without
+                this line ${plugin-scripts} in the web.xml file won't be
+                replaced -->
+              <exportAntProperties>true</exportAntProperties>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <version>${maven-resources-plugin-version}</version>
+        <executions>
+          <execution>
+            <!-- defining this maven plugin in the same phase as the
+              maven-antrun-plugin but *after* we've configured the
+              maven-antrun-plugin ensures we filter resources *after*
+              we've discovered the plugin .js files. -->
+            <id>copy-resources</id>
+            <phase>generate-sources</phase>
+            <goals>
+              <goal>resources</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- maven-bundle-plugin config, needed to make this war
+        deployable in karaf, defines the context that this bundle
+        should handle requests on -->
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <version>${maven-bundle-plugin-version}</version>
+        <executions>
+          <execution>
+            <id>bundle-manifest</id>
+            <phase>process-classes</phase>
+            <goals>
+              <goal>manifest</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <manifestLocation>${webapp-outdir}/META-INF</manifestLocation>
+          <supportedProjectTypes>
+            <supportedProjectType>jar</supportedProjectType>
+            <supportedProjectType>bundle</supportedProjectType>
+            <supportedProjectType>war</supportedProjectType>
+          </supportedProjectTypes>
+          <instructions>
+            <Webapp-Context>${plugin-context}</Webapp-Context>
+            <Web-ContextPath>${plugin-context}</Web-ContextPath>
+
+            <Embed-Directory>WEB-INF/lib</Embed-Directory>
+            <Embed-Dependency>*;scope=compile|runtime</Embed-Dependency>
+            <Embed-Transitive>true</Embed-Transitive>
+
+            <Export-Package>${fuse.osgi.export}</Export-Package>
+            <Import-Package>${fuse.osgi.import}</Import-Package>
+            <DynamicImport-Package>${fuse.osgi.dynamic}</DynamicImport-Package>
+            <Private-Package>${fuse.osgi.private.pkg}</Private-Package>
+
+            <Bundle-ClassPath>.,WEB-INF/classes</Bundle-ClassPath>
+
+            <Bundle-Name>${project.description}</Bundle-Name>
+            <Bundle-SymbolicName>${project.groupId}.${project.artifactId}</Bundle-SymbolicName>
+            <Implementation-Title>HawtIO</Implementation-Title>
+            <Implementation-Version>${project.version}</Implementation-Version>
+          </instructions>
+        </configuration>
+      </plugin>
+
+      <!-- We define the maven-war-plugin here and make sure it uses
+        the manifest file generated by the maven-bundle-plugin.  We
+        also ensure it picks up our filtered web.xml and not the one
+        in src/main/resources -->
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-war-plugin</artifactId>
+        <version>${war-plugin-version}</version>
+        <configuration>
+          <outputFileNameMapping>@{artifactId}@-@{baseVersion}@@{dashClassifier?}@.@{extension}@</outputFileNameMapping>
+          <packagingExcludes>**/classes/OSGI-INF/**</packagingExcludes>
+          <failOnMissingWebXml>false</failOnMissingWebXml>
+          <archive>
+            <manifestFile>${webapp-outdir}/META-INF/MANIFEST.MF</manifestFile>
+          </archive>
+          <webResources>
+            <resource>
+              <filtering>true</filtering>
+              <directory>src/main/resources</directory>
+              <includes>
+                <include>**/*.*</include>
+              </includes>
+            </resource>
+          </webResources>
+        </configuration>
+      </plugin>
+
+    </plugins>
+  </build>
+</project>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/java/io/hawt/example/simpleplugin/PluginContextListener.java
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/java/io/hawt/example/simpleplugin/PluginContextListener.java b/console/hawtio/src/main/java/io/hawt/example/simpleplugin/PluginContextListener.java
new file mode 100644
index 0000000..a417786
--- /dev/null
+++ b/console/hawtio/src/main/java/io/hawt/example/simpleplugin/PluginContextListener.java
@@ -0,0 +1,37 @@
+package io.hawt.dispatch;
+
+import io.hawt.web.plugin.HawtioPlugin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+public class PluginContextListener implements ServletContextListener {
+
+  private static final Logger LOG = LoggerFactory.getLogger(PluginContextListener.class);
+
+  HawtioPlugin plugin = null;
+
+  @Override
+  public void contextInitialized(ServletContextEvent servletContextEvent) {
+
+    ServletContext context = servletContextEvent.getServletContext();
+
+    plugin = new HawtioPlugin();
+    plugin.setContext((String)context.getInitParameter("plugin-context"));
+    plugin.setName(context.getInitParameter("plugin-name"));
+    plugin.setScripts(context.getInitParameter("plugin-scripts"));
+    plugin.setDomain(null);
+    plugin.init();
+
+    LOG.info("Initialized {} plugin", plugin.getName());
+  }
+
+  @Override
+  public void contextDestroyed(ServletContextEvent servletContextEvent) {
+    plugin.destroy();
+    LOG.info("Destroyed {} plugin", plugin.getName());
+  }
+}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/resources/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/resources/WEB-INF/web.xml b/console/hawtio/src/main/resources/WEB-INF/web.xml
new file mode 100644
index 0000000..30ae7f0
--- /dev/null
+++ b/console/hawtio/src/main/resources/WEB-INF/web.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+	      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
+	      http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+	      version="2.4">
+
+  <description>Qpid Dispatch Router console hawt.io plugin</description>
+  <display-name>Dispatch Router console</display-name>
+
+  <context-param>
+    <description>Plugin's path on the server</description>
+    <param-name>plugin-context</param-name>
+    <param-value>${plugin-context}</param-value>
+  </context-param>
+
+  <context-param>
+    <description>Plugin's path on the server</description>
+    <param-name>plugin-name</param-name>
+    <param-value>${project.artifactId}</param-value>
+  </context-param>
+
+  <context-param>
+    <description>Plugin's path on the server</description>
+    <param-name>plugin-domain</param-name>
+    <param-value>${plugin-domain}</param-value>
+  </context-param>
+
+  <context-param>
+    <description>Plugin's path on the server</description>
+    <param-name>plugin-scripts</param-name>
+    <param-value>${plugin-scripts}</param-value>
+  </context-param>
+
+  <listener>
+    <listener-class>io.hawt.dispatch.PluginContextListener</listener-class>
+  </listener>
+
+
+</web-app>
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/resources/log4j.properties b/console/hawtio/src/main/resources/log4j.properties
new file mode 100644
index 0000000..a2ecc8d
--- /dev/null
+++ b/console/hawtio/src/main/resources/log4j.properties
@@ -0,0 +1,6 @@
+log4j.rootLogger=INFO, console
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%-5p | %t | %m%n
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/css/plugin.css
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/css/plugin.css b/console/hawtio/src/main/webapp/plugin/css/plugin.css
new file mode 100644
index 0000000..c6f5cc1
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/css/plugin.css
@@ -0,0 +1,725 @@
+/*
+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.
+*/
+main-display > .span8 {
+  height: 100%;
+  position: relative;
+}
+
+ul.qdrListNodes > li > span {
+  padding: 6px 20px; 6px; 6px;
+  display: block;
+}
+
+.qdrList .gridStyle {
+    width: 20em;
+    margin-right: 0;
+    float: left;
+}
+
+
+.qdrList div.gridDetails {
+
+    width: auto;
+}
+
+.selectedItems {
+/*    margin-left: 21em; */
+}
+
+.qdrListPane {
+    top: 110px;
+}
+
+.qdrListActions {
+    width: auto;
+}
+
+div.listAttrName {
+    padding-top: 5px;
+}
+
+div.listAttrName i.icon-bar-chart {
+    float: right;
+    margin: 3px 5px;
+}
+
+div.listAttrName i.icon-bar-chart.active, div.hastip i.icon-bar-chart.active, li.haschart i {
+    background-color: #AAFFAA;
+}
+
+div#main div ul.nav li a:not(.btn) {
+    background: initial !important;
+}
+
+div#main div ul.nav li.active a {
+    background-color: #f0f0ff !important;
+}
+
+div#main.qdr {
+    margin-top: 44px !important;
+}
+
+div.charts-header {
+  font-size: 1.2em;
+  color: #666666;
+  margin: 1em 0;
+}
+
+.selectedNode, .selectedAction, .selectedEntity {
+    font-weight: 600;
+    color: #606066;
+}
+
+.okButton {
+    text-align: center;
+    margin: 1em;
+}
+
+span.showChartsLink {
+    border: 1px solid blue;
+    padding: 1px 2px;
+}
+
+div.listGraphs p {
+    margin: 1em 0 2em 2em;
+    text-align: center;
+}
+
+div.centered {
+    text-align: center;
+    margin: 4em;
+}
+
+.modal-body.centered {
+	margin: 0;
+}
+
+/* dialog */
+div.aChart {
+    height: 200px;
+    width:  400px;
+    margin: 1em;
+}
+
+/* dashboard */
+div.aChart.hDash {
+	/* width: 21em; */
+	/* height: 17em; */
+	width: 100%;
+	height: 87%;
+	margin: 0;
+	padding: 0;
+
+}
+div.chartContainer {
+    float: left;
+	width: 100%;
+	height: 100%;
+	overflow: hidden;
+}
+
+/* the x and y axis lines */
+.d3Chart g.axis path.domain {
+    stroke-width: 1;
+    stroke: black;
+}
+
+/* the line surrounding the area chart */
+div.d3Chart path {
+/*    stroke: black; */
+    stroke-width: 0;
+/*	opacity: 0.5; */
+}
+
+/* the line above the area chart */
+/* the color gets overridden */
+div.d3Chart path.line {
+    stroke: steelblue;
+    stroke-width: 1.5;
+    fill: none;
+    opacity: 1;
+}
+
+.mo-rect {
+    fill: #ffffdd;
+    stroke: #f0f0f0;
+    stroke-width: 1;
+}
+
+.mo-guide {
+    fill: none;
+    stroke: #d0d0d0;
+    stroke-width: 2;
+    stroke-dasharray: 3,3;
+}
+
+div.d3Chart .title {
+    text-decoration: underline;
+}
+
+
+.axis line, .axis path {
+  fill: none;
+  shape-rendering: crispEdges;
+  stroke-width: 1;
+  stroke: #000000;
+}
+
+.axis line {
+  stroke: #C0C0C0;
+  stroke-dasharray: 1,1;
+  opacity: 0.5;
+}
+
+.y.axis text, .x.axis text, .focus text, div.d3Chart .title {
+    font-size: 12px;
+}
+
+.y.axis path {
+   stroke: #000;
+ }
+
+.overlay {
+   fill: none;
+   pointer-events: all;
+ }
+
+.focus circle {
+   fill: none;
+   stroke: steelblue;
+ }
+.focus .fo-table {
+	/* box-shadow: 2px 2px 3px #EEE; */
+}
+
+div.d3Chart {
+    padding: 1em 0;
+    border: 1px solid #C0C0C0;
+}
+div.d3Chart.hDash {
+    border: 0px;
+}
+
+div.d3Chart .axis path {
+	display: inherit;
+}
+.c3-circle {
+	display: none;
+}
+
+.fo-table {
+	border: 1px solid darkgray;
+	background-color: white;
+	font-size: .85em;
+}
+
+.fo-table td {
+	padding: 4px;
+	border-left: 1px solid darkgray;
+}
+.fo-table tr.detail td {
+	padding: 1px 4px;
+}
+.fo-title {
+	color: white;
+	background-color: darkgray;
+}
+
+.fo-table-legend {
+	width: 8px;
+	height: 8px;
+	border: 1px solid black;
+	margin: 0 4px;
+	display: inline-block;
+}
+
+svg .legend {
+	dominant-baseline: central;
+}
+
+div.chartContainer div.aChart {
+    margin-top: 0.5em;
+}
+
+div.qdr-attributes .tree-header select {
+	width: 100%;
+}
+
+div#main.qdr div ul.nav li.active a {
+  background-color: #e0e0ff !important;
+  color: #000000;
+}
+
+div#main.qdr .selected, .box.selected {
+  color: #000000;
+  text-shadow: none;
+}
+
+/* the selected node on the list page */
+div.qdrList li.active, ul.qdrListNodes li.active {
+    background-color: #e0e0ff;
+}
+
+div.qdr-attributes span.dynatree-selected a {
+    background-color: #e0e0ff;
+}
+div.qdr-attributes.pane {
+	position: absolute;
+	margin-left: 10px;
+}
+
+/* the selected row in the name table */
+div#main.qdr div.qdrList div.selected {
+  background-color: #e0e0ff !important;
+}
+
+#dialogChart {
+    height: 200px;
+}
+
+div.qdrCharts p.chartLabels button {
+    float: right;
+}
+
+div.qdrCharts p.chartLabels {
+     padding-right: 1em;;
+ }
+
+p.dialogHeader {
+    text-align: center;
+}
+
+p.dialogHeader input {
+    margin-top: 10px;
+    width: 480px;
+}
+
+.ui-slider-tick {
+  position: absolute;
+  background-color: #666;
+  width: 2px;
+  height: 8px;
+  top: 12px;
+  z-index: -1;
+}
+
+label.rateGroup {
+    float: left;
+}
+
+div.chartOptions div.dlg-slider {
+    float: left;
+    margin-left: 2em;
+    width: 28em;
+    font-size: 14px;
+}
+
+div.chartOptions div.duration {
+  width: 35em !important;
+}
+
+div.chartOptions .slider {
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+
+input[type="radio"] {
+    margin-top: 0 !important;
+}
+
+div.chartOptions legend {
+    font-size: 1.2em;
+    margin-bottom: 10px;
+}
+
+div.chartOptions span.minicolors-swatch {
+    width: 14px;
+    height: 14px;
+}
+
+.minicolors-input {
+    width: 4em;
+    padding: 0 0 0 24px !important;
+}
+
+div.colorPicker div.colorText {
+	display: inline-block;
+	width: 10em;
+}
+
+div.chartOptions p.sep {
+    height: 1em;
+}
+
+ul.nav-tabs {
+    border-bottom: 1px solid #ddd !important;
+}
+
+.chartOptions ul.nav-tabs {
+    margin-bottom: 0px !important;
+}
+
+div.tabbable div.tab-content {
+    overflow: visible;
+}
+
+div.tabbable ul.nav-tabs > .active > a {
+  background-color: #f8f8f8;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+}
+
+div.tabbable .tab-pane {
+    background-color: #f8f8f8;
+    padding: 12px;
+    border-right: 1px solid #ddd;
+    border-left: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+}
+div.dlg-large div.tabbable .tab-pane {
+	margin-left: 11em;
+}
+
+div.tabbable ul.nav-tabs {
+  margin-bottom: 0;
+}
+
+ul.qdrTopoModes {
+    position: relative;
+    top: -10px;
+}
+.overview.section {
+	/* width: 35em; */
+}
+.overview.section .ngGrid {
+	height: 12em !important;
+	min-height: 12em !important;
+}
+
+.overview.routers.section .ngGrid {
+ 	height: 16em !important;
+ 	min-height: 16em !important;
+}
+.overview.routers.section {
+ 	/*width: 15em; */
+ }
+
+.grid-align-value {
+	text-align: right;
+}
+
+.overview .ngRow:hover {
+	background:#e0e0ff;
+}
+
+.qdr-overview.pane.left, .qdr-attributes.pane.left {
+	top: 100px;
+}
+.qdr-overview.pane.left {
+	left: 10px;
+}
+
+.treeContainer {
+	/*width: 250px;
+	float: left;
+	overflow-y: auto;
+	border-right: 1px solid lightgrey;
+	height: 100vh;
+*/
+}
+
+#entityNames {
+    width: 20em;
+    float: left;
+}
+
+.treeDetails {
+	/* margin-left: 300px; */
+}
+
+.gridStyle:not(.noHighlight) .ui-grid-row:hover .ui-grid-cell-contents {
+	background-color: #e0e0ff;
+}
+
+.ngCellText {
+	padding: 4px 0 0 4px;
+}
+
+.overview {
+	border-bottom: 1px solid #d4d4d4;
+}
+
+.ui-grid-row.ui-grid-row-selected > [ui-grid-row] > .ui-grid-cell {
+  background-color: #e0e0ff;
+}
+
+.tab-content .tab-pane {
+    background-color: #f8f8f8;
+    padding: 12px;
+    border-right: 1px solid #ddd;
+    border-left: 1px solid #ddd;
+    border-bottom: 1px solid #ddd;
+}
+
+div.chartOptions ul.nav-tabs > .active > a {
+  background-color: #f8f8f8;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+}
+
+div.chartOptions label:nth-of-type(2) {
+    margin-left: 1em;
+}
+div.chartOptions label {
+	font-weight: normal;
+	display: inline-block;
+}
+
+/*
+.form-horizontal .control-label {
+    float: left;
+    width: 160px;
+    padding-top: 5px;
+    text-align: right;
+}
+
+.form-horizontal .controls {
+    margin-left: 180px;
+}
+
+.form-horizontal input,  {
+    display: inline-block;
+    margin-bottom: 0;
+    vertical-align: middle;
+}
+
+input[type="text"], input[type="number"], input[type="password"] {
+    background-color: #ffffff;
+    border: 1px solid #cccccc;
+    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+    -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+    -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
+    -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
+    -o-transition: border linear 0.2s, box-shadow linear 0.2s;
+    transition: border linear 0.2s, box-shadow linear 0.2s;
+}
+
+input[type="text"], input[type="number"], input[type="password"] {
+    display: inline-block;
+    width: 200px;
+    padding: 4px 6px;
+    margin-bottom: 10px;
+    font-size: 14px;
+    line-height: 20px;
+    color: #555555;
+    vertical-align: middle;
+    -webkit-border-radius: 4px;
+    -moz-border-radius: 4px;
+    border-radius: 4px;
+}
+
+.login input[type="checkbox"] {
+	margin-top: 0.75em;
+}
+*/
+
+#dispatch-login-container {
+	/* width: 18.5em; */
+	margin-top: 2em;
+}
+/*
+div.login.container {
+	width: 550px;
+}
+*/
+
+
+#overtree .fancytree-container {
+/*	border: 0px; */
+}
+
+#overtree span.fancytree-alert-icon.ui-icon-refresh {
+	/*background-position: -64px -80px; */
+}
+#overtree span.fancytree-alert-icon.ui-icon-transfer-e-w {
+	/*background-position: -112px -80px;*/
+}
+
+#alerts {
+	position: fixed;
+	right: 0;
+	top: 0;
+	z-index: 100;
+}
+
+.alert-enter,
+.alert-leave,
+.alert-move {
+  -webkit-transition: 1s linear all;
+  -moz-transition: 1s linear all;
+  -o-transition: 1s linear all;
+  transition: 1s linear all;
+  position:relative;
+}
+
+.alert-enter {
+  left:-10px;
+  opacity:0;
+}
+.alert-enter.alert-enter-active {
+  left:0;
+  opacity:1;
+}
+
+.alert-leave {
+  left:0;
+  opacity:1;
+}
+.alert-leave.alert-leave-active {
+  left:-10px;
+  opacity:0;
+}
+
+.alert-move {
+  opacity:0.5;
+}
+.alert-move.alert-move-active {
+  opacity:1;
+}
+
+.overview .table-striped tr:hover  td {
+	background-color: #e0e0ff !important;
+}
+
+#entityNames div.ngViewport {
+	overflow-x: hidden;
+}
+
+.connect-column.connect-form {
+	width: 30em;
+}
+
+.chartLabels button a {
+	text-decoration: none;
+}
+
+.fancytree-ico-c.router .fancytree-icon {
+
+}
+
+.tabs-left .nav-tabs {
+	float: left;
+}
+.tabs-left .nav-tabs > li {
+/*	float: initial; */
+}
+
+div.modal.dlg-large {
+	width: 53em;
+}
+
+button.hdash-button a {
+	text-decoration: none;
+	color: #fff;
+}
+
+div.widget-body > div {
+	height: 100%;
+}
+
+div.qdrCharts {
+	height: 100%;
+}
+
+ul.dispatch-view {
+	margin-bottom: 0 !important;
+}
+
+.qdr-overview.pane.left span:not(.dynatree-has-children) .dynatree-icon:before,
+.qdr-attributes.pane.left span:not(.dynatree-has-children) .dynatree-icon:before {
+    color: green;
+}
+
+span:not(.dynatree-has-children).address .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.address .dynatree-icon:before {
+    content: "\f0ac";
+}
+
+span:not(.dynatree-has-children).connection.external .dynatree-icon:before,
+span:not(.dynatree-has-children).connection.normal .dynatree-icon:before {
+    content: "\f08e";
+}
+span:not(.dynatree-has-children).connection.inter-router .dynatree-icon:before {
+     content: "\f152";
+}
+span:not(.dynatree-has-children).no-data .dynatree-icon:before {
+     content: "\f05e";
+     color: red !important;
+}
+span:not(.dynatree-has-children).loading .dynatree-icon:before {
+     content: "\f254";
+}
+span:not(.dynatree-has-children).router\.node .dynatree-icon:before {
+     content: "\f013";
+}
+span:not(.dynatree-has-children).connector .dynatree-icon:before {
+     content: "\f126";
+}
+span:not(.dynatree-has-children).container .dynatree-icon:before {
+     content: "\f16c";
+}
+span:not(.dynatree-has-children).log .dynatree-icon:before {
+     content: "\f0f6";
+}
+span:not(.dynatree-has-children).router\.link.inter-router .dynatree-icon:before {
+     content: "\f04e";
+}
+span:not(.dynatree-has-children).router\.link.endpoint .dynatree-icon:before {
+     content: "\f051";
+}
+span:not(.dynatree-has-children).listener .dynatree-icon:before {
+     content: "\f025";
+}
+span:not(.dynatree-has-children).connection .dynatree-icon:before {
+     content: "\f07e";
+}
+span:not(.dynatree-has-children).waypoint .dynatree-icon:before {
+     content: "\f0ec";
+}
+span:not(.dynatree-has-children).router .dynatree-icon:before {
+     content: "\f047";
+}
+span:not(.dynatree-has-children).fixedAddress .dynatree-icon:before {
+     content: "\f015";
+}
+span:not(.dynatree-has-children).linkRoutePattern .dynatree-icon:before {
+     content: "\f039";
+}
+span:not(.dynatree-has-children).allocator .dynatree-icon:before {
+     content: "\f170";
+}
+
+.ngCellText {
+/*    color: #333333; */
+}
+
+.changed {
+    color: #339933;
+}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css b/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css
new file mode 100644
index 0000000..0eac80d
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css
@@ -0,0 +1,495 @@
+/*
+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.
+*/
+
+svg {
+  background-color: transparent;
+  cursor: default;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  -o-user-select: none;
+  user-select: none;
+}
+
+svg:not(.active):not(.ctrl) {
+  cursor: crosshair;
+}
+#end-arrow-selected, #start-arrow-selected {
+	stroke: #00F;
+	fill: #00F;
+}
+
+path.link {
+  fill: none;
+  stroke: #000;
+  stroke-width: 4px;
+  cursor: default;
+}
+
+svg:not(.active):not(.ctrl) path.link {
+  cursor: pointer;
+}
+
+path.link.selected {
+  stroke-dasharray: 10,2;
+  stroke: #00F  !important;
+}
+
+
+path.link.highlighted {
+    stroke: #0F0 !important;
+
+}
+
+path.link.temp {
+  opacity: 0.3;
+}
+path.link.temp.over {
+  opacity: 0.8;
+  stroke-dasharray: 10,2;
+}
+
+path.link.dragline {
+  pointer-events: none;
+}
+
+path.link.hidden {
+  stroke-width: 0;
+}
+
+
+circle.node {
+    stroke-width: 1.5px;
+    cursor: pointer;
+    stroke: darkgray;
+    fill: lightgray;
+}
+
+circle.node.reflexive {
+    stroke: #F00 !important;
+    stroke-width: 2.5px;
+}
+circle.node.selected {
+    stroke: #F00 !important;
+    stroke-width: 2px;
+    fill: #e0e0ff !important;
+}
+circle.node.inter-router {
+    fill: #EAEAEA;
+}
+circle.node.normal {
+    fill: #F0F000;
+}
+circle.node.on-demand {
+    fill: #00F000;
+}
+
+circle.node.fixed {
+    stroke-dasharray: 10,2;
+}
+circle.node.temp {
+    stroke: #f80;
+    fill: #f0f0ff;
+}
+
+text {
+  font: 12px sans-serif;
+  pointer-events: none;
+  /*font-family: monospace;*/
+
+}
+
+.tooltipsy
+{
+    padding: 10px;
+/*    max-width: 320px;*/
+    color: #303030;
+    background-color: #fcfcfe;
+    border: 1px solid #deca7e;
+    border-radius: 5px;
+}
+
+.tiptable {
+
+}
+.tiptable tr {
+	border-bottom: 1px solid #ccc;
+}
+
+.tiptable tr:last-child {
+	border-bottom: 0px;
+}
+
+.tiptable tr:nth-child(even) {
+	background: #fcfcfe;
+}
+.tiptable tr:nth-child(odd) {
+	background: #FFF
+}
+
+text.id {
+  text-anchor: middle;
+  font-weight: bold;
+}
+
+.row-fluid.tertiary {
+  position: relative;
+  left: 20px;
+}
+
+.row-fluid.tertiary.left {
+  float: left;
+}
+
+.row-fluid.tertiary.panel {
+  width: 410px;
+  height: 100%;
+}
+
+.panel-adjacent {
+  margin-left: 430px;
+}
+
+#topologyForm.selected {
+  border: 1px solid red;
+}
+#topologyForm {
+  border: 1px solid white;
+  padding: 2px;
+  position: relative;
+  top: -8px;
+}
+
+#topologyForm > div {
+  width:396px;
+  /*height: 100vh;*/
+}
+
+/* globe */
+.land {
+  fill: #999;
+  stroke-opacity: 1;
+}
+
+.graticule {
+  fill: none;
+  stroke: black;
+  stroke-width:.5;
+  opacity:.1;
+}
+
+.labels {
+    font: 18px sans-serif;
+    fill: black;
+    opacity: .85;
+	text-anchor: middle;
+}
+
+.noclicks { pointer-events:none; }
+
+.point {  opacity:.6; }
+
+.arcs {
+  opacity:.7;
+  stroke: darkgreen;
+  stroke-width: 3;
+}
+.flyers {
+  stroke-width:1;
+  opacity: 0;
+  stroke: darkred;
+}
+.arc, .flyer {
+  stroke-linejoin: round;
+  fill:none;
+}
+.arc { }
+.arc:hover {
+  stroke: darkred;
+}
+.flyer { }
+.flyer:hover {
+  stroke: darkgreen;
+}
+.arc.inter-router {
+  stroke: darkblue;
+}
+
+#addNodeForm {
+  padding: 1em;
+}
+
+
+li.currentStep {
+  font-weight: bold;
+}
+
+.qdrTopology div.panel {
+  position: absolute;
+}
+/*
+.ui-dialog-titlebar {
+    border: 0;
+    background: transparent;
+}
+*/
+
+/*
+.ui-tabs.ui-tabs-vertical {
+    padding: 0;
+    width: 48em;
+}
+.ui-tabs.ui-tabs-vertical .ui-widget-header {
+    border: none;
+}
+.ui-tabs.ui-tabs-vertical .ui-tabs-nav {
+    float: left;
+    width: 10em;
+    background: #CCC;
+    border-radius: 4px 0 0 4px;
+    border-right: 1px solid gray;
+}
+.ui-tabs.ui-tabs-vertical .ui-tabs-nav li {
+    clear: left;
+    width: 100%;
+    margin: 0.1em 0;
+    border: 1px solid gray;
+    border-width: 1px 0 1px 1px;
+    border-radius: 4px 0 0 4px;
+    overflow: hidden;
+    position: relative;
+    right: -2px;
+    z-index: 2;
+}
+.ui-tabs.ui-tabs-vertical .ui-tabs-nav li a {
+    display: block;
+    width: 100%;
+    padding: 0.1em 1em;
+}
+.ui-tabs.ui-tabs-vertical .ui-tabs-nav li a:hover {
+    cursor: pointer;
+}
+.ui-tabs.ui-tabs-vertical .ui-tabs-nav li.ui-tabs-active {
+    margin-bottom: 0.2em;
+    padding-bottom: 0;
+    border-right: 1px solid white;
+}
+.ui-tabs.ui-tabs-vertical .ui-tabs-nav li:last-child {
+    margin-bottom: 10px;
+}
+.ui-tabs.ui-tabs-vertical .ui-tabs-panel {
+    float: left;
+    width: 34em;
+    border-left: 1px solid gray;
+    border-radius: 0;
+    position: relative;
+    left: -1px;
+}
+
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected {
+    right: -3px !important;
+}
+
+.ui-tabs li i.ui-icon {
+    display: inline-block;
+}
+*/
+.ui-tabs .ui-tabs-panel {
+    /* padding-top: 0 !important; */
+}
+
+.ui-widget-content fieldset {
+  float: left;
+  padding: 0 1em 0 0;
+}
+
+.entity-description {
+  color: #960;
+  font-size: 90%;
+}
+
+.attr-description {
+  padding-top: 1.5em;
+  float: right;
+  width: 17em;
+}
+.attr-annotations {
+    padding-top: 2.5em;
+    clear: both;
+}
+.attr-annotations > span {
+    padding-top: 0.5em;
+    border-top: 1px dashed darkgray;
+    display: block;
+}
+
+.attr-type {
+    color: #990;
+    font-size: 85%;
+}
+.attr-required {
+    color: red;
+    font-size: 85%;
+}
+.attr-unique {
+    color: green;
+    font-size: 85%;
+}
+
+#tabs.nodeEntities {
+  border: 0;
+}
+
+#tabs ul.nodeTabs {
+  background: #fff;
+}
+
+#tabs #Container {
+  border-left: 1px solid #aaa;
+}
+
+#tabs.ui-tabs .ui-tabs-nav li {
+  border-bottom: 1px solid #aaa !important;
+}
+
+.entity-fields {
+  /* height: 400px; */
+  overflow-y: scroll;
+  overflow-x: hidden;
+}
+
+div.boolean label:first-child {
+    float: left;
+    margin-right: 1em;
+}
+div.boolean {
+    padding-bottom: 1em;
+}
+
+.entity-fields label {
+    font-weight: 600;
+    margin-top: 0.5em;
+	display: inline;
+}
+
+.aggregate {
+	text-align: right;
+}
+
+.aggregate i {
+	float: right;
+    margin: 3px 3px 3px 8px;
+}
+
+.aggregate .hastip {
+	padding: 5px;
+}
+
+.subTip .tipsy-inner {
+	background-color: white;
+	color: black;
+	font-size: 1.3em;
+	border: 1px solid black;
+}
+
+.subTip .tipsy-arrow-n { border-bottom-color: black; }
+.subTip .tipsy-arrow-s { border-top-color: black; }
+.subTip .tipsy-arrow-e { border-left-color: black; }
+.subTip .tipsy-arrow-w { border-right-color: black; }
+
+
+.contextMenu {
+    display:none;
+	position:absolute;
+	left:30px;
+	top:-30px;
+	z-index:999;
+	/* width:300px; */
+}
+.contextMenu ul {
+	width:300px;
+	margin:0;
+	/* padding:10px; */
+	list-style:none;
+	background:#fff;
+	color:#333;
+    font-weight: 600;
+	/* -moz-border-radius:5px; -webkit-border-radius:5px; border-radius:5px; */
+	-moz-box-shadow:5px 5px 5px #ddd; -webkit-box-shadow:5px 5px 5px #999; box-shadow:5px 5px 5px #ddd;
+	border: 1px solid #aaa;
+}
+.contextMenu ul li {
+	padding:5px 10px;
+	/* border-bottom: solid 1px #ccc; */
+}
+.contextMenu ul li:hover {
+	background:#4a90d9; color:#fff;
+}
+.contextMenu ul li:last-child {
+    border:none;
+}
+
+.na {
+    display: none;
+}
+.contextMenu ul li.new {
+    display: block;
+}
+.contextMenu ul li.adding, .contextMenu ul li.adding + li {
+    display: block;
+}
+.contextMenu ul li.force-display {
+    display: block;
+}
+.contextMenu ul li.context-separator {
+    background-color: lightgray;
+    height: 1px;
+    padding: 0;
+}
+
+#crosssection {
+    display: none;
+    position: absolute;
+    top: 200px;
+    left: 600px;
+}
+
+.node circle {
+/*  fill: rgb(31, 119, 180);
+  fill-opacity: .25; */
+  fill: #cfe2f3;
+  fill-opacity: .98;
+  stroke: black;
+  stroke-width: 3px;
+}
+
+.leaf circle {
+  fill: #6fa8dc;
+  fill-opacity: 0.95;
+  stroke-width: 3px;
+}
+
+.leaf circle[title] {
+    font-family: monospace;
+
+}
+
+.qdrListActions .ngGrid {
+	height: 100vh;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrCharts.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrCharts.html b/console/hawtio/src/main/webapp/plugin/html/qdrCharts.html
new file mode 100644
index 0000000..90df9b3
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrCharts.html
@@ -0,0 +1,63 @@
+<div class="main-display row-fluid qdrCharts" ng-controller="QDR.ChartsController">
+    <div ng-hide="dashLogin" ng-repeat="chart in svgCharts" class="chartContainer ">
+        <p class="chartLabels">
+            <button ng-click="editChart(chart)" title="Configure"><i class="icon-edit"></i></button>
+            <button ng-click="zoomChart(chart)" title="Zoom {{chart.zoomed ? 'in' : 'out'}}" ng-if="!chart.chart.request().nodeList"><i ng-class="chart.zoomed ? 'icon-zoom-in' : 'icon-zoom-out'"></i></button>
+        </p><div style="clear:both"></div>
+        <div id="{{chart.chart.id()}}" class="hDash aChart d3Chart"></div>
+    </div>
+    <div ng-init="chartsLoaded()"></div>
+    <div ng-show="dashLogin" class="centered alert alert-warning">
+        <p>You need to <a ng-href="{{loginHref}}">login</a> to Dispatch Router before viewing this chart.</p>
+    </div>
+</div>
+
+<!--
+    This is the template for the graph dialog that is displayed. It uses the
+    dialogCtrl controller in qdrCharts.js.
+-->
+<script type="text/ng-template" id="chart-config-template.html">
+<div class="chartOptions">
+    <div class="modal-header">
+        <h3 class="modal-title">Chart {{chart.attr() | humanify}}</h3>
+    </div>
+    <div class="modal-body">
+        <div id="{{svgDivId}}" class="d3Chart"></div>
+        <tabset>
+            <tab heading="Type">
+                <legend>Chart type</legend>
+                <label><input type="radio" ng-model="dialogChart.type" value="value" /> Value Chart</label>
+                <label><input type="radio" ng-model="dialogChart.type" value="rate" /> Rate Chart</label>
+                <div class="dlg-slider" ng-show="dialogChart.type=='rate'">
+                    <span>Rate Window: {{rateWindow}} second{{rateWindow > 1 ? "s" : ""}}</span>
+                    <div class="slider" ui-slider="slider.options" ng-model="rateWindow"></div>
+                </div>
+                <div style="clear:both;"> </div>
+            </tab>
+            <tab ng-hide="$parent.chart.aggregate()" heading="Colors">
+                <legend>Chart colors</legend>
+                <div class="colorPicker">
+                    <div class="colorText">Area ({{dialogChart.areaColor}}):</div><div hawtio-color-picker="dialogChart.areaColor"></div>
+                </div>
+                <div class="colorPicker">
+                    <div class="colorText">Line ({{dialogChart.lineColor}}):</div><div hawtio-color-picker="dialogChart.lineColor"></div>
+                </div>
+                <div style="clear:both;"> </div>
+            </tab>
+            <tab heading="Duration">
+                <legend>Chart duration</legend>
+                <div class="dlg-slider duration">
+                    <span>Show data for past {{dialogChart.visibleDuration}} minute{{dialogChart.visibleDuration > 1 ? "s" : ""}}</span> <div class="slider" ui-slider="duration.options" ng-model="dialogChart.visibleDuration"></div>
+                </div>
+                <div style="clear:both;"> </div>
+
+            </tab>
+        </tabset>
+    </div>
+    <div class="modal-footer">
+        <button class="btn btn-success" type="button" ng-click="apply()">Apply</button>
+        <button class="btn btn-primary" type="button" ng-click="okClick()">Close</button>
+    </div>
+</div>
+</script>
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrConnect.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrConnect.html b/console/hawtio/src/main/webapp/plugin/html/qdrConnect.html
new file mode 100644
index 0000000..83b5e2d
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrConnect.html
@@ -0,0 +1,38 @@
+<div class="row-fluid" ng-controller="QDR.SettingsController">
+      <div class="login container" ng-hide="connecting">
+          <div class="row" id="dispatch-login-container">
+              <div class="connect-column">
+                  <div class="alert alert-success">
+                      <p>
+                          Enter the address and port of a <strong><a href="http://qpid.apache.org/components/dispatch-router/" target="_blank">Qpid Dispatch Router</a></strong> to connect..
+                      </p>
+
+                      <p>
+                          The port should be a websockets <==> tcp proxy.
+                      </p>
+
+                      <p>
+                          When Autostart is checked, you will be automatically logged in to the router the next time you start the console.
+                      </p>
+
+                  </div>
+              </div>
+              <div class="connect-column connect-form">
+                  <div simple-form name="settings" data="formConfig" entity="formEntity"></div>
+                  <p></p>
+                  <div>
+                      <button class="btn btn-primary pull-right" ng-disabled="settings.$invalid" ng-click="connect()">{{buttonText()}}</button>
+                  </div>
+              </div>
+          </div>
+      </div>
+
+      <div class="centered" ng-show="connecting">
+        <i class="icon-spin icon-spinner icon-4x"></i>
+        <p>Please wait, connecting now...</p>
+      </div>
+      <div class="centered" ng-show="connectionError">
+        <p>There was a connection error: {{connectionErrorText}}</p>
+      </div>
+
+</div>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrGraphs.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrGraphs.html b/console/hawtio/src/main/webapp/plugin/html/qdrGraphs.html
new file mode 100644
index 0000000..c311246
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrGraphs.html
@@ -0,0 +1,15 @@
+<div ng-controller="QDR.ListController" title=""
+     class="prefs">
+  <div class="row-fluid">
+    <div class="tabbable">
+      <div value="graphs"
+           class="tab-pane" 
+           title="graphs">
+          <div>
+              <div id="graph"></div>
+
+          </div>
+      </div>
+     </div>
+  </div>
+</div>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrLayout.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrLayout.html b/console/hawtio/src/main/webapp/plugin/html/qdrLayout.html
new file mode 100644
index 0000000..80cd5ea
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrLayout.html
@@ -0,0 +1,26 @@
+<!--
+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.
+-->
+<ul class="nav nav-tabs connected dispatch-view" ng-controller="QDR.NavBarController">
+  <li ng-repeat="link in breadcrumbs" title="{{link.title}}" ng-show="isValid(link)" ng-class='{active : isActive(link.href), "pull-right" : isRight(link)}'>
+    <a ng-href="{{link.href}}" ng-bind-html-unsafe="link.content"></a>
+  </li>
+</ul>
+<div class="row-fluid dispatch-router">
+  <div ng-view></div>
+</div>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrList.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrList.html b/console/hawtio/src/main/webapp/plugin/html/qdrList.html
new file mode 100644
index 0000000..adb249f
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrList.html
@@ -0,0 +1,55 @@
+<!--
+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.
+-->
+
+<div ng-controller="QDR.ListController">
+    <hawtio-pane class="qdr-attributes" position="left" width="200">
+        <div class="treeContainer">
+            <div class="tree-header"><select ng-options="node as node.name for node in nodes" ng-model="currentNode" ng-change="selectNode(currentNode)"></select></div>
+            <div id="entityTree" onSelect="onTreeSelected" onRoot="onRootReady" hideRoot="true"></div>
+            <div ng-init="treeReady()"></div>
+        </div>
+    </hawtio-pane>
+    <div class="row-fluid qdrListActions">
+        <h4>{{selectedRecordName}}</h4>
+        <div ng-show="currentMode.id === 'attributes'" class="selectedItems">
+            <div ng-grid="details"></div>
+        </div>
+        <div ng-show="currentMode.id === 'operations'">
+            Operations are not implemented yet.
+        </div>
+    </div>
+</div>
+
+<!--
+    This is the template for the graph dialog that is displayed.
+-->
+<script type="text/ng-template" id="template-from-script.html">
+    <div class="modal-header">
+        <h3 class="modal-title">Chart {{chart.attr() | humanify}}</h3>
+    </div>
+    <div class="modal-body">
+            <div id="{{svgDivId}}" class="d3Chart"></div>
+    </div>
+    <div class="modal-footer">
+        <span>
+            <button class="btn btn-success hdash-button" type="button" ng-click="addHChart()" title="Add this chart to a dashboard"><a ng-href="{{addToDashboardLink()}}"><i class="icon-share"></i> Add this chart to a dashboard</a></button>
+        </span>
+        <button class="btn btn-primary" type="button" ng-click="ok()">Close</button>
+    </div>
+</script>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html b/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html
new file mode 100644
index 0000000..61d5143
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html
@@ -0,0 +1,92 @@
+<!--
+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.
+-->
+<!--
+<div ng-controller="QDR.OverviewController">
+
+    <div class="treeContainer">
+        <div id="overtree"></div>
+    </div>
+
+    <div class="treeDetails" ng-include="template.url"></div>
+</div>
+-->
+<div ng-controller="QDR.OverviewController">
+    <hawtio-pane class="qdr-overview" position="left" width="300">
+        <div class="treeContainer">
+            <div id="overtree"></div>
+        </div>
+    </hawtio-pane>
+    <div class="treeDetails" ng-include="template.url"></div>
+</div>
+
+<!-- the following scripts are content that gets loaded into the above div that has the temple.url -->
+<script type="text/ng-template" id="routers.html">
+    <div class="row-fluid">
+        <h3>Routers</h3>
+        <div class="overview">
+            <div class="gridStyle" ng-grid="allRouters"></div>
+        </div>
+    </div>
+</script>
+
+<script type="text/ng-template" id="router.html">
+    <div class="row-fluid">
+        <h3>Router {{router.data.title}}</h3>
+        <div class="gridStyle noHighlight" ng-grid="routerGrid"></div>
+    </div>
+</script>
+
+<script type="text/ng-template" id="addresses.html">
+    <div class="row-fluid">
+    <h3>Addresses</h3>
+    <div class="overview">
+        <div class="gridStyle" ng-grid="addressGrid"></div>
+    </div>
+    </div>
+</script>
+<script type="text/ng-template" id="address.html">
+    <div class="row-fluid">
+        <h3>Address {{address.data.title}}</h3>
+        <div class="gridStyle noHighlight" ng-grid="addressGrid"></div>
+    </div>
+</script>
+<script type="text/ng-template" id="connections.html">
+    <div class="row-fluid">
+    <h3>Connections</h3>
+    <div class="overview">
+        <div class="gridStyle" ng-grid="allConnectionGrid"></div>
+    </div>
+    </div>
+</script>
+<script type="text/ng-template" id="connection.html">
+    <div class="row-fluid">
+    <h3>Connection {{connection.data.title}}</h3>
+    <div class="gridStyle noHighlight" ng-grid="connectionGrid"></div>
+    </div>
+</script>
+<script type="text/ng-template" id="logs.html">
+    <div class="row-fluid">
+    <h3>Logs</h3>
+    </div>
+</script>
+<script type="text/ng-template" id="log.html">
+    <div class="row-fluid">
+    <h3>Log {{log.data.title}}</h3>
+    </div>
+</script>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrSchema.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrSchema.html b/console/hawtio/src/main/webapp/plugin/html/qdrSchema.html
new file mode 100644
index 0000000..15ebb46
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrSchema.html
@@ -0,0 +1,21 @@
+<!--
+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.
+-->
+<div class="main-display row-fluid" ng-controller="QDR.SchemaController">
+    <json-formatter json="schema" open="2"></json-formatter>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html b/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html
new file mode 100644
index 0000000..704c8e2
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html
@@ -0,0 +1,201 @@
+<!--
+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.
+-->
+<div class="qdrTopology row-fluid" ng-controller="QDR.TopologyController">
+    <div class="tertiary left panel">
+        <div id="topologyForm" ng-class="{selected : isSelected()}">
+            <!-- <div ng-repeat="form in forms" ng-show="isVisible(form)" ng-class='{selected : isSelected(form)}'> -->
+
+            <div ng-show="isGeneral()">
+                <h4>Router Info</h4>
+                <div class="gridStyle" ng-grid="topoGridOptions"></div>
+            </div>
+            <div ng-show="isConnections()">
+                <h4>Connection Info</h4>
+                <div class="gridStyle" ng-grid="topoConnOptions"></div>
+            </div>
+            <div id="addNodeForm" ng-show="isAddNode()">
+                <h4>Add a new router</h4>
+                <ul>
+                    <li>Click on an existing router to create a connection to the new router</li>
+                    <li>Double-click on the new router to <button ng-click="editNewRouter()">edit</button> its properties</li>
+                    <li ng-show="addingNode.hasLink" >Right-click on a new connection to edit its properties</li>
+                </ul>
+                <button ng-click="cancel()">Cancel</button>
+            </div>
+        </div>
+    </div>
+    <div class="panel-adjacent">
+
+<!--
+        <ul class="nav nav-tabs ng-scope qdrTopoModes">
+            <li ng-repeat="mode in modes" ng-class="{active : isModeActive(mode.name), 'pull-right' : isRight(mode)}" ng-click="selectMode('{{mode.name}}')" >
+                <a data-placement="bottom" class="ng-binding"> {{mode.name}} </a></li>
+        </ul>
+-->
+        <div id="topology" ng-show="mode == 'Diagram'"><!-- d3 toplogy here --></div>
+        <div id="crosssection"><!-- d3 pack here --></div>
+        <!-- <div id="addRouter" ng-show="mode == 'Add Node'"></div> -->
+        <div id="node_context_menu" class="contextMenu">
+            <ul>
+                <li class="na" ng-class="{new: contextNode.cls == 'temp'}" ng-click="addingNode.trigger = 'editNode'">Edit...</li>
+                <li class="na" ng-class="{adding: addingNode.step > 0}" ng-click="addingNode.step = 0">Cancel add</li>
+                <li class="context-separator"></li>
+                <li class="na" ng-class="{'force-display': !isFixed()}" ng-click="setFixed(true)">Freeze in place</li>
+                <li class="na" ng-class="{'force-display': isFixed()}" ng-click="setFixed(false)">Unfreeze</li>
+            </ul>
+        </div>
+        <div id="svg_context_menu" class="contextMenu">
+            <ul>
+                <li ng-click="addingNode.step = 2">Add a new router</li>
+            </ul>
+        </div>
+        <div id="link_context_menu" class="contextMenu">
+            <ul>
+                <li ng-click="reverseLink()">Reverse connection direction</li>
+                <li ng-click="removeLink()">Remove connection</li>
+            </ul>
+        </div>
+
+    </div>
+</div>
+<!--
+    This is the template for the node edit dialog that is displayed.
+-->
+<script type="text/ng-template" id="node-config-template.html">
+    <div class="modal-header">
+        <h3 class="modal-title">Configure new router</h3>
+    </div>
+    <div class="modal-body">
+        <form novalidate name="editForm">
+
+            <tabset vertical="true" class="tabs-left">
+                <tab ng-repeat="entity in entities"> <!-- ng-class="{separated: entity.tabName == 'listener0'}" -->
+                    <tab-heading>
+                        <i ng-if="entity.icon !== ''" ng-class="entity.icon ? 'ui-icon-arrowthick-1-w' : 'ui-icon-arrowthick-1-e'" class="ui-icon"></i>{{entity.humanName}}
+                    </tab-heading>
+                    <div class="entity-description">{{entity.description}}</div>
+                    <fieldset>
+                        <div ng-mouseenter="showDescription(attribute, $event)" ng-repeat="attribute in entity.attributes">
+                            <label for="{{attribute.name}}">{{attribute.humanName}}</label>
+<!-- we can't do <input type="{angular expression}"> because... jquery throws an exception because... -->
+                            <div ng-if="attribute.input == 'input'">
+                                <!-- ng-pattern="testPattern(attribute)" -->
+                                <input ng-if="attribute.type == 'number'" type="number" name="{{attribute.name}}" id="{{attribute.name}}" ng-model="attribute.value" ng-required="attribute.required" class="ui-widget-content ui-corner-all"/>
+                                <input ng-if="attribute.type == 'text'" type="text" name="{{attribute.name}}" id="{{attribute.name}}" ng-model="attribute.value" ng-required="attribute.required" class="ui-widget-content ui-corner-all"/>
+                            </div>
+                            <div ng-if="attribute.input == 'select'">
+                                <select id="{{attribute.name}}" ng-model="attribute.selected" ng-options="item for item in attribute.rawtype"></select>
+                            </div>
+                            <div ng-if="attribute.input == 'boolean'" class="boolean">
+                                <label><input type="radio" ng-model="attribute.value" value="true"> True</label>
+                                <label><input type="radio" ng-model="attribute.value" value="false"> False</label>
+                            </div>
+                        </div>
+                    </fieldset>
+                    <div class="attr-description">{{attributeDescription}}
+                        <div class="attr-type">{{attributeType}}</div>
+                        <div class="attr-required">{{attributeRequired}}</div>
+                        <div class="attr-unique">{{attributeUnique}}</div>
+                    </div>
+                    <div class="attr-annotations" ng-repeat="annotation in entity.annotatedBy">
+                        <span>You can also enter the <button ng-click="selectAnnotationTab(annotation)">{{annotation}}</button> values.</span>
+                    </div>
+                </tab>
+            </tabset>
+
+
+        </form>
+    </div>
+    <div class="modal-footer">
+        <button class="btn btn-primary" type="button" ng-click="download()">Download</button>
+        <button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
+    </div>
+</script>
+
+<script type="text/ng-template" id="config-file-header.html">##
+## 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
+##
+</script>
+
+<script type="text/ng-template" id="download-dialog-template.html">
+    <div class="modal-header">
+        <h3 class="modal-title">Configure new router</h3>
+    </div>
+    <div class="modal-body">
+
+        <label title="Show descriptions and default values in confile files"><input type="checkbox" ng-model="verbose"> Verbose output</label>
+        <div>
+            <button ng-click="download()">Download</button>
+            <button class="btn" zero-clipboard data-clipboard-text="{{output}}" title="Copy to clipboard">
+                <i class="icon-copy"></i>
+            </button> configuration file for {{newRouterName}}
+        </div>
+        <div ng-repeat="part in parts">
+            <button ng-click="downloadPart(part)">Download</button>
+            <button class="btn" zero-clipboard data-clipboard-text="{{part.output}}" title="Copy to clipboard">
+                <i class="icon-copy"></i>
+            </button> connector section for {{part.name}}
+        </div>
+
+    </div>
+    <div class="modal-footer">
+        <button class="btn btn-primary" type="button" ng-click="done()">Done</button>
+    </div>
+
+<!--
+
+    <div title="Configure new router">
+
+        <label title="Show descriptions and default values in confile files"><input type="checkbox" ng-model="verbose"> Verbose output</label>
+        <div>
+            <button ng-click="download()">Download</button>
+            <button class="btn" zero-clipboard data-clipboard-text="{{output}}" title="Copy to clipboard">
+                <i class="icon-copy"></i>
+            </button> configuration file for {{newRouterName}}
+        </div>
+        <div ng-repeat="part in parts">
+            <button ng-click="downloadPart(part)">Download</button>
+            <button class="btn" zero-clipboard data-clipboard-text="{{part.output}}" title="Copy to clipboard">
+                <i class="icon-copy"></i>
+            </button> connector section for {{part.name}}
+        </div>
+
+        <div class="okButton">
+            <button ng-click="done()">Done</button>
+        </div>
+
+    </div>
+-->
+</script>
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js b/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js
new file mode 100644
index 0000000..59ea4d3
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js
@@ -0,0 +1,241 @@
+/**
+ * @module QDR
+ * @mail QDR
+ *
+ * The main entry point for the QDR module
+ *
+ */
+var QDR = (function(QDR) {
+
+  /**
+   * @property pluginName
+   * @type {string}
+   *
+   * The name of this plugin
+   */
+  QDR.pluginName = 'dispatch_plugin';
+  QDR.pluginRoot = "/" + QDR.pluginName;
+  /**
+   * @property log
+   * @type {Logging.Logger}
+   *
+   * This plugin's logger instance
+   */
+  QDR.log = Logger.get('QDR');
+
+  /**
+   * @property contextPath
+   * @type {string}
+   *
+   * The top level path of this plugin on the server
+   *
+   */
+  QDR.contextPath = "/dispatch-plugin/";
+
+  /**
+   * @property templatePath
+   * @type {string}
+   *
+   * The path to this plugin's partials
+   */
+  QDR.templatePath = QDR.contextPath + "plugin/html/";
+
+  QDR.SETTINGS_KEY = 'QDRSettings';
+  QDR.LAST_LOCATION = "QDRLastLocation";
+
+  /**
+   * @property module
+   * @type {object}
+   *
+   * This plugin's angularjs module instance.  This plugin only
+   * needs hawtioCore to run, which provides services like
+   * workspace, viewRegistry and layoutFull used by the
+   * run function
+   */
+  QDR.module = angular.module('dispatch_plugin', ['bootstrap', 'jsonFormatter', 'hawtio-ui', 'hawtio-forms', 'ui.bootstrap.dialog', 'hawtioCore'])
+      .config(function($routeProvider) {
+        /**
+         * Here we define the route for our plugin.  One note is
+         * to avoid using 'otherwise', as hawtio has a handler
+         * in place when a route doesn't match any routes that
+         * routeProvider has been configured with.
+         */
+		 $routeProvider
+			.when('/dispatch_plugin', {
+				templateUrl: QDR.templatePath + 'qdrConnect.html'
+			})
+			.when('/dispatch_plugin/overview', {
+				templateUrl: QDR.templatePath + 'qdrOverview.html'
+			})
+			.when('/dispatch_plugin/topology', {
+				templateUrl: QDR.templatePath + 'qdrTopology.html'
+			})
+			.when('/dispatch_plugin/list', {
+				templateUrl: QDR.templatePath + 'qdrList.html'
+			})
+			.when('/dispatch_plugin/schema', {
+				templateUrl: QDR.templatePath + 'qdrSchema.html'
+			})
+			.when('/dispatch_plugin/charts', {
+				templateUrl: QDR.templatePath + 'qdrCharts.html'
+			})
+			.when('/dispatch_plugin/connect', {
+				templateUrl: QDR.templatePath + 'qdrConnect.html'
+			})
+      })
+	  .config(function ($compileProvider) {
+			var cur = $compileProvider.urlSanitizationWhitelist();
+			$compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|blob):/);
+			cur = $compileProvider.urlSanitizationWhitelist();
+	  })
+	  .config(function (JSONFormatterConfigProvider) {
+			// Enable the hover preview feature
+			JSONFormatterConfigProvider.hoverPreviewEnabled = true;
+	  })
+	  .filter('to_trusted', function($sce){
+			return function(text) {
+				return $sce.trustAsHtml(text);
+			};
+      })
+      .filter('humanify', function (QDRService) {
+			return function (input) {
+				return QDRService.humanify(input);
+			};
+	  })
+      .filter('shortName', function () {
+			return function (name) {
+				var nameParts = name.split('/')
+				return nameParts.length > 1 ? nameParts[nameParts.length-1] : name;
+			};
+	  });
+/*
+	QDR.module.config(['$locationProvider', function($locationProvider) {
+        $locationProvider.html5Mode(true);
+    }]);
+*/
+  /**
+   * Here we define any initialization to be done when this angular
+   * module is bootstrapped.  In here we do a number of things:
+   *
+   * 1.  We log that we've been loaded (kinda optional)
+   * 2.  We load our .css file for our views
+   * 3.  We configure the viewRegistry service from hawtio for our
+   *     route; in this case we use a pre-defined layout that uses
+   *     the full viewing area
+   * 4.  We configure our top-level tab and provide a link to our
+   *     plugin.  This is just a matter of adding to the workspace's
+   *     topLevelTabs array.
+   */
+  QDR.module.run(function(workspace, viewRegistry, layoutFull, $rootScope, $location, localStorage, QDRService, QDRChartService) {
+		QDR.log.info(QDR.pluginName, " loaded");
+		Core.addCSS(QDR.contextPath + "plugin/css/dispatch.css");
+		Core.addCSS(QDR.contextPath + "plugin/css/qdrTopology.css");
+		Core.addCSS(QDR.contextPath + "plugin/css/plugin.css");
+		Core.addCSS("https://cdn.rawgit.com/mohsen1/json-formatter/master/dist/json-formatter.min.css");
+		Core.addCSS("https://cdnjs.cloudflare.com/ajax/libs/jquery.tipsy/1.0.2/jquery.tipsy.css");
+		Core.addCSS("https://code.jquery.com/ui/1.8.24/themes/base/jquery-ui.css");
+
+		// tell hawtio that we have our own custom layout for
+		// our view
+		viewRegistry["dispatch_plugin"] = QDR.templatePath + "qdrLayout.html";
+
+		var settings = angular.fromJson(localStorage[QDR.SETTINGS_KEY]);
+		QDRService.addConnectAction(function() {
+			QDRChartService.init(); // initialize charting service after we are connected
+		});
+		if (settings && settings.autostart) {
+			QDRService.addConnectAction(function() {
+				if ($location.path().startsWith(QDR.pluginRoot)) {
+					var lastLocation = localStorage[QDR.LAST_LOCATION];
+					if (!angular.isDefined(lastLocation))
+						lastLocation = QDR.pluginRoot + "/overview";
+					$location.path(lastLocation);
+					$location.replace();
+					$rootScope.$apply();
+				}
+			});
+			QDRService.connect(settings);
+        }
+
+        $rootScope.$on('$routeChangeSuccess', function() {
+            var path = $location.path();
+			if (path.startsWith(QDR.pluginRoot)) {
+				if (path != QDR.pluginRoot && path != QDR.pluginRoot + "/connect") {
+		            localStorage[QDR.LAST_LOCATION] = $location.path();
+				}
+			}
+        });
+
+		$rootScope.$on( "$routeChangeStart", function(event, next, current) {
+			if (next.templateUrl == QDR.templatePath + "qdrConnect.html" && QDRService.connected) {
+				// clicked connect from another dispatch page
+				if (current.loadedTemplateUrl.startsWith(QDR.contextPath)) {
+					return;
+				}
+				// clicked the Dispatch Router top level tab from a different plugin
+				var lastLocation = localStorage[QDR.LAST_LOCATION];
+				if (!angular.isDefined(lastLocation))
+					lastLocation = QDR.pluginRoot + "/overview";
+				// show the last page visited
+				$location.path(lastLocation)
+			}
+	    });
+    /* Set up top-level link to our plugin.  Requires an object
+       with the following attributes:
+
+         id - the ID of this plugin, used by the perspective plugin
+              and by the preferences page
+         content - The text or HTML that should be shown in the tab
+         title - This will be the tab's tooltip
+         isValid - A function that returns whether or not this
+                   plugin has functionality that can be used for
+                   the current JVM.  The workspace object is passed
+                   in by hawtio's navbar controller which lets
+                   you inspect the JMX tree, however you can do
+                   any checking necessary and return a boolean
+         href - a function that returns a link, normally you'd
+                return a hash link like #/foo/bar but you can
+                also return a full URL to some other site
+         isActive - Called by hawtio's navbar to see if the current
+                    $location.url() matches up with this plugin.
+                    Here we use a helper from workspace that
+                    checks if $location.url() starts with our
+                    route.
+     */
+    workspace.topLevelTabs.push({
+      id: "dispatch",
+      content: "Dispatch Router",
+      title: "Dispatch console",
+      isValid: function(workspace) { return true; },
+      href: function() { return "#/dispatch_plugin"; },
+      isActive: function(workspace) { return workspace.isLinkActive("dispatch_plugin"); }
+    });
+
+  });
+
+  return QDR;
+
+})(QDR || {});
+
+
+// tell the hawtio plugin loader about our plugin so it can be
+// bootstrapped with the rest of angular
+hawtioPluginLoader.addModule(QDR.pluginName);
+
+$.getScript('https://cdn.rawgit.com/angular-ui/ui-slider/master/src/slider.js', function() {
+	hawtioPluginLoader.addModule('ui.slider');
+});
+$.getScript('https://cdn.rawgit.com/mohsen1/json-formatter/master/dist/json-formatter.min.js', function() {
+	hawtioPluginLoader.addModule('jsonFormatter');
+});
+
+// force an more modern version of d3 to load
+$.getScript('https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.14/d3.min.js', function() {});
+// tooltips on the list page
+$.getScript('https://cdn.rawgit.com/jaz303/tipsy/master/src/javascripts/jquery.tipsy.js', function() {});
+// tooltips on the topology page
+$.getScript('https://cdn.rawgit.com/briancray/tooltipsy/master/tooltipsy.min.js', function() {});
+
+
+
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/eb2e027a/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
new file mode 100644
index 0000000..d305f38
--- /dev/null
+++ b/console/hawtio/src/main/webapp/plugin/js/navbar.js
@@ -0,0 +1,128 @@
+/*
+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: "#/dispatch_plugin/connect"
+    },
+    {
+        content: '<i class="fa fa-home"></i> Overview',
+        title: "View router overview",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/dispatch_plugin/overview"
+      },
+    {
+        content: '<i class="icon-list "></i> Details',
+        title: "View the attributes of the router nodes",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/dispatch_plugin/list"
+      },
+    {
+        content: '<i class="icon-star-empty"></i> Topology',
+        title: "View router network topology",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/dispatch_plugin/topology"
+      },
+/*
+    {
+        content: '<i class="icon-bar-chart"></i> Charts',
+        title: "View charts",
+        isValid: function (QDRService, $location) { return QDRService.isConnected(); },
+        href: "#/dispatch_plugin/charts"
+    },
+*/
+    {
+        content: '<i class="icon-align-left"></i> Schema',
+        title: "View dispatch schema",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/dispatch_plugin/schema",
+        right: true
+
+      }
+  ];
+  /**
+   * @function NavBarController
+   *
+   * @param $scope
+   * @param workspace
+   *
+   * The controller for this plugin's navigation bar
+   *
+   */
+QDR.NavBarController = function($scope, QDRService, QDRChartService, $location, $routeParams) {
+    $scope.breadcrumbs = QDR.breadcrumbs;
+    $scope.isValid = function(link) {
+      return link.isValid(QDRService, $location);
+    };
+
+    $scope.isActive = function(href) {
+        return href.split("#")[1] == $location.path();
+    };
+
+    $scope.isRight = function (link) {
+        return angular.isDefined(link.right);
+    };
+
+    $scope.hasChart = function (link) {
+        if (link.href == "#/dispatch_plugin/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);
+    };
+
+  };
+
+
+  return QDR;
+
+} (QDR || {}));


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