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/18 19:04:51 UTC

[06/19] qpid-dispatch git commit: moved standalone to a subdir

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/html/qdrCharts.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrCharts.html b/console/stand-alone/plugin/html/qdrCharts.html
new file mode 100644
index 0000000..8ba1ffe
--- /dev/null
+++ b/console/stand-alone/plugin/html/qdrCharts.html
@@ -0,0 +1,150 @@
+<div class="main-display row-fluid qdrCharts" ng-controller="QDR.ChartsController">
+    <div ng-show="hasCharts()">
+        <div ng-repeat="chart in svgCharts" class="chartContainer">
+            <p class="chartLabels">
+                <button ng-click="delChart(chart)" title="Delete"><i class="icon-trash"></i></button>
+                <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="aChart d3Chart"></div>
+        </div>
+        <div ng-init="chartsLoaded()"></div>
+    </div>
+    <div ng-hide="hasCharts()" class="centered">
+        There are no charts. To add charts to this page, click on a <i class="icon-bar-chart"></i> icon on the <button ng-click="showListPage()"><i class="icon-list "></i> List</button> page.
+    </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>
+        <uib-tabset>
+            <uib-tab heading="Type">
+                <legend>Chart type</legend>
+                <label><input type="radio" ng-model="dialogChart.type" value="value" ng-change="chartChanged()" /> Value Chart</label>
+                <label><input type="radio" ng-model="dialogChart.type" value="rate" ng-change="chartChanged()" /> 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>
+            </uib-tab>
+            <uib-tab heading="Colors">
+                <legend>Chart colors</legend>
+                <div class="colorPicker">
+                    <div>Area</div><input minicolors="areaColor" id="position-bottom-left" type="text" ng-model="dialogChart.areaColor" />
+                </div>
+                <div class="colorPicker">
+                    <div>Line</div><input minicolors="lineColor" id="position-bottom-left" type="text" ng-model="dialogChart.lineColor" />
+                </div>
+                <div style="clear:both;"> </div>
+            </uib-tab>
+            <uib-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>
+
+            </uib-tab>
+        </uib-tabset>
+    </div>
+    <div class="modal-footer">
+        <button class="btn btn-success" type="button" ng-click="apply()">Apply to existing chart</button>
+        <button class="btn btn-info" type="button" ng-click="copyToDashboard()">Create new chart</button>
+        <button class="btn btn-primary" type="button" ng-click="okClick()">Close</button>
+    </div>
+</div>
+<!--
+    <div class="chartOptions" title="Configure chart" ng-controller="QDR.ChartDialogController">
+        <p class="dialogHeader">Title: <input type="text" ng-model="userTitle" /></p>
+        <div id="{{svgDivId}}" class="d3Chart"></div>
+        <p></p>
+        <uib-tabset>
+            <uib-tab heading="Type">
+                <legend>Chart type</legend>
+                <label><input type="radio" ng-model="dialogChart.type" value="value" ng-change="chartChanged()" /> Value Chart</label>
+                <label><input type="radio" ng-model="dialogChart.type" value="rate" ng-change="chartChanged()" /> 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>
+            </uib-tab>
+            <uib-tab heading="Colors">
+                <legend>Chart colors</legend>
+                <div class="colorPicker">
+                    <div>Area</div><input minicolors="areaColor" id="position-bottom-left" type="text" ng-model="dialogChart.areaColor" />
+                </div>
+                <div class="colorPicker">
+                    <div>Line</div><input minicolors="lineColor" id="position-bottom-left" type="text" ng-model="dialogChart.lineColor" />
+                </div>
+                <div style="clear:both;"> </div>
+            </uib-tab>
+            <uib-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>
+
+            </uib-tab>
+        </uib-tabset>
+
+        <tabset>
+            <tab heading="Type">
+                <div>
+                    <legend>Chart type</legend>
+                    <label>
+                        <input type="radio" ng-model="dialogChart.type" value="value" ng-change="chartChanged()" />
+                        Value Chart
+                    </label>
+                    <label class="rateGroup">
+                        <input type="radio" ng-model="dialogChart.type" value="rate" ng-change="chartChanged()" /> 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>
+                <div style="clear:both;"> </div>
+            </tab>
+            <tab heading="Colors">
+                <div>
+                    <legend>Chart colors</legend>
+                    <div class="colorPicker">
+                        <div>Area</div><input minicolors="areaColor" id="position-bottom-left" type="text" ng-model="dialogChart.areaColor" />
+                    </div>
+                    <div class="colorPicker">
+                        <div>Line</div><input minicolors="lineColor" id="position-bottom-left" type="text" ng-model="dialogChart.lineColor" />
+                    </div>
+                </div>
+                <div style="clear:both;"> </div>
+            </tab>
+            <tab heading="Duration">
+                <div>
+                    <legend>Chart duration</legend>
+                </div>
+                <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 class="okButton">
+            <button ng-click="apply()">Apply to existing chart</button>
+            <button ng-click="copyToDashboard()">Create new chart</button>
+            <button ng-click="okClick()">Close</button>
+        </div>
+    </div>
+-->
+</script>
+

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/html/qdrConnect.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrConnect.html b/console/stand-alone/plugin/html/qdrConnect.html
new file mode 100644
index 0000000..34e452f
--- /dev/null
+++ b/console/stand-alone/plugin/html/qdrConnect.html
@@ -0,0 +1,65 @@
+<div class="row-fluid" ng-controller="QDR.SettingsController">
+      <div class="login container" ng-hide="connecting">
+          <div class="row" id="login-container">
+              <div class="span2 offset4">
+                  <!-- <div simple-form name="settings" data="formConfig" entity="formEntity"></div> -->
+                  <form class="form-horizontal no-bottom-margin" novalidate name="settings" action="" method="post">
+                      <fieldset>
+                          <div class="control-group">
+                              <label class="control-label">Address: </label>
+                              <div class="controls">
+                                  <input type="text" description="Router address" ng-model="formEntity.address" name="address" required="required" autofocus="autofocus" class="ng-pristine ng-valid ng-valid-required">
+                                  <span class="help-block"></span>
+                              </div>
+                          </div>
+                          <div class="control-group">
+                              <label class="control-label" title="Port to connect to, by default 5673">Port: </label>
+                              <div class="controls">
+                                  <input type="number" description="Router port" tooltip="Ports to connect to, by default 5672" ng-model="formEntity.port" name="port" title="Port to connect to, by default 5673" class="ng-scope ng-pristine ng-valid ng-valid-number">
+                                  <span class="help-block"></span>
+                              </div>
+                          </div>
+                          <!--
+                                          <div class="control-group">
+                                              <label class="control-label">Username: </label>
+                                              <div class="controls">
+                                                  <input type="text" description="User Name" ng-model="formEntity.username" name="username" class="ng-pristine ng-valid">
+                                                  <span class="help-block"></span>
+                                              </div>
+                                          </div>
+                                          <div class="control-group">
+                                              <label class="control-label">Password: </label>
+                                              <div class="controls">
+                                                  <input type="password" description="Password" ng-model="formEntity.password" name="password" class="ng-pristine ng-valid">
+                                                  <span class="help-block"></span>
+                                              </div>
+                                          </div>
+                          -->
+                          <div class="control-group">
+                              <label class="control-label" title="Whether or not the connection should be started as soon as you navigate to the console">Autostart: </label>
+                              <div class="controls">
+                                  <input type="checkbox" description="Connect at startup" tooltip="Whether or not the connection should be started as soon as you navigate to the console" ng-model="formEntity.autostart" name="autostart" title="Whether or not the connection should be started as soon as you log into hawtio" class="ng-scope ng-pristine ng-valid">
+                                  <span class="help-block"></span>
+                              </div>
+                          </div>
+                          <input type="submit" style="position: absolute; left: -9999px; width: 1px; height: 1px;">
+                      </fieldset>
+                  </form>
+                  <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="span4 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 class="span4"></div>
+</div>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/html/qdrGraphs.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrGraphs.html b/console/stand-alone/plugin/html/qdrGraphs.html
new file mode 100644
index 0000000..c311246
--- /dev/null
+++ b/console/stand-alone/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/4940e63d/console/stand-alone/plugin/html/qdrLayout.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrLayout.html b/console/stand-alone/plugin/html/qdrLayout.html
new file mode 100644
index 0000000..d164f8a
--- /dev/null
+++ b/console/stand-alone/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" ng-controller="QDR.NavBarController">
+  <li ng-repeat="link in breadcrumbs" ng-show="isValid(link)" ng-class='{active : isActive(link.href), "pull-right" : isRight(link), haschart: hasChart(link)}'>
+    <a ng-href="{{link.href}}{{hash}}" ng-bind-html="link.content | to_trusted"></a>
+  </li>
+</ul>
+<div class="row-fluid">
+  <div ng-view></div>
+</div>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/html/qdrList.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrList.html b/console/stand-alone/plugin/html/qdrList.html
new file mode 100644
index 0000000..156b21a
--- /dev/null
+++ b/console/stand-alone/plugin/html/qdrList.html
@@ -0,0 +1,56 @@
+<!--
+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 qdrList" ng-controller="QDR.ListController">
+    <ul class="nav nav-tabs qdrListNodes">
+          <li ng-repeat="node in nodes" ng-click="selectNode(node)" ng-class="{active : isNodeSelected(node.id)}"> <span>{{node.name}}</span> </li>
+    </ul>
+
+    <div class="row-fluid qdrListActions">
+        <ul class="nav nav-tabs ng-scope">
+            <li ng-repeat="entity in entities" ng-class="{active : isActionActive(entity.name)}" ng-click="selectAction(entity.name)" >
+                <a title="{{entity.title}}" data-placement="bottom"> {{entity.humanName}} </a></li>
+        </ul>
+            <div class="gridStyle" ng-style="getTableHeight()" ui-grid-auto-resize ui-grid-selection ui-grid="gridDef"></div>
+            <div class="selectedItems">
+                <div ng-style="getDetailsTableHeight()" ui-grid-auto-resize ui-grid-selection ui-grid="details"></div>
+            </div>
+    </div>
+</div>
+<!--
+    This is the template for the graph dialog that is displayed. It uses the
+    dialogCtrl controller in qdrList.js.
+-->
+<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 ng-hide="isOnChartsPage()">
+            <button class="btn btn-success" type="button" ng-click="addChartsPage()"><i class="icon-bar-chart"></i> Add</button> this chart to the Charts page.
+        </span>
+        <span ng-show="isOnChartsPage()">
+            <button class="btn btn-danger" type="button" ng-click="delChartsPage()">Remove</button> this chart from the <button class="btn btn-success" type="button" ng-click="showChartsPage()"><i class="icon-bar-chart"></i> Charts</button> page.
+        </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/4940e63d/console/stand-alone/plugin/html/qdrOverview.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrOverview.html b/console/stand-alone/plugin/html/qdrOverview.html
new file mode 100644
index 0000000..d63ea0e
--- /dev/null
+++ b/console/stand-alone/plugin/html/qdrOverview.html
@@ -0,0 +1,83 @@
+<!--
+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>
+
+
+<!-- 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-style="getGridHeight(allRouters)" ui-grid-auto-resize ui-grid-selection ui-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 ng-style="getGridHeight(routerGrid)" ui-grid-auto-resize ui-grid="routerGrid" class="gridStyle noHighlight"></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-style="getGridHeight(addressGrid)" ui-grid-auto-resize ui-grid-selection ui-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 ng-style="getGridHeight(addressGrid)" ui-grid-auto-resize class="gridStyle noHighlight" ui-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-style="getGridHeight(allConnectionGrid)" ui-grid-auto-resize ui-grid-selection ui-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 ng-style="getGridHeight(connectionGrid)" ui-grid-auto-resize class="gridStyle noHighlight" ui-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/4940e63d/console/stand-alone/plugin/html/qdrSchema.html
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/html/qdrSchema.html b/console/stand-alone/plugin/html/qdrSchema.html
new file mode 100644
index 0000000..15ebb46
--- /dev/null
+++ b/console/stand-alone/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/4940e63d/console/stand-alone/plugin/img/ZeroClipboard.swf
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/img/ZeroClipboard.swf b/console/stand-alone/plugin/img/ZeroClipboard.swf
new file mode 100644
index 0000000..a3aaa20
Binary files /dev/null and b/console/stand-alone/plugin/img/ZeroClipboard.swf differ

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/img/ajax-loader.gif
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/img/ajax-loader.gif b/console/stand-alone/plugin/img/ajax-loader.gif
new file mode 100644
index 0000000..3c2f7c0
Binary files /dev/null and b/console/stand-alone/plugin/img/ajax-loader.gif differ

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/img/dynatree/icons.gif
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/img/dynatree/icons.gif b/console/stand-alone/plugin/img/dynatree/icons.gif
new file mode 100644
index 0000000..6e237a0
Binary files /dev/null and b/console/stand-alone/plugin/img/dynatree/icons.gif differ

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/img/dynatree/loading.gif
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/img/dynatree/loading.gif b/console/stand-alone/plugin/img/dynatree/loading.gif
new file mode 100644
index 0000000..2a96840
Binary files /dev/null and b/console/stand-alone/plugin/img/dynatree/loading.gif differ

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/js/navbar.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/navbar.js b/console/stand-alone/plugin/js/navbar.js
new file mode 100644
index 0000000..ebda4d4
--- /dev/null
+++ b/console/stand-alone/plugin/js/navbar.js
@@ -0,0 +1,119 @@
+/*
+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: "#connect"
+    },
+    {
+        content: '<i class="fa fa-home"></i> Overview',
+        title: "View router overview",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/overview"
+      },
+    {
+        content: '<i class="icon-star-empty"></i> Topology',
+        title: "View router network topology",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/topology"
+      },
+    {
+        content: '<i class="icon-list "></i> List',
+        title: "View router nodes as a list",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/list"
+      },
+    {
+        content: '<i class="icon-bar-chart"></i> Charts',
+        title: "View charts",
+        isValid: function (QDRService, $location) { return QDRService.isConnected(); },
+        href: "#/charts"
+    },
+    {
+        content: '<i class="icon-align-left"></i> Schema',
+        title: "View dispatch schema",
+        isValid: function (QDRService) { return QDRService.isConnected(); },
+        href: "#/schema",
+        right: true
+
+      }
+  ];
+  /**
+   * @function NavBarController
+   *
+   * @param $scope
+   * @param workspace
+   *
+   * The controller for this plugin's navigation bar
+   *
+   */
+  QDR.module.controller("QDR.NavBarController", ['$scope', '$sce', 'QDRService', 'QDRChartService', '$location', function($scope, $sce, QDRService, QDRChartService, $location) {
+
+	QDR.log.debug("navbar started with location.url: " + $location.url());
+	QDR.log.debug("navbar started with window.location.pathname : " + window.location.pathname);
+
+    if ($location.path().startsWith("/topology")
+    && !QDRService.isConnected()) {
+      $location.path("/connect");
+    }
+
+    if ($location.path().startsWith("/connect")
+    && QDRService.isConnected()) {
+      $location.path("/topology");
+    }
+
+    $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 == "#/charts") {
+            return QDRChartService.charts.some(function (c) { return c.dashboard });
+        }
+    }
+  }]);
+
+  return QDR;
+
+} (QDR || {}));

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/js/qdrChartService.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrChartService.js b/console/stand-alone/plugin/js/qdrChartService.js
new file mode 100644
index 0000000..6535384
--- /dev/null
+++ b/console/stand-alone/plugin/js/qdrChartService.js
@@ -0,0 +1,956 @@
+/*
+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) {
+
+    // The QDR chart service handles periodic gathering data for charts and displaying the charts
+    QDR.module.factory("QDRChartService", ['$rootScope', 'QDRService', '$http', '$resource',
+    function($rootScope, QDRService, $http, $resource) {
+
+        var instance = 0;   // counter for chart instances
+        var bases = [];
+        var findBase = function (name, attr, request) {
+            for (var i=0; i<bases.length; ++i) {
+                var base = bases[i];
+                if (base.equals(name, attr, request))
+                    return base;
+            }
+            return null;
+        }
+
+        function ChartBase(name, attr, request) {
+            // the base chart attributes
+            this.name = name;           // the record's "name" field
+            this.attr = attr;           // the record's attr field to chart
+            this.request = request;     // the associated request that fetches the data
+
+            // copy the savable properties to an object
+            this.copyProps = function (o) {
+                o.name = this.name;
+                o.attr = this.attr;
+                this.request.copyProps(o);
+            }
+
+            this.equals = function (name, attr, request) {
+                return (this.name == name && this.attr == attr && this.request.equals(request));
+            }
+        };
+
+        // Object that represents a visible chart
+        // There can be multiple of these per ChartBase (eg. one rate  and one value chart)
+        function Chart(name, attr, request) {
+
+            var base = findBase(name, attr, request);
+            if (!base) {
+                base = new ChartBase(name, attr, request);
+                bases.push(base);
+            }
+            this.base = base;
+            this.instance = instance++;
+            this.dashboard = false;     // is this chart on the dashboard page
+            this.type = "value";        // value or rate
+            this.rateWindow = 1000;     // calculate the rate of change over this time interval. higher == smother graph
+            this.areaColor = "#c0e0ff"; // the chart's area color when not an empty string
+            this.lineColor = "#4682b4"; // the chart's line color when not an empty string
+            this.visibleDuration = 10;  // number of minutes of data to show (<= base.duration)
+            this.userTitle = null;      // user title overrides title()
+
+            // generate a unique id for this chart
+            this.id = function () {
+                var key = this.request().nodeId + this.request().entity + this.name() + this.attr() + "_" + this.instance;
+                // remove all characters except letters,numbers, and _
+                return key.replace(/[^\w]/gi, '')
+            }
+            // copy the savable properties to an object
+            this.copyProps = function (o) {
+                o.type = this.type;
+                o.rateWindow = this.rateWindow;
+                o.areaColor = this.areaColor;
+                o.lineColor = this.lineColor;
+                o.visibleDuration = this.visibleDuration;
+                o.userTitle = this.userTitle;
+                this.base.copyProps(o);
+            }
+            this.name = function (_) {
+                if (!arguments.length) return this.base.name;
+                this.base.name = _;
+                return this;
+            }
+            this.attr = function (_) {
+                if (!arguments.length) return this.base.attr;
+                this.base.attr = _;
+                return this;
+            }
+            this.nodeId = function (_) {
+                if (!arguments.length) return this.base.request.nodeId;
+                this.base.request.nodeId = _;
+                return this;
+            }
+            this.entity = function (_) {
+                if (!arguments.length) return this.base.request.entity;
+                this.base.request.entity = _;
+                return this;
+            }
+            this.request = function (_) {
+                if (!arguments.length) return this.base.request;
+                this.base.request = _;
+                return this;
+            }
+            this.data = function () {
+                return this.base.request.data(this.base.name, this.base.attr); // refernce to chart's data array
+            }
+            this.interval = function (_) {
+                if (!arguments.length) return this.base.request.interval;
+                this.base.request.interval = _;
+                return this;
+            }
+            this.duration = function (_) {
+                if (!arguments.length) return this.base.request.duration;
+                this.base.request.duration = _;
+                return this;
+            }
+            this.title = function (_) {
+				var name = this.request().aggregate ? 'Aggregate' : QDRService.nameFromId(this.nodeId());
+                var computed = name +
+                                   " " + QDRService.humanify(this.attr()) +
+                                   " - " + this.name()
+                if (!arguments.length) return this.userTitle || computed;
+
+                // don't store computed title in userTitle
+                if (_ === computed)
+                    _ = null;
+                this.userTitle = _;
+                return this;
+            }
+            this.title_short = function (_) {
+                if (!arguments.length) return this.userTitle || this.name();
+                return this;
+            }
+            this.copy = function () {
+                var chart = self.registerChart(this.nodeId(), this.entity(),
+                            this.name(), this.attr(), this.interval(), true, this.base.request.aggregate);
+                chart.type = this.type;
+                chart.areaColor = this.areaColor;
+                chart.lineColor = this.lineColor;
+                chart.rateWindow = this.rateWindow;
+                chart.visibleDuration = this.visibleDuration;
+                chart.userTitle = this.userTitle;
+                return chart;
+            }
+            // compare to a chart
+            this.equals = function (c) {
+                return (c.instance == this.instance &&
+                        c.base.equals(this.base.name, this.base.attr, this.base.request) &&
+                        c.type == this.type &&
+                        c.rateWindow == this.rateWindow &&
+                        c.areaColor == this.areaColor &&
+                        c.lineColor == this.lineColor)
+            }
+        }
+
+        // Object that represents the management request to fetch and store data for multiple charts
+        function ChartRequest(nodeId, entity, name, attr, interval, aggregate) {
+            this.duration = 10;         // number of minutes to keep the data
+            this.nodeId = nodeId;       // eg amqp:/_topo/0/QDR.A/$management
+            this.entity = entity;       // eg .router.address
+			// sorted since the responses will always be sorted
+			this.aggregate = aggregate;   // list of nodeIds for aggregate charts
+            this.datum = {};            // object containing array of arrays for each attr
+                                        // like {attr1: [[date,value],[date,value]...], attr2: [[date,value]...]}
+
+            this.interval = interval;   // number of milliseconds between updates to data
+            this.setTimeoutHandle = null;   // used to cancel the next request
+            // copy the savable properties to an object
+
+			this.data = function (name, attr) {
+				if (this.datum[name] && this.datum[name][attr])
+					return this.datum[name][attr]
+				return null;
+			}
+			this.addAttrName = function (name, attr) {
+				if (Object.keys(this.datum).indexOf(name) == -1) {
+					this.datum[name] = {}
+				}
+				if (Object.keys(this.datum[name]).indexOf(attr) == -1) {
+					this.datum[name][attr] = [];
+				}
+			}
+			this.addAttrName(name, attr)
+
+            this.copyProps = function (o) {
+                o.nodeId = this.nodeId;
+                o.entity = this.entity;
+                o.interval = this.interval;
+				o.aggregate = this.aggregate;
+				o.duration = this.duration;
+            }
+
+			this.removeAttr = function (name, attr) {
+				if (this.datum[name]) {
+					if (this.datum[name][attr]) {
+						delete this.datum[name][attr]
+					}
+				}
+				return this.attrs().length;
+			}
+
+            this.equals = function (r, entity, aggregate) {
+				if (arguments.length == 3) {
+					var o = {nodeId: r, entity: entity, aggregate: aggregate}
+					r = o;
+				}
+                return (this.nodeId === r.nodeId && this.entity === r.entity && this.aggregate == r.aggregate)
+			}
+			this.names = function () {
+				return Object.keys(this.datum)
+			}
+			this.attrs = function () {
+				var attrs = {}
+				Object.keys(this.datum).forEach( function (name) {
+					Object.keys(this.datum[name]).forEach( function (attr) {
+						attrs[attr] = 1;
+					})
+				}, this)
+				return Object.keys(attrs);
+			}
+        };
+        var self = {
+            charts: [],         // list of charts to gather data for
+            chartRequests: [],  // the management request info (multiple charts can be driven off of a single request
+
+            init: function () {
+                self.loadCharts();
+            },
+
+			findChartRequest: function (nodeId, entity, aggregate) {
+				var ret = null;
+				self.chartRequests.some( function (request) {
+					if (request.equals(nodeId, entity, aggregate)) {
+						ret = request;
+						return true;
+					}
+				})
+				return ret;
+			},
+
+            findCharts: function (name, attr, nodeId, entity) {
+                return self.charts.filter( function (chart) {
+                    return (chart.name() == name &&
+                            chart.attr() == attr &&
+                            chart.nodeId() == nodeId &&
+                            chart.entity() == entity)
+                });
+            },
+
+            delChartRequest: function (request) {
+                for (var i=0; i<self.chartRequests.length; ++i) {
+                    var r = self.chartRequests[i];
+                    if (request.equals(r)) {
+	                    QDR.log.debug("removed request: " + request.nodeId + " " + request.entity);
+                        self.chartRequests.splice(i, 1);
+                        self.stopCollecting(request);
+                        return;
+                    }
+                }
+            },
+
+            delChart: function (chart) {
+                var foundBases = 0;
+                for (var i=0; i<self.charts.length; ++i) {
+                    var c = self.charts[i];
+					if (c.base === chart.base)
+						++foundBases;
+                    if (c.equals(chart)) {
+                        self.charts.splice(i, 1);
+                        if (chart.dashboard)
+                            self.saveCharts();
+                    }
+                }
+                if (foundBases == 1) {
+                    var baseIndex = bases.indexOf(chart.base)
+                    bases.splice(baseIndex, 1);
+                }
+            },
+
+            registerChart: function (nodeId, entity, name, attr, interval, forceCreate, aggregate) {
+                var request = self.findChartRequest(nodeId, entity, aggregate);
+                if (request) {
+                    // add any new attr or name to the list
+                    request.addAttrName(name, attr)
+                } else {
+                    // the nodeId/entity did not already exist, so add a new request and chart
+                    QDR.log.debug("added new request: " + nodeId + " " + entity);
+                    request = new ChartRequest(nodeId, entity, name, attr, interval, aggregate);
+                    self.chartRequests.push(request);
+                    self.startCollecting(request);
+                }
+                var charts = self.findCharts(name, attr, nodeId, entity);
+                var chart;
+                if (charts.length == 0 || forceCreate) {
+                    chart = new Chart(name, attr, request);
+                    self.charts.push(chart);
+                } else {
+                    chart = charts[0];
+                }
+                return chart;
+            },
+
+            // remove the chart for name/attr
+            // if all attrs are gone for this request, remove the request
+            unRegisterChart: function (chart) {
+                // remove the chart
+                for (var i=0; i<self.charts.length; ++i) {
+                    var c = self.charts[i];
+                    if (chart.equals(c)) {
+                        var request = chart.request();
+                        self.delChart(chart);
+                        if (request) {
+                            // see if any other charts use this attr
+                            for (var i=0; i<self.charts.length; ++i) {
+                                var c = self.charts[i];
+                                if (c.attr() == chart.attr() && c.request().equals(chart.request()))
+                                    return;
+                            }
+                            // no other charts use this attr, so remove it
+                            if (request.removeAttr(chart.name(), chart.attr()) == 0) {
+                                self.stopCollecting(request);
+                                self.delChartRequest(request);
+                            }
+                        }
+                    }
+                }
+            },
+
+            stopCollecting: function (request) {
+                if (request.setTimeoutHandle) {
+                    clearTimeout(request.setTimeoutHandle);
+                    request.setTimeoutHandle = null;
+                }
+            },
+
+            startCollecting: function (request) {
+                // Using setTimeout instead of setInterval because the response may take longer than interval
+                request.setTimeoutHandle = setTimeout(self.sendChartRequest, request.interval, request);
+            },
+            // send the request
+            sendChartRequest: function (request) {
+                // ensure the response has the name field so we can associate the response values with the correct chart
+                var attrs = request.attrs();
+                attrs.push("name");
+
+	            // this is called when the response is received
+				var saveResponse = function (nodeId, entity, response) {
+	                //QDR.log.debug("got chart results for " + nodeId + " " + entity);
+	                // records an array that has data for all names
+	                var records = response.results;
+	                if (!records)
+	                    return;
+
+	                var now = new Date();
+	                var cutOff = new Date(now.getTime() - request.duration * 60 * 1000);
+	                // index of the "name" attr in the response
+	                var nameIndex = response.attributeNames.indexOf("name");
+	                if (nameIndex < 0)
+	                    return;
+
+					var names = request.names();
+	                // for each record returned, find the name/attr for this request and save the data with this timestamp
+	                for (var i=0; i<records.length; ++i) {
+	                    var name = records[i][nameIndex];
+						// if we want to store the values for some attrs for this name
+	                    if (names.indexOf(name) > -1) {
+	                        attrs.forEach( function (attr) {
+		                        var data = request.data(name, attr) // get a reference to the data array
+								if (data) {
+		                            var attrIndex = response.attributeNames.indexOf(attr)
+			                        if (request.aggregate) {
+			                            data.push([now, response.aggregates[i][attrIndex].sum, response.aggregates[i][attrIndex].detail])
+			                        } else {
+										data.push([now, records[i][attrIndex]])
+			                        }
+	                                // expire the old data
+	                                while (data[0][0] < cutOff) {
+	                                    data.shift();
+	                                }
+								}
+	                        })
+	                    }
+	                }
+				}
+				if (request.aggregate) {
+					var nodeList = QDRService.nodeIdList()
+					QDRService.getMultipleNodeInfo(nodeList, request.entity, attrs, saveResponse, request.nodeId);
+				} else {
+                    QDRService.getNodeInfo(request.nodeId, request.entity, attrs, saveResponse);
+				}
+                // it is now safe to send another request
+                request.setTimeoutHandle = setTimeout(self.sendChartRequest, request.interval, request)
+            },
+
+            numCharts: function () {
+                return self.charts.length;
+            },
+
+            isAttrCharted: function (nodeId, entity, name, attr) {
+                var charts = self.findCharts(name, attr, nodeId, entity);
+                // if any of the matching charts are on the dashboard page, return true
+                return charts.some(function (chart) {
+                    return (chart.dashboard) });
+            },
+
+            addDashboard: function (chart) {
+                chart.dashboard = true;
+                self.saveCharts();
+            },
+            delDashboard: function (chart) {
+                chart.dashboard = false;
+                self.saveCharts();
+            },
+            // save the charts to local storage
+            saveCharts: function () {
+                var charts = [];
+                var minCharts = [];
+
+                self.charts.forEach(function (chart) {
+                    var minChart = {};
+                    // don't save chart unless it is on the dashboard
+                    if (chart.dashboard) {
+                        chart.copyProps(minChart);
+                        minCharts.push(minChart);
+                    }
+                })
+                localStorage["QDRCharts"] = angular.toJson(minCharts);
+            },
+            loadCharts: function () {
+                var charts = angular.fromJson(localStorage["QDRCharts"]);
+                if (charts) {
+                    charts.forEach(function (chart) {
+                        if (!chart.interval)
+                            chart.interval = 1000;
+                        if (!chart.duration)
+                            chart.duration = 10;
+                        if (chart.nodeList)
+                            chart.aggregate = true;
+                        var newChart = self.registerChart(chart.nodeId, chart.entity, chart.name, chart.attr, chart.interval, true, chart.aggregate);
+                        newChart.dashboard = true;  // we only save the dashboard charts
+                        newChart.type = chart.type;
+                        newChart.rateWindow = chart.rateWindow;
+                        newChart.areaColor = chart.areaColor ? chart.areaColor : "#c0e0ff";
+                        newChart.lineColor = chart.lineColor ? chart.lineColor : "#4682b4";
+                        newChart.duration(chart.duration);
+                        newChart.visibleDuration = chart.visibleDuration ? chart.visibleDuration : 10;
+                        if (chart.userTitle)
+                            newChart.title(chart.userTitle);
+                    })
+                }
+            },
+
+            AreaChart: function (chart, url) {
+                if (!chart)
+                    return;
+
+				// if this is an aggregate chart, show it stacked
+				var stacked = chart.request().aggregate;
+                this.chart = chart; // reference to underlying chart
+                this.svgchart = null;
+                if (url)
+                    url = "/dispatch" + url;
+                else
+                    url = "";
+                this.url = url;
+
+                // callback function. called by svgchart when binding data
+                // the variable 'this' refers to the svg and not the AreaChart,
+                // but since we are still in the scope of the AreaChart we have access to the passed in chart argument
+                this.chartData = function () {
+
+                    var now = new Date();
+                    var visibleDate = new Date(now.getTime() - chart.visibleDuration * 60 * 1000);
+					var data = chart.data();
+					var nodeList = QDRService.nodeIdList();
+
+                    if (chart.type == "rate") {
+                        var rateData = [];
+                        var datalen = data.length;
+                        k = 0;  // inner loop optimization
+                        for (var i=0; i<datalen; ++i) {
+                            var d = data[i];
+                            if (d[0] >= visibleDate) {
+                                for (var j=k+1; j<datalen; ++j) {
+                                    var d1 = data[j];
+                                    if (d1[0] - d[0] >= chart.rateWindow) { // rateWindow is the timespan to calculate rates
+										var elapsed = Math.max((d1[0] - d[0]) / 1000, 1); // number of seconds that elapsed
+										var rd = [d1[0],(d1[1] - d[1])/elapsed]
+                                        k = j; // start here next time
+										// this is a stacked (aggregate) chart
+										if (stacked) {
+											var detail = [];
+											nodeList.forEach( function (node, nodeIndex) {
+												if (d1[2][nodeIndex] && d[2][nodeIndex])
+													detail.push({node: QDRService.nameFromId(node), val: (d1[2][nodeIndex].val- d[2][nodeIndex].val)/elapsed})
+											})
+											rd.push(detail)
+										}
+										rateData.push(rd);
+                                        break;
+                                    }
+                                }
+                            }
+                        }
+                        // we need at least a point to chart
+                        if (rateData.length == 0) {
+                            rateData[0] = [chart.data()[0][0],0,[{node:'',val:0}]];
+                        }
+                        return rateData;
+                    }
+                    if (chart.visibleDuration != chart.duration()) {
+                        return data.filter(function (d) { return d[0]>=visibleDate});
+                    } else
+                        return data;
+                }
+
+                this.zoom = function (id, zoom) {
+                    if (this.svgchart) {
+                        this.svgchart.attr("zoom", zoom)
+						d3.select('#' + id)
+							.data([this.chartData()])
+							.call(this.svgchart)
+                    }
+                }
+
+                // called by the controller on the page that displays the chart
+                // called whenever the controller wants to redraw the chart
+                // note: the data is collected independently of how often the chart is redrawn
+                this.tick = function (id) {
+
+                    // can't draw charts that don't have data yet
+                    if (this.chart.data().length == 0) {
+                        return;
+                    }
+
+                    // if we haven't created the svg yet
+                    if (!this.svgchart) {
+
+						// make sure the dom element exists on the page
+                        var div = angular.element('#' + id);
+                        if (!div)
+                            return;
+
+                        var width = div.width();
+                        var height = div.height();
+
+						// make sure the dom element has a size. otherwise we wouldn't see anything anyway
+                        if (!width)
+                            return;
+
+						var tooltipGenerator;
+						// stacked charts have a different tooltip
+                        if (stacked) {
+							tooltipGenerator = function (d, color, format) {
+			                    var html = "<table class='fo-table'><tbody><tr class='fo-title'>"+
+			                    "<td align='center' colspan='2' nowrap>Time: "+d[0].toTimeString().substring(0, 8)+"</td></tr>"
+			                    d[2].forEach( function (detail) {
+			                        html += "<tr class='detail'><td align='right' nowrap>"
+			                        + detail.node
+			                        + "<div class='fo-table-legend' style='background-color: "+color(detail.node)+"'></div>"
+			                        + "</td><td>"+format(detail.val)+"</td></tr>"
+			                    })
+			                    html += "</tbody></table>"
+			                    return html;
+							}
+                        } else {
+                            tooltipGenerator = function (d, color, format) {
+								var html = "<table class='fo-table'><tbody><tr class='fo-title'>"+
+		                                "<td align='center'>Time</td><td align='center'>Value</td></tr><tr><td>" +
+		                                    d[0].toTimeString().substring(0, 8) +
+		                                "</td><td>" +
+		                                   format(d[1]) +
+		                                "</td></tr></tbody></table>"
+			                    return html;
+                            }
+						}
+                        // create and initialize the chart
+                        this.svgchart = self.timeSeriesStackedChart(id, width, height,
+                                QDRService.humanify(this.chart.attr()),
+                                this.chart.name(),
+                                QDRService.nameFromId(this.chart.nodeId()),
+                                this.chart.entity(),
+                                stacked)
+                            .tooltipGenerator(tooltipGenerator);
+
+                    }
+                    // in case the chart properties have changed, set the new props
+                    this.svgchart
+                        .attr("type", this.chart.type)
+                        .attr("areaColor", this.chart.areaColor)
+                        .attr("lineColor", this.chart.lineColor)
+                        .attr("url", this.url)
+                        .attr("title", this.chart.userTitle);
+
+                    // bind the new data and update the chart
+                    d3.select('#' + id)         // the div id on the page/dialog
+						.data([this.chartData()])
+                        .call(this.svgchart);       // the charting function
+                }
+            },
+
+            timeSeriesStackedChart: function (id, width, height, attrName, name, node, entity, stacked) {
+				var margin = {top: 20, right: 18, bottom: 10, left: 15}
+				// attrs that can be changed after the chart is created by using
+				// chart.attr(<attrname>, <attrvalue>);
+				var attrs = {
+							attrName: attrName, // like Deliveries to Container. Put at top of chart
+							name: name,         // like router.address/qdrhello  Put at bottom of chart with node
+							node: node,         // put at bottom of chart with name
+							entity: entity,     // like .router.address  Not used atm
+							title: "",          // user title overrides the node and name at the bottom of the chart
+							url: "",            // needed to reference filters and clip because of angular's location service
+							type: "value",      // value or rate
+							areaColor: "",      // can be set for non-stacked charts
+							lineColor: "",      // can be set for non-stacked charts
+							zoom: false         // should the y-axis range start at 0 or the min data value
+				}
+				var width = width - margin.left - margin.right,
+					height = height - margin.top - margin.bottom
+					yAxisTransitionDuration = 0
+
+				var x = d3.time.scale()
+			    var y = d3.scale.linear()
+			          .rangeRound([height, 0]);
+                // The x-accessor for the path generator; xScale ∘ xValue.
+				var X = function (d) { return x(d[0]) }
+                // The x-accessor for the path generator; yScale ∘ yValue.
+                var Y = function Y(d) { return y(d[1]) }
+
+                var xAxis = d3.svg.axis().scale(x).orient("bottom")
+					.outerTickSize(6)
+					.innerTickSize(-(height-margin.top-margin.bottom))
+                    .tickPadding(2)
+                    .ticks(d3.time.minutes, 2)
+                var yAxis = d3.svg.axis().scale(y).orient("right")
+                    .outerTickSize(8)
+                    .innerTickSize(-(width-margin.left-margin.right))
+                    .tickPadding(10)
+                    .ticks(3)
+                    .tickFormat(function(d) { return formatValue(d)})
+
+				var tooltipGenerator = function (d, color, format) {return ""}; // should be overridden to set an appropriate tooltip
+                var formatValue = d3.format(".2s");
+                var formatPrecise = d3.format(",");
+                var bisectDate = d3.bisector(function(d) { return d[0]; }).left;
+				var line = d3.svg.line();
+
+		        var stack = d3.layout.stack()
+			          .offset("zero")
+			          .values(function (d) { return d.values; })
+			          .x(function (d) { return x(d.date); })
+			          .y(function (d) { return d.value; });
+
+			    var area = d3.svg.area()
+
+				if (stacked) {
+			        area.interpolate("cardinal")
+			          .x(function (d) { return x(d.date); })
+			          .y0(function (d) { return y(d.y0); })
+			          .y1(function (d) { return y(d.y0 + d.y); });
+				} else {
+                    area.interpolate("basis").x(X).y1(Y)
+                    line.x(X).y(Y)
+				}
+				var color = d3.scale.category20();
+
+			    var sv = d3.select("#"+id).append("svg")
+			          .attr("width",  width  + margin.left + margin.right)
+			          .attr("height", height + margin.top  + margin.bottom)
+				var svg = sv
+			        .append("g")
+			          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+			    var clip = svg.append("defs").append("svg:clipPath")
+			        .attr("id", "clip")
+			        .append("svg:rect")
+			        .attr("id", "clip-rect")
+			        .attr("x", "0")
+			        .attr("y", "0")
+			        .attr("width", width)
+			        .attr("height", height);
+
+				// we want all our areas to appear before the axiis
+				svg.append("g")
+					.attr("class", "section-container")
+
+		        svg.append("g")
+			            .attr("class", "x axis")
+
+		        svg.append("g")
+			            .attr("class", "y axis")
+
+                svg.append("text").attr("class", "title")
+                        .attr("x", (width / 2) - (margin.left + margin.right) / 2)
+                        .attr("y", 0 - (margin.top / 2))
+                        .attr("text-anchor", "middle")
+                        .text(attrs.attrName);
+
+                svg.append("text").attr("class", "legend")
+                        .attr("x", (width / 2) - (margin.left + margin.right) / 2)
+                        .attr("y", height + (margin.bottom / 2) )
+                        .attr("text-anchor", "middle")
+                        .text(!stacked ? attrs.node + " " + attrs.name : attrs.name);
+
+                var focus = sv.append("g")
+                  .attr("class", "focus")
+                  .style("display", "none");
+
+                focus.append("circle")
+                  .attr("r", 4.5);
+
+                var focusg = focus.append("g");
+                focusg.append("rect")
+                    .attr("class", "mo-guide y")
+                    .attr("width", 1)
+                    .attr("height", height - (margin.top + margin.bottom));
+                focusg.append("rect")
+                    .attr("class", "mo-guide x")
+                    .attr("width", width - (margin.left + margin.right))
+                    .attr("height", 1);
+				focus.append("foreignObject")
+					.attr('class', 'svg-tooltip')
+					.append("xhtml:span");
+
+
+                function chart(selection) {
+                    selection.each(function(data) {
+
+					var seriesArr = []
+					if (stacked) {
+						var detailNames = data[0][2].map(function (detail){ return detail.node })
+						var revNames = angular.copy(detailNames).reverse();
+						color.domain(revNames);
+
+				        var series = {};
+				        detailNames.forEach(function (name) {
+							series[name] = {name: name, values:[]};
+							seriesArr.unshift(series[name]);    // insert at beginning
+				        });
+
+				        data.forEach(function (d) {
+							detailNames.map(function (name, i) {
+								series[name].values.push({date: d[0], value: d[2][i] ? d[2][i].val : 0});
+							});
+				        });
+
+				        // this decorates seriesArr with x,y,and y0 properties
+				        stack(seriesArr);
+					}
+
+                    var extent = d3.extent(data, function(d) {return d[0];});
+                    x.domain(extent)
+                      .range([0, width - margin.left - margin.right]);
+
+                    // Update the y-scale.
+                    var min = attrs.zoom ? 0 : d3.min(data, function(d) {return d[1]});
+                    var max = d3.max(data, function(d) {return d[1]});
+                    var mean = d3.mean(data, function(d) {return d[1]});
+                    //max = max * 1.01;
+                    var diff = (max - min);
+                    if (diff == 0) {
+                        max = max + 1;
+                        diff = 1;
+                    }
+                    var ratio = mean != 0 ? diff / mean : 1;
+                    if (ratio < .05)
+                        formatValue = d3.format(".3s")
+
+					if (stacked) {
+	                    y.domain([min, max])
+	                      .range([height - margin.top - margin.bottom, 0]);
+					} else {
+                        y
+                          .domain([min, max])
+                          .range([height - margin.top - margin.bottom, 0]);
+					}
+                        if (attrs.type == "rate") {
+                            area.interpolate("basis");  // rate charts look better smoothed
+                            line.interpolate("basis");
+                        }
+                        else {
+                            area.interpolate("linear"); // don't smooth value charts
+                            line.interpolate("linear");
+                        }
+
+                    // adjust the xaxis based on the range of x values (domain)
+                    var timeSpan = (extent[1] - extent[0]) / (1000 * 60);   // number of minutes
+                    if (timeSpan < 1.5)
+                        xAxis.ticks(d3.time.seconds, 10);
+                    else if (timeSpan < 3)
+                        xAxis.ticks(d3.time.seconds, 30);
+                    else if (timeSpan < 8)
+                        xAxis.ticks(d3.time.minutes, 1);
+                    else
+                        xAxis.ticks(d3.time.minutes, 2);
+
+                    // adjust the number of yaxis ticks based on the range of y values
+                    if (formatValue(min) === formatValue(max))
+                        yAxis.ticks(2);
+
+					var container = svg.select('.section-container');
+					container.selectAll('.series').remove();
+					if (stacked) {
+	                    y.domain([Math.min(min, 0), d3.max(seriesArr, function (c) {
+	                        return d3.max(c.values, function (d) { return d.y0 + d.y; });
+	                      })]);
+
+						// creates a .series g path for each section in the detail
+						// since we don't get more sections this selection is only run once
+	                    var series = container.selectAll(".series")
+	                      .data(seriesArr)
+
+						series.enter().append("g")
+	                        .attr("class", "series")
+				          .append("path")
+							.attr("class", "streamPath")
+							.style("fill", function (d) { return color(d.name); })
+							.style("stroke", "grey");
+
+						series.exit().remove()
+
+						//series.exit().remove()
+						// each time the data is updated, update each section
+						container.selectAll(".series .streamPath").data(seriesArr)
+							.attr("d", function (d) { return area(d.values); })
+					} else {
+	                    var series = container.selectAll(".series")
+	                      .data([data], function(d) { return d; })
+
+	                      series.enter().append("g")
+	                        .append("path")
+	                        .attr("class", "area")
+						  series.enter().append("path")
+	                        .attr("class", "line")
+
+						series.exit().remove()
+
+                        // Update the area path.
+                        container.select(".area").data([data])
+                          .attr("d", area.y0(y.range()[0]))
+						  .style("fill", attrs.areaColor);
+
+                        //Update the line path.
+                        container.select(".line").data([data])
+                          .attr("d", line)
+						  .style("stroke", attrs.lineColor)
+					}
+                    // Update the x-axis.
+                    svg.select(".x.axis")
+                      .attr("transform", "translate(0," + (height - margin.top - margin.bottom + 1) + ")")
+                      .call(xAxis);
+
+                    svg.select(".y.axis")
+						.transition().duration(yAxisTransitionDuration)  // animate the y axis
+                      .attr("transform", "translate(" + (width - margin.right - margin.left) + ",0)")
+                      .call(yAxis);
+                    yAxisTransitionDuration = 1000  // only do a transition after the chart is 1st drawn
+
+                    // TODO: fix this
+                    // need to recreate this every update... not sure why
+                    var overlay = sv.select(".overlay");
+                    if (!overlay.empty())
+                            overlay.remove();
+                    sv.append("rect")
+                      .attr("class", "overlay")
+                      .attr("width", width)
+                      .attr("height", height)
+                      .on("mouseover", function () {focus.style("display", null)})
+                      .on("mouseout", function () {focus.style("display", "none")})
+                      .on("mousemove", mousemove)
+
+	                    function mousemove() {
+	                        var x0 = x.invert(d3.mouse(this)[0] - margin.left);
+	                        var i = bisectDate(data, x0, 1);
+	                        if (i < data.length && i > 0) {
+	                            var d0 = data[i - 1];
+	                            var d1 = data[i];
+								// set d to the data that is closest to the mouse position
+	                            var d = x0 - d0[0] > d1[0] - x0 ? d1 : d0;
+	                            focus.attr("transform", "translate(" + (x(d[0]) + margin.left) + "," + (y(d[1]) + margin.top) + ")");
+
+								var tipFormat = formatPrecise;
+								if (attrs.type === "rate")
+									tipFormat = d3.format(".2n")
+								// set the tooltip html and position it
+								focus.select('.svg-tooltip span')
+									.html(tooltipGenerator(d, color, tipFormat))
+
+								var foBounds = focus.select('table')[0][0].getBoundingClientRect();
+	                            var mx = x(d[0]); // mouse x
+	                            var my = y(d[1]); // mouse y
+
+	                            // perfer to put the tooltip in the nw corner relative to the focus circle
+	                            var foy = -foBounds.height;
+								var fox = -foBounds.width;
+								// off the left side
+								if (mx - foBounds.width - margin.left < 0)
+									fox = 0;
+								// above the top
+								if (my - foBounds.height - margin.top < 0)
+									foy = 0;
+								// won't fit above or below, just put it at bottom
+								if (my + foBounds.height > height)
+									foy = -(foBounds.height - (height - my));
+
+								focus.select('.svg-tooltip')
+									.attr('x', fox).attr('y', foy);
+
+								// position the guide lines
+	                            focus.select(".mo-guide.y")
+	                                .attr("y", -my);
+	                            focus.select(".mo-guide.x")
+	                                .attr("x", -mx);
+
+	                        } else {
+	                            focus.attr("transform", "translate(-10,-10)");
+	                        }
+	                    }
+
+                    })
+
+
+                }
+				chart.attr = function (attrName, value) {
+					if (arguments.length < 2)
+						return arguments.length == 1 ? attrs[attrName] : chart;
+					if (angular.isDefined(attrs[attrName]))
+						attrs[attrName] = value;
+					return chart;
+				}
+				chart.tooltipGenerator = function (_) {
+					tooltipGenerator = _;
+					return chart;
+				}
+
+				return chart;
+            }
+        }
+        return self;
+  }]);
+
+  return QDR;
+}(QDR || {}));

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/4940e63d/console/stand-alone/plugin/js/qdrCharts.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrCharts.js b/console/stand-alone/plugin/js/qdrCharts.js
new file mode 100644
index 0000000..e484b07
--- /dev/null
+++ b/console/stand-alone/plugin/js/qdrCharts.js
@@ -0,0 +1,230 @@
+/*
+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
+ */
+/**
+ * @module QDR
+ */
+var QDR = (function (QDR) {
+
+  /**
+   * @method ChartsController
+   * @param $scope
+   * @param QDRServer
+   * @param QDRChartServer
+   *
+   * Controller that handles the QDR charts page
+   */
+  QDR.module.controller("QDR.ChartsController", ['$scope', 'QDRService', 'QDRChartService', '$uibModal', '$location',
+  function($scope, QDRService, QDRChartService, $uibModal, $location) {
+    var updateTimer = null;
+
+    QDR.log.debug("started Charts controller");
+    if (!angular.isDefined(QDRService.schema))
+        return;
+
+    $scope.svgCharts = [];
+    // create an svg object for each chart
+    QDRChartService.charts.filter(function (chart) {return chart.dashboard}).forEach(function (chart) {
+        var svgChart = new QDRChartService.AreaChart(chart, $location.$$path)
+        svgChart.zoomed = false;
+        $scope.svgCharts.push(svgChart);
+    })
+
+    // redraw the charts every second
+    var updateCharts = function () {
+        $scope.svgCharts.forEach(function (svgChart) {
+            svgChart.tick(svgChart.chart.id()); // on this page we are using the chart.id() as the div id in which to render the chart
+        })
+        updateHandle = setTimeout(updateCharts, 1100);
+    }
+	$scope.chartsLoaded = function () {
+		setTimeout(updateCharts, 0);
+	}
+
+	$scope.zoomChart = function (chart) {
+		chart.zoomed = !chart.zoomed;
+		chart.zoom(chart.chart.id(), chart.zoomed);
+	}
+    $scope.showListPage = function () {
+        $location.path("/list");
+    };
+
+    $scope.hasCharts = function () {
+        return QDRChartService.numCharts() > 0;
+    };
+
+    $scope.editChart = function (chart) {
+        doDialog("chart-config-template.html", chart.chart);
+    };
+
+    $scope.delChart = function (chart) {
+        QDRChartService.unRegisterChart(chart.chart);
+        // remove from svgCharts
+        $scope.svgCharts.forEach(function (svgChart, i) {
+            if (svgChart === chart) {
+                delete $scope.svgCharts.splice(i, 1);
+            }
+        })
+    };
+
+    // called from dialog when we want to clone the dialog chart
+    // the chart argument here is a QDRChartService chart
+    $scope.addChart = function (chart) {
+        $scope.svgCharts.push(new QDRChartService.AreaChart(chart, $location.$$path));
+    };
+
+    $scope.$on("$destroy", function( event ) {
+        if (updateTimer) {
+            cancelTimer(updateTimer);
+            updateTimer = null;
+        }
+        for (var i=$scope.svgCharts.length-1; i>=0; --i) {
+            delete $scope.svgCharts.splice(i, 1);
+        }
+    });
+
+    function doDialog(template, chart) {
+
+	    var modalInstance = $uibModal.open({
+	      animation: true,
+	      templateUrl: template,
+	      controller: 'QDR.ChartDialogController',
+	      resolve: {
+	        chart: function () {
+	          return chart;
+	        },
+	        dashboard: function () {
+	            return $scope;
+	        }
+	      }
+	    });
+    };
+
+  }]);
+
+  QDR.module.controller("QDR.ChartDialogController", function($scope, QDRChartService, $location, $uibModalInstance, $rootScope, chart, dashboard) {
+        var dialogSvgChart = null;
+        $scope.svgDivId = "dialogChart";    // the div id for the svg chart
+
+		$scope.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);
+            }
+        })
+        // the stored rateWindow is in milliseconds, but the slider is in seconds
+        $scope.rateWindow = $scope.chart.rateWindow / 1000;
+
+		var cleanup = function () {
+			if ($scope.updateTimer) {
+				clearTimeout($scope.updateTimer);
+				$scope.updateTimer = null;
+			}
+			QDRChartService.unRegisterChart($scope.dialogChart);     // remove the chart
+		}
+		$scope.okClick = function () {
+			cleanup();
+	        $uibModalInstance.close();
+	    };
+
+        // initialize the rateWindow slider
+        $scope.slider = {
+            'options': {
+                min: 1,
+                max: 10,
+                step: 1,
+                tick: true,
+                stop: function (event, ui) {
+                    $scope.dialogChart.rateWindow = ui.value * 1000;
+                    if (dialogSvgChart)
+                        dialogSvgChart.tick($scope.svgDivId);
+                }
+            }
+		};
+
+        $scope.visibleDuration =
+        $scope.duration = {
+            'options': {
+                min: 1,
+                max: 10,
+                step: 1,
+                tick: true,
+                stop: function (event, ui) {
+                    if (dialogSvgChart)
+                        dialogSvgChart.tick($scope.svgDivId);
+                }
+            }
+		};
+
+        // 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.dialogChart.rateWindow;
+            $scope.chart.title($scope.dialogChart.title());
+            $scope.chart.visibleDuration = $scope.dialogChart.visibleDuration;
+            QDRChartService.saveCharts();
+        }
+
+        // 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
+            $scope.updateTimer = setTimeout(updateDialogChart, 1000);
+        }
+
+        var showChart = function () {
+            // ensure the div for our chart is loaded in the dom
+            var div = angular.element("#dialogChart");
+            if (!div.width()) {
+                setTimeout(showChart, 100);
+                return;
+            }
+            dialogSvgChart = new QDRChartService.AreaChart($scope.dialogChart, $location.$$path);
+            updateDialogChart();
+        }
+        showChart();
+
+
+  });
+
+  return QDR;
+
+}(QDR || {}));
+


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