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/10/19 12:49:00 UTC
[01/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Repository: qpid-dispatch
Updated Branches:
refs/heads/master 979030344 -> 0c58c3814
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.node-controller.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.node-controller.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.node-controller.js
new file mode 100644
index 0000000..2f31cca
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.node-controller.js
@@ -0,0 +1,294 @@
+/*
+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) {
+ 'use strict';
+
+ angular
+ .module('horizon.dashboard.dispatch.topology')
+ .controller('horizon.dashboard.dispatch.topology.TopologyNodeController', TopologyNodeController);
+
+ TopologyNodeController.$inject = [
+ '$scope',
+ 'horizon.dashboard.dispatch.comService'
+ ]
+
+ function TopologyNodeController($scope, QDRService, dialog, newname) {
+ var schema = QDRService.schema;
+ var myEntities = ['router', 'log', 'listener' ];
+ var typeMap = {integer: 'number', string: 'text', path: 'text', boolean: 'boolean'};
+ var newLinks = $('path.temp').toArray(); // jquery array of new links for the added router
+ var nodeInfo = QDRService.topology.nodeInfo();
+ var separatedEntities = []; // additional entities required if a link is reversed
+ var myPort = 0, myAddr = '0.0.0.0'; // port and address for new router
+ $scope.entities = [];
+
+ // find max port number that is used in all the listeners
+ var getMaxPort = function (nodeInfo) {
+ var maxPort = 5674;
+ for (var key in nodeInfo) {
+ var node = nodeInfo[key];
+ var listeners = node['.listener'];
+ var attrs = listeners.attributeNames;
+ for (var i=0; i<listeners.results.length; ++i) {
+ var res = listeners.results[i];
+ var port = QDRService.valFor(attrs, res, 'port');
+ if (parseInt(port, 10) > maxPort)
+ maxPort = parseInt(port, 10);
+ }
+ }
+ return maxPort;
+ }
+ var maxPort = getMaxPort(nodeInfo);
+
+ // construct an object that contains all the info needed for a single tab's fields
+ var entity = function (actualName, tabName, humanName, ent, icon, link) {
+ var nameIndex = -1; // the index into attributes that the name field was placed
+ var index = 0;
+ var info = {
+ actualName: actualName,
+ tabName: tabName,
+ humanName: humanName,
+ description:ent.description,
+ icon: angular.isDefined(icon) ? icon : '',
+ references: ent.references,
+ link: link,
+
+ attributes: $.map(ent.attributes, function (value, key) {
+ // skip identity and depricated fields
+ if (key == 'identity' || value.description.startsWith('Deprecated'))
+ return null;
+ var val = value['default'];
+ if (key == 'name')
+ nameIndex = index;
+ index++;
+ return { name: key,
+ humanName: QDRService.humanify(key),
+ description:value.description,
+ type: typeMap[value.type],
+ rawtype: value.type,
+ input: typeof value.type == 'string' ? value.type == 'boolean' ? 'boolean' : 'input'
+ : 'select',
+ selected: val ? val : undefined,
+ 'default': value['default'],
+ value: val,
+ required: value.required,
+ unique: value.unique
+ };
+ })
+ }
+ // move the 'name' attribute to the 1st position
+ if (nameIndex > -1) {
+ var tmp = info.attributes[0];
+ info.attributes[0] = info.attributes[nameIndex];
+ info.attributes[nameIndex] = tmp;
+ }
+ return info;
+ }
+
+ // remove the annotation fields
+ var stripAnnotations = function (entityName, ent, annotations) {
+ if (ent.references) {
+ var newEnt = {attributes: {}};
+ ent.references.forEach( function (annoKey) {
+ if (!annotations[annoKey])
+ annotations[annoKey] = {};
+ annotations[annoKey][entityName] = true; // create the key/consolidate duplicates
+ var keys = Object.keys(schema.annotations[annoKey].attributes);
+ for (var attrib in ent.attributes) {
+ if (keys.indexOf(attrib) == -1) {
+ newEnt.attributes[attrib] = ent.attributes[attrib];
+ }
+ }
+ // add a field for the reference name
+ newEnt.attributes[annoKey] = {type: 'string',
+ description: 'Name of the ' + annoKey + ' section.',
+ 'default': annoKey, required: true};
+ })
+ newEnt.references = ent.references;
+ newEnt.description = ent.description;
+ return newEnt;
+ }
+ return ent;
+ }
+
+ var annotations = {};
+ myEntities.forEach(function (entityName) {
+ var ent = schema.entityTypes[entityName];
+ var hName = QDRService.humanify(entityName);
+ if (entityName == 'listener')
+ hName = "Listener for clients";
+ var noAnnotations = stripAnnotations(entityName, ent, annotations);
+ var ediv = entity(entityName, entityName, hName, noAnnotations, undefined);
+ if (ediv.actualName == 'router') {
+ ediv.attributes.filter(function (attr) { return attr.name == 'name'})[0].value = newname;
+ // if we have any new links (connectors), then the router's mode should be interior
+ if (newLinks.length) {
+ var roleAttr = ediv.attributes.filter(function (attr) { return attr.name == 'mode'})[0];
+ roleAttr.value = roleAttr.selected = "interior";
+ }
+ }
+ if (ediv.actualName == 'container') {
+ ediv.attributes.filter(function (attr) { return attr.name == 'containerName'})[0].value = newname + "-container";
+ }
+ if (ediv.actualName == 'listener') {
+ // find max port number that is used in all the listeners
+ ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = ++maxPort;
+ }
+ // special case for required log.module since it doesn't have a default
+ if (ediv.actualName == 'log') {
+ var moduleAttr = ediv.attributes.filter(function (attr) { return attr.name == 'module'})[0];
+ moduleAttr.value = moduleAttr.selected = "DEFAULT";
+ }
+ $scope.entities.push( ediv );
+ })
+
+ // add a tab for each annotation that was found
+ var annotationEnts = [];
+ for (var key in annotations) {
+ ent = angular.copy(schema.annotations[key]);
+ ent.attributes.name = {type: "string", unique: true, description: "Unique name that is used to refer to this set of attributes."}
+ var ediv = entity(key, key+'tab', QDRService.humanify(key), ent, undefined);
+ ediv.attributes.filter(function (attr) { return attr.name == 'name'})[0].value = key;
+ $scope.entities.push( ediv );
+ annotationEnts.push( ediv );
+ }
+
+ // add an additional listener tab if any links are reversed
+ ent = schema.entityTypes['listener'];
+ newLinks.some(function (link) {
+ if (link.__data__.right) {
+ var noAnnotations = stripAnnotations('listener', ent, annotations);
+ var ediv = entity("listener", "listener0", "Listener (internal)", noAnnotations, undefined);
+ ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = ++maxPort;
+ // connectors from other routers need to connect to this addr:port
+ myPort = maxPort;
+ myAddr = ediv.attributes.filter(function (attr) { return attr.name == 'host'})[0].value
+
+ // override the role. 'normal' is the default, but we want inter-router
+ ediv.attributes.filter(function( attr ) { return attr.name == 'role'})[0].selected = 'inter-router';
+ separatedEntities.push( ediv );
+ return true; // stop looping
+ }
+ return false; // continue looping
+ })
+
+ // Add connector tabs for each new link on the topology graph
+ ent = schema.entityTypes['connector'];
+ newLinks.forEach(function (link, i) {
+ var noAnnotations = stripAnnotations('connector', ent, annotations);
+ var ediv = entity('connector', 'connector' + i, " " + link.__data__.source.name, noAnnotations, link.__data__.right, link)
+
+ // override the connector role. 'normal' is the default, but we want inter-router
+ ediv.attributes.filter(function( attr ) { return attr.name == 'role'})[0].selected = 'inter-router';
+
+ // find the addr:port of the inter-router listener to use
+ var listener = nodeInfo[link.__data__.source.key]['.listener'];
+ var attrs = listener.attributeNames;
+ for (var i=0; i<listener.results.length; ++i) {
+ var res = listener.results[i];
+ var role = QDRService.valFor(attrs, res, 'role');
+ if (role == 'inter-router') {
+ ediv.attributes.filter(function( attr ) { return attr.name == 'host'})[0].value =
+ QDRService.valFor(attrs, res, 'host')
+ ediv.attributes.filter(function( attr ) { return attr.name == 'port'})[0].value =
+ QDRService.valFor(attrs, res, 'port')
+ break;
+ }
+ }
+ if (link.__data__.right) {
+ // connectors from other nodes need to connect to the new router's listener addr:port
+ ediv.attributes.filter(function (attr) { return attr.name == 'port'})[0].value = myPort;
+ ediv.attributes.filter(function (attr) { return attr.name == 'host'})[0].value = myAddr;
+
+ separatedEntities.push(ediv)
+ }
+ else
+ $scope.entities.push( ediv );
+ })
+ Array.prototype.push.apply($scope.entities, separatedEntities);
+
+ // update the description on all the annotation tabs
+ annotationEnts.forEach ( function (ent) {
+ var shared = Object.keys(annotations[ent.actualName]);
+ ent.description += " These fields are shared by " + shared.join(" and ") + ".";
+
+ })
+
+ $scope.testPattern = function (attr) {
+ if (attr.rawtype == 'path')
+ return /^(\/)?([^/\0]+(\/)?)+$/;
+ //return /^(.*\/)([^/]*)$/;
+ return /(.*?)/;
+ }
+
+ $scope.attributeDescription = '';
+ $scope.attributeType = '';
+ $scope.attributeRequired = '';
+ $scope.attributeUnique = '';
+ $scope.active = 'router'
+ $scope.fieldsetDivs = "/fieldsetDivs.html"
+ $scope.setActive = function (tabName) {
+ $scope.active = tabName
+ }
+ $scope.isActive = function (tabName) {
+ return $scope.active === tabName
+ }
+ $scope.showDescription = function (attr, e) {
+ $scope.attributeDescription = attr.description;
+ var offset = jQuery(e.currentTarget).offset()
+ jQuery('.attr-description').offset({top: offset.top})
+
+ $scope.attributeType = "Type: " + JSON.stringify(attr.rawtype);
+ $scope.attributeRequired = attr.required ? 'required' : '';
+ $scope.attributeUnique = attr.unique ? 'Must be unique' : '';
+ }
+ // handle the download button click
+ // copy the dialog's values to the original node
+ $scope.download = function () {
+ dialog.close({entities: $scope.entities, annotations: annotations});
+ }
+ $scope.cancel = function () {
+ dialog.close()
+ };
+
+ $scope.selectAnnotationTab = function (tabName) {
+ var tabs = $( "#tabs" ).tabs();
+ tabs.tabs("select", tabName);
+ }
+
+ var initTabs = function () {
+ var div = angular.element("#tabs");
+ if (!div.width()) {
+ setTimeout(initTabs, 100);
+ return;
+ }
+ $( "#tabs" )
+ .tabs()
+ .addClass('ui-tabs-vertical ui-helper-clearfix');
+ }
+ // start the update loop
+ initTabs();
+
+ };
+
+ return QDR;
+}(QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/templates/dispatch/base.html
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/templates/dispatch/base.html b/console/dispatch-dashboard/dispatch/templates/dispatch/base.html
new file mode 100644
index 0000000..e0c28e6
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/templates/dispatch/base.html
@@ -0,0 +1,10 @@
+{% extends 'base.html' %}
+
+{% block sidebar %}
+ {% include 'horizon/common/_sidebar.html' %}
+{% endblock %}
+
+{% block main %}
+ {% include "horizon/_messages.html" %}
+ {% block dispatch_main %}{% endblock %}
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/topology/__init__.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/topology/__init__.py b/console/dispatch-dashboard/dispatch/topology/__init__.py
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/topology/panel.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/topology/panel.py b/console/dispatch-dashboard/dispatch/topology/panel.py
new file mode 100644
index 0000000..6efbeda
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/topology/panel.py
@@ -0,0 +1,20 @@
+# Licensed 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.
+
+from django.utils.translation import ugettext_lazy as _
+
+import horizon
+
+
+class Topology(horizon.Panel):
+ name = _("Topology")
+ slug = "topology"
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/topology/templates/topology/index.html
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/topology/templates/topology/index.html b/console/dispatch-dashboard/dispatch/topology/templates/topology/index.html
new file mode 100644
index 0000000..80d76bd
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/topology/templates/topology/index.html
@@ -0,0 +1,35 @@
+{% extends 'base.html' %}
+{% load breadcrumb_nav %}
+{% load i18n %}
+{% block title %}{% trans "Topology" %}{% endblock %}
+
+{% block content %}
+<div class='topbar'>
+ {% include "header/_header.html" %}
+</div>
+<div id='main_content' class="topology-container">
+ {% include "horizon/_messages.html" %}
+ {% block sidebar %}
+ {% include 'horizon/common/_sidebar.html' %}
+ {% endblock %}
+ <div id='content_body'>
+ <div class='container-fluid'>
+ <div class="row">
+ <div class="col-xs-12">
+ <div class="page-breadcrumb">
+ {% block breadcrumb_nav %}
+ {% breadcrumb_nav %}
+ {% endblock %}
+ </div>
+
+ {% block page_header %}
+ {% endblock %}
+ {% block main %}
+ <ng-include src="'dispatch/topology.html'"></ng-include>
+ {% endblock %}
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+{% endblock %}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/topology/tests.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/topology/tests.py b/console/dispatch-dashboard/dispatch/topology/tests.py
new file mode 100644
index 0000000..b321fc1
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/topology/tests.py
@@ -0,0 +1,19 @@
+# Licensed 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.
+
+from horizon.test import helpers as test
+
+
+class TopologyTests(test.TestCase):
+ # Unit tests for topology.
+ def test_me(self):
+ self.assertTrue(1 + 1 == 2)
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/topology/urls.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/topology/urls.py b/console/dispatch-dashboard/dispatch/topology/urls.py
new file mode 100644
index 0000000..d8629f9
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/topology/urls.py
@@ -0,0 +1,20 @@
+# Licensed 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.
+
+from django.conf.urls import url
+
+from dispatch.topology import views
+
+
+urlpatterns = [
+ url(r'^$', views.IndexView.as_view(), name='index'),
+]
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/topology/views.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/topology/views.py b/console/dispatch-dashboard/dispatch/topology/views.py
new file mode 100644
index 0000000..03b6012
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/topology/views.py
@@ -0,0 +1,22 @@
+# Licensed 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.
+
+from horizon import views
+
+
+class IndexView(views.APIView):
+ # A very simple class-based view...
+ template_name = 'dispatch/topology/index.html'
+
+ def get_data(self, request, context, *args, **kwargs):
+ # Add data to the context here...
+ return context
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/enabled/_4000_dispatch.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/enabled/_4000_dispatch.py b/console/dispatch-dashboard/enabled/_4000_dispatch.py
new file mode 100644
index 0000000..16d3215
--- /dev/null
+++ b/console/dispatch-dashboard/enabled/_4000_dispatch.py
@@ -0,0 +1,33 @@
+# Licensed 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.
+
+# The name of the dashboard to be added to HORIZON['dashboards']. Required.
+DASHBOARD = 'dispatch'
+
+DEFAULT = False
+
+ADD_EXCEPTIONS = {}
+
+# If set to True, this dashboard will not be added to the settings.
+DISABLED = False
+
+# A list of applications to be added to INSTALLED_APPS.
+ADD_INSTALLED_APPS = ['dispatch']
+ADD_ANGULAR_MODULES = [
+ 'horizon.dashboard.dispatch',
+]
+
+ADD_SCSS_FILES = [
+ 'dashboard/dispatch/dispatch.scss',
+]
+
+AUTO_DISCOVER_STATIC_FILES = True
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/enabled/_4030_dispatch_overv_panel.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/enabled/_4030_dispatch_overv_panel.py b/console/dispatch-dashboard/enabled/_4030_dispatch_overv_panel.py
new file mode 100644
index 0000000..9431be6
--- /dev/null
+++ b/console/dispatch-dashboard/enabled/_4030_dispatch_overv_panel.py
@@ -0,0 +1,9 @@
+# The slug of the panel to be added to HORIZON_CONFIG. Required.
+PANEL = 'overv'
+# The slug of the dashboard the PANEL associated with. Required.
+PANEL_DASHBOARD = 'dispatch'
+# The slug of the panel group the PANEL is associated with.
+PANEL_GROUP = 'default'
+
+# Python panel class of the PANEL to be added.
+ADD_PANEL = 'dispatch.overv.panel.Overv'
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/enabled/_4050_dispatch_topology_panel.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/enabled/_4050_dispatch_topology_panel.py b/console/dispatch-dashboard/enabled/_4050_dispatch_topology_panel.py
new file mode 100644
index 0000000..c641594
--- /dev/null
+++ b/console/dispatch-dashboard/enabled/_4050_dispatch_topology_panel.py
@@ -0,0 +1,9 @@
+# The slug of the panel to be added to HORIZON_CONFIG. Required.
+PANEL = 'topology'
+# The slug of the dashboard the PANEL associated with. Required.
+PANEL_DASHBOARD = 'dispatch'
+# The slug of the panel group the PANEL is associated with.
+PANEL_GROUP = 'default'
+
+# Python panel class of the PANEL to be added.
+ADD_PANEL = 'dispatch.topology.panel.Topology'
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/setup.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/setup.py b/console/dispatch-dashboard/setup.py
new file mode 100644
index 0000000..1815410
--- /dev/null
+++ b/console/dispatch-dashboard/setup.py
@@ -0,0 +1,42 @@
+#! /usr/bin/env python
+#
+# Licensed 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.
+
+from setuptools import setup, find_packages
+
+setup(
+ name = 'dispatch',
+ version = '0.0.1',
+ description = 'sample dashboard extension for OpenStack Dashboard',
+ author = 'Cindy Lu',
+ author_email = 'clu@us.ibm.com',
+ classifiers = [
+ 'Environment :: OpenStack',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Operating System :: OS Independent',
+ 'Operating System :: POSIX :: Linux',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.7',
+ 'Topic :: Internet :: WWW/HTTP',
+ ],
+ packages=find_packages(),
+ include_package_data = True,
+)
+
+
+
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[08/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/d3.v3.min.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/d3.v3.min.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/d3.v3.min.js
new file mode 100644
index 0000000..1664873
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/d3.v3.min.js
@@ -0,0 +1,5 @@
+!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function r(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)<0?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n(t[u],e)>0?i=u:r=u+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function l(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function c(){this._=Object.create(null)}function f(n){return(n+="")===bo||n[0]===_o?_o+n:n}function s(n){return(n+="")[0]===_o?n.slice(1):n}function h(n){return f(n)in this._}function p(n){return(n=f(n))in this._&&delete this
._[n]}function g(){var n=[];for(var t in this._)n.push(s(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function y(){this._=Object.create(null)}function m(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=wo.length;r>e;++e){var i=wo[e]+t;if(i in n)return i}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new c;return t.on=function(t,i){var u,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,u=e.indexOf(o)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function S(){ao.event.preventDefault()}function k(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function N(n){for(var t=new _,e=0,r=arguments.length;++e<r;)t[arguments[e]]=w(t);return t.of
=function(e,r){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(e,r)}finally{ao.event=u}}},t}function E(n){return ko(n,Co),n}function A(n){return"function"==typeof n?n:function(){return No(n,this)}}function C(n){return"function"==typeof n?n:function(){return Eo(n,this)}}function z(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=ao.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?u:i}function L(n){return n.trim().replace(/\s+/g," ")}function q(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s
+/)}function R(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=T(n).map(D);var i=n.length;return"function"==typeof t?r:e}function D(n){var t=q(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",L(i+" "+n))):e.setAttribute("class",L(i.replace(t," ")))}}function P(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function U(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}function j(n){function t(){var t=this.ownerDocument,e=this.namespaceURI;return e===zo&&t.documentElement.namespaceURI===zo?
t.createElement(n):t.createElementNS(e,n)}function e(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?e:t}function F(){var n=this.parentNode;n&&n.removeChild(this)}function H(n){return{__data__:n}}function O(n){return function(){return Ao(this,n)}}function I(n){return arguments.length||(n=e),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function Y(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,e);return n}function Z(n){return ko(n,qo),n}function V(n){var t,e;return function(r,i,u){var o,a=n[u].update,l=a.length;for(u!=e&&(e=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<l;);return o}}function X(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=l(t,co(arguments));r.call(this),this.addEventListener(n,this[o]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var r in this)if(t=r.matc
h(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),l=$;a>0&&(n=n.slice(0,a));var c=To.get(n);return c&&(n=c,l=B),a?t?i:r:t?b:u}function $(n,t){return function(e){var r=ao.event;ao.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Do,i="click"+r,u=ao.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ro&&(Ro="onselectstart"in e?!1:x(e.style,"userSelect")),Ro){var o=n(e).style,a=o[Ro];o[Ro]="none"}return function(n){if(u.on(r,null),Ro&&(o[Ro]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var i=r.createSVGPoint();if(0>Po){var u=t(n);if(u.scrollX||u.scrollY){r=ao.select
("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Po=!(o.f||o.e),r.remove()}}return Po?(i.x=e.pageX,i.y=e.pageY):(i.x=e.clientX,i.y=e.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ao.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nn(n){return n>1?0:-1>n?Fo:Math.acos(n)}function tn(n){return n>1?Io:-1>n?-Io:Math.asin(n)}function en(n){return((n=Math.exp(n))-1/n)/2}function rn(n){return((n=Math.exp(n))+1/n)/2}function un(n){return((n=Math.exp(2*n))-1)/(n+1)}function on(n){return(n=Math.sin(n/2))*n}function an(){}function ln(n,t,e){return this instanceof ln?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof ln?new ln(n.h,n.s,n.l):_n(""+n,wn
,ln):new ln(n,t,e)}function cn(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(o-u)*n/60:180>n?o:240>n?u+(o-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,u=2*e-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,e){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?gn(n.l,n.a,n.b):gn((n=Sn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,e)}function sn(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(e,Math.cos(n*=Yo)*t,Math.sin(n)*t)}function hn(n,t,e){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):Sn((n=mn(n)).r,n.g,n.b):new hn(n,t,e)}function pn(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=vn(i)*na,r=vn(r)*ta,u=vn(u)*ea,new mn(yn(3.2404542*i-1.53713
85*r-.4985314*u),yn(-.969266*i+1.8760108*r+.041556*u),yn(.0556434*i-.2040259*r+1.0572252*u))}function gn(n,t,e){return n>0?new fn(Math.atan2(e,t)*Zo,Math.sqrt(t*t+e*e),n):new fn(NaN,NaN,n)}function vn(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function dn(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function yn(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mn(n,t,e){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):_n(""+n,mn,cn):new mn(n,t,e)}function Mn(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return Mn(n)+""}function bn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function _n(n,t,e){var r,i,u,o=0,a=0,l=0;if(r=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(Nn(i[0]),Nn(i[1]),Nn(i[2]))}return(u=ua.g
et(n))?t(u.r,u.g,u.b):(null==n||"#"!==n.charAt(0)||isNaN(u=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&u)>>4,o=o>>4|o,a=240&u,a=a>>4|a,l=15&u,l=l<<4|l):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,l=255&u)),t(o,a,l))}function wn(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-u,l=(o+u)/2;return a?(i=.5>l?a/(o+u):a/(2-o-u),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=NaN,i=l>0&&1>l?0:r),new ln(r,i,l)}function Sn(n,t,e){n=kn(n),t=kn(t),e=kn(e);var r=dn((.4124564*n+.3575761*t+.1804375*e)/na),i=dn((.2126729*n+.7151522*t+.072175*e)/ta),u=dn((.0193339*n+.119192*t+.9503041*e)/ea);return hn(116*i-16,500*(r-i),200*(i-u))}function kn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function Nn(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function En(n){return"function"==typeof n?n:function(){return n}}function An(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),
Cn(t,e,n,r)}}function Cn(n,t,e,r){function i(){var n,t=l.status;if(!t&&Ln(l)||t>=200&&300>t||304===t){try{n=e.call(u,l)}catch(r){return void o.error.call(u,r)}o.load.call(u,n)}else o.error.call(u,l)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},l=new XMLHttpRequest,c=null;return!this.XDomainRequest||"withCredentials"in l||!/^(http(s)?:)?\/\//.test(n)||(l=new XDomainRequest),"onload"in l?l.onload=l.onerror=i:l.onreadystatechange=function(){l.readyState>3&&i()},l.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,l)}finally{ao.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(c=n,u):c},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(co(arguments)))}}),u.send=function(e,r,i){if(
2===arguments.length&&"function"==typeof r&&(i=r,r=null),l.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),l.setRequestHeader)for(var f in a)l.setRequestHeader(f,a[f]);return null!=t&&l.overrideMimeType&&l.overrideMimeType(t),null!=c&&(l.responseType=c),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,l),l.send(null==r?null:r),u},u.abort=function(){return l.abort(),u},ao.rebind(u,o,"on"),null==r?u:u.get(zn(r))}function zn(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Ln(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qn(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var i=e+t,u={c:n,t:i,n:null};return aa?aa.n=u:oa=u,aa=u,la||(ca=clearTimeout(ca),la=1,fa(Tn)),u}function Tn(){var n=Rn(),t=Dn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),la=0):(la=1,fa(Tn))}function Rn(){for(var n=Date.now(),t=oa;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Dn(){for(var
n,t=oa,e=1/0;t;)t.c?(t.t<e&&(e=t.t),t=(n=t).n):t=n?n.n=t.n:oa=t.n;return aa=n,e}function Pn(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function Un(n,t){var e=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function jn(n){var t=n.decimal,e=n.thousands,r=n.grouping,i=n.currency,u=r&&e?function(n,t){for(var i=n.length,u=[],o=0,a=r[0],l=0;i>0&&a>0&&(l+a+1>t&&(a=Math.max(1,t-l)),u.push(n.substring(i-=a,i+a)),!((l+=a+1)>t));)a=r[o=(o+1)%r.length];return u.reverse().join(e)}:m;return function(n){var e=ha.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",l=e[4]||"",c=e[5],f=+e[6],s=e[7],h=e[8],p=e[9],g=1,v="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(c||"0"===r&&"="===o)&&(c=r="0",o="="),p){case"n":s=!0,p="g";break;case"%":g=100,d="%",p="f";break;case"p":g=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===l&&(v="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":g=-1,p="r"}"$"===l&&(v=i[0],d=i[1]),"r"!=p||h||(p
="g"),null!=h&&("g"==p?h=Math.max(1,Math.min(21,h)):"e"!=p&&"f"!=p||(h=Math.max(0,Math.min(20,h)))),p=pa.get(p)||Fn;var M=c&&s;return function(n){var e=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>g){var l=ao.formatPrefix(n,h);n=l.scale(n),e=l.symbol+d}else n*=g;n=p(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!c&&s&&(x=u(x,1/0));var S=v.length+x.length+b.length+(M?0:i.length),k=f>S?new Array(S=f-S+1).join(r):"";return M&&(x=u(k+x,k.length?f-b.length:1/0)),i+=v,n=x+b,("<"===o?i+n+k:">"===o?k+i+n:"^"===o?k.substring(0,S>>=1)+i+n+k.substring(S):i+(M?n:k+n))+e}}}function Fn(n){return n+""}function Hn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function On(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new va(e-1)),1),e}function u(n,e){return t(n=new va(+n),e
),n}function o(n,r,u){var o=i(n),a=[];if(u>1)for(;r>o;)e(o)%u||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{va=Hn;var r=new Hn;return r._=n,o(r,t,e)}finally{va=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=o;var l=n.utc=In(n);return l.floor=l,l.round=In(r),l.ceil=In(i),l.offset=In(u),l.range=a,n}function In(n){return function(t,e){try{va=Hn;var r=new Hn;return r._=t,n(r,e)._}finally{va=Date}}}function Yn(n){function t(n){function t(t){for(var e,i,u,o=[],a=-1,l=0;++a<r;)37===n.charCodeAt(a)&&(o.push(n.slice(l,a)),null!=(i=ya[e=n.charAt(++a)])&&(e=n.charAt(++a)),(u=A[e])&&(e=u(t,null==i?"e"===e?" ":"0":i)),o.push(e),l=a+1);return o.push(n.slice(l,a)),o.join("")}var r=n.length;return t.parse=function(t){var r={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=e(r,n,t,0);if(i!=t.length)return null;"p"in r&&(r.H=r.H%12+12*r.p);var u=null!=r.Z&&va!==Hn,o=new(u?Hn:va);return"j"in r?o.setFullYear(r.y,0,r.j):"W"in r||"U"in r?("w"in r||(
r.w="W"in r?1:0),o.setFullYear(r.y,0,1),o.setFullYear(r.y,0,"W"in r?(r.w+6)%7+7*r.W-(o.getDay()+5)%7:r.w+7*r.U-(o.getDay()+6)%7)):o.setFullYear(r.y,r.m,r.d),o.setHours(r.H+(r.Z/100|0),r.M+r.Z%100,r.S,r.L),u?o._:o},t.toString=function(){return n},t}function e(n,t,e,r){for(var i,u,o,a=0,l=t.length,c=e.length;l>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=C[o in ya?t.charAt(a++):o],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){N.lastIndex=0;var r=N.exec(t.slice(e));return r?(n.m=E.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,A.c.toString(),t,r)}fun
ction l(n,t,r){return e(n,A.x.toString(),t,r)}function c(n,t,r){return e(n,A.X.toString(),t,r)}function f(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var s=n.dateTime,h=n.date,p=n.time,g=n.periods,v=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function e(n){try{va=Hn;var t=new va;return t._=n,r(t)}finally{va=Date}}var r=t(n);return e.parse=function(n){try{va=Hn;var t=r.parse(n);return t&&t._}finally{va=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ct;var M=ao.map(),x=Vn(v),b=Xn(v),_=Vn(d),w=Xn(d),S=Vn(y),k=Xn(y),N=Vn(m),E=Xn(m);g.forEach(function(n,t){M.set(n.toLowerCase(),t)});var A={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Zn(n.getDate(),t,2)},e:function(n,t){return Zn(n.getDate(),t,2)},H:function(n,t){return Zn(n.getHours(),t,2)},I:function(n,t){return Zn(n.getHours()%12||12,t,2)},j:fu
nction(n,t){return Zn(1+ga.dayOfYear(n),t,3)},L:function(n,t){return Zn(n.getMilliseconds(),t,3)},m:function(n,t){return Zn(n.getMonth()+1,t,2)},M:function(n,t){return Zn(n.getMinutes(),t,2)},p:function(n){return g[+(n.getHours()>=12)]},S:function(n,t){return Zn(n.getSeconds(),t,2)},U:function(n,t){return Zn(ga.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Zn(ga.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Zn(n.getFullYear()%100,t,2)},Y:function(n,t){return Zn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},C={a:r,A:i,b:u,B:o,c:a,d:tt,e:tt,H:rt,I:rt,j:et,L:ot,m:nt,M:it,p:f,S:ut,U:Bn,w:$n,W:Wn,x:l,X:c,y:Gn,Y:Jn,Z:Kn,"%":lt};return t}function Zn(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Vn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Xn(n){for(var t=new c,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function $n(n,t,e){ma.lastIn
dex=0;var r=ma.exec(t.slice(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Bn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.U=+r[0],e+r[0].length):-1}function Wn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e));return r?(n.W=+r[0],e+r[0].length):-1}function Jn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Gn(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.y=Qn(+r[0]),e+r[0].length):-1}function Kn(n,t,e){return/^[+-]\d{4}$/.test(t=t.slice(e,e+5))?(n.Z=-t,e+5):-1}function Qn(n){return n+(n>68?1900:2e3)}function nt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function tt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function et(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function rt(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].len
gth):-1}function it(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ut(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ot(n,t,e){ma.lastIndex=0;var r=ma.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function at(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=xo(t)/60|0,i=xo(t)%60;return e+Zn(r,"0",2)+Zn(i,"0",2)}function lt(n,t,e){Ma.lastIndex=0;var r=Ma.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ct(n){for(var t=n.length,e=-1;++e<t;)n[e][0]=this(n[e][0]);return function(t){for(var e=0,r=n[e];!r[1](t);)r=n[++e];return r[0](t)}}function ft(){}function st(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function ht(n,t){n&&wa.hasOwnProperty(n.type)&&wa[n.type](n,t)}function pt(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1],r[2]);t.lineEnd()}function gt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)pt(n[e],t,1);t.polygonEnd
()}function vt(){function n(n,t){n*=Yo,t=t*Yo/2+Fo/4;var e=n-r,o=e>=0?1:-1,a=o*e,l=Math.cos(t),c=Math.sin(t),f=u*c,s=i*l+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),r=n,i=l,u=c}var t,e,r,i,u;Na.point=function(o,a){Na.point=n,r=(t=o)*Yo,i=Math.cos(a=(e=a)*Yo/2+Fo/4),u=Math.sin(a)},Na.lineEnd=function(){n(t,e)}}function dt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function yt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function mt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Mt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function xt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function bt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function _t(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Uo&&xo(n[1]-t[1])<Uo}function St(n,t){n*=Yo;var e=Math.cos(t*=Yo);kt(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function kt(n,t,e){++Ea,Ca+=(n-Ca)/Ea,za+=(t-za)/Ea,La+=
(e-La)/Ea}function Nt(){function n(n,i){n*=Yo;var u=Math.cos(i*=Yo),o=u*Math.cos(n),a=u*Math.sin(n),l=Math.sin(i),c=Math.atan2(Math.sqrt((c=e*l-r*a)*c+(c=r*o-t*l)*c+(c=t*a-e*o)*c),t*o+e*a+r*l);Aa+=c,qa+=c*(t+(t=o)),Ta+=c*(e+(e=a)),Ra+=c*(r+(r=l)),kt(t,e,r)}var t,e,r;ja.point=function(i,u){i*=Yo;var o=Math.cos(u*=Yo);t=o*Math.cos(i),e=o*Math.sin(i),r=Math.sin(u),ja.point=n,kt(t,e,r)}}function Et(){ja.point=St}function At(){function n(n,t){n*=Yo;var e=Math.cos(t*=Yo),o=e*Math.cos(n),a=e*Math.sin(n),l=Math.sin(t),c=i*l-u*a,f=u*o-r*l,s=r*a-i*o,h=Math.sqrt(c*c+f*f+s*s),p=r*o+i*a+u*l,g=h&&-nn(p)/h,v=Math.atan2(h,p);Da+=g*c,Pa+=g*f,Ua+=g*s,Aa+=v,qa+=v*(r+(r=o)),Ta+=v*(i+(i=a)),Ra+=v*(u+(u=l)),kt(r,i,u)}var t,e,r,i,u;ja.point=function(o,a){t=o,e=a,ja.point=n,o*=Yo;var l=Math.cos(a*=Yo);r=l*Math.cos(o),i=l*Math.sin(o),u=Math.sin(a),kt(r,i,u)},ja.lineEnd=function(){n(t,e),ja.lineEnd=Et,ja.point=St}}function Ct(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.inv
ert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function zt(){return!0}function Lt(n,t,e,r,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(wt(e,r)){i.lineStart();for(var a=0;t>a;++a)i.point((e=n[a])[0],e[1]);return void i.lineEnd()}var l=new Tt(e,n,null,!0),c=new Tt(e,null,l,!1);l.o=c,u.push(l),o.push(c),l=new Tt(r,n,null,!1),c=new Tt(r,null,l,!0),l.o=c,u.push(l),o.push(c)}}),o.sort(t),qt(u),qt(o),u.length){for(var a=0,l=e,c=o.length;c>a;++a)o[a].e=l=!l;for(var f,s,h=u[0];;){for(var p=h,g=!0;p.v;)if((p=p.n)===h)return;f=p.z,i.lineStart();do{if(p.v=p.o.v=!0,p.e){if(g)for(var a=0,c=f.length;c>a;++a)i.point((s=f[a])[0],s[1]);else r(p.x,p.n.x,1,i);p=p.n}else{if(g){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else r(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,g=!g}while(!p.v);i.lineEnd()}}}function qt(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.n=e=n[r],e.p=i,i=e;i.n=e=n[0],e.p=i}}function Tt(n,t,e,r){this.x=n,t
his.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Rt(n,t,e,r){return function(i,u){function o(t,e){var r=i(t,e);n(t=r[0],e=r[1])&&u.point(t,e)}function a(n,t){var e=i(n,t);d.point(e[0],e[1])}function l(){m.point=a,d.lineStart()}function c(){m.point=o,d.lineEnd()}function f(n,t){v.push([n,t]);var e=i(n,t);x.point(e[0],e[1])}function s(){x.lineStart(),v=[]}function h(){f(v[0][0],v[0][1]),x.lineEnd();var n,t=x.clean(),e=M.buffer(),r=e.length;if(v.pop(),g.push(v),v=null,r)if(1&t){n=e[0];var i,r=n.length-1,o=-1;if(r>0){for(b||(u.polygonStart(),b=!0),u.lineStart();++o<r;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else r>1&&2&t&&e.push(e.pop().concat(e.shift())),p.push(e.filter(Dt))}var p,g,v,d=t(u),y=i.invert(r[0],r[1]),m={point:o,lineStart:l,lineEnd:c,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],g=[]},polygonEnd:function(){m.point=o,m.lineStart=l,m.lineEnd=c,p=ao.merge(p);var n=Ot(y,g);p.length?(b||(u.polygonStart(),b=!0),Lt(p,Ut,n,e,u)):n&&(b||(u.polygonSta
rt(),b=!0),u.lineStart(),e(null,null,1,u),u.lineEnd()),b&&(u.polygonEnd(),b=!1),p=g=null},sphere:function(){u.polygonStart(),u.lineStart(),e(null,null,1,u),u.lineEnd(),u.polygonEnd()}},M=Pt(),x=t(M),b=!1;return m}}function Dt(n){return n.length>1}function Pt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Ut(n,t){return((n=n.x)[0]<0?n[1]-Io-Uo:Io-n[1])-((t=t.x)[0]<0?t[1]-Io-Uo:Io-t[1])}function jt(n){var t,e=NaN,r=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?Fo:-Fo,l=xo(u-e);xo(l-Fo)<Uo?(n.point(e,r=(r+o)/2>0?Io:-Io),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(u,r),t=0):i!==a&&l>=Fo&&(xo(e-i)<Uo&&(e-=i*Uo),xo(u-a)<Uo&&(u-=a*Uo),r=Ft(e,r,u,o),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=u,r=o),i=a},lineEnd:function(){n.lineEnd(),e=r=
NaN},clean:function(){return 2-t}}}function Ft(n,t,e,r){var i,u,o=Math.sin(n-e);return xo(o)>Uo?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+r)/2}function Ht(n,t,e,r){var i;if(null==n)i=e*Io,r.point(-Fo,i),r.point(0,i),r.point(Fo,i),r.point(Fo,0),r.point(Fo,-i),r.point(0,-i),r.point(-Fo,-i),r.point(-Fo,0),r.point(-Fo,i);else if(xo(n[0]-t[0])>Uo){var u=n[0]<t[0]?Fo:-Fo;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Ot(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,o=0;ka.reset();for(var a=0,l=t.length;l>a;++a){var c=t[a],f=c.length;if(f)for(var s=c[0],h=s[0],p=s[1]/2+Fo/4,g=Math.sin(p),v=Math.cos(p),d=1;;){d===f&&(d=0),n=c[d];var y=n[0],m=n[1]/2+Fo/4,M=Math.sin(m),x=Math.cos(m),b=y-h,_=b>=0?1:-1,w=_*b,S=w>Fo,k=g*M;if(ka.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),u+=S?b+_*Ho:b,S^h>=e^y>=e){var N=mt(dt(s),dt(n));bt(N);var E=mt(i,N);bt(E);var A=(S^b>=0?-1:1)*tn(E[2]);(r>A||r==
=A&&(N[0]||N[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=y,g=M,v=x,s=n}}return(-Uo>u||Uo>u&&-Uo>ka)^1&o}function It(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function e(n){var e,u,l,c,f;return{lineStart:function(){c=l=!1,f=1},point:function(s,h){var p,g=[s,h],v=t(s,h),d=o?v?0:i(s,h):v?i(s+(0>s?Fo:-Fo),h):0;if(!e&&(c=l=v)&&n.lineStart(),v!==l&&(p=r(e,g),(wt(e,p)||wt(g,p))&&(g[0]+=Uo,g[1]+=Uo,v=t(g[0],g[1]))),v!==l)f=0,v?(n.lineStart(),p=r(g,e),n.point(p[0],p[1])):(p=r(e,g),n.point(p[0],p[1]),n.lineEnd()),e=p;else if(a&&e&&o^v){var y;d&u||!(y=r(g,e,!0))||(f=0,o?(n.lineStart(),n.point(y[0][0],y[0][1]),n.point(y[1][0],y[1][1]),n.lineEnd()):(n.point(y[1][0],y[1][1]),n.lineEnd(),n.lineStart(),n.point(y[0][0],y[0][1])))}!v||e&&wt(e,g)||n.point(g[0],g[1]),e=g,l=v,u=d},lineEnd:function(){l&&n.lineEnd(),e=null},clean:function(){return f|(c&&l)<<1}}}function r(n,t,e){var r=dt(n),i=dt(t),o=[1,0,0],a=mt(r,i),l=yt(a,a),c=a[0],f=l-c*c;if(!f)return!e&&n;var s=u*l/f,h=-u*c/f,p=mt(o,a),g=xt(o,s)
,v=xt(a,h);Mt(g,v);var d=p,y=yt(g,d),m=yt(d,d),M=y*y-m*(yt(g,g)-1);if(!(0>M)){var x=Math.sqrt(M),b=xt(d,(-y-x)/m);if(Mt(b,g),b=_t(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],N=t[1];w>S&&(_=w,w=S,S=_);var E=S-w,A=xo(E-Fo)<Uo,C=A||Uo>E;if(!A&&k>N&&(_=k,k=N,N=_),C?A?k+N>0^b[1]<(xo(b[0]-w)<Uo?k:N):k<=b[1]&&b[1]<=N:E>Fo^(w<=b[0]&&b[0]<=S)){var z=xt(d,(-y+x)/m);return Mt(z,g),[b,_t(z)]}}}function i(t,e){var r=o?n:Fo-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Uo,l=ve(n,6*Yo);return Rt(t,e,l,o?[0,-n]:[-Fo,n-Fo])}function Yt(n,t,e,r){return function(i){var u,o=i.a,a=i.b,l=o.x,c=o.y,f=a.x,s=a.y,h=0,p=1,g=f-l,v=s-c;if(u=n-l,g||!(u>0)){if(u/=g,0>g){if(h>u)return;p>u&&(p=u)}else if(g>0){if(u>p)return;u>h&&(h=u)}if(u=e-l,g||!(0>u)){if(u/=g,0>g){if(u>p)return;u>h&&(h=u)}else if(g>0){if(h>u)return;p>u&&(p=u)}if(u=t-c,v||!(u>0)){if(u/=v,0>v){if(h>u)return;p>u&&(p=u)}else if(v>0){if(u>p)return;u>h&&(h=u)}if(u=r-c,v||!(0>u)){if(u/=v,0>v){if(u>p)return;u>h&&
(h=u)}else if(v>0){if(h>u)return;p>u&&(p=u)}return h>0&&(i.a={x:l+h*g,y:c+h*v}),1>p&&(i.b={x:l+p*g,y:c+p*v}),i}}}}}}function Zt(n,t,e,r){function i(r,i){return xo(r[0]-n)<Uo?i>0?0:3:xo(r[0]-e)<Uo?i>0?2:1:xo(r[1]-t)<Uo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function l(n){for(var t=0,e=d.length,r=n[1],i=0;e>i;++i)for(var u,o=1,a=d[i],l=a.length,c=a[0];l>o;++o)u=a[o],c[1]<=r?u[1]>r&&Q(c,u,n)>0&&++t:u[1]<=r&&Q(c,u,n)<0&&--t,c=u;return 0!==t}function c(u,a,l,c){var f=0,s=0;if(null==u||(f=i(u,l))!==(s=i(a,l))||o(u,a)<0^l>0){do c.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+l+4)%4)!==s)}else c.point(a[0],a[1])}function f(i,u){return i>=n&&e>=i&&u>=t&&r>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){C.point=g,d&&d.push(y=[]),S=!0,w=!1,b=_=NaN}function p(){v&&(g(m,M),x&&w&&E.rejoin(),v.push(E.buffer())),C.point=s,w&&a.lineEnd()}function g(n,t){n=
Math.max(-Ha,Math.min(Ha,n)),t=Math.max(-Ha,Math.min(Ha,t));var e=f(n,t);if(d&&y.push([n,t]),S)m=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};A(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,y,m,M,x,b,_,w,S,k,N=a,E=Pt(),A=Yt(n,t,e,r),C={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=E,v=[],d=[],k=!0},polygonEnd:function(){a=N,v=ao.merge(v);var t=l([n,r]),e=k&&t,i=v.length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),c(null,null,1,a),a.lineEnd()),i&&Lt(v,u,t,c,a),a.polygonEnd()),v=d=y=null}};return C}}function Vt(n){var t=0,e=Fo/3,r=ae(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Fo/180,e=n[1]*Fo/180):[t/Fo*180,e/Fo*180]},i}function Xt(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),o-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),o=M
ath.sqrt(u)/i;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/i,tn((u-(n*n+e*e)*i*i)/(2*i))]},e}function $t(){function n(n,t){Ia+=i*n-r*t,r=n,i=t}var t,e,r,i;$a.point=function(u,o){$a.point=n,t=r=u,e=i=o},$a.lineEnd=function(){n(t,e)}}function Bt(n,t){Ya>n&&(Ya=n),n>Va&&(Va=n),Za>t&&(Za=t),t>Xa&&(Xa=t)}function Wt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function i(){o.push("Z")}var u=Jt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return u=Jt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Jt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Gt(n,t){Ca+=n,za+=t,++La}function Kt(){function n(n,r){var i=n-t,u=r-e,o=Math.sqrt(i*i+u*u);qa+=o*(t+n)/2,Ta+=o*(e+r)/2,Ra+=o,G
t(t=n,e=r)}var t,e;Wa.point=function(r,i){Wa.point=n,Gt(t=r,e=i)}}function Qt(){Wa.point=Gt}function ne(){function n(n,t){var e=n-r,u=t-i,o=Math.sqrt(e*e+u*u);qa+=o*(r+n)/2,Ta+=o*(i+t)/2,Ra+=o,o=i*n-r*t,Da+=o*(r+n),Pa+=o*(i+t),Ua+=3*o,Gt(r=n,i=t)}var t,e,r,i;Wa.point=function(u,o){Wa.point=n,Gt(t=r=u,e=i=o)},Wa.lineEnd=function(){n(t,e)}}function te(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,Ho)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function ee(n){function t(n){return(a?r:e)(n)}function e(t){return ue(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=NaN,S.point=u,t.lineStart()}function u(e,r){var u=dt([e,r]),o=n(e,r);i(M,x,m,b,_,
w,M=o[0],x=o[1],m=e,b=u[0],_=u[1],w=u[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function l(){
+r(),S.point=c,S.lineEnd=f}function c(n,t){u(s=n,h=t),p=M,g=x,v=b,d=_,y=w,S.point=u}function f(){i(M,x,m,b,_,w,p,g,s,v,d,y,a,t),S.lineEnd=o,o()}var s,h,p,g,v,d,y,m,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=l},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function i(t,e,r,a,l,c,f,s,h,p,g,v,d,y){var m=f-t,M=s-e,x=m*m+M*M;if(x>4*u&&d--){var b=a+p,_=l+g,w=c+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),N=xo(xo(w)-1)<Uo||xo(r-h)<Uo?(r+h)/2:Math.atan2(_,b),E=n(N,k),A=E[0],C=E[1],z=A-t,L=C-e,q=M*z-m*L;(q*q/x>u||xo((m*z+M*L)/x-.5)>.3||o>a*p+l*g+c*v)&&(i(t,e,r,a,l,c,A,C,N,b/=S,_/=S,w,d,y),y.point(A,C),i(A,C,N,b,_,w,f,s,h,p,g,v,d,y))}}var u=.5,o=Math.cos(30*Yo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function re(n){var t=ee(function(t,e){return n([t*Zo,e*Zo])});return function(n){return le(t(n))}}function ie(n){this.stream=n}function ue(n,t){return{point:t,sphere:functio
n(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function oe(n){return ae(function(){return n})()}function ae(n){function t(n){return n=a(n[0]*Yo,n[1]*Yo),[n[0]*h+l,c-n[1]*h]}function e(n){return n=a.invert((n[0]-l)/h,(c-n[1])/h),n&&[n[0]*Zo,n[1]*Zo]}function r(){a=Ct(o=se(y,M,x),u);var n=u(v,d);return l=p-n[0]*h,c=g+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,l,c,f,s=ee(function(n,t){return n=u(n,t),[n[0]*h+l,c-n[1]*h]}),h=150,p=480,g=250,v=0,d=0,y=0,M=0,x=0,b=Fa,_=m,w=null,S=null;return t.stream=function(n){return f&&(f.valid=!1),f=le(b(o,s(_(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Fa):It((w=+n)*Yo),i()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Zt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return argum
ents.length?(p=+n[0],g=+n[1],r()):[p,g]},t.center=function(n){return arguments.length?(v=n[0]%360*Yo,d=n[1]%360*Yo,r()):[v*Zo,d*Zo]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Yo,M=n[1]%360*Yo,x=n.length>2?n[2]%360*Yo:0,r()):[y*Zo,M*Zo,x*Zo]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function le(n){return ue(n,function(t,e){n.point(t*Yo,e*Yo)})}function ce(n,t){return[n,t]}function fe(n,t){return[n>Fo?n-Ho:-Fo>n?n+Ho:n,t]}function se(n,t,e){return n?t||e?Ct(pe(n),ge(t,e)):pe(n):t||e?ge(t,e):fe}function he(n){return function(t,e){return t+=n,[t>Fo?t-Ho:-Fo>t?t+Ho:t,e]}}function pe(n){var t=he(n);return t.invert=he(-n),t}function ge(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math.sin(t),f=c*r+a*i;return[Math.atan2(l*u-f*o,a*r-c*i),tn(f*u+l*o)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,l=Math.sin(n)*e,c=Math
.sin(t),f=c*u-l*o;return[Math.atan2(l*u+c*o,a*r+f*i),tn(f*r-a*i)]},e}function ve(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,o,a){var l=o*t;null!=i?(i=de(e,i),u=de(e,u),(o>0?u>i:i>u)&&(i+=o*Ho)):(i=n+o*Ho,u=n-.5*l);for(var c,f=i;o>0?f>u:u>f;f-=l)a.point((c=_t([e,-r*Math.cos(f),-r*Math.sin(f)]))[0],c[1])}}function de(n,t){var e=dt(t);e[0]-=n,bt(e);var r=nn(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Uo)%(2*Math.PI)}function ye(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function me(n,t,e){var r=ao.range(n,t-Uo,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Me(n){return n.source}function xe(n){return n.target}function be(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(r),a=Math.sin(r),l=i*Math.cos(n),c=i*Math.sin(n),f=o*Math.cos(e),s=o*Math.sin(e),h=2*Math.asin(Math.sqrt(on(r-t)+i*o*on(e-n))),p=1/Math.sin(h),g=h?function(n){var t=Math.sin(n*=h)*p,e=Math.sin(h-n)*p,r=e*l+t*f,i=e*c+t*s
,o=e*u+t*a;return[Math.atan2(i,r)*Zo,Math.atan2(o,Math.sqrt(r*r+i*i))*Zo]}:function(){return[n*Zo,t*Zo]};return g.distance=h,g}function _e(){function n(n,i){var u=Math.sin(i*=Yo),o=Math.cos(i),a=xo((n*=Yo)-t),l=Math.cos(a);Ja+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*u-e*o*l)*a),e*u+r*o*l),t=n,e=u,r=o}var t,e,r;Ga.point=function(i,u){t=i*Yo,e=Math.sin(u*=Yo),r=Math.cos(u),Ga.point=n},Ga.lineEnd=function(){Ga.point=Ga.lineEnd=b}}function we(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,r*o),Math.asin(r&&e*u/r)]},e}function Se(n,t){function e(n,t){o>0?-Io+Uo>t&&(t=-Io+Uo):t>Io-Uo&&(t=Io-Uo);var e=o/Math.pow(i(t),u);return[e*Math.sin(u*n),o-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Fo/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),o=r*Math.pow(i(n),u)/u;return u?(e.inver
t=function(n,t){var e=o-t,r=K(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(o/r,1/u))-Io]},e):Ne}function ke(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return xo(i)<Uo?ce:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-K(i)*Math.sqrt(n*n+e*e)]},e)}function Ne(n,t){return[n,Math.log(Math.tan(Fo/4+t/2))]}function Ee(n){var t,e=oe(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=u.apply(e,arguments);if(o===e){if(t=null==n){var a=Fo*r(),l=i();u([[l[0]-a,l[1]-a],[l[0]+a,l[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ae(n,t){return[Math.log(Math.tan(Fo/4+t/2)),-n]}function Ce(n){return n[0]}function ze(n){return n[1]}function Le(n){for(va
r t=n.length,e=[0,1],r=2,i=2;t>i;i++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[i])<=0;)--r;e[r++]=i}return e.slice(0,r)}function qe(n,t){return n[0]-t[0]||n[1]-t[1]}function Te(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Re(n,t,e,r){var i=n[0],u=e[0],o=t[0]-i,a=r[0]-u,l=n[1],c=e[1],f=t[1]-l,s=r[1]-c,h=(a*(l-c)-s*(i-u))/(s*o-a*f);return[i+h*o,l+h*f]}function De(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Pe(){rr(this),this.edge=this.site=this.circle=null}function Ue(n){var t=cl.pop()||new Pe;return t.site=n,t}function je(n){Be(n),ol.remove(n),cl.push(n),rr(n)}function Fe(n){var t=n.circle,e=t.x,r=t.cy,i={x:e,y:r},u=n.P,o=n.N,a=[n];je(n);for(var l=u;l.circle&&xo(e-l.circle.x)<Uo&&xo(r-l.circle.cy)<Uo;)u=l.P,a.unshift(l),je(l),l=u;a.unshift(l),Be(l);for(var c=o;c.circle&&xo(e-c.circle.x)<Uo&&xo(r-c.circle.cy)<Uo;)o=c.N,a.push(c),je(c),c=o;a.push(c),Be(c);var f,s=a.length;for(f=1;s>f;++f)c=a[f],l=a[f-1],nr(c.edge,l.site,c.site,i);l=a[0],c=a[s-
1],c.edge=Ke(l.site,c.site,null,i),$e(l),$e(c)}function He(n){for(var t,e,r,i,u=n.x,o=n.y,a=ol._;a;)if(r=Oe(a,o)-u,r>Uo)a=a.L;else{if(i=u-Ie(a,o),!(i>Uo)){r>-Uo?(t=a.P,e=a):i>-Uo?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var l=Ue(n);if(ol.insert(t,l),t||e){if(t===e)return Be(t),e=Ue(t.site),ol.insert(l,e),l.edge=e.edge=Ke(t.site,l.site),$e(t),void $e(e);if(!e)return void(l.edge=Ke(t.site,l.site));Be(t),Be(e);var c=t.site,f=c.x,s=c.y,h=n.x-f,p=n.y-s,g=e.site,v=g.x-f,d=g.y-s,y=2*(h*d-p*v),m=h*h+p*p,M=v*v+d*d,x={x:(d*m-p*M)/y+f,y:(h*M-v*m)/y+s};nr(e.edge,c,g,x),l.edge=Ke(c,n,null,x),e.edge=Ke(n,g,null,x),$e(t),$e(e)}}function Oe(n,t){var e=n.site,r=e.x,i=e.y,u=i-t;if(!u)return r;var o=n.P;if(!o)return-(1/0);e=o.site;var a=e.x,l=e.y,c=l-t;if(!c)return a;var f=a-r,s=1/u-1/c,h=f/c;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*c)-l+c/2+i-u/2)))/s+r:(r+a)/2}function Ie(n,t){var e=n.N;if(e)return Oe(e,t);var r=n.site;return r.y===t?r.x:1/0}function Ye(n){this.site=n,this.edges=[]}function Z
e(n){for(var t,e,r,i,u,o,a,l,c,f,s=n[0][0],h=n[1][0],p=n[0][1],g=n[1][1],v=ul,d=v.length;d--;)if(u=v[d],u&&u.prepare())for(a=u.edges,l=a.length,o=0;l>o;)f=a[o].end(),r=f.x,i=f.y,c=a[++o%l].start(),t=c.x,e=c.y,(xo(r-t)>Uo||xo(i-e)>Uo)&&(a.splice(o,0,new tr(Qe(u.site,f,xo(r-s)<Uo&&g-i>Uo?{x:s,y:xo(t-s)<Uo?e:g}:xo(i-g)<Uo&&h-r>Uo?{x:xo(e-g)<Uo?t:h,y:g}:xo(r-h)<Uo&&i-p>Uo?{x:h,y:xo(t-h)<Uo?e:p}:xo(i-p)<Uo&&r-s>Uo?{x:xo(e-p)<Uo?t:s,y:p}:null),u.site,null)),++l)}function Ve(n,t){return t.angle-n.angle}function Xe(){rr(this),this.x=this.y=this.arc=this.site=this.cy=null}function $e(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,i=n.site,u=e.site;if(r!==u){var o=i.x,a=i.y,l=r.x-o,c=r.y-a,f=u.x-o,s=u.y-a,h=2*(l*s-c*f);if(!(h>=-jo)){var p=l*l+c*c,g=f*f+s*s,v=(s*p-c*g)/h,d=(l*g-f*p)/h,s=d+a,y=fl.pop()||new Xe;y.arc=n,y.site=i,y.x=v+o,y.y=s+Math.sqrt(v*v+d*d),y.cy=s,n.circle=y;for(var m=null,M=ll._;M;)if(y.y<M.y||y.y===M.y&&y.x<=M.x){if(!M.L){m=M.P;break}M=M.L}else{if(!M.R){m=M;break}M=M.R}ll.insert(
m,y),m||(al=y)}}}}function Be(n){var t=n.circle;t&&(t.P||(al=t.N),ll.remove(t),fl.push(t),rr(t),n.circle=null)}function We(n){for(var t,e=il,r=Yt(n[0][0],n[0][1],n[1][0],n[1][1]),i=e.length;i--;)t=e[i],(!Je(t,n)||!r(t)||xo(t.a.x-t.b.x)<Uo&&xo(t.a.y-t.b.y)<Uo)&&(t.a=t.b=null,e.splice(i,1))}function Je(n,t){var e=n.b;if(e)return!0;var r,i,u=n.a,o=t[0][0],a=t[1][0],l=t[0][1],c=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,g=s.x,v=s.y,d=(h+g)/2,y=(p+v)/2;if(v===p){if(o>d||d>=a)return;if(h>g){if(u){if(u.y>=c)return}else u={x:d,y:l};e={x:d,y:c}}else{if(u){if(u.y<l)return}else u={x:d,y:c};e={x:d,y:l}}}else if(r=(h-g)/(v-p),i=y-r*d,-1>r||r>1)if(h>g){if(u){if(u.y>=c)return}else u={x:(l-i)/r,y:l};e={x:(c-i)/r,y:c}}else{if(u){if(u.y<l)return}else u={x:(c-i)/r,y:c};e={x:(l-i)/r,y:l}}else if(v>p){if(u){if(u.x>=a)return}else u={x:o,y:r*o+i};e={x:a,y:r*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:r*a+i};e={x:o,y:r*o+i}}return n.a=u,n.b=e,!0}function Ge(n,t){this.l=n,this.r=t,this.a=this.b=null}function Ke(n,t,
e,r){var i=new Ge(n,t);return il.push(i),e&&nr(i,n,t,e),r&&nr(i,t,n,r),ul[n.i].edges.push(new tr(i,n,t)),ul[t.i].edges.push(new tr(i,t,n)),i}function Qe(n,t,e){var r=new Ge(n,null);return r.a=t,r.b=e,il.push(r),r}function nr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function tr(n,t,e){var r=n.a,i=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(i.x-r.x,r.y-i.y):Math.atan2(r.x-i.x,i.y-r.y)}function er(){this._=null}function rr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ir(n,t){var e=t,r=t.R,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function ur(n,t){var e=t,r=t.L,i=e.U;i?i.L===e?i.L=r:i.R=r:n._=r,r.U=i,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function or(n){for(;n.L;)n=n.L;return n}function ar(n,t){var e,r,i,u=n.sort(lr).pop();for(il=[],ul=new Array(n.length),ol=new er,ll=new er;;)if(i=al,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===e&&u.y===r||(ul[u.i]=new Ye(u),He(u),e=u.x,r=u.y),u=n.pop();else{if(!i)break
;Fe(i.arc)}t&&(We(t),Ze(t));var o={cells:ul,edges:il};return ol=ll=il=ul=null,o}function lr(n,t){return t.y-n.y||t.x-n.x}function cr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var o=.5*(e+i),a=.5*(r+u),l=t.nodes;l[0]&&pr(n,l[0],e,r,o,a),l[1]&&pr(n,l[1],o,r,i,a),l[2]&&pr(n,l[2],e,a,o,u),l[3]&&pr(n,l[3],o,a,i,u)}}function gr(n,t,e,r,i,u,o){var a,l=1/0;return function c(n,f,s,h,p){if(!(f>u||s>o||r>h||i>p)){if(g=n.point){var g,v=t-n.x,d=e-n.y,y=v*v+d*d;if(l>y){var m=Math.sqrt(l=y);r=t-m,i=e-m,u=t+m,o=e+m,a=g}}for(var M=n.nodes,x=.5*(f+h),b=.5*(s+p),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:c(n,f,s,x,b);break;case 1:c(n,x,s,h,b);break;case 2:c(n,f,b,x,p);break;case 3:c(n,x,b,h,p)}}}(n,r,i,u,o),a}function vr(n,t){n=ao.rgb(n),t=ao.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,o=t.g-r,a=t.b-i;return function
(n){return"#"+bn(Math.round(e+u*n))+bn(Math.round(r+o*n))+bn(Math.round(i+a*n))}}function dr(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Mr(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function yr(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function mr(n,t){var e,r,i,u=hl.lastIndex=pl.lastIndex=0,o=-1,a=[],l=[];for(n+="",t+="";(e=hl.exec(n))&&(r=pl.exec(t));)(i=r.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,l.push({i:o,x:yr(e,r)})),u=pl.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?l[0]?(t=l[0].x,function(n){return t(n)+""}):function(){return t}:(t=l.length,function(n){for(var e,r=0;t>r;++r)a[(e=l[r]).i]=e.x(n);return a.join("")})}function Mr(n,t){for(var e,r=ao.interpolators.length;--r>=0&&!(e=ao.interpolators[r](n,t)););return e}function xr(n,t){var e,r=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e
;++e)r.push(Mr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;o>e;++e)i[e]=t[e];return function(n){for(e=0;a>e;++e)i[e]=r[e](n);return i}}function br(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function _r(n){return function(t){return 1-n(1-t)}}function wr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function Sr(n){return n*n}function kr(n){return n*n*n}function Nr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function Er(n){return function(t){return Math.pow(t,n)}}function Ar(n){return 1-Math.cos(n*Io)}function Cr(n){return Math.pow(2,10*(n-1))}function zr(n){return 1-Math.sqrt(1-n*n)}function Lr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Ho*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Ho/t)}}function qr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Tr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)
*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Rr(n,t){n=ao.hcl(n),t=ao.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,o=t.c-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(e+u*n,r+o*n,i+a*n)+""}}function Dr(n,t){n=ao.hsl(n),t=ao.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,o=t.s-r,a=t.l-i;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return cn(e+u*n,r+o*n,i+a*n)+""}}function Pr(n,t){n=ao.lab(n),t=ao.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,o=t.a-r,a=t.b-i;return function(n){return pn(e+u*n,r+o*n,i+a*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function jr(n){var t=[n.a,n.b],e=[n.c,n.d],r=Hr(t),i=Fr(t,e),u=Hr(Or(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Zo,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Zo:0}function
Fr(n,t){return n[0]*t[0]+n[1]*t[1]}function Hr(n){var t=Math.sqrt(Fr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Or(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Ir(n){return n.length?n.pop()+",":""}function Yr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push("translate(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else(t[0]||t[1])&&e.push("translate("+t+")")}function Zr(n,t,e,r){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),r.push({i:e.push(Ir(e)+"rotate(",null,")")-2,x:yr(n,t)})):t&&e.push(Ir(e)+"rotate("+t+")")}function Vr(n,t,e,r){n!==t?r.push({i:e.push(Ir(e)+"skewX(",null,")")-2,x:yr(n,t)}):t&&e.push(Ir(e)+"skewX("+t+")")}function Xr(n,t,e,r){if(n[0]!==t[0]||n[1]!==t[1]){var i=e.push(Ir(e)+"scale(",null,",",null,")");r.push({i:i-4,x:yr(n[0],t[0])},{i:i-2,x:yr(n[1],t[1])})}else 1===t[0]&&1===t[1]||e.push(Ir(e)+"scale("+t+")")}function $r(n,t){var e=[],r=[];return n=ao.transform(n),t=ao.transform(t),Yr(n.translate,t.translate,e,r),Zr(n.rotate,t.r
otate,e,r),Vr(n.skew,t.skew,e,r),Xr(n.scale,t.scale,e,r),n=t=null,function(n){for(var t,i=-1,u=r.length;++i<u;)e[(t=r[i]).i]=t.x(n);return e.join("")}}function Br(n,t){return t=(t-=n=+n)||1/t,function(e){return(e-n)/t}}function Wr(n,t){return t=(t-=n=+n)||1/t,function(e){return Math.max(0,Math.min(1,(e-n)/t))}}function Jr(n){for(var t=n.source,e=n.target,r=Kr(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(var u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Gr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Kr(n,t){if(n===t)return n;for(var e=Gr(n),r=Gr(t),i=e.pop(),u=r.pop(),o=null;i===u;)o=i,i=e.pop(),u=r.pop();return o}function Qr(n){n.fixed|=2}function ni(n){n.fixed&=-7}function ti(n){n.fixed|=4,n.px=n.x,n.py=n.y}function ei(n){n.fixed&=-5}function ri(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,l=-1;++l<a;)u=o[l],null!=u&&(ri(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.l
eaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var c=t*e[n.point.index];n.charge+=n.pointCharge=c,r+=c*n.point.x,i+=c*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function ii(n,t){return ao.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=fi,n}function ui(n,t){for(var e=[n];null!=(n=e.pop());)if(t(n),(i=n.children)&&(r=i.length))for(var r,i;--r>=0;)e.push(i[r])}function oi(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)e.push(u[o]);for(;null!=(n=r.pop());)t(n)}function ai(n){return n.children}function li(n){return n.value}function ci(n,t){return t.value-n.value}function fi(n){return ao.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function si(n){return n.x}function hi(n){return n.y}function pi(n,t,e){n.y0=t,n.y=e}function gi(n){return ao.range(n.length)}function vi(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function di(n){for(var t,e=1,r=0,
i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function Mi(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function bi(n){return[ao.min(n),ao.max(n)]}function _i(n,t){return n.value-t.value}function wi(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Si(n,t){n._pack_next=t,t._pack_prev=n}function ki(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ni(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),p=Math.max(n.y+n.r,p)}if((e=n.children)&&(c=e.length)){var e,r,i,u,o,a,l,c,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(e.forEach(Ei),r=e[0],r.x=-r.r,r.y=0,t(r),c>1&&(i=e[1],i.x=i.r,i.y=0,t(i),c>2))for(u=e[2],zi(r,i,u),t(u),wi(r,u),r._pack_prev=u,wi(u,i),i=r._pack_next,o=3;c>o;o++){zi(r,i,u=e[o]);var g=0,v=1,d=1;for(a=i._pack_
next;a!==i;a=a._pack_next,v++)if(ki(a,u)){g=1;break}if(1==g)for(l=r._pack_prev;l!==a._pack_prev&&!ki(l,u);l=l._pack_prev,d++);g?(d>v||v==d&&i.r<r.r?Si(r,i=a):Si(r=l,i),o--):(wi(r,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,M=0;for(o=0;c>o;o++)u=e[o],u.x-=y,u.y-=m,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(Ai)}}function Ei(n){n._pack_next=n._pack_prev=n}function Ai(n){delete n._pack_next,delete n._pack_prev}function Ci(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,o=i.length;++u<o;)Ci(i[u],t,e,r)}function zi(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var o=t.r+e.r,a=i*i+u*u;o*=o,r*=r;var l=.5+(r-o)/(2*a),c=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+l*i+c*u,e.y=n.y+l*u-c*i}else e.x=n.x+r,e.y=n.y}function Li(n,t){return n.parent==t.parent?1:2}function qi(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,e=n.children;return(t=e.length)?e[t-1]:n.t}function Ri(n,t,e){var r=e/(t.i-n.i);t.c-=r,t.s+=e,n.c+
=r,t.z+=e,t.m+=e}function Di(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Pi(n,t,e){return n.a.parent===t.parent?n.a:e}function Ui(n){return 1+ao.max(n,function(n){return n.y})}function ji(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Hi(n){var t,e=n.children;return e&&(t=e.length)?Hi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ii(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Zi(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Vi(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Xi(n,t){var e,r=0,i=n.length-1,u=n[r],o=n[i];return u>o&&(e=r,r=i,i=e,e=u,u=o,o=e),n[r]=t.floor(u),n[i]=t.ceil(o),n}function $i(n){return n?{f
loor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:Sl}function Bi(n,t,e,r){var i=[],u=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)i.push(e(n[o-1],n[o])),u.push(r(t[o-1],t[o]));return function(t){var e=ao.bisect(n,t,1,a)-1;return u[e](i[e](t))}}function Wi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Bi:Vi,l=r?Wr:Br;return o=i(n,t,l,e),a=i(t,n,l,Mr),u}function u(n){return o(n)}var o,a;return u.invert=function(n){return a(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Qi(n,t)},u.tickFormat=function(t,e){return nu(n,t,e)},u.nice=function(t){return Gi(n,t),i()},u.copy=function(){return Wi(n,
t,e,r)},i()}function Ji(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Gi(n,t){return Xi(n,$i(Ki(n,t)[2])),Xi(n,$i(Ki(n,t)[2])),n}function Ki(n,t){null==t&&(t=10);var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Qi(n,t){return ao.range.apply(ao,Ki(n,t))}function nu(n,t,e){var r=Ki(n,t);if(e){var i=ha.exec(e);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(r[0]),xo(r[1])));return i[7]||(i[7]="."+tu(u.scale(r[2]))),i[8]="f",e=ao.format(i.join("")),function(n){return e(u.scale(n))+u.symbol}}i[7]||(i[7]="."+eu(i[8],r)),e=i.join("")}else e=",."+tu(r[2])+"f";return ao.format(e)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function eu(n,t){var e=tu(t[2]);return n in kl?Math.abs(e-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):e-2*("%"===n)}function ru(n,t,e,r){function i(n){return(e?
Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(i(t))}return o.invert=function(t){return u(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(i)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(i)),o):t},o.nice=function(){var t=Xi(r.map(i),e?Math:El);return n.domain(t),r=t.map(u),o},o.ticks=function(){var n=Yi(r),o=[],a=n[0],l=n[1],c=Math.floor(i(a)),f=Math.ceil(i(l)),s=t%1?2:t;if(isFinite(f-c)){if(e){for(;f>c;c++)for(var h=1;s>h;h++)o.push(u(c)*h);o.push(u(c))}else for(o.push(u(c));c++<f;)for(var h=s-1;h>0;h--)o.push(u(c)*h);for(c=0;o[c]<a;c++);for(f=o.length;o[f-1]>l;f--);o=o.slice(c,f)}return o},o.tickFormat=function(n,e){if(!arguments.length)return Nl;arguments.length<2?e=Nl:"function"!=typeof e&&(e=ao.format(e));var r=Math.max(1,t*n/o.ticks().length);return function(n){var o=n/u(Math.round(i(n)));return t-.5>o*t&&(o*=t),r>=o?e(n)
:""}},o.copy=function(){return ru(n.copy(),t,e,r)},Ji(o,n)}function iu(n,t,e){function r(t){return n(i(t))}var i=uu(t),u=uu(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Qi(e,n)},r.tickFormat=function(n,t){return nu(e,n,t)},r.nice=function(n){return r.domain(Gi(e,n))},r.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return iu(n.copy(),t,e)},Ji(r,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(e){return u[((i.get(e)||("range"===t.t?i.set(e,n.push(e)):NaN))-1)%u.length]}function r(t,e){return ao.range(n.length).map(function(n){return t+e*n})}var i,u,o;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new c;for(var u,o=-1,a=r.length;++o<a;)i.has(u=r[o])||i.set(u,n.push(u));return e[t.t].apply(e,t.a)},e.range=function(n){return arg
uments.length?(u=n,o=0,t={t:"range",a:arguments},e):u},e.rangePoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=(l+c)/2,0):(c-l)/(n.length-1+a);return u=r(l+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var l=i[0],c=i[1],f=n.length<2?(l=c=Math.round((l+c)/2),0):(c-l)/(n.length-1+a)|0;return u=r(l+Math.round(f*a/2+(c-l-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},e},e.rangeBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=(s-f)/(n.length-a+2*l);return u=r(f+h*l,h),c&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,a,l){arguments.length<2&&(a=0),arguments.length<3&&(l=a);var c=i[1]<i[0],f=i[c-0],s=i[1-c],h=Math.floor((s-f)/(n.length-a+2*l));return u=r(f+Math.round((s-f-(n.length-a)*h)/2),h),c&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=fu
nction(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function au(n,t){function u(){var e=0,r=t.length;for(a=[];++e<r;)a[e-1]=ao.quantile(n,e/r);return o}function o(n){return isNaN(n=+n)?void 0:t[ao.bisect(a,n)]}var a;return o.domain=function(t){return arguments.length?(n=t.map(r).filter(i).sort(e),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(e){return e=t.indexOf(e),0>e?[NaN,NaN]:[e>0?a[e-1]:n[0],e<a.length?a[e]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),o=e.length-1,r}var u,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},r.copy=function(){return
lu(n,t,e)},i()}function cu(n,t){function e(e){return e>=e?t[ao.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return cu(n,t)},e}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Qi(n,t)},t.tickFormat=function(t,e){return nu(n,t,e)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function gu(n){return n.startAngle}function vu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function mu(n,t,e,r,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?r:-r)/Math.sqrt(u*u+o*o),l=a*o,c=-a*u,f=n[0]+l,s=n[1]+c,h=t[0]+l,p=t[1]+c,g=(f+h)/2,v=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,M=e-r,x=f*p-h*s,b=(0>y?-1:1)*Math
.sqrt(Math.max(0,M*M*m-x*x)),_=(x*y-d*b)/m,w=(-x*d-y*b)/m,S=(x*y+d*b)/m,k=(-x*d+y*b)/m,N=_-g,E=w-v,A=S-g,C=k-v;return N*N+E*E>A*A+C*C&&(_=S,w=k),[[_-l,w-c],[_*e/M,w*e/M]]}function Mu(n){function t(t){function o(){c.push("M",u(n(f),a))}for(var l,c=[],f=[],s=-1,h=t.length,p=En(e),g=En(r);++s<h;)i.call(this,l=t[s],s)?f.push([+p.call(this,l,s),+g.call(this,l,s)]):f.length&&(o(),f=[]);return f.length&&o(),c.length?c.join(""):null}var e=Ce,r=ze,i=zt,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?u=n:(u=Tl.get(n)||xu).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function xu(n){return n.length>1?n.join("L"):n+"Z"}function bu(n){return n.join("L")+"Z"}function _u(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1])
;return e>1&&i.push("H",r[0]),i.join("")}function wu(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Su(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function ku(n,t){return n.length<4?xu(n):n[1]+Au(n.slice(1,-1),Cu(n,t))}function Nu(n,t){return n.length<3?bu(n):n[0]+Au((n.push(n[0]),n),Cu([n[n.length-2]].concat(n,[n[1]]),t))}function Eu(n,t){return n.length<3?xu(n):n[0]+Au(n,Cu(n,t))}function Au(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],o=t[0],a=o,l=1;if(e&&(r+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],l=2),t.length>1){a=t[1],u=n[l],l++,r+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var c=2;c<t.length;c++,l++)u=n[l],a=t[c],r+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(e){var f=n[l];r+="Q"+(u[0]+2*a[0]/3
)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return r}function Cu(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],o=n[1],a=1,l=n.length;++a<l;)e=u,u=o,o=n[a],r.push([i*(o[0]-e[0]),i*(o[1]-e[1])]);return r}function zu(n){if(n.length<3)return xu(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],o=[i,i,i,(r=n[1])[0]],a=[u,u,u,r[1]],l=[i,",",u,"L",Ru(Pl,o),",",Ru(Pl,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Du(l,o,a);return n.pop(),l.push("L",r),l.join("")}function Lu(n){if(n.length<4)return xu(n);for(var t,e=[],r=-1,i=n.length,u=[0],o=[0];++r<3;)t=n[r],u.push(t[0]),o.push(t[1]);for(e.push(Ru(Pl,u)+","+Ru(Pl,o)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Du(e,u,o);return e.join("")}function qu(n){for(var t,e,r=-1,i=n.length,u=i+4,o=[],a=[];++r<4;)e=n[r%i],o.push(e[0]),a.push(e[1]);for(t=[Ru(Pl,o),",",Ru(Pl,a)],--r;++r<u;)e=n[r%i],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Du(t,o,a);return t.join("")}function Tu(n,t){var e=n.length-1;if(e)for(va
r r,i,u=n[0][0],o=n[0][1],a=n[e][0]-u,l=n[e][1]-o,c=-1;++c<=e;)r=n[c],i=c/e,r[0]=t*r[0]+(1-t)*(u+i*a),r[1]=t*r[1]+(1-t)*(o+i*l);return zu(n)}function Ru(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Du(n,t,e){n.push("C",Ru(Rl,t),",",Ru(Rl,e),",",Ru(Dl,t),",",Ru(Dl,e),",",Ru(Pl,t),",",Ru(Pl,e))}function Pu(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Uu(n){for(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],o=r[0]=Pu(i,u);++t<e;)r[t]=(o+(o=Pu(i=u,u=n[t+1])))/2;return r[t]=o,r}function ju(n){for(var t,e,r,i,u=[],o=Uu(n),a=-1,l=n.length-1;++a<l;)t=Pu(n[a],n[a+1]),xo(t)<Uo?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*e,o[a+1]=i*r));for(a=-1;++a<=l;)i=(n[Math.min(l,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),u.push([i||0,o[a]*i||0]);return u}function Fu(n){return n.length<3?xu(n):n[0]+Au(n,ju(n))}function Hu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]-Io,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Ou(n){function t(t
){function l(){v.push("M",a(n(y),s),f,c(n(d.reverse()),s),"Z")}for(var h,p,g,v=[],d=[],y=[],m=-1,M=t.length,x=En(e),b=En(i),_=e===r?function(){
+return p}:En(r),w=i===u?function(){return g}:En(u);++m<M;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),g=+b.call(this,h,m)]),y.push([+_.call(this,h,m),+w.call(this,h,m)])):d.length&&(l(),d=[],y=[]);return d.length&&l(),v.length?v.join(""):null}var e=Ce,r=Ce,i=0,u=ze,o=zt,a=xu,l=a.key,c=a,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(l="function"==typeof n?a=n:(a=Tl.get(n)||xu).key,c=a.reverse||a,f=a.closed?"M":"L",t):l},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Iu(n){return n.radius}function Yu(n){return[n.x,n.y]}function Zu(n){return function(){var t=n.apply(this,
arguments),e=t[0],r=t[1]-Io;return[e*Math.cos(r),e*Math.sin(r)]}}function Vu(){return 64}function Xu(){return"circle"}function $u(n){var t=Math.sqrt(n/Fo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Bu(n){return function(){var t,e,r;(t=this[n])&&(r=t[e=t.active])&&(r.timer.c=null,r.timer.t=NaN,--t.count?delete t[e]:delete this[n],t.active+=.5,r.event&&r.event.interrupt.call(this,this.__data__,r.index))}}function Wu(n,t,e){return ko(n,Yl),n.namespace=t,n.id=e,n}function Ju(n,t,e,r){var i=n.id,u=n.namespace;return Y(n,"function"==typeof e?function(n,o,a){n[u][i].tween.set(t,r(e.call(n,n.__data__,o,a)))}:(e=r(e),function(n){n[u][i].tween.set(t,e)}))}function Gu(n){return null==n&&(n=""),function(){this.textContent=n}}function Ku(n){return null==n?"__transition__":"__transition_"+n+"__"}function Qu(n,t,e,r,i){function u(n){var t=v.delay;return f.t=t+l,n>=t?o(n-t):void(f.c=o)}function o(e){var i=g.active,u=g[i];u&&(u.timer.c=null,u.timer.t=NaN,--g.coun
t,delete g[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in g)if(r>+o){var c=g[o];c.timer.c=null,c.timer.t=NaN,--g.count,delete g[o]}f.c=a,qn(function(){return f.c&&a(e||1)&&(f.c=null,f.t=NaN),1},0,l),g.active=r,v.event&&v.event.start.call(n,n.__data__,t),p=[],v.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&p.push(r)}),h=v.ease,s=v.duration}function a(i){for(var u=i/s,o=h(u),a=p.length;a>0;)p[--a].call(n,o);return u>=1?(v.event&&v.event.end.call(n,n.__data__,t),--g.count?delete g[r]:delete n[e],1):void 0}var l,f,s,h,p,g=n[e]||(n[e]={active:0,count:0}),v=g[r];v||(l=i.time,f=qn(u,0,l),v=g[r]={tween:new c,time:l,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++g.count)}function no(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function to(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function eo(n){return n.toISOString()}function ro
(n,t,e){function r(t){return n(t)}function i(n,e){var r=n[1]-n[0],i=r/e,u=ao.bisect(Kl,i);return u==Kl.length?[t.year,Ki(n.map(function(n){return n/31536e6}),e)[2]]:u?t[i/Kl[u-1]<Kl[u]/i?u-1:u]:[tc,Ki(n,e)[2]]}return r.invert=function(t){return io(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(io)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,io(+e+1),t).length}var u=r.domain(),o=Yi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),r.domain(Xi(u,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=io(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Yi(r.domain()),u=null==n?i(e,10):"number"==typeof n?i(e,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(e[0],io(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return ro(n.copy(),t,e)},Ji(r,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.respon
seText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.17"},lo=[].slice,co=function(n){return lo.call(n)},fo=this.document;if(fo)try{co(fo.documentElement.childNodes)[0].nodeType}catch(so){co=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),fo)try{fo.createElement("DIV").style.setProperty("opacity",0,"")}catch(ho){var po=this.Element.prototype,go=po.setAttribute,vo=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){go.call(this,n,t+"")},po.setAttributeNS=function(n,t,e){vo.call(this,n,t,e+"")},yo.setProperty=function(n,t,e){mo.call(this,n,t+"",e)}}ao.ascending=e,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}e
lse{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},ao.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(r=n[i])&&r>=r){e=r;break}for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u;)if(null!=(r=t.call(n,n[i],i))&&r>=r){e=r;break}for(;++i<u;)null!=(r=t.call(n,n[i],i))&&r>e&&(e=r)}return e},ao.extent=function(n,t){var e,r,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(r=n[u])&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<o;)if(null!=(r=t.call(n,n[u],u))&&r>=r){e=i=r;break}for(;++u<o;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},ao.sum=function(n,t){var e,r=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(e=+n[o])&&(r+=e);else for(;++o<u;)i(e=+t.call(n,n[o],o))&&(r+=e);return r},ao.mean=function(n,t){var e,u=0,o=n.length,a=-1,l=o;if(1===arguments.length)for(;++a<o;)i(e=r(n[a]))?u+=e:--l;els
e for(;++a<o;)i(e=r(t.call(n,n[a],a)))?u+=e:--l;return l?u/l:void 0},ao.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,l=-1;if(1===arguments.length)for(;++l<a;)i(u=r(n[l]))&&o.push(u);else for(;++l<a;)i(u=r(t.call(n,n[l],l)))&&o.push(u);return o.length?ao.quantile(o.sort(e),.5):void 0},ao.variance=function(n,t){var e,u,o=n.length,a=0,l=0,c=-1,f=0;if(1===arguments.length)for(;++c<o;)i(e=r(n[c]))&&(u=e-a,a+=u/++f,l+=u*(e-a));else for(;++c<o;)i(e=r(t.call(n,n[c],c)))&&(u=e-a,a+=u/++f,l+=u*(e-a));return f>1?l/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var Mo=u(e);ao.bisectLeft=Mo.left,ao.bisect=ao.bisectRight=Mo.right,ao.bisector=function(n){return u(1===n.length?function(t,r){return e(n(t),r)}:n)},ao.shuffle=function(n,t,e){(u=arguments.length)<3&&(e=n.length,2>u&&(t=0));for(var r,i,u=e-t;u;)i=Math.random()*u--|0,r=n[u+t],n[u+
t]=n[i+t],n[i+t]=r;return n},ao.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ao.pairs=function(n){for(var t,e=0,r=n.length-1,i=n[0],u=new Array(0>r?0:r);r>e;)u[e]=[t=i,i=n[++e]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,e=ao.min(n,o),r=new Array(e);++t<e;)for(var i,u=-1,a=r[t]=new Array(i);++u<i;)a[u]=n[u][t];return r},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var e in n)t.push(e);return t},ao.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},ao.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},ao.merge=function(n){for(var t,e,r,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(e=new Array(o);--i>=0;)for(r=n[i],t=r.length;--t>=0;)e[--o]=r[t];return e};var xo=Math.abs;ao.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,i=[],u=a(xo(e)),o=-1;if(n*=u
,t*=u,e*=u,0>e)for(;(r=n+e*++o)>t;)i.push(r/u);else for(;(r=n+e*++o)<t;)i.push(r/u);return i},ao.map=function(n,t){var e=new c;if(n instanceof c)n.forEach(function(n,t){e.set(n,t)});else if(Array.isArray(n)){var r,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)e.set(i,n[i]);else for(;++i<u;)e.set(t.call(n,r=n[i],i),r)}else for(var o in n)e.set(o,n[o]);return e};var bo="__proto__",_o="\x00";l(c,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:g,values:function(){var n=[];for(var t in this._)n.push(this._[t]);return n},entries:function(){var n=[];for(var t in this._)n.push({key:s(t),value:this._[t]});return n},size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t),this._[t])}}),ao.nest=function(){function n(t,o,a){if(a>=u.length)return r?r.call(i,o):e?o.sort(e):o;for(var l,f,s,h,p=-1,g=o.length,v=u[a++],d=new c;++p<g;)(h=d.get(l=v(f=o[p])))?h.push(f):d.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,a))})
:(f={},s=function(e,r){f[e]=n(t,r,a)}),d.forEach(s),f}function t(n,e){if(e>=u.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,i={},u=[],o=[];return i.map=function(t,e){return n(e,t,0)},i.entries=function(e){return t(n(ao.map,e,0),0)},i.key=function(n){return u.push(n),i},i.sortKeys=function(n){return o[u.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},ao.set=function(n){var t=new y;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},l(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:g,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=M(n,t,t[e]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new _,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=w(n);
return n},_.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(So,"\\$&")};var So=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ko={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},No=function(n,t){return t.querySelector(n)},Eo=function(n,t){return t.querySelectorAll(n)},Ao=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(Ao=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(No=function(n,t){return Sizzle(n,t)[0]||null},Eo=Sizzle,Ao=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var Co=ao.selection.prototype=[];Co.select=function(n){var t,e,r,i,u=[];n=A(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(r=this[o]).parentN
ode;for(var l=-1,c=r.length;++l<c;)(i=r[l])?(t.push(e=n.call(i,i.__data__,l,o)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return E(u)},Co.selectAll=function(n){var t,e,r=[];n=C(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,l=o.length;++a<l;)(e=o[a])&&(r.push(t=co(n.call(e,e.__data__,a,i))),t.parentNode=e);return E(r)};var zo="http://www.w3.org/1999/xhtml",Lo={svg:"http://www.w3.org/2000/svg",xhtml:zo,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};ao.ns={prefix:Lo,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&"xmlns"!==(e=n.slice(0,t))&&(n=n.slice(t+1)),Lo.hasOwnProperty(e)?{space:Lo[e],local:n}:n}},Co.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ao.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},Co.classed=function(n,t){if(argument
s.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!q(n[i]).test(t))return!1;return!0}for(t in n)this.each(R(t,n[t]));return this}return this.each(R(n,t))},Co.style=function(n,e,r){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},Co.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},Co.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},Co.html=function(n){return arguments.length?this.ea
ch("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Co.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},Co.insert=function(n,t){return n=j(n),t=A(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},Co.remove=function(){return this.each(F)},Co.data=function(n,t){function e(n,e){var r,i,u,o=n.length,s=e.length,h=Math.min(o,s),p=new Array(s),g=new Array(s),v=new Array(o);if(t){var d,y=new c,m=new Array(o);for(r=-1;++r<o;)(i=n[r])&&(y.has(d=t.call(i,i.__data__,r))?v[r]=i:y.set(d,i),m[r]=d);for(r=-1;++r<s;)(i=y.get(d=t.call(e,u=e[r],r)))?i!==!0&&(p[r]=i,i.__data__=u):g[r]=H(u),y.set(d,!0);for(r=-1;++r<o;)r in m&&y.get(m[r])!==!0&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],u=e[r],i?(i.__data__=u,p[r]=i):g[r]=H(u);for(;s>r;++r)g[r]=H(e[r]);for(;o>r
;++r)v[r]=n[r]}g.update=p,g.parentNode=p.parentNode=v.parentNode=n.parentNode,a.push(g),l.push(p),f.push(v)}var r,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++u<o;)(i=r[u])&&(n[u]=i.__data__);return n}var a=Z([]),l=E([]),f=E([]);if("function"==typeof n)for(;++u<o;)e(r=this[u],n.call(r,r.parentNode.__data__,u));else for(;++u<o;)e(r=this[u],n);return l.enter=function(){return a},l.exit=function(){return f},l},Co.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Co.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=O(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var a=0,l=e.length;l>a;a++)(r=e[a])&&n.call(r,r.__data__,a,u)&&t.push(r)}return E(i)},Co.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Co.sort=function(n){n=
I.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Co.each=function(n){return Y(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Co.call=function(n){var t=co(arguments);return n.apply(t[0]=this,t),this},Co.empty=function(){return!this.node()},Co.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Co.size=function(){var n=0;return Y(this,function(){++n}),n};var qo=[];ao.selection.enter=Z,ao.selection.enter.prototype=qo,qo.append=Co.append,qo.empty=Co.empty,qo.node=Co.node,qo.call=Co.call,qo.size=Co.size,qo.select=function(n){for(var t,e,r,i,u,o=[],a=-1,l=this.length;++a<l;){r=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var c=-1,f=i.length;++c<f;)(u=i[c])?(t.push(r[c]=e=n.call(i.parentNode,u.__data__,c,a)),e.__data__=u.__data__):t.push(null)}return E(o)},qo.insert=function(n,t){return arguments.length<2&&(t=V(this)),Co.insert.call(this,n,t)},ao.se
lect=function(t){var e;return"string"==typeof t?(e=[No(t,fo)],e.parentNode=fo.documentElement):(e=[t],e.parentNode=n(t)),E([e])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=co(Eo(n,fo)),t.parentNode=fo.documentElement):(t=co(n),t.parentNode=null),E([t])},Co.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var To=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&To.forEach(function(n){"on"+n in fo&&To.remove(n)});var Ro,Do=0;ao.mouse=function(n){return J(n,k())};var Po=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,i=0,u=t.length;u>i;++i)if((r=t[i]).identifier===e)return J(n,r)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function e(n,t,e,u,o){return functi
on(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],g|=n|e,M=r,p({type:"drag",x:r[0]+c[0],y:r[1]+c[1],dx:n,dy:e}))}function l(){t(h,v)&&(y.on(u+d,null).on(o+d,null),m(g),p({type:"dragend"}))}var c,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=r.of(f,arguments),g=0,v=n(),d=".drag"+(null==v?"":"-"+v),y=ao.select(e(s)).on(u+d,a).on(o+d,l),m=W(s),M=t(h,v);i?(c=i.apply(f,arguments),c=[c.x-M[0],c.y-M[1]]):c=[0,0],p({type:"dragstart"})}}var r=N(n,"drag","dragstart","dragend"),i=null,u=e(b,ao.mouse,t,"mousemove","mouseup"),o=e(G,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,r,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?co(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Uo=1e-6,jo=Uo*Uo,Fo=Math.PI,Ho=2*Fo,Oo=Ho-Uo,Io=Fo/2,Yo=Fo/180,Zo=180/Fo,Vo=Math.SQRT2,Xo=2,$o=4;ao.interpolateZoom=function(n,t){var e,r,i=n[0],u=n[1],o=n[
2],a=t[0],l=t[1],c=t[2],f=a-i,s=l-u,h=f*f+s*s;if(jo>h)r=Math.log(c/o)/Vo,e=function(n){return[i+n*f,u+n*s,o*Math.exp(Vo*n*r)]};else{var p=Math.sqrt(h),g=(c*c-o*o+$o*h)/(2*o*Xo*p),v=(c*c-o*o-$o*h)/(2*c*Xo*p),d=Math.log(Math.sqrt(g*g+1)-g),y=Math.log(Math.sqrt(v*v+1)-v);r=(y-d)/Vo,e=function(n){var t=n*r,e=rn(d),a=o/(Xo*p)*(e*un(Vo*t+d)-en(d));return[i+a*f,u+a*s,o*e/rn(Vo*t+d)]}}return e.duration=1e3*r,e},ao.behavior.zoom=function(){function n(n){n.on(L,s).on(Wo+".zoom",p).on("dblclick.zoom",g).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function i(n){k.k=Math.max(A[0],Math.min(A[1],n))}function u(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},i(Math.pow(2,o)),u(d=e,r),t=ao.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k})
.map(_.invert))}function l(n){z++||n({type:"zoomstart"})}function c(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function f(n){--z||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),c(o)}function r(){s.on(q,null).on(T,null),p(a),f(o)}var i=this,o=D.of(i,arguments),a=0,s=ao.select(t(i)).on(q,n).on(T,r),h=e(ao.mouse(i)),p=W(i);Il.call(i),l(o)}function h(){function n(){var n=ao.touches(g);return p=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,r).on(b,a),_.push(t);for(var e=ao.event.changedTouches,i=0,u=e.length;u>i;++i)d[e[i].identifier]=null;var l=n(),c=Date.now();if(1===l.length){if(500>c-M){var f=l[0];o(g,f,d[f.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=c}else if(l.length>1){var f=l[0],s=l[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function r(){var n,t,e,r,o=ao.touches(g);Il.call(g);for(var a=0,l=o.length;l>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e
,t=r}if(r){var f=(f=e[0]-n[0])*f+(f=e[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],i(s*p)}M=null,u(n,t),c(v)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var i in d)return void n()}ao.selectAll(_).on(m,null),w.on(L,s).on(R,h),N(),f(v)}var p,g=this,v=D.of(g,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,b="touchend"+m,_=[],w=ao.select(g),N=W(g);t(),l(v),w.on(L,null).on(R,t)}function p(){var n=D.of(this,arguments);m?clearTimeout(m):(Il.call(this),v=e(d=y||ao.mouse(this)),l(n)),m=setTimeout(function(){m=null,f(n)},50),S(),i(Math.pow(2,.002*Bo())*k.k),u(d,v),c(n)}function g(){var n=ao.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,y,m,M,x,b,_,w,k={x:0,y:0,k:1},E=[960,500],A=Jo,C=250,z=0,L="mousedown.zoom",q="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoo
m",D=N(n,"zoomstart","zoom","zoomend");return Wo||(Wo="onwheel"in fo?(Bo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Bo=function(){return ao.event.wheelDelta},"mousewheel"):(Bo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Hl?ao.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},l(n)}).tween("zoom:zoom",function(){var e=E[0],r=E[1],i=d?d[0]:e/2,u=d?d[1]:r/2,o=ao.interpolateZoom([(i-k.x)/k.k,(u-k.y)/k.k,e/k.k],[(i-t.x)/t.k,(u-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:i-r[0]*a,y:u-r[1]*a,k:a},c(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=k,l(n),c(n),f(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:null},i(+t),a(),n):k.k},n.s
caleExtent=function(t){return arguments.length?(A=null==t?Jo:[+t[0],+t[1]],n):A},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(E=t&&[+t[0],+t[1]],n):E},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ao.rebind(n,D,"on")};var Bo,Wo,Jo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=ln;var Go=ln.prototype=new an;Go.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,this.l/n)},Go.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new ln(this.h,this.s,n*this.l)},Go.rgb=function(){return cn(this.h,this.s,this.l)},ao.hcl=fn;var Ko=fn.prototype=new an;Ko.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Ko.darker=function(n){return n
ew fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Ko.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,na=.95047,ta=1,ea=1.08883,ra=hn.prototype=new an;ra.brighter=function(n){return new hn(Math.min(100,this.l+Qo*(arguments.length?n:1)),this.a,this.b)},ra.darker=function(n){return new hn(Math.max(0,this.l-Qo*(arguments.length?n:1)),this.a,this.b)},ra.rgb=function(){return pn(this.l,this.a,this.b)},ao.rgb=mn;var ia=mn.prototype=new an;ia.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),new mn(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mn(i,i,i)},ia.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ia.hsl=function(){return wn(this.r,this.g,this.b)},ia.toString=function(){return"#"+bn(this.r)+bn(this.g)+bn(this.b)};var ua=ao.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,
aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,
<TRUNCATED>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[09/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.scss
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.scss b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.scss
new file mode 100644
index 0000000..bd69b21
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.scss
@@ -0,0 +1,2135 @@
+/*
+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.
+*/
+
+@import "https://cdn.rawgit.com/angular-ui/bower-ui-grid/master/ui-grid.min.css";
+@import "https://cdn.rawgit.com/mar10/dynatree/master/dist/skin/ui.dynatree.css";
+
+$sep-width: 6px;
+$left-width: 200px;
+$pane-top: 50px;
+#paneSep {
+ background-color: #FFF;
+ position: absolute;
+ top: $pane-top;
+ bottom: 0;
+ left: 200px;
+ width: $sep-width;
+ cursor: e-resize;
+ border-right: 1px solid #CCC;
+}
+
+#paneLeft {
+ position: absolute;
+ top: $pane-top;
+ bottom: 0;
+ left: 0;
+ width: $left-width;
+ overflow: auto;
+}
+
+#paneRight {
+ position: absolute;
+ top: $pane-top;
+ bottom: 0;
+ left: $left-width + $sep-width;
+ right: 0;
+ overflow: hidden;
+ margin: 0 1em;
+}
+
+$topology-left-width: 400px;
+$topology-pane-top: 80px;
+div.topology-container #paneLeft {
+ top: $topology-pane-top;
+ width: $topology-left-width;
+}
+div.topology-container #paneRight {
+ top: $topology-pane-top;
+ left: $topology-left-width + $sep-width;
+}
+div.topology-container #paneSep {
+ top: $topology-pane-top;
+ left: $topology-left-width;
+}
+
+.ui-grid-viewport {
+/* overflow: auto; */
+}
+div.qdr-attributes span.dynatree-selected a {
+ background-color: #e0e0ff;
+}
+
+.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).address.mobile .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.address.mobile .dynatree-icon:before {
+ content: "\f109";
+}
+span:not(.dynatree-has-children).address.internal.mobile .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.address.internal.mobile .dynatree-icon:before {
+ content: "\f0ac";
+}
+span:not(.dynatree-has-children).address.router .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.address.router .dynatree-icon:before {
+ content: "\f047";
+}
+
+span.address-link .dynatree-icon:before {
+ content: "\f0ac";
+}
+
+span:not(.dynatree-has-children).connection.external .dynatree-icon:before {
+ content: "\f109";
+}
+span:not(.dynatree-has-children).connection.normal .dynatree-icon:before {
+ content: "\f08e";
+}
+span:not(.dynatree-has-children).connection.external.quiesced .dynatree-icon:before {
+ content: "\f14c";
+ color: red;
+}
+span:not(.dynatree-has-children).connection.inter-router .dynatree-icon:before {
+ content: "\f07e";
+}
+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).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\.node .dynatree-icon:before {
+ content: "\f013";
+}
+span:not(.dynatree-has-children).link.inter-router .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.link.inter-router .dynatree-icon:before{
+ content: "\f07e";
+}
+span:not(.dynatree-has-children).link.endpoint .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.link.endpoint .dynatree-icon:before{
+ content: "\f109";
+}
+span:not(.dynatree-has-children).link.console .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.link.console .dynatree-icon:before {
+ content: "\f108";
+}
+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).connection.console .dynatree-icon:before {
+ content: "\f108";
+}
+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";
+}
+
+span.filter-icon {
+ padding-left: 1em;
+}
+
+button.filter-close {
+ width: 15px;
+ height: 20px;
+ padding: 0;
+ position: absolute;
+ right: 4px;
+ top: 4px;
+}
+
+div.filter-title h6 {
+ margin: 0 0 0.5em 0;
+}
+
+.links button.btn-filter {
+ padding: 0 1em 0 0;
+ margin-left: 1em;
+ font-size: 1em;
+}
+
+button.btn-filter {
+ float: right;
+}
+span.dynatree-expanded button.btn-filter,
+a.dynatree-title:hover button.btn-filter {
+ visibility: visible;
+}
+
+div.hdash-button a {
+ color: white;
+}
+
+.linkDirIn {
+ color: red;
+ background-color: #f3f3f3;
+}
+
+.linkDirOut {
+ color: blue;
+ background-color: white;
+}
+
+ul.dynatree-container {
+ background: inherit;
+}
+ul.dynatree-container li {
+ background: inherit;
+}
+
+span.dynatree-icon {
+ position: relative;
+ top: -2px;
+ font-size: 17px;
+}
+
+span:not(.dynatree-has-children) .dynatree-icon:before {
+ font-family: FontAwesome;
+ content: "\f013";
+}
+
+ul.inline,
+ol.inline {
+ margin-left: 0;
+ list-style: none;
+}
+
+ul.inline > li,
+ol.inline > li {
+ display: inline-block;
+ padding-right: 2px;
+ padding-left: 2px;
+}
+
+[class^="dynatree-folder icon-"], [class*=" dynatree-folder icon-"] {
+
+}
+
+[class^="dynatree-folder icon-"]:before, [class*=" dynatree-folder icon-"]:before {
+ font-size: 17px;
+ margin-left: 18px;
+}
+
+
+[class^="dynatree-folder icon-"], [class*=" dynatree-folder icon-"] .dynatree-connector {
+ display: none;
+}
+
+[class^="dynatree-folder icon-"], [class*=" dynatree-folder icon-"] .dynatree-icon {
+ display: none;
+}
+
+ul.dynatree-container {
+ overflow: visible;
+}
+
+ul.dynatree-container {
+ background: inherit;
+}
+ul.dynatree-container li {
+ background: inherit;
+}
+
+i.expandable-indicator {
+ color: #666;
+}
+
+span.dynatree-expander {
+ color: #728271;
+}
+
+span.dynatree-icon {
+ color: #EECA7C;
+}
+span:not(.dynatree-has-children) .dynatree-icon:before {
+ color: gray;
+}
+
+span.dynatree-empty,
+span.dynatree-vline,
+span.dynatree-connector,
+span.dynatree-expander,
+span.dynatree-icon,
+span.dynatree-checkbox,
+span.dynatree-radio,
+span.dynatree-drag-helper-img,
+#dynatree-drop-marker
+{
+ font-family: FontAwesome;
+ font-weight: normal;
+ font-style: normal;
+ display: inline-block;
+ text-decoration: inherit;
+ background-image: none;
+ vertical-align: middle;
+}
+
+.dynatree-checkbox {
+ color: #888888
+}
+
+/* Dynatree checkbox */
+span.dynatree-checkbox:before
+{
+ margin-top: 1px;
+ background-position: 0 0;
+ cursor: pointer;
+ content: "";
+}
+
+span.dynatree-checkbox:before:hover
+{
+ background-position: 0 0;
+ content: "";
+}
+
+.dynatree-selected span.dynatree-checkbox:before
+{
+ margin-top: 1px;
+ background-position: 0 0;
+ cursor: pointer;
+ content: "\f00c";
+}
+
+.dynatree-selected span.dynatree-checkbox:before:hover
+{
+ background-position: 0 0;
+ content: "\f00c";
+}
+
+
+.dynatree-expander {
+ color: #888888
+}
+
+/* Dynatree expander */
+span.dynatree-expander:before
+{
+ margin-top: 1px;
+ background-position: 0 0;
+ cursor: pointer;
+ content: "\f054";
+}
+
+span.dynatree-expander:before:hover
+{
+ background-position: 0 0;
+ content: "\f054";
+}
+
+.dynatree-exp-e span.dynatree-expander:before, /* Expanded, not delayed, not last sibling */
+.dynatree-exp-ed span.dynatree-expander:before, /* Expanded, delayed, not last sibling */
+.dynatree-exp-el span.dynatree-expander:before, /* Expanded, not delayed, last sibling */
+.dynatree-exp-edl span.dynatree-expander:before /* Expanded, delayed, last sibling */
+{
+ background-position: 0 0;
+ content: "\f078";
+}
+.dynatree-exp-e span.dynatree-expander:before:hover, /* Expanded, not delayed, not last sibling */
+.dynatree-exp-ed span.dynatree-expander:before:hover, /* Expanded, delayed, not last sibling */
+.dynatree-exp-el span.dynatree-expander:before:hover, /* Expanded, not delayed, last sibling */
+.dynatree-exp-edl span.dynatree-expander:before:hover /* Expanded, delayed, last sibling */
+{
+ background-position: 0 0;
+ content: "\f0da";
+}
+
+/* closed folder */
+.dynatree-ico-cf span.dynatree-icon:before {
+ background-position: 0 0;
+ content: "\f07b";
+}
+
+/* open folder */
+.dynatree-ico-ef span.dynatree-icon:before {
+ background-position: 0 0;
+ content: "\f07c";
+}
+
+span.dynatree-icon:before {
+ background-position: 0px 0px;
+ content: "\f013";
+}
+
+span.dynatree-folder a {
+ font-weight: normal;
+}
+
+div.treeContainer ul.dynatree-container {
+ border: 0px;
+}
+
+#linkFilter {
+ display: none;
+ padding: 0.5em;
+ border: 1px solid grey;
+ background-color: #F0F0F0;
+ position: absolute;
+ z-index: 100;
+ right: 1em;
+}
+
+span.filter-icon {
+ padding-left: 1em;
+}
+
+button.filter-close {
+ width: 15px;
+ height: 20px;
+ padding: 0;
+ position: absolute;
+ right: 4px;
+ top: 4px;
+}
+
+div.filter-title h6 {
+ margin: 0 0 0.5em 0;
+}
+
+.links button.btn-filter {
+ padding: 0 1em 0 0;
+ margin-left: 1em;
+ font-size: 1em;
+}
+
+button.btn-filter {
+ float: right;
+}
+
+div.formLine label, div.formLine input {
+ display: inline-block;
+ padding: 0 8px;
+}
+
+.ui-grid {
+ border: 0;
+/* height: auto; */
+}
+
+
+.ui-grid-viewport {
+/* height: 100% !important; */
+/* overflow-x: auto !important; */
+}
+/*
+.ui-grid-row.ui-grid-row-selected > [ui-grid-row] > .ui-grid-cell {
+ background-color: #e0e0ff;
+}
+*/
+.grid:not(.noHighlight) .ui-grid-row:hover .ui-grid-cell-contents {
+ background-color: #EEE;
+}
+
+.ui-grid-top-panel {
+ background: inherit;
+}
+
+/*
+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: #C0FFC0;
+}
+circle.node.on-demand.artemis {
+ fill: #FCC;
+ /*opacity: 0.2; */
+}
+
+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;
+}
+
+text.label {
+ text-anchor: start;
+ font-weight: bold;
+}
+
+.row-fluid.tertiary {
+ position: relative;
+ left: 20px;
+}
+
+.row-fluid.tertiary.left {
+ float: left;
+}
+
+.row-fluid.tertiary.panel {
+ width: 410px;
+ /*height: 100%; */
+}
+/*
+div#topologyForm .ngViewport, div#topologyForm .grid {
+ height: inherit !important;
+ min-height: initial !important;
+ overflow: initial;
+}
+*/
+
+#topologyForm {
+ max-height: 20em;
+}
+
+div#multiple_details, div#link_details {
+ height: 300px;
+ width: 700px;
+/* display: none; */
+ visibility: hidden;
+ padding: 0.5em;
+ border: 1px solid;
+ position: absolute;
+ background-color: white;
+ max-height: 330px !important;
+ overflow: hidden;
+ z-index: 99;
+}
+
+/*
+div#multiple_details div.ngRow.selected {
+ background-color: #c9dde1 !important;
+}
+*/
+div.grid-values {
+ text-align: right;
+}
+
+div.grid-values.ngCellText span {
+ padding-right: 4px;
+}
+
+.panel-adjacent {
+ margin-left: 430px;
+}
+
+#topologyForm.selected {
+ border: 1px solid red;
+}
+#topologyForm {
+ border: 1px solid white;
+ padding: 1em 1.5em;
+}
+
+div.qdr-topology.pane.left .ngViewport {
+ /* border: 1px solid lightgray; */
+}
+
+#topologyForm > div {
+ /* width:396px; */
+}
+
+/* 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-left: 0;
+ 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;
+}
+
+.ui-tabs.ui-tabs-vertical .ui-tabs-nav li.separated {
+ margin-top: 1em;
+}
+
+#crosssection {
+ display: none;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 100;
+}
+
+.node circle {
+/* fill: rgb(31, 119, 180);
+ fill-opacity: .25; */
+ fill: #cfe2f3;
+ fill-opacity: .98;
+ stroke: black;
+ stroke-width: 3px;
+}
+
+circle.subcircle {
+ stroke-width: 1px;
+ /* stroke-dasharray: 2; */
+ fill-opacity: 0;
+ stroke: darkgray;
+}
+
+.leaf circle {
+ fill: #6fa8dc;
+ fill-opacity: 0.95;
+ stroke-width: 3px;
+}
+
+.leaf circle[title] {
+ font-family: monospace;
+
+}
+
+#svg_legend {
+ position: absolute;
+ top: 74px;
+ right: 0;
+ border: 1px solid #ccc;
+ border-radius: 5px;
+ background-color: #fcfcfc;
+ margin-right: 1.3em;
+ padding: 1em;
+}
+
+#svg_legend svg {
+ height: 235px;
+ width: 180px;
+}
+
+/*
+#multiple_details div.grid {
+ min-height: 70px !important;
+ height: auto !important;
+}
+
+#multiple_details .ngViewport {
+ height: auto !important;
+}
+
+#multiple_details .gridCellButton button, #link_details .gridCellButton button {
+ margin: .25em .4em;
+ font-size: 12px;
+ height: 2em;
+ padding-top: .1em;
+}
+*/
+
+#linkFilter {
+ display: none;
+ padding: 0.5em;
+ border: 1px solid grey;
+ background-color: #F0F0F0;
+ position: absolute;
+ z-index: 100;
+ right: 1em;
+}
+div.formLine label, div.formLine input {
+ display: inline-block;
+ padding: 0 8px;
+}
+
+span.filter-icon {
+ padding-left: 1em;
+}
+
+button.filter-close {
+ width: 15px;
+ height: 20px;
+ padding: 0;
+ position: absolute;
+ right: 4px;
+ top: 4px;
+}
+
+div.filter-title h6 {
+ margin: 0 0 0.5em 0;
+}
+
+.links button.btn-filter {
+ padding: 0 1em 0 0;
+ margin-left: 1em;
+ font-size: 1em;
+}
+
+button.btn-filter {
+ float: right;
+}
+span.dynatree-expanded button.btn-filter,
+a.dynatree-title:hover button.btn-filter {
+ visibility: visible;
+}
+
+div.hdash-button a {
+ color: white;
+}
+
+.linkDirIn {
+ color: red;
+ background-color: #f3f3f3;
+}
+
+.linkDirOut {
+ color: blue;
+ background-color: white;
+}
+
+div.topoGrid .ui-grid-viewport {
+ overflow: hidden !important;
+}
+
+@-moz-document url-prefix() {
+ .btn {padding: 2px 12px 8px !important;}
+}
+
+.ui-fancytree.fancytree-container {
+ font-size: 14px;
+}
+
+.grid-title {
+ background-color: #FAFAFA;
+ background-image: -moz-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#f2f2f2));
+ background-image: -webkit-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: -o-linear-gradient(top, #ffffff, #f2f2f2);
+ background-image: linear-gradient(to bottom, #ffffff, #f2f2f2);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFF', endColorstr='#F2F2F2', GradientType=0);
+ border-bottom: 1px solid #d4d4d4;
+ text-shadow: 0 1px 0 #FFFFFF;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ margin: 0 0 10px 0;
+ padding-bottom: 4px;
+}
+
+.expand-collapse {
+ float: right;
+ margin-right: 0.5em;
+}
+
+.pane-viewport {
+ top: 24px !important;
+}/*
+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: 56px !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; */
+ font-weight: bold;
+}
+
+
+.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;
+}
+
+#list-controller .tree-header {
+ position: absolute;
+ height: auto;
+}
+
+#list-controller select {
+ height: auto;
+ float: left;
+}
+
+
+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, div.qdr-topology.pane {
+ position: absolute;
+ margin-left: 10px;
+}
+div.qdr-overview.pane {
+ position: absolute;
+}
+div.qdr-topology.pane.left {
+ width: auto;
+}
+
+/* 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.colorPicker div:nth-of-type(1), /* first span under div.colorPicker */
+ div.minicolors{
+ float:left;
+ margin-right: 0.5em;
+}
+
+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;
+}
+
+.grid-align-value .ngCellText {
+ padding-right: 10px;
+}
+
+.overview .ngRow:hover {
+ background:#e0e0ff;
+}
+
+.qdr-overview.pane.left, .qdr-attributes.pane.left {
+ top: 104px;
+}
+.qdr-topology.pane.left {
+ top: 104px;
+}
+.qdr-overview.pane.left, .qdr-attributes.pane.left, .qdr-topology.pane.left {
+ left: 10px;
+}
+
+.treeContainer {
+ width: 100%;
+ float: left;
+}
+
+.pane-content {
+ overflow: auto;
+}
+
+#entityNames {
+ width: 20em;
+ float: left;
+}
+
+.treeDetails {
+ margin-left: 260px;
+}
+
+.gridStyle:not(.noHighlight) .ui-grid-row:hover .ui-grid-cell-contents {
+ background-color: #e0e0ff;
+}
+
+.ngCellText {
+ padding: 4px 0 0 4px;
+}
+
+.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: 20em;
+}
+
+.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).address.mobile .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.address.mobile .dynatree-icon:before {
+ content: "\f109";
+}
+span:not(.dynatree-has-children).address.internal.mobile .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.address.internal.mobile .dynatree-icon:before {
+ content: "\f0ac";
+}
+span:not(.dynatree-has-children).address.router .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.address.router .dynatree-icon:before {
+ content: "\f047";
+}
+
+span.address-link .dynatree-icon:before {
+ content: "\f0ac";
+}
+
+span:not(.dynatree-has-children).connection.external .dynatree-icon:before {
+ content: "\f109";
+}
+span:not(.dynatree-has-children).connection.normal .dynatree-icon:before {
+ content: "\f08e";
+}
+span:not(.dynatree-has-children).connection.external.quiesced .dynatree-icon:before {
+ content: "\f14c";
+ color: red;
+}
+span:not(.dynatree-has-children).connection.inter-router .dynatree-icon:before {
+ content: "\f07e";
+}
+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).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\.node .dynatree-icon:before {
+ content: "\f013";
+}
+span:not(.dynatree-has-children).link.inter-router .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.link.inter-router .dynatree-icon:before{
+ content: "\f07e";
+}
+span:not(.dynatree-has-children).link.endpoint .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.link.endpoint .dynatree-icon:before{
+ content: "\f109";
+}
+span:not(.dynatree-has-children).link.console .dynatree-icon:before,
+span:not(.dynatree-has-children).router\.link.console .dynatree-icon:before {
+ content: "\f108";
+}
+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).connection.console .dynatree-icon:before {
+ content: "\f108";
+}
+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;
+}
+
+div.dispatch-router div.help {
+ width: auto;
+ padding: 1em;
+ background-color: lavender;
+ border-radius: 6px;
+ margin-top: 1em;
+ text-align: center;
+}
+
+div.operations tr:nth-child(even) {
+ background: #f3f3f3;
+}
+div.operations tr:nth-child(odd), div.operations tr:last-child {
+ background: #fff;
+}
+
+div.operations tr input {
+ margin: 0;
+ padding: 3px 6px;
+}
+div.operations table {
+ width: 100%;
+}
+div.operations th {
+ width: 50%;
+ border-bottom: 1px solid #cccccc;
+ text-align: left;
+}
+div.operations td:nth-child(odd), div.operations th:nth-child(odd) {
+ border-right: 1px solid #cccccc;
+}
+div.operations td:nth-child(odd) {
+ padding-left: 0;
+}
+div.operations td:nth-child(even), div.operations th:nth-child(even) {
+ padding-left: 5px;
+}
+div.operations th {
+ padding: 5px;
+}
+div.operations .tab-pane.active {
+ padding: 12px 12px 12px 0;
+}
+div.operations label {
+ padding-top: 4px;
+ margin-bottom: 4px;
+}
+.qdrListActions .ngGrid {
+ /*min-height: 40em;
+ height: 100%; */
+}
+div.qdrListActions .ngViewport {
+ height: initial !important;
+}
+
+div.operations .boolean {
+ padding-bottom: 0;
+}
+
+table.log-entry {
+ margin-bottom: 1em;
+ border-top: 1px solid black;
+}
+
+table.log-entry pre {
+ background-color: #f5f5f5;
+ color: inherit;
+ margin: 0;
+}
+
+circle.node.normal.console {
+ fill: lightcyan;
+}
+
+text.console, text.on-demand, text.normal {
+ font-family: FontAwesome;
+ font-weight: normal;
+ font-size: 16px;
+}
+
+@font-face {
+ font-family:"Brokers";
+ src: url("brokers.ttf") /* TTF file for CSS3 browsers */
+}
+
+text.artemis.on-demand {
+ font-family: Brokers;
+ font-size: 20px;
+ font-weight: bold;
+}
+
+text.qpid-cpp.on-demand {
+ font-family: Brokers;
+ font-size: 18px;
+ font-weight: bold;
+}
+
+i.red {
+ color: red;
+}
+
+.qdrListActions div.delete {
+ width: 20em;
+ margin: auto;
+ border: 1px solid #eaeaea;
+ height: 5em;
+ padding: 4em;
+ background-color: #fcfcfc;
+}
+
+.btn:focus {
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
+select:focus, input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus {
+ outline:3px solid rgba(82, 168, 236, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
+}
+
+btn.disabled, .btn[disabled] {
+ opacity: 0.35;
+}
+
+#dispatch-login-container .ng-invalid-range {
+ border-color: #e9322d !important;
+}
+
+div#durationSlider, div#rateSlider {
+ margin-top: 1em;
+}
+
+.list-grid {
+ padding-left: 10px;
+}
+
+div#list-controller {
+ padding-left: 300px;
+}
+
+div.topology-container #content_body,
+div.topology-container #content_body .container-fluid,
+div.topology-container #content_body .container-fluid .row,
+div.topology-container #content_body .container-fluid .row .col-xs-12 {
+ height: inherit;
+}
+
+#overview-controller {
+ position: relative;
+}
+
+#overview-controller div.grid {
+ padding-left: 1em;
+}
+
+.overview-dropdown {
+ float: left;
+ color: #666;
+}
+
+.overview-dropdown * {
+ color: inherit;
+}
+
+.overview-dropdown.selected,
+ .overview-dropdown.selected1 {
+ color: black;
+}
+
+.overview-dropdown select {
+ border: 1px solid black;
+ background: initial;
+ padding: 0.5em;
+ font-size: small;
+}
+
+#overview_dropdowns, #overview_charts {
+ display: flex;
+ justify-content: space-between;
+}
+
+.dropdown-entity {
+ font-weight: bold;
+}
+
+.link-menu-item label {
+ font-weight: normal;
+ padding-left: 8px;
+}
+
+.link-menu-item input {
+ margin: 0 0 0 4px;
+ position: relative;
+ top: 4px;
+}
+
+#overview_charts .d3Chart {
+ height: 180px;
+ width: 300px;
+ border: 0;
+ /*background-color: #EEE;*/
+ float: left;
+}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/jquery.dynatree.min.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/jquery.dynatree.min.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/jquery.dynatree.min.js
new file mode 100644
index 0000000..d946469
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/jquery.dynatree.min.js
@@ -0,0 +1,4 @@
+/*! jQuery Dynatree Plugin - v1.2.8 - 2015-07-04
+* https://github.com/mar10/dynatree/
+* Copyright (c) 2015 Martin Wendt; Licensed MIT, GPL */function _log(a){if(_canLog){var b=Array.prototype.slice.apply(arguments,[1]),c=new Date,d=c.getHours()+":"+c.getMinutes()+":"+c.getSeconds()+"."+c.getMilliseconds();b[0]=d+" - "+b[0];try{switch(a){case"info":window.console.info.apply(window.console,b);break;case"warn":window.console.warn.apply(window.console,b);break;default:window.console.log.apply(window.console,b)}}catch(e){window.console?-2146827850===e.number&&window.console.log(b.join(", ")):_canLog=!1}}}function logMsg(){Array.prototype.unshift.apply(arguments,["debug"]),_log.apply(this,arguments)}var _canLog=!0,getDynaTreePersistData=null,DTNodeStatus_Error=-1,DTNodeStatus_Loading=1,DTNodeStatus_Ok=0;!function($){function getDtNodeFromElement(a){return alert("getDtNodeFromElement is deprecated"),$.ui.dynatree.getNode(a)}function noop(){}function offsetString(a){return 0===a?"":a>0?"+"+a:""+a}function _checkBrowser(){function a(a){a=a.toLowerCase();var b=/(chrome)[ \/]([
\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}}var b,c;return b=a(navigator.userAgent),c={},b.browser&&(c[b.browser]=!0,c.version=b.version),c.chrome?c.webkit=!0:c.webkit&&(c.safari=!0),c}function versionCompare(a,b){var c,d,e,f=(""+a).split("."),g=(""+b).split("."),h=Math.min(f.length,g.length);for(e=0;h>e;e++)if(c=parseInt(f[e],10),d=parseInt(g[e],10),isNaN(c)&&(c=f[e]),isNaN(d)&&(d=g[e]),c!=d)return c>d?1:d>c?-1:0/0;return f.length===g.length?0:f.length<g.length?-1:1}function _initDragAndDrop(a){var b=a.options.dnd||null;b&&(b.onDragStart||b.onDrop)&&_registerDnd(),b&&b.onDragStart&&a.$tree.draggable({addClasses:!1,appendTo:"body",containment:!1,delay:0,distance:4,revert:b.revert!==!0?!1:function(a){if(logMsg("draggable.revert(), dropped=",a),"boolean"==typeof a)return!a;var b=$.ui.ddmanager&&
$.ui.ddmanager.current&&$.ui.ddmanager.current.helper,c=b&&b.hasClass("dynatree-drop-reject");return c},scroll:!0,scrollSpeed:7,scrollSensitivity:10,connectToDynatree:!0,helper:function(a){var b=$.ui.dynatree.getNode(a.target);return b?b.tree._onDragEvent("helper",b,null,a,null,null):"<div></div>"},start:function(a,b){var c=b.helper.data("dtSourceNode");return!!c}}),b&&b.onDrop&&a.$tree.droppable({addClasses:!1,tolerance:"pointer",greedy:!1})}var Class={create:function(){return function(){this.initialize.apply(this,arguments)}}},BROWSER=_checkBrowser(),jquerySupports={positionMyOfs:versionCompare($.ui.version,"1.9")>=0},DynaTreeNode=Class.create();DynaTreeNode.prototype={initialize:function(a,b,c){this.parent=a,this.tree=b,"string"==typeof c&&(c={title:c}),c.key=null==c.key?"_"+b._nodeCount++:""+c.key,this.data=$.extend({},$.ui.dynatree.nodedatadefaults,c),this.li=null,this.span=null,this.ul=null,this.childList=null,this._isLoading=!1,this.hasSubSel=!1,this.bExpanded=!1,this.bSelect
ed=!1},toString:function(){return"DynaTreeNode<"+this.data.key+">: '"+this.data.title+"'"},toDict:function(a,b){var c,d=$.extend({},this.data);if(d.activate=this.tree.activeNode===this,d.focus=this.tree.focusNode===this,d.expand=this.bExpanded,d.select=this.bSelected,b&&b(d),a&&this.childList){d.children=[];for(var e=0,f=this.childList.length;f>e;e++)c=this.childList[e],c.isStatusNode()||d.children.push(c.toDict(!0,b))}else delete d.children;return d},fromDict:function(a){var b=a.children;return void 0===b?(this.data=$.extend(this.data,a),void this.render()):(a=$.extend({},a),a.children=void 0,this.data=$.extend(this.data,a),this.removeChildren(),void this.addChild(b))},_getInnerHtml:function(){var a,b=this.tree,c=b.options,d=b.cache,e=this.getLevel(),f=this.data,g="";e<c.minExpandLevel?e>1&&(g+=d.tagConnector):g+=this.hasChildren()!==!1?d.tagExpander:d.tagConnector,c.checkbox&&f.hideCheckbox!==!0&&!f.isStatusNode&&(g+=d.tagCheckbox),f.icon?(a="/"===f.icon.charAt(0)?f.icon:c.imagePa
th+f.icon,g+="<img src='"+a+"' alt='' />"):f.icon===!1||(g+=f.iconClass?"<span class=' "+f.iconClass+"'></span>":d.tagNodeIcon);var h="";if(c.onCustomRender&&(h=c.onCustomRender.call(b,this)||""),!h){var i=f.tooltip?' title="'+f.tooltip.replace(/\"/g,""")+'"':"",j=f.href||"#";h=c.noLink||f.noLink?'<span style="display:inline-block;" class="'+c.classNames.title+'"'+i+">"+f.title+"</span>":'<a href="'+j+'" class="'+c.classNames.title+'"'+i+">"+f.title+"</a>"}return g+=h},_fixOrder:function(){var a=this.childList;if(a&&this.ul)for(var b=this.ul.firstChild,c=0,d=a.length-1;d>c;c++){var e=a[c],f=b.dtnode;e!==f?(this.tree.logDebug("_fixOrder: mismatch at index "+c+": "+e+" != "+f),this.ul.insertBefore(e.li,f.li)):b=b.nextSibling}},render:function(a,b){var c=this.tree,d=this.parent,e=this.data,f=c.options,g=f.classNames,h=this.isLastSibling(),i=!1;if(d||this.ul){if(d){this.li||(i=!0,this.li=document.createElement("li"),this.li.dtnode=this,e.key&&f.generateIds&&(this.li.id=f.idPrefix+e
.key),this.span=document.createElement("span"),this.span.className=g.title,this.li.appendChild(this.span),d.ul||(d.ul=document.createElement("ul"),d.ul.style.display="none",d.li.appendChild(d.ul)),d.ul.appendChild(this.li)),this.span.innerHTML=this._getInnerHtml();var j=[];j.push(g.node),e.isFolder&&j.push(g.folder),this.bExpanded&&j.push(g.expanded),this.hasChildren()!==!1&&j.push(g.hasChildren),e.isLazy&&null===this.childList&&j.push(g.lazy),h&&j.push(g.lastsib),this.bSelected&&j.push(g.selected),this.hasSubSel&&j.push(g.partsel),c.activeNode===this&&j.push(g.active),e.addClass&&j.push(e.addClass),j.push(g.combinedExpanderPrefix+(this.bExpanded?"e":"c")+(e.isLazy&&null===this.childList?"d":"")+(h?"l":"")),j.push(g.combinedIconPrefix+(this.bExpanded?"e":"c")+(e.isFolder?"f":"")),this.span.className=j.join(" "),this.li.className=h?g.lastsib:"",i&&f.onCreate&&f.onCreate.call(c,this,this.span),f.onRender&&f.onRender.call(c,this,this.span)}}else this.li=this.span=null,this.ul=document.
createElement("ul"),this.ul.className=f.minExpandLevel>1?g.container+" "+g.noConnector:g.container;if((this.bExpanded||b===!0)&&this.childList){for(var k=0,l=this.childList.length;l>k;k++)this.childList[k].render(!1,b);this._fixOrder()}if(this.ul){var m="none"===this.ul.style.display,n=!!this.bExpanded;if(a&&f.fx&&m===n){var o=f.fx.duration||200;$(this.ul).animate(f.fx,o)}else this.ul.style.display=this.bExpanded||!d?"":"none"}},getKeyPath:function(a){var b=[],c=this.tree.options.keyPathSeparator;return this.visitParents(function(a){a.parent&&b.unshift(a.data.key)},!a),c+b.join(c)},getParent:function(){return this.parent},getChildren:function(){return void 0===this.hasChildren()?void 0:this.childList},hasChildren:function(){return this.data.isLazy?null===this.childList||void 0===this.childList?void 0:0===this.childList.length?!1:1===this.childList.length&&this.childList[0].isStatusNode()?void 0:!0:!!this.childList},isFirstSibling:function(){var a=this.parent;return!a||a.childList[0]
===this},isLastSibling:function(){var a=this.parent;return!a||a.childList[a.childList.length-1]===this},isLoading:function(){return!!this._isLoading},getPrevSibling:function(){if(!this.parent)return null;for(var a=this.parent.childList,b=1,c=a.length;c>b;b++)if(a[b]===this)return a[b-1];return null},getNextSibling:function(){if(!this.parent)return null;for(var a=this.parent.childList,b=0,c=a.length-1;c>b;b++)if(a[b]===this)return a[b+1];return null},isStatusNode:function(){return this.data.isStatusNode===!0},isChildOf:function(a){return this.parent&&this.parent===a},isDescendantOf:function(a){if(!a)return!1;for(var b=this.parent;b;){if(b===a)return!0;b=b.parent}return!1},countChildren:function(){var a=this.childList;if(!a)return 0;for(var b=a.length,c=0,d=b;d>c;c++){var e=a[c];b+=e.countChildren()}return b},sortChildren:function(a,b){var c=this.childList;if(c){if(a=a||function(a,b){var c=a.data.title.toLowerCase(),d=b.data.title.toLowerCase();return c===d?0:c>d?1:-1},c.sort(a),b)for
(var d=0,e=c.length;e>d;d++)c[d].childList&&c[d].sortChildren(a,"$norender$");"$norender$"!==b&&this.render()}},_setStatusNode:function(a){var b=this.childList?this.childList[0]:null;if(a)b?(a.isStatusNode=!0,a.key="_statusNode",b.data=a,b.render()):(a.isStatusNode=!0,a.key="_statusNode",b=this.addChild(a));else if(b&&b.isStatusNode()){try{this.ul&&(this.ul.removeChild(b.li),b.li=null)}catch(c){}1===this.childList.length?this.childList=[]:this.childList.shift()}},setLazyNodeStatus:function(a,b){var c=b&&b.tooltip?b.tooltip:null,d=b&&b.info?" ("+b.info+")":"";switch(a){case DTNodeStatus_Ok:this._setStatusNode(null),$(this.span).removeClass(this.tree.options.classNames.nodeLoading),this._isLoading=!1,this.tree.options.autoFocus&&(this===this.tree.tnRoot&&this.childList&&this.childList.length>0?this.childList[0].focus():this.focus());break;case DTNodeStatus_Loading:this._isLoading=!0,$(this.span).addClass(this.tree.options.classNames.nodeLoading),this.parent||this._setStatusNode({title
:this.tree.options.strings.loading+d,tooltip:c,addClass:this.tree.options.classNames.nodeWait});break;case DTNodeStatus_Error:this._isLoading=!1,this._setStatusNode({title:this.tree.options.strings.loadError+d,tooltip:c,addClass:this.tree.options.classNames.nodeError});break;default:throw"Bad LazyNodeStatus: '"+a+"'."}},_parentList:function(a,b){for(var c=[],d=b?this:this.parent;d;)(a||d.parent)&&c.unshift(d),d=d.parent;return c},getLevel:function(){for(var a=0,b=this.parent;b;)a++,b=b.parent;return a},_getTypeForOuterNodeEvent:function(a){var b=this.tree.options.classNames,c=a.target;if(c.className.indexOf(b.node)<0)return null;for(var d=a.pageX-c.offsetLeft,e=a.pageY-c.offsetTop,f=0,g=c.childNodes.length;g>f;f++){var h=c.childNodes[f],i=h.offsetLeft-c.offsetLeft,j=h.offsetTop-c.offsetTop,k=h.clientWidth,l=h.clientHeight;if(d>=i&&i+k>=d&&e>=j&&j+l>=e){if(h.className==b.title)return"title";if(h.className==b.expander)return"expander";if(h.className==b.checkbox||h.className==b.radio)r
eturn"checkbox";if(h.className==b.nodeIcon)return"icon"}}return"prefix"},getEventTargetType:function(a){var b=a&&a.target?a.target.className:"",c=this.tree.options.classNames;return b.indexOf(c.title)>=0?"title":b.indexOf(c.expander)>=0?"expander":b.indexOf(c.checkbox)>=0||b.indexOf(c.radio)>=0?"checkbox":b.indexOf(c.nodeIcon)>=0?"icon":b.indexOf(c.empty)>=0||b.indexOf(c.vline)>=0||b.indexOf(c.connector)>=0?"prefix":b.indexOf(c.node)>=0?this._getTypeForOuterNodeEvent(a):null},isVisible:function(){for(var a=this._parentList(!0,!1),b=0,c=a.length;c>b;b++)if(!a[b].bExpanded)return!1;return!0},makeVisible:function(){for(var a=this._parentList(!0,!1),b=0,c=a.length;c>b;b++)a[b]._expand(!0)},focus:function(){this.makeVisible();try{$(this.span).find(">a").focus()}catch(a){}},isFocused:function(){return this.tree.tnFocused===this},_activate:function(a,b){this.tree.logDebug("dtnode._activate(%o, fireEvents=%o) - %o",a,b,this);var c=this.tree.options;if(!this.data.isStatusNode)if(a){if(b&&c.o
nQueryActivate&&c.onQueryActivate.call(this.tree,a,this)===!1)return;if(this.tree.activeNode){if(this.tree.activeNode===this)return;this.tree.activeNode.deactivate()}c.activeVisible&&this.makeVisible(),this.tree.activeNode=this,c.persist&&$.cookie(c.cookieId+"-active",this.data.key,c.cookie),this.tree.persistence.activeKey=this.data.key,$(this.span).addClass(c.classNames.active),b&&c.onActivate&&c.onActivate.call(this.tree,this)}else if(this.tree.activeNode===this){if(c.onQueryActivate&&c.onQueryActivate.call(this.tree,!1,this)===!1)return;$(this.span).removeClass(c.classNames.active),c.persist&&$.cookie(c.cookieId+"-active","",c.cookie),this.tree.persistence.activeKey=null,this.tree.activeNode=null,b&&c.onDeactivate&&c.onDeactivate.call(this.tree,this)}},activate:function(){this._activate(!0,!0)},activateSilently:function(){this._activate(!0,!1)},deactivate:function(){this._activate(!1,!0)},isActive:function(){return this.tree.activeNode===this},_userActivate:function(){var a=!0,b=
!1;if(this.data.isFolder)switch(this.tree.options.clickFolderMode){case 2:a=!1,b=!0;break;case 3:a=b=!0}null===this.parent&&(b=!1),b&&(this.toggleExpand(),this.focus()),a&&this.activate()},_setSubSel:function(a){a?(this.hasSubSel=!0,$(this.span).addClass(this.tree.options.classNames.partsel)):(this.hasSubSel=!1,$(this.span).removeClass(this.tree.options.classNames.partsel))},_updatePartSelectionState:function(){var a;if(!this.hasChildren())return a=this.bSelected&&!this.data.unselectable&&!this.data.isStatusNode,this._setSubSel(!1),a;var b,c,d=this.childList,e=!0,f=!0;for(b=0,c=d.length;c>b;b++){var g=d[b],h=g._updatePartSelectionState();h!==!1&&(f=!1),h!==!0&&(e=!1)}return a=e?!0:f?!1:void 0,this._setSubSel(void 0===a),this.bSelected=a===!0,a},_fixSelectionState:function(){var a,b,c;if(this.bSelected)for(this.visit(function(a){a.parent._setSubSel(!0),a.data.unselectable||a._select(!0,!1,!1)}),a=this.parent;a;){a._setSubSel(!0);var d=!0;for(b=0,c=a.childList.length;c>b;b++){var e=a.
childList[b];if(!e.bSelected&&!e.data.isStatusNode&&!e.data.unselectable){d=!1;break}}d&&a._select(!0,!1,!1),a=a.parent}else for(this._setSubSel(!1),this.visit(function(a){a._setSubSel(!1),a._select(!1,!1,!1)}),a=this.parent;a;){a._select(!1,!1,!1);var f=!1;for(b=0,c=a.childList.length;c>b;b++)if(a.childList[b].bSelected||a.childList[b].hasSubSel){f=!0;break}a._setSubSel(f),a=a.parent}},_select:function(a,b,c){var d=this.tree.options;this.data.isStatusNode||this.bSelected!==a&&(b&&d.onQuerySelect&&d.onQuerySelect.call(this.tree,a,this)===!1||(1==d.selectMode&&a&&this.tree.visit(function(a){return a.bSelected?(a._select(!1,!1,!1),!1):void 0}),this.bSelected=a,a?(d.persist&&this.tree.persistence.addSelect(this.data.key),$(this.span).addClass(d.classNames.selected),c&&3===d.selectMode&&this._fixSelectionState(),b&&d.onSelect&&d.onSelect.call(this.tree,!0,this)):(d.persist&&this.tree.persistence.clearSelect(this.data.key),$(this.span).removeClass(d.classNames.selected),c&&3===d.selectMo
de&&this._fixSelectionState(),b&&d.onSelect&&d.onSelect.call(this.tree,!1,this))))},select:function(a){return this.data.unselectable?this.bSelected:this._select(a!==!1,!0,!0)},toggleSelect:function(){return this.select(!this.bSelected)},isSelected:function(){return this.bSelected},isLazy:function(){return!!this.data.isLazy},_loadContent:function(){try{var a=this.tree.options;this.tree.logDebug("_loadContent: start - %o",this),this.setLazyNodeStatus(DTNodeStatus_Loading),!0===a.onLazyRead.call(this.tree,this)&&(this.setLazyNodeStatus(DTNodeStatus_Ok),this.tree.logDebug("_loadContent: succeeded - %o",this))}catch(b){this.tree.logWarning("_loadContent: failed - %o",b),this.setLazyNodeStatus(DTNodeStatus_Error,{tooltip:""+b})}},_expand:function(a,b){if(this.bExpanded===a)return void this.tree.logDebug("dtnode._expand(%o) IGNORED - %o",a,this);this.tree.logDebug("dtnode._expand(%o) - %o",a,this);var c=this.tree.options;if(!a&&this.getLevel()<c.minExpandLevel)return void this.tree.logDebu
g("dtnode._expand(%o) prevented collapse - %o",a,this);if(!c.onQueryExpand||c.onQueryExpand.call(this.tree,a,this)!==!1){this.bExpanded=a,c.persist&&(a?this.tree.persistence.addExpand(this.data.key):this.tree.persistence.clearExpand(this.data.key));var d=!(this.data.isLazy&&null===this.childList||this._isLoading||b);if(this.render(d),this.bExpanded&&this.parent&&c.autoCollapse)for(var e=this._parentList(!1,!0),f=0,g=e.length;g>f;f++)e[f].collapseSiblings();return c.activeVisible&&this.tree.activeNode&&!this.tree.activeNode.isVisible()&&this.tree.activeNode.deactivate(),a&&this.data.isLazy&&null===this.childList&&!this._isLoading?void this._loadContent():void(c.onExpand&&c.onExpand.call(this.tree,a,this))}},isExpanded:function(){return this.bExpanded},expand:function(a){a=a!==!1,(this.childList||this.data.isLazy||!a)&&(null!==this.parent||a)&&this._expand(a)},scheduleAction:function(a,b){this.tree.timer&&(clearTimeout(this.tree.timer),this.tree.logDebug("clearTimeout(%o)",this.tree.t
imer));var c=this;switch(a){case"cancel":break;case"expand":this.tree.timer=setTimeout(function(){c.tree.logDebug("setTimeout: trigger expand"),c.expand(!0)},b);break;case"activate":this.tree.timer=setTimeout(function(){c.tree.logDebug("setTimeout: trigger activate"),c.activate()},b);break;default:throw"Invalid mode "+a}this.tree.logDebug("setTimeout(%s, %s): %s",a,b,this.tree.timer)},toggleExpand:function(){this.expand(!this.bExpanded)},collapseSiblings:function(){if(null!==this.parent)for(var a=this.parent.childList,b=0,c=a.length;c>b;b++)a[b]!==this&&a[b].bExpanded&&a[b]._expand(!1)},_onClick:function(a){var b=this.getEventTargetType(a);if("expander"===b)this.toggleExpand(),this.focus();else if("checkbox"===b)this.toggleSelect(),this.focus();else{this._userActivate();var c=this.span.getElementsByTagName("a");if(!c[0])return!0;BROWSER.msie&&parseInt(BROWSER.version,10)<9||c[0].focus()}a.preventDefault()},_onDblClick:function(){},_onKeydown:function(a){var b,c=!0;switch(a.which){ca
se 107:case 187:this.bExpanded||this.toggleExpand();break;case 109:case 189:this.bExpanded&&this.toggleExpand();break;case 32:this._userActivate();break;case 8:this.parent&&this.parent.focus();break;case 37:this.bExpanded?(this.toggleExpand(),this.focus()):this.parent&&this.parent.parent&&this.parent.focus();break;case 39:this.bExpanded||!this.childList&&!this.data.isLazy?this.childList&&this.childList[0].focus():(this.toggleExpand(),this.focus());break;case 38:for(b=this.getPrevSibling();b&&b.bExpanded&&b.childList;)b=b.childList[b.childList.length-1];!b&&this.parent&&this.parent.parent&&(b=this.parent),b&&b.focus();break;case 40:if(this.bExpanded&&this.childList)b=this.childList[0];else for(var d=this._parentList(!1,!0),e=d.length-1;e>=0&&!(b=d[e].getNextSibling());e--);b&&b.focus();break;default:c=!1}c&&a.preventDefault()},_onKeypress:function(){},_onFocus:function(a){var b=this.tree.options;"blur"==a.type||"focusout"==a.type?(b.onBlur&&b.onBlur.call(this.tree,this),this.tree.tnF
ocused&&$(this.tree.tnFocused.span).removeClass(b.classNames.focused),this.tree.tnFocused=null,b.persist&&$.cookie(b.cookieId+"-focus","",b.cookie)):("focus"==a.type||"focusin"==a.type)&&(this.tree.tnFocused&&this.tree.tnFocused!==this&&(this.tree.logDebug("dtnode.onFocus: out of sync: curFocus: %o",this.tree.tnFocused),$(this.tree.tnFocused.span).removeClass(b.classNames.focused)),this.tree.tnFocused=this,b.onFocus&&b.onFocus.call(this.tree,this),$(this.tree.tnFocused.span).addClass(b.classNames.focused),b.persist&&$.cookie(b.cookieId+"-focus",this.data.key,b.cookie))},visit:function(a,b){var c=!0;if(b===!0&&(c=a(this),c===!1||"skip"===c))return c;if(this.childList)for(var d=0,e=this.childList.length;e>d&&(c=this.childList[d].visit(a,!0),c!==!1);d++);return c},visitParents:function(a,b){if(b&&a(this)===!1)return!1;for(var c=this.parent;c;){if(a(c)===!1)return!1;c=c.parent}return!0},remove:function(){if(this===this.tree.root)throw"Cannot remove system root";return this.parent.remove
Child(this)},removeChild:function(a){var b=this.childList;if(1===b.length){if(a!==b[0])throw"removeChild: invalid child";return this.removeChildren()}a===this.tree.activeNode&&a.deactivate(),this.tree.options.persist&&(a.bSelected&&this.tree.persistence.clearSelect(a.data.key),a.bExpanded&&this.tree.persistence.clearExpand(a.data.key)),a.removeChildren(!0),this.ul&&a.li&&this.ul.removeChild(a.li);for(var c=0,d=b.length;d>c;c++)if(b[c]===a){this.childList.splice(c,1);break}},removeChildren:function(a,b){this.tree.logDebug("%s.removeChildren(%o)",this,a);var c=this.tree,d=this.childList;if(d){for(var e=0,f=d.length;f>e;e++){var g=d[e];g!==c.activeNode||b||g.deactivate(),this.tree.options.persist&&!b&&(g.bSelected&&this.tree.persistence.clearSelect(g.data.key),g.bExpanded&&this.tree.persistence.clearExpand(g.data.key)),g.removeChildren(!0,b),this.ul&&g.li&&$("li",$(this.ul)).remove()}this.childList=null}a||(this._isLoading=!1,this.render())},setTitle:function(a){this.fromDict({title:a}
)},reload:function(){throw"Use reloadChildren() instead"},reloadChildren:function(a){if(null===this.parent)throw"Use tree.reload() instead";if(!this.data.isLazy)throw"node.reloadChildren() requires lazy nodes.";if(a){var b=this,c="nodeLoaded.dynatree."+this.tree.$tree.attr("id")+"."+this.data.key;this.tree.$tree.bind(c,function(d,e,f){if(b.tree.$tree.unbind(c),b.tree.logDebug("loaded %o, %o, %o",d,e,f),e!==b)throw"got invalid load event";a.call(b.tree,e,f)})}this.removeChildren(),this._loadContent()},_loadKeyPath:function(a,b){var c=this.tree;if(c.logDebug("%s._loadKeyPath(%s)",this,a),""===a)throw"Key path must not be empty";var d=a.split(c.options.keyPathSeparator);if(""===d[0])throw"Key path must be relative (don't start with '/')";var e=d.shift();if(this.childList)for(var f=0,g=this.childList.length;g>f;f++){var h=this.childList[f];if(h.data.key===e){if(0===d.length)b.call(c,h,"ok");else if(!h.data.isLazy||null!==h.childList&&void 0!==h.childList)b.call(c,h,"loaded"),h._loadKeyP
ath(d.join(c.options.keyPathSeparator),b);else{c.logDebug("%s._loadKeyPath(%s) -> reloading %s...",this,a,h);var i=this;h.reloadChildren(function(e,f){f?(c.logDebug("%s._loadKeyPath(%s) -> reloaded %s.",e,a,e),b.call(c,h,"loaded"),e._loadKeyPath(d.join(c.options.keyPathSeparator),b)):(c.logWarning("%s._loadKeyPath(%s) -> reloadChildren() failed.",i,a),b.call(c,h,"error"))})}return}}b.call(c,void 0,"notfound",e,0===d.length),c.logWarning("Node not found: "+e)},resetLazy:function(){if(null===this.parent)throw"Use tree.reload() instead";if(!this.data.isLazy)throw"node.resetLazy() requires lazy nodes.";this.expand(!1),this.removeChildren()},_addChildNode:function(a,b){var c=this.tree,d=c.options,e=c.persistence;if(a.parent=this,null===this.childList?this.childList=[]:b||this.childList.length>0&&$(this.childList[this.childList.length-1].span).removeClass(d.classNames.lastsib),b){var f=$.inArray(b,this.childList);if(0>f)throw"<beforeNode> must be a child of <this>";this.childList.splice(f
,0,a)}else this.childList.push(a);var g=c.isInitializing();if(d.persist&&e.cookiesFound&&g?(e.activeKey===a.data.key&&(c.activeNode=a),e.focusedKey===a.data.key&&(c.focusNode=a),a.bExpanded=$.inArray(a.data.key,e.expandedKeyList)>=0,a.bSelected=$.inArray(a.data.key,e.selectedKeyList)>=0):(a.data.activate&&(c.activeNode=a,d.persist&&(e.activeKey=a.data.key)),a.data.focus&&(c.focusNode=a,d.persist&&(e.focusedKey=a.data.key)),a.bExpanded=a.data.expand===!0,a.bExpanded&&d.persist&&e.addExpand(a.data.key),a.bSelected=a.data.select===!0,a.bSelected&&d.persist&&e.addSelect(a.data.key)),d.minExpandLevel>=a.getLevel()&&(this.bExpanded=!0),a.bSelected&&3==d.selectMode)for(var h=this;h;)h.hasSubSel||h._setSubSel(!0),h=h.parent;return c.bEnableUpdate&&this.render(),a},addChild:function(a,b){if("string"==typeof a)throw"Invalid data type for "+a;if(a&&0!==a.length){if(a instanceof DynaTreeNode)return this._addChildNode(a,b);a.length||(a=[a]);for(var c=this.tree.enableUpdate(!1),d=null,e=0,f=a.len
gth;f>e;e++){var g=a[e],h=this._addChildNode(new DynaTreeNode(this,this.tree,g),b);d||(d=h),g.children&&h.addChild(g.children,null)}return this.tree.enableUpdate(c),d}},append:function(a){return this.tree.logWarning("node.append() is deprecated (use node.addChild() instead)."),this.addChild(a,null)},appendAjax:function(a){var b=this;if(this.removeChildren(!1,!0),this.setLazyNodeStatus(DTNodeStatus_Loading),a.debugLazyDelay){var c=a.debugLazyDelay;return a.debugLazyDelay=0,this.tree.logInfo("appendAjax: waiting for debugLazyDelay "+c),void setTimeout(function(){b.appendAjax(a)},c)}var d=a.success,e=a.error,f="nodeLoaded.dynatree."+this.tree.$tree.attr("id")+"."+this.data.key,g=$.extend({},this.tree.options.ajaxDefaults,a,{success:function(a,c){var e=b.tree.phase,g=b.tree.options;b.tree.phase="init",g.postProcess?a=g.postProcess.call(this,a,this.dataType):a&&a.hasOwnProperty("d")&&(a="string"==typeof a.d?$.parseJSON(a.d):a.d),$.isArray(a)&&0===a.length||b.addChild(a,null),b.tree.phase
="postInit",d&&d.call(g,b,a,c),b.tree.logDebug("trigger "+f),b.tree.$tree.trigger(f,[b,!0]),b.tree.phase=e,b.setLazyNodeStatus(DTNodeStatus_Ok),$.isArray(a)&&0===a.length&&(b.childList=[],b.render())},error:function(a,c,d){b.tree.logWarning("appendAjax failed:",c,":\n",a,"\n",d),e&&e.call(g,b,a,c,d),b.tree.$tree.trigger(f,[b,!1]),b.setLazyNodeStatus(DTNodeStatus_Error,{info:c,tooltip:""+d})}});$.ajax(g)},move:function(a,b){var c;if(this!==a){if(!this.parent)throw"Cannot move system root";(void 0===b||"over"==b)&&(b="child");var d=this.parent,e="child"===b?a:a.parent;if(e.isDescendantOf(this))throw"Cannot move a node to it's own descendant";if(1==this.parent.childList.length)this.parent.childList=this.parent.data.isLazy?[]:null,this.parent.bExpanded=!1;else{if(c=$.inArray(this,this.parent.childList),0>c)throw"Internal error";this.parent.childList.splice(c,1)}if(this.parent.ul&&this.li&&this.parent.ul.removeChild(this.li),this.parent=e,e.hasChildren())switch(b){case"child":e.childList
.push(this);break;case"before":if(c=$.inArray(a,e.childList),0>c)throw"Internal error";e.childList.splice(c,0,this);break;case"after":if(c=$.inArray(a,e.childList),0>c)throw"Internal error";e.childList.splice(c+1,0,this);break;default:throw"Invalid mode "+b}else e.childList=[this];if(e.ul||(e.ul=document.createElement("ul"),e.ul.style.display="none",e.li&&e.li.appendChild(e.ul)),this.li&&e.ul.appendChild(this.li),this.tree!==a.tree)throw this.visit(function(b){b.tree=a.tree},null,!0),"Not yet implemented.";d.isDescendantOf(e)||d.render(),e.isDescendantOf(d)||e.render()}},lastentry:void 0};var DynaTreeStatus=Class.create();DynaTreeStatus._getTreePersistData=function(a,b){var c=new DynaTreeStatus(a,b);return c.read(),c.toDict()},getDynaTreePersistData=DynaTreeStatus._getTreePersistData,DynaTreeStatus.prototype={initialize:function(a,b){void 0===a&&(a=$.ui.dynatree.prototype.options.cookieId),b=$.extend({},$.ui.dynatree.prototype.options.cookie,b),this.cookieId=a,this.cookieOpts=b,this
.cookiesFound=void 0,this.activeKey=null,this.focusedKey=null,this.expandedKeyList=null,this.selectedKeyList=null},_log:function(){Array.prototype.unshift.apply(arguments,["debug"]),_log.apply(this,arguments)},read:function(){this.cookiesFound=!1;var a=$.cookie(this.cookieId+"-active");this.activeKey=a||"",a&&(this.cookiesFound=!0),a=$.cookie(this.cookieId+"-focus"),this.focusedKey=a||"",a&&(this.cookiesFound=!0),a=$.cookie(this.cookieId+"-expand"),this.expandedKeyList=a?a.split(","):[],a&&(this.cookiesFound=!0),a=$.cookie(this.cookieId+"-select"),this.selectedKeyList=a?a.split(","):[],a&&(this.cookiesFound=!0)},write:function(){$.cookie(this.cookieId+"-active",null===this.activeKey?"":this.activeKey,this.cookieOpts),$.cookie(this.cookieId+"-focus",null===this.focusedKey?"":this.focusedKey,this.cookieOpts),$.cookie(this.cookieId+"-expand",null===this.expandedKeyList?"":this.expandedKeyList.join(","),this.cookieOpts),$.cookie(this.cookieId+"-select",null===this.selectedKeyList?"":thi
s.selectedKeyList.join(","),this.cookieOpts)},addExpand:function(a){$.inArray(a,this.expandedKeyList)<0&&(this.expandedKeyList.push(a),$.cookie(this.cookieId+"-expand",this.expandedKeyList.join(","),this.cookieOpts))},clearExpand:function(a){var b=$.inArray(a,this.expandedKeyList);b>=0&&(this.expandedKeyList.splice(b,1),$.cookie(this.cookieId+"-expand",this.expandedKeyList.join(","),this.cookieOpts))},addSelect:function(a){$.inArray(a,this.selectedKeyList)<0&&(this.selectedKeyList.push(a),$.cookie(this.cookieId+"-select",this.selectedKeyList.join(","),this.cookieOpts))},clearSelect:function(a){var b=$.inArray(a,this.selectedKeyList);b>=0&&(this.selectedKeyList.splice(b,1),$.cookie(this.cookieId+"-select",this.selectedKeyList.join(","),this.cookieOpts))},isReloading:function(){return this.cookiesFound===!0},toDict:function(){return{cookiesFound:this.cookiesFound,activeKey:this.activeKey,focusedKey:this.activeKey,expandedKeyList:this.expandedKeyList,selectedKeyList:this.selectedKeyLis
t}},lastentry:void 0};var DynaTree=Class.create();DynaTree.version="@@Version",DynaTree.prototype={initialize:function(a){this.phase="init",this.$widget=a,this.options=a.options,this.$tree=a.element,this.timer=null,this.divTree=this.$tree.get(0),_initDragAndDrop(this)},_load:function(a){var b=(this.$widget,this.options),c=this;this.bEnableUpdate=!0,this._nodeCount=1,this.activeNode=null,this.focusNode=null,void 0!==b.rootVisible&&this.logWarning("Option 'rootVisible' is no longer supported."),b.minExpandLevel<1&&(this.logWarning("Option 'minExpandLevel' must be >= 1."),b.minExpandLevel=1),b.classNames!==$.ui.dynatree.prototype.options.classNames&&(b.classNames=$.extend({},$.ui.dynatree.prototype.options.classNames,b.classNames)),b.ajaxDefaults!==$.ui.dynatree.prototype.options.ajaxDefaults&&(b.ajaxDefaults=$.extend({},$.ui.dynatree.prototype.options.ajaxDefaults,b.ajaxDefaults)),b.dnd!==$.ui.dynatree.prototype.options.dnd&&(b.dnd=$.extend({},$.ui.dynatree.prototype.options.dnd,b.dnd
)),b.imagePath||$("script").each(function(){var a=/.*dynatree[^\/]*\.js$/i;return this.src.search(a)>=0?(b.imagePath=this.src.indexOf("/")>=0?this.src.slice(0,this.src.lastIndexOf("/"))+"/skin/":"skin/",c.logDebug("Guessing imagePath from '%s': '%s'",this.src,b.imagePath),!1):void 0}),this.persistence=new DynaTreeStatus(b.cookieId,b.cookie),b.persist&&($.cookie||_log("warn","Please include jquery.cookie.js to use persistence."),this.persistence.read()),this.logDebug("DynaTree.persistence: %o",this.persistence.toDict()),this.cache={tagEmpty:"<span class='"+b.classNames.empty+"'></span>",tagVline:"<span class='"+b.classNames.vline+"'></span>",tagExpander:"<span class='"+b.classNames.expander+"'></span>",tagConnector:"<span class='"+b.classNames.connector+"'></span>",tagNodeIcon:"<span class='"+b.classNames.nodeIcon+"'></span>",tagCheckbox:"<span class='"+b.classNames.checkbox+"'></span>",lastentry:void 0},(b.children||b.initAjax&&b.initAjax.url||b.initId)&&$(this.divTree).empty();var
d=this.$tree.find(">ul:first").hide();this.tnRoot=new DynaTreeNode(null,this,{}),this.tnRoot.bExpanded=!0,this.tnRoot.render(),this.divTree.appendChild(this.tnRoot.ul);var e=this.tnRoot,f=b.persist&&this.persistence.isReloading(),g=!1,h=this.enableUpdate(!1);this.logDebug("Dynatree._load(): read tree structure..."),b.children?e.addChild(b.children):b.initAjax&&b.initAjax.url?(g=!0,e.data.isLazy=!0,this._reloadAjax(a)):b.initId?this._createFromTag(e,$("#"+b.initId)):(this._createFromTag(e,d),d.remove()),this._checkConsistency(),g||3!=b.selectMode||e._updatePartSelectionState(),this.logDebug("Dynatree._load(): render nodes..."),this.enableUpdate(h),this.logDebug("Dynatree._load(): bind events..."),this.$widget.bind(),this.logDebug("Dynatree._load(): postInit..."),this.phase="postInit",b.persist&&this.persistence.write(),this.focusNode&&this.focusNode.isVisible()&&(this.logDebug("Focus on init: %o",this.focusNode),this.focusNode.focus()),g||(b.onPostInit&&b.onPostInit.call(this,f,!1),a
&&a.call(this,"ok")),this.phase="idle"},_reloadAjax:function(a){var b=this.options;if(!b.initAjax||!b.initAjax.url)throw"tree.reload() requires 'initAjax' mode.";var c=this.persistence,d=$.extend({},b.initAjax);d.addActiveKey&&(d.data.activeKey=c.activeKey),d.addFocusedKey&&(d.data.focusedKey=c.focusedKey),d.addExpandedKeyList&&(d.data.expandedKeyList=c.expandedKeyList.join(",")),d.addSelectedKeyList&&(d.data.selectedKeyList=c.selectedKeyList.join(",")),d.success&&this.logWarning("initAjax: success callback is ignored; use onPostInit instead."),d.error&&this.logWarning("initAjax: error callback is ignored; use onPostInit instead.");var e=c.isReloading();d.success=function(c){3==b.selectMode&&c.tree.tnRoot._updatePartSelectionState(),b.onPostInit&&b.onPostInit.call(c.tree,e,!1),a&&a.call(c.tree,"ok")},d.error=function(c,d,f,g){b.onPostInit&&b.onPostInit.call(c.tree,e,!0,d,f,g),a&&a.call(c.tree,"error",d,f,g)},this.logDebug("Dynatree._init(): send Ajax request..."),this.tnRoot.appendA
jax(d)},toString:function(){return"Dynatree '"+this.$tree.attr("id")+"'"},toDict:function(a){var b=this.tnRoot.toDict(!0);return a?b:b.children},serializeArray:function(a){
+for(var b=this.getSelectedNodes(a),c=this.$tree.attr("name")||this.$tree.attr("id"),d=[],e=0,f=b.length;f>e;e++)d.push({name:c,value:b[e].data.key});return d},getPersistData:function(){return this.persistence.toDict()},logDebug:function(){this.options.debugLevel>=2&&(Array.prototype.unshift.apply(arguments,["debug"]),_log.apply(this,arguments))},logInfo:function(){this.options.debugLevel>=1&&(Array.prototype.unshift.apply(arguments,["info"]),_log.apply(this,arguments))},logWarning:function(){Array.prototype.unshift.apply(arguments,["warn"]),_log.apply(this,arguments)},isInitializing:function(){return"init"==this.phase||"postInit"==this.phase},isReloading:function(){return("init"==this.phase||"postInit"==this.phase)&&this.options.persist&&this.persistence.cookiesFound},isUserEvent:function(){return"userEvent"==this.phase},redraw:function(){this.tnRoot.render(!1,!1)},renderInvisibleNodes:function(){this.tnRoot.render(!1,!0)},reload:function(a){this._load(a)},getRoot:function(){return
this.tnRoot},enable:function(){this.$widget.enable()},disable:function(){this.$widget.disable()},getNodeByKey:function(a){var b=document.getElementById(this.options.idPrefix+a);if(b)return b.dtnode?b.dtnode:null;var c=null;return this.visit(function(b){return b.data.key===a?(c=b,!1):void 0},!0),c},getActiveNode:function(){return this.activeNode},reactivate:function(a){var b=this.activeNode;b&&(this.activeNode=null,b.activate(),a&&b.focus())},getSelectedNodes:function(a){var b=[];return this.tnRoot.visit(function(c){return c.bSelected&&(b.push(c),a===!0)?"skip":void 0}),b},activateKey:function(a){var b=null===a?null:this.getNodeByKey(a);return b?(b.focus(),b.activate(),b):(this.activeNode&&this.activeNode.deactivate(),this.activeNode=null,null)},loadKeyPath:function(a,b){var c=a.split(this.options.keyPathSeparator);return""===c[0]&&c.shift(),c[0]==this.tnRoot.data.key&&(this.logDebug("Removed leading root key."),c.shift()),a=c.join(this.options.keyPathSeparator),this.tnRoot._loadKeyP
ath(a,b)},selectKey:function(a,b){var c=this.getNodeByKey(a);return c?(c.select(b),c):null},enableUpdate:function(a){return this.bEnableUpdate==a?a:(this.bEnableUpdate=a,a&&this.redraw(),!a)},count:function(){return this.tnRoot.countChildren()},visit:function(a,b){return this.tnRoot.visit(a,b)},_createFromTag:function(parentTreeNode,$ulParent){var self=this;$ulParent.find(">li").each(function(){var $li=$(this),$liSpan=$li.find(">span:first"),$liA=$li.find(">a:first"),title,href=null,target=null,tooltip;if($liSpan.length)title=$liSpan.html();else if($liA.length)title=$liA.html(),href=$liA.attr("href"),target=$liA.attr("target"),tooltip=$liA.attr("title");else{title=$li.html();var iPos=title.search(/<ul/i);title=$.trim(iPos>=0?title.substring(0,iPos):title)}var data={title:title,tooltip:tooltip,isFolder:$li.hasClass("folder"),isLazy:$li.hasClass("lazy"),expand:$li.hasClass("expanded"),select:$li.hasClass("selected"),activate:$li.hasClass("active"),focus:$li.hasClass("focused"),noLink:
$li.hasClass("noLink")};if(href&&(data.href=href,data.target=target),$li.attr("title")&&(data.tooltip=$li.attr("title")),$li.attr("id")&&(data.key=""+$li.attr("id")),$li.attr("data")){var dataAttr=$.trim($li.attr("data"));if(dataAttr){"{"!=dataAttr.charAt(0)&&(dataAttr="{"+dataAttr+"}");try{$.extend(data,eval("("+dataAttr+")"))}catch(e){throw"Error parsing node data: "+e+"\ndata:\n'"+dataAttr+"'"}}}var childNode=parentTreeNode.addChild(data),$ul=$li.find(">ul:first");$ul.length&&self._createFromTag(childNode,$ul)})},_checkConsistency:function(){},_setDndStatus:function(a,b,c,d,e){var f,g=a?$(a.span):null,h=$(b.span),i=0,j="center";if(this.$dndMarker||(this.$dndMarker=$("<div id='dynatree-drop-marker'></div>").hide().css({"z-index":1e3}).prependTo($(this.divTree).parent())),"after"===d||"before"===d||"over"===d){switch(d){case"before":this.$dndMarker.removeClass("dynatree-drop-after dynatree-drop-over"),this.$dndMarker.addClass("dynatree-drop-before"),j="top";break;case"after":this.$
dndMarker.removeClass("dynatree-drop-before dynatree-drop-over"),this.$dndMarker.addClass("dynatree-drop-after"),j="bottom";break;default:this.$dndMarker.removeClass("dynatree-drop-after dynatree-drop-before"),this.$dndMarker.addClass("dynatree-drop-over"),h.addClass("dynatree-drop-target"),i=8}f=jquerySupports.positionMyOfs?{my:"left"+offsetString(i)+" center",at:"left "+j,of:h}:{my:"left center",at:"left "+j,of:h,offset:""+i+" 0"},this.$dndMarker.show().position(f)}else h.removeClass("dynatree-drop-target"),this.$dndMarker.hide();"after"===d?h.addClass("dynatree-drop-after"):h.removeClass("dynatree-drop-after"),"before"===d?h.addClass("dynatree-drop-before"):h.removeClass("dynatree-drop-before"),e===!0?(g&&g.addClass("dynatree-drop-accept"),h.addClass("dynatree-drop-accept"),c.addClass("dynatree-drop-accept")):(g&&g.removeClass("dynatree-drop-accept"),h.removeClass("dynatree-drop-accept"),c.removeClass("dynatree-drop-accept")),e===!1?(g&&g.addClass("dynatree-drop-reject"),h.addCla
ss("dynatree-drop-reject"),c.addClass("dynatree-drop-reject")):(g&&g.removeClass("dynatree-drop-reject"),h.removeClass("dynatree-drop-reject"),c.removeClass("dynatree-drop-reject"))},_onDragEvent:function(a,b,c,d,e,f){var g,h,i,j=this.options.dnd,k=null,l=$(b.span);switch(a){case"helper":var m=$("<div class='dynatree-drag-helper'><span class='dynatree-drag-helper-img' /></div>").append(l.find(".dynatree-title").clone());$("ul.dynatree-container",b.tree.divTree).append(m),m.data("dtSourceNode",b),k=m;break;case"start":b.isStatusNode()?k=!1:j.onDragStart&&(k=j.onDragStart(b)),k===!1?(this.logDebug("tree.onDragStart() cancelled"),e.helper.trigger("mouseup"),e.helper.hide()):l.addClass("dynatree-drag-source");break;case"enter":i=j.onDragEnter?j.onDragEnter(b,c,e,f):null,k=i?$.isArray(i)?{over:$.inArray("over",i)>=0,before:$.inArray("before",i)>=0,after:$.inArray("after",i)>=0}:{over:i===!0||"over"===i,before:i===!0||"before"===i,after:i===!0||"after"===i}:!1,e.helper.data("enterResponse
",k);break;case"over":if(h=e.helper.data("enterResponse"),g=null,h===!1);else if("string"==typeof h)g=h;else{var n=l.offset(),o={x:d.pageX-n.left,y:d.pageY-n.top},p={x:o.x/l.width(),y:o.y/l.height()};h.after&&p.y>.75?g="after":!h.over&&h.after&&p.y>.5?g="after":h.before&&p.y<=.25?g="before":!h.over&&h.before&&p.y<=.5?g="before":h.over&&(g="over"),j.preventVoidMoves&&(b===c?g=null:"before"===g&&c&&b===c.getNextSibling()?g=null:"after"===g&&c&&b===c.getPrevSibling()?g=null:"over"===g&&c&&c.parent===b&&c.isLastSibling()&&(g=null)),e.helper.data("hitMode",g)}"over"===g&&j.autoExpandMS&&b.hasChildren()!==!1&&!b.bExpanded&&b.scheduleAction("expand",j.autoExpandMS),g&&j.onDragOver&&(k=j.onDragOver(b,c,g,e,f),("over"===k||"before"===k||"after"===k)&&(g=k)),this._setDndStatus(c,b,e.helper,g,k!==!1&&null!==g);break;case"drop":var q=e.helper.hasClass("dynatree-drop-reject");g=e.helper.data("hitMode"),g&&j.onDrop&&!q&&j.onDrop(b,c,g,e,f);break;case"leave":b.scheduleAction("cancel"),e.helper.dat
a("enterResponse",null),e.helper.data("hitMode",null),this._setDndStatus(c,b,e.helper,"out",void 0),j.onDragLeave&&j.onDragLeave(b,c,e,f);break;case"stop":l.removeClass("dynatree-drag-source"),j.onDragStop&&j.onDragStop(b);break;default:throw"Unsupported drag event: "+a}return k},cancelDrag:function(){var a=$.ui.ddmanager.current;a&&a.cancel()},lastentry:void 0},$.widget("ui.dynatree",{_init:function(){return versionCompare($.ui.version,"1.8")<0?(this.options.debugLevel>=0&&_log("warn","ui.dynatree._init() was called; you should upgrade to jquery.ui.core.js v1.8 or higher."),this._create()):void(this.options.debugLevel>=2&&_log("debug","ui.dynatree._init() was called; no current default functionality."))},_create:function(){var a=this.options;a.debugLevel>=1&&logMsg("Dynatree._create(): version='%s', debugLevel=%o.",$.ui.dynatree.version,this.options.debugLevel),this.options.event+=".dynatree";this.element.get(0);this.tree=new DynaTree(this),this.tree._load(),this.tree.logDebug("Dyn
atree._init(): done.")},bind:function(){function a(a){a=$.event.fix(a||window.event);var b=$.ui.dynatree.getNode(a.target);return b?b._onFocus(a):!1}this.unbind();var b="click.dynatree dblclick.dynatree";this.options.keyboard&&(b+=" keypress.dynatree keydown.dynatree"),this.element.bind(b,function(a){var b=$.ui.dynatree.getNode(a.target);if(!b)return!0;var c=b.tree,d=c.options;c.logDebug("event(%s): dtnode: %s",a.type,b);var e=c.phase;c.phase="userEvent";try{switch(a.type){case"click":return d.onClick&&d.onClick.call(c,b,a)===!1?!1:b._onClick(a);case"dblclick":return d.onDblClick&&d.onDblClick.call(c,b,a)===!1?!1:b._onDblClick(a);case"keydown":return d.onKeydown&&d.onKeydown.call(c,b,a)===!1?!1:b._onKeydown(a);case"keypress":return d.onKeypress&&d.onKeypress.call(c,b,a)===!1?!1:b._onKeypress(a)}}catch(f){c.logWarning("bind(%o): dtnode: %o, error: %o",a,b,f)}finally{c.phase=e}});var c=this.tree.divTree;c.addEventListener?(c.addEventListener("focus",a,!0),c.addEventListener("blur",a,!
0)):c.onfocusin=c.onfocusout=a},unbind:function(){this.element.unbind(".dynatree")},enable:function(){this.bind(),$.Widget.prototype.enable.apply(this,arguments)},disable:function(){this.unbind(),$.Widget.prototype.disable.apply(this,arguments)},getTree:function(){return this.tree},getRoot:function(){return this.tree.getRoot()},getActiveNode:function(){return this.tree.getActiveNode()},getSelectedNodes:function(){return this.tree.getSelectedNodes()},lastentry:void 0}),versionCompare($.ui.version,"1.8")<0&&($.ui.dynatree.getter="getTree getRoot getActiveNode getSelectedNodes"),$.extend($.ui.dynatree,{version:"1.2.8",buildType:"release",_DynaTreeClass:DynaTree,_DynaTreeNodeClass:DynaTreeNode,getNode:function(a){if(a instanceof DynaTreeNode)return a;for(void 0!==a.selector&&(a=a[0]);a;){if(a.dtnode)return a.dtnode;a=a.parentNode}return null},getPersistData:DynaTreeStatus._getTreePersistData}),$.ui.dynatree.prototype.options={title:"Dynatree",minExpandLevel:1,imagePath:null,children:nul
l,initId:null,initAjax:null,autoFocus:!0,keyboard:!0,persist:!1,autoCollapse:!1,clickFolderMode:3,activeVisible:!0,checkbox:!1,selectMode:2,fx:null,noLink:!1,onClick:null,onDblClick:null,onKeydown:null,onKeypress:null,onFocus:null,onBlur:null,onQueryActivate:null,onQuerySelect:null,onQueryExpand:null,onPostInit:null,onActivate:null,onDeactivate:null,onSelect:null,onExpand:null,onLazyRead:null,onCustomRender:null,onCreate:null,onRender:null,postProcess:null,dnd:{onDragStart:null,onDragStop:null,revert:!1,autoExpandMS:1e3,preventVoidMoves:!0,onDragEnter:null,onDragOver:null,onDrop:null,onDragLeave:null},ajaxDefaults:{cache:!1,timeout:0,dataType:"json"},strings:{loading:"Loading…",loadError:"Load error!"},generateIds:!1,idPrefix:"dynatree-id-",keyPathSeparator:"/",cookieId:"dynatree",cookie:{expires:null},classNames:{container:"dynatree-container",node:"dynatree-node",folder:"dynatree-folder",empty:"dynatree-empty",vline:"dynatree-vline",expander:"dynatree-expander",connector:"dy
natree-connector",checkbox:"dynatree-checkbox",radio:"dynatree-radio",nodeIcon:"dynatree-icon",title:"dynatree-title",noConnector:"dynatree-no-connector",nodeError:"dynatree-statusnode-error",nodeWait:"dynatree-statusnode-wait",hidden:"dynatree-hidden",combinedExpanderPrefix:"dynatree-exp-",combinedIconPrefix:"dynatree-ico-",nodeLoading:"dynatree-loading",hasChildren:"dynatree-has-children",active:"dynatree-active",selected:"dynatree-selected",expanded:"dynatree-expanded",lazy:"dynatree-lazy",focused:"dynatree-focused",partsel:"dynatree-partsel",lastsib:"dynatree-lastsib"},debugLevel:1,lastentry:void 0},versionCompare($.ui.version,"1.8")<0&&($.ui.dynatree.defaults=$.ui.dynatree.prototype.options),$.ui.dynatree.nodedatadefaults={title:null,key:null,isFolder:!1,isLazy:!1,tooltip:null,href:null,icon:null,addClass:null,noLink:!1,activate:!1,focus:!1,expand:!1,select:!1,hideCheckbox:!1,unselectable:!1,children:null,lastentry:void 0};var didRegisterDnd=!1,_registerDnd=function(){didRegist
erDnd||($.ui.plugin.add("draggable","connectToDynatree",{start:function(a,b){var c=$(this).data("ui-draggable")||$(this).data("draggable"),d=b.helper.data("dtSourceNode")||null;return d?(c.offset.click.top=-2,c.offset.click.left=16,d.tree._onDragEvent("start",d,null,a,b,c)):void 0},drag:function(a,b){var c=$(this).data("ui-draggable")||$(this).data("draggable"),d=b.helper.data("dtSourceNode")||null,e=b.helper.data("dtTargetNode")||null,f=$.ui.dynatree.getNode(a.target);if(a.target&&!f){var g=$(a.target).closest("div.dynatree-drag-helper,#dynatree-drop-marker").length>0;if(g)return}b.helper.data("dtTargetNode",f),e&&e!==f&&e.tree._onDragEvent("leave",e,d,a,b,c),f&&f.tree.options.dnd.onDrop&&(f===e?f.tree._onDragEvent("over",f,d,a,b,c):f.tree._onDragEvent("enter",f,d,a,b,c))},stop:function(a,b){var c=$(this).data("ui-draggable")||$(this).data("draggable"),d=b.helper.data("dtSourceNode")||null,e=b.helper.data("dtTargetNode")||null,f=a.type,g="mouseup"==f&&1==a.which;logMsg("draggable-c
onnectToDynatree.stop: targetNode(from event): %s, dtTargetNode: %s",e,b.helper.data("dtTargetNode")),g||logMsg("Drag was cancelled"),e&&(g&&e.tree._onDragEvent("drop",e,d,a,b,c),e.tree._onDragEvent("leave",e,d,a,b,c)),d&&d.tree._onDragEvent("stop",d,null,a,b,c)}}),didRegisterDnd=!0)}}(jQuery);
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[02/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.controller.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.controller.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.controller.js
new file mode 100644
index 0000000..a500cea
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.controller.js
@@ -0,0 +1,1703 @@
+/*
+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) {
+ 'use strict';
+
+ angular
+ .module('horizon.dashboard.dispatch.topology')
+ .controller('horizon.dashboard.dispatch.topology.TopologyController', TopologyController);
+
+ TopologyController.$inject = [
+ '$scope',
+ '$rootScope',
+ 'horizon.dashboard.dispatch.comService',
+ '$location',
+ '$timeout',
+ '$modal',
+ ]
+
+ var mouseX, mouseY;
+ var dontHide = false;
+ function hideLinkDetails() {
+ d3.select("#link_details").transition()
+ .duration(500)
+ .style("opacity", 0)
+ .each("end", function (d) {
+ d3.select("#link_details").style("visibility", "hidden")
+ })
+ }
+
+ function TopologyController(
+ $scope,
+ $rootScope,
+ QDRService,
+ $location,
+ $timeout,
+ $modal) {
+
+ var ctrl = this;
+ QDRService.addConnectAction( function () {
+ Topology($scope, $rootScope, QDRService, $location, $timeout, $modal)
+ })
+ QDRService.loadConnectOptions(QDRService.connect);
+
+ $scope.multiData = []
+ $scope.multiDetails = {
+ data: 'multiData',
+ enableRowSelection: true,
+ enableRowHeaderSelection: false,
+ multiSelect: false,
+ enableColumnResize: true,
+ enableColumnReordering: true,
+ enableVerticalScrollbar: 0,
+ enableHorizontalScrollbar: 0,
+ onRegisterApi: function(gridApi){
+ gridApi.selection.on.rowSelectionChanged($scope, function(row){
+ var detailsDiv = d3.select('#link_details')
+ var isVis = detailsDiv.style('visibility') === 'visible';
+ if (!dontHide && isVis && $scope.connectionId === row.entity.connectionId) {
+ hideLinkDetails();
+ return;
+ }
+ dontHide = false;
+ $scope.multiDetails.showLinksList(row)
+ });
+ },
+ showLinksList: function (obj) {
+ $scope.linkData = obj.entity.linkData;
+ $scope.connectionId = obj.entity.connectionId;
+ var visibleLen = Math.min(obj.entity.linkData.length, 10)
+ var left = parseInt(d3.select('#multiple_details').style("left"))
+ var bounds = $("#topology").position()
+ var detailsDiv = d3.select('#link_details')
+ detailsDiv
+ .style({
+ visibility: 'visible',
+ opacity: 1,
+ left: (left + 20) + "px",
+ top: (mouseY + 40 - bounds.top + $(document).scrollTop()) + "px",
+ height: ((visibleLen + 1) * 30) + 40 + "px", // +1 for the header row
+ 'overflow-y': obj.entity.linkData > 10 ? 'scroll' : 'hidden'})
+ },
+ columnDefs: [
+ {
+ field: 'host',
+ displayName: 'Connection host'
+ },
+ {
+ field: 'user',
+ displayName: 'User'
+ },
+ {
+ field: 'properties',
+ displayName: 'Properties'
+ },
+/*
+ {
+ cellClass: 'gridCellButton',
+ cellTemplate: '<button title="{{quiesceText(row)}} the links" type="button" ng-class="quiesceClass(row)" class="btn" ng-click="$event.stopPropagation();quiesceConnection(row)" ng-disabled="quiesceDisabled(row)">{{quiesceText(row)}}</button>'
+ },
+*/
+ ]
+ };
+ $scope.linkData = [];
+ $scope.linkDetails = {
+ data: 'linkData',
+ enableRowSelection: true,
+ enableRowHeaderSelection: false,
+ multiSelect: false,
+ enableColumnResize: true,
+ enableColumnReordering: true,
+ enableVerticalScrollbar: 0,
+ enableHorizontalScrollbar: 0,
+ columnDefs: [
+ {
+ field: 'adminStatus',
+ displayName: 'Admin state'
+ },
+ {
+ field: 'operStatus',
+ displayName: 'Oper state'
+ },
+ {
+ field: 'dir',
+ displayName: 'dir'
+ },
+ {
+ field: 'owningAddr',
+ displayName: 'Address'
+ },
+ {
+ field: 'deliveryCount',
+ displayName: 'Delivered',
+ cellClass: 'grid-values'
+
+ },
+ {
+ field: 'uncounts',
+ displayName: 'Outstanding',
+ cellClass: 'grid-values'
+ }/*,
+ {
+ cellClass: 'gridCellButton',
+ cellTemplate: '<button title="{{quiesceLinkText(row)}} this link" type="button" ng-class="quiesceLinkClass(row)" class="btn" ng-click="quiesceLink(row)" ng-disabled="quiesceLinkDisabled(row)">{{quiesceLinkText(row)}}</button>'
+ }*/
+ ]
+ }
+ }
+
+ function Topology(
+ $scope,
+ $rootScope,
+ QDRService,
+ $location,
+ $timeout,
+ $modal) {
+
+ $scope.quiesceState = {}
+ $scope.quiesceConnection = function (row) {
+ // call method to set adminStatus
+ }
+ $scope.quiesceDisabled = function (row) {
+ return false;
+ }
+ $scope.quiesceText = function (row) {
+ return 'Quiesce'
+ }
+ $scope.quiesceClass = function (row) {
+ var stateClassMap = {
+ enabled: 'btn-primary',
+ quiescing: 'btn-warning',
+ reviving: 'btn-warning',
+ quiesced: 'btn-danger'
+ }
+ return 'btn-primary'
+ }
+ $scope.quiesceLinkClass = function (row) {
+ var stateClassMap = {
+ enabled: 'btn-primary',
+ disabled: 'btn-danger'
+ }
+ return stateClassMap[row.entity.adminStatus]
+ }
+ $scope.quiesceLink = function (row) {
+ QDRService.quiesceLink(row.entity.nodeId, row.entity.name);
+ }
+ $scope.quiesceLinkDisabled = function (row) {
+ return (row.entity.operStatus !== 'up' && row.entity.operStatus !== 'down')
+ }
+ $scope.quiesceLinkText = function (row) {
+ return row.entity.operStatus === 'down' ? "Revive" : "Quiesce";
+ }
+
+ // we are currently connected. setup a handler to get notified if we are ever disconnected
+ QDRService.addDisconnectAction( function () {
+ QDR.log.debug("disconnected from router. show a toast message");
+ })
+
+ var urlPrefix = $location.absUrl();
+ urlPrefix = urlPrefix.split("#")[0]
+ QDR.log.debug("started QDR.TopologyController with urlPrefix: " + urlPrefix);
+
+ $scope.addingNode = {
+ step: 0,
+ hasLink: false,
+ trigger: ''
+ };
+
+ $scope.cancel = function () {
+ $scope.addingNode.step = 0;
+ }
+ $scope.editNewRouter = function () {
+ $scope.addingNode.trigger = 'editNode';
+ }
+
+ var NewRouterName = "__NEW__";
+ // mouse event vars
+ var selected_node = null,
+ selected_link = null,
+ mousedown_link = null,
+ mousedown_node = null,
+ mouseup_node = null,
+ initial_mouse_down_position = null;
+
+ $scope.schema = "Not connected";
+
+ $scope.modes = [
+ {title: 'Topology view', name: 'Diagram', right: false},
+ /* {title: 'Add a new router node', name: 'Add Router', right: true} */
+ ];
+ $scope.mode = "Diagram";
+ $scope.contextNode = null; // node that is associated with the current context menu
+
+ $scope.isModeActive = function (name) {
+ if ((name == 'Add Router' || name == 'Diagram') && $scope.addingNode.step > 0)
+ return true;
+ return ($scope.mode == name);
+ }
+ $scope.selectMode = function (name) {
+ if (name == "Add Router") {
+ name = 'Diagram';
+ if ($scope.addingNode.step > 0) {
+ $scope.addingNode.step = 0;
+ } else {
+ // start adding node mode
+ $scope.addingNode.step = 1;
+ }
+ } else {
+ $scope.addingNode.step = 0;
+ }
+
+ $scope.mode = name;
+ }
+ $scope.$watch(function () {return $scope.addingNode.step}, function (newValue, oldValue) {
+ if (newValue == 0 && oldValue != 0) {
+ // we are cancelling the add
+
+ // find the New node
+ nodes.every(function (n, i) {
+ // for the placeholder node, the key will be __internal__
+ if (QDRService.nameFromId(n.key) == '__internal__') {
+ var newLinks = links.filter(function (e, i) {
+ return e.source.id == n.id || e.target.id == n.id;
+ })
+ // newLinks is an array of links to remove
+ newLinks.map(function (e) {
+ links.splice(links.indexOf(e), 1);
+ })
+ // i is the index of the node to remove
+ nodes.splice(i, 1);
+ force.nodes(nodes).links(links).start();
+ restart(false);
+ return false; // stop looping
+ }
+ return true;
+ })
+ updateForm(Object.keys(QDRService.topology.nodeInfo())[0], 'router', 0);
+
+ } else if (newValue > 0) {
+ // we are starting the add mode
+ $scope.$broadcast('showAddForm')
+
+ resetMouseVars();
+ selected_node = null;
+ selected_link = null;
+ // add a new node
+ var id = "amqp:/_topo/0/__internal__/$management";
+ var x = radiusNormal * 4;
+ var y = x;;
+ if (newValue > 1) { // add at current mouse position
+ var offset = jQuery('#topology').offset();
+ x = mouseX - offset.left + $(document).scrollLeft();
+ y = mouseY - offset.top + $(document).scrollTop();;
+ }
+ NewRouterName = genNewName();
+ nodes.push( aNode(id, NewRouterName, "inter-router", undefined, nodes.length, x, y, undefined, true) );
+ force.nodes(nodes).links(links).start();
+ restart(false);
+ }
+ })
+
+ $scope.isRight = function (mode) {
+ return mode.right;
+ }
+
+ // for ng-grid that shows details for multiple consoles/clients
+ // generate unique name for router and containerName
+ var genNewName = function () {
+ var nodeInfo = QDRService.topology.nodeInfo();
+ var nameIndex = 1;
+ var newName = "R." + nameIndex;
+
+ var names = [];
+ for (var key in nodeInfo) {
+ var node = nodeInfo[key];
+ var router = node['.router'];
+ var attrNames = router.attributeNames;
+ var name = QDRService.valFor(attrNames, router.results[0], 'routerId')
+ if (!name)
+ name = QDRService.valFor(attrNames, router.results[0], 'name')
+ names.push(name);
+ }
+
+ while (names.indexOf(newName) >= 0) {
+ newName = "R." + nameIndex++;
+ }
+ return newName;
+ }
+
+ $scope.$watch(function () {return $scope.addingNode.trigger}, function (newValue, oldValue) {
+ if (newValue == 'editNode') {
+ $scope.addingNode.trigger = "";
+ editNode();
+ }
+ })
+
+ function editNode() {
+ doAddDialog(NewRouterName);
+ };
+ $scope.reverseLink = function () {
+ if (!mousedown_link)
+ return;
+ var d = mousedown_link;
+ var tmp = d.left;
+ d.left = d.right;;
+ d.right = tmp;
+ restart(false);
+ tick();
+ }
+ $scope.removeLink = function () {
+ if (!mousedown_link)
+ return;
+ var d = mousedown_link;
+ links.every( function (l, i) {
+ if (l.source.id == d.source.id && l.target.id == d.target.id) {
+ links.splice(i, 1);
+ force.links(links).start();
+ return false; // exit the 'every' loop
+ }
+ return true;
+ });
+ restart(false);
+ tick();
+ }
+ $scope.setFixed = function (b) {
+ if ($scope.contextNode) {
+ $scope.contextNode.fixed = b;
+ }
+ restart();
+ }
+ $scope.isFixed = function () {
+ if (!$scope.contextNode)
+ return false;
+ return ($scope.contextNode.fixed & 0b1);
+ }
+
+ // event handlers for popup context menu
+ $(document).mousemove(function (e) {
+ mouseX = e.clientX;
+ mouseY = e.clientY;
+ //console.log("("+mouseX+"," + mouseY+")")
+ });
+ $(document).mousemove();
+ $(document).click(function (e) {
+ $scope.contextNode = null;
+ $(".contextMenu").fadeOut(200);
+ });
+
+ // set up SVG for D3
+ var colors = {'inter-router': "#EAEAEA", 'normal': "#F0F000", 'on-demand': '#00F000'};
+ var radii = {'inter-router': 25, 'normal': 15, 'on-demand': 15};
+ var radius = 25;
+ var radiusNormal = 15;
+ var svg, lsvg;
+ var force;
+ var animate = false; // should the force graph organize itself when it is displayed
+ var path, circle;
+ var savedKeys = {};
+ var width = 0;
+ var height = 0;
+
+ var getSizes = function () {
+ var legendWidth = 196;
+ var gap = 5;
+ var width = $('.qdrTopology').width() - gap - legendWidth;
+ var top = $('#topology').offset().top
+ var tpformHeight = $('#topologyForm').height()
+ var height = window.innerHeight - tpformHeight - top - gap;
+ if (height < 400)
+ height = 400;
+/*
+ QDR.log.debug("window.innerHeight:" + window.innerHeight +
+ " tpformHeight:" + tpformHeight +
+ " top:" + top +
+ " gap:" + gap +
+ " width:" + width +
+ " height:" + height)
+*/
+ if (width < 10 || height < 30) {
+ QDR.log.info("page width and height are abnormal w:" + width + " height:" + height)
+ return [0,0];
+ }
+ return [width, height]
+ }
+ var resize = function () {
+ var sizes = getSizes();
+ width = sizes[0]
+ height = sizes[1]
+ if (width > 0) {
+ // set attrs and 'resume' force
+ svg.attr('width', width);
+ svg.attr('height', height);
+ force.size(sizes).resume();
+ }
+ }
+ window.addEventListener('resize', resize);
+ var sizes = getSizes()
+ width = sizes[0]
+ height = sizes[1]
+ height = 300
+ if (width <= 0 || height <= 0)
+ return
+
+ // set up initial nodes and links
+ // - nodes are known by 'id', not by index in array.
+ // - selected edges are indicated on the node (as a bold red circle).
+ // - links are always source < target; edge directions are set by 'left' and 'right'.
+ var nodes = [];
+ var links = [];
+
+ var aNode = function (id, name, nodeType, nodeInfo, nodeIndex, x, y, resultIndex, fixed, properties) {
+ properties = properties || {};
+ var routerId;
+ if (nodeInfo) {
+ var node = nodeInfo[id];
+ if (node) {
+ var router = node['.router'];
+ routerId = QDRService.valFor(router.attributeNames, router.results[0], 'id')
+ if (!routerId)
+ routerId = QDRService.valFor(router.attributeNames, router.results[0], 'routerId')
+ }
+ }
+ return { key: id,
+ name: name,
+ nodeType: nodeType,
+ properties: properties,
+ routerId: routerId,
+ x: x,
+ y: y,
+ id: nodeIndex,
+ resultIndex: resultIndex,
+ fixed: fixed,
+ cls: name == NewRouterName ? 'temp' : ''
+ };
+ };
+
+
+ var initForm = function (attributes, results, entityType, formFields) {
+
+ while(formFields.length > 0) {
+ // remove all existing attributes
+ formFields.pop();
+ }
+
+ for (var i=0; i<attributes.length; ++i) {
+ var name = attributes[i];
+ var val = results[i];
+ var desc = "";
+ if (entityType.attributes[name])
+ if (entityType.attributes[name].description)
+ desc = entityType.attributes[name].description;
+
+ formFields.push({'attributeName': name, 'attributeValue': val, 'description': desc});
+ }
+ }
+
+ // initialize the nodes and links array from the QDRService.topology._nodeInfo object
+ var initForceGraph = function () {
+ nodes = [];
+ links = [];
+
+ svg = d3.select('#topology')
+ .append('svg')
+ .attr("id", "SVG_ID")
+ .attr('width', width)
+ .attr('height', height)
+ .on("contextmenu", function(d) {
+ if (QDR.isHorizon)
+ return;
+ if (d3.event.defaultPrevented)
+ return;
+ d3.event.preventDefault();
+ if ($scope.addingNode.step != 0)
+ return;
+ if (d3.select('#svg_context_menu').style('display') !== 'block')
+ $(document).click();
+ d3.select('#svg_context_menu')
+ .style('left', (mouseX + $(document).scrollLeft()) + "px")
+ .style('top', (mouseY + $(document).scrollTop()) + "px")
+ .style('display', 'block');
+ })
+ .on('click', function (d) {
+ removeCrosssection()
+ });
+
+ $(document).keyup(function(e) {
+ if (e.keyCode === 27) {
+ removeCrosssection()
+ }
+ });
+
+ // the legend
+ lsvg = d3.select("#svg_legend")
+ .append('svg')
+ .attr('id', 'svglegend')
+ lsvg = lsvg.append('svg:g')
+ .attr('transform', 'translate('+(radii['inter-router']+2)+','+(radii['inter-router']+2)+')')
+ .selectAll('g');
+
+ // mouse event vars
+ selected_node = null;
+ selected_link = null;
+ mousedown_link = null;
+ mousedown_node = null;
+ mouseup_node = null;
+
+ // initialize the list of nodes
+ var yInit = 10;
+ var nodeInfo = QDRService.topology.nodeInfo();
+ var nodeCount = Object.keys(nodeInfo).length;
+ for (var id in nodeInfo) {
+ var name = QDRService.nameFromId(id);
+ // if we have any new nodes, animate the force graph to position them
+ var position = angular.fromJson(localStorage[name]);
+ if (!angular.isDefined(position)) {
+ animate = true;
+ position = {x: width / 4 + ((width / 2)/nodeCount) * nodes.length,
+ y: 200 + yInit,
+ fixed: false};
+ }
+ if (position.y > height)
+ position.y = 200 - yInit;
+ nodes.push( aNode(id, name, "inter-router", nodeInfo, nodes.length, position.x, position.y, undefined, position.fixed) );
+ yInit *= -1;
+ //QDR.log.debug("adding node " + nodes.length-1);
+ }
+
+ // initialize the list of links
+ var source = 0;
+ var client = 1;
+ for (var id in nodeInfo) {
+ var onode = nodeInfo[id];
+ var conns = onode['.connection'].results;
+ var attrs = onode['.connection'].attributeNames;
+ var parent = getNodeIndex(QDRService.nameFromId(id));
+ //QDR.log.debug("external client parent is " + parent);
+ var normalsParent = {console: undefined, client: undefined}; // 1st normal node for this parent
+
+ for (var j = 0; j < conns.length; j++) {
+ var role = QDRService.valFor(attrs, conns[j], "role");
+ var properties = QDRService.valFor(attrs, conns[j], "properties") || {};
+ var dir = QDRService.valFor(attrs, conns[j], "dir");
+ if (role == "inter-router") {
+ var connId = QDRService.valFor(attrs, conns[j], "container");
+ var target = getContainerIndex(connId);
+ if (target >= 0)
+ getLink(source, target, dir);
+ } else if (role == "normal" || role == "on-demand") {
+ // not a router, but an external client
+ //QDR.log.debug("found an external client for " + id);
+ var name = QDRService.nameFromId(id) + "." + client;
+ //QDR.log.debug("external client name is " + name + " and the role is " + role);
+
+ // if we have any new clients, animate the force graph to position them
+ var position = angular.fromJson(localStorage[name]);
+ if (!angular.isDefined(position)) {
+ animate = true;
+ position = {x: nodes[parent].x + 40 + Math.sin(Math.PI/2 * client),
+ y: nodes[parent].y + 40 + Math.cos(Math.PI/2 * client),
+ fixed: false};
+ }
+ if (position.y > height)
+ position.y = nodes[parent].y + 40 + Math.cos(Math.PI/2 * client)
+ var node = aNode(id, name, role, nodeInfo, nodes.length, position.x, position.y, j, position.fixed, properties)
+ var nodeType = QDRService.isAConsole(properties, QDRService.valFor(attrs, conns[j], "identity"), role, node.key)
+
+ if (role === 'normal') {
+ node.user = QDRService.valFor(attrs, conns[j], "user")
+ node.isEncrypted = QDRService.valFor(attrs, conns[j], "isEncrypted")
+ node.host = QDRService.valFor(attrs, conns[j], "host")
+ node.connectionId = QDRService.valFor(attrs, conns[j], "identity")
+
+ if (!normalsParent[nodeType]) {
+ normalsParent[nodeType] = node;
+ nodes.push( node );
+ node.normals = [node];
+ // now add a link
+ getLink(parent, nodes.length-1, dir);
+ client++;
+ } else {
+ normalsParent[nodeType].normals.push(node)
+ }
+ } else {
+ nodes.push( node)
+ // now add a link
+ getLink(parent, nodes.length-1, dir);
+ client++;
+ }
+ }
+ }
+ source++;
+ }
+
+ $scope.schema = QDRService.schema;
+ // init D3 force layout
+ force = d3.layout.force()
+ .nodes(nodes)
+ .links(links)
+ .size([width, height])
+ .linkDistance(function(d) { return d.target.nodeType === 'inter-router' ? 150 : 65 })
+ .charge(-1800)
+ .friction(.10)
+ .gravity(0.0001)
+ .on('tick', tick)
+ .start()
+
+ svg.append("svg:defs").selectAll('marker')
+ .data(["end-arrow", "end-arrow-selected"]) // Different link/path types can be defined here
+ .enter().append("svg:marker") // This section adds in the arrows
+ .attr("id", String)
+ .attr("viewBox", "0 -5 10 10")
+ //.attr("refX", 25)
+ .attr("markerWidth", 4)
+ .attr("markerHeight", 4)
+ .attr("orient", "auto")
+ .append("svg:path")
+ .attr('d', 'M 0 -5 L 10 0 L 0 5 z')
+
+ svg.append("svg:defs").selectAll('marker')
+ .data(["start-arrow", "start-arrow-selected"]) // Different link/path types can be defined here
+ .enter().append("svg:marker") // This section adds in the arrows
+ .attr("id", String)
+ .attr("viewBox", "0 -5 10 10")
+ .attr("refX", 5)
+ .attr("markerWidth", 4)
+ .attr("markerHeight", 4)
+ .attr("orient", "auto")
+ .append("svg:path")
+ .attr('d', 'M 10 -5 L 0 0 L 10 5 z');
+
+ // handles to link and node element groups
+ path = svg.append('svg:g').selectAll('path'),
+ circle = svg.append('svg:g').selectAll('g');
+
+ force.on('end', function() {
+ //QDR.log.debug("force end called");
+ circle
+ .attr('cx', function(d) {
+ localStorage[d.name] = angular.toJson({x: d.x, y: d.y, fixed: d.fixed});
+ return d.x; });
+ });
+
+ // app starts here
+ restart(false);
+ force.start();
+ setTimeout(function () {
+ updateForm(Object.keys(QDRService.topology.nodeInfo())[0], 'router', 0);
+ }, 10)
+
+ }
+
+ function updateForm (key, entity, resultIndex) {
+ var nodeInfo = QDRService.topology.nodeInfo();
+ var onode = nodeInfo[key]
+ if (onode) {
+ var nodeResults = onode['.' + entity].results[resultIndex]
+ var nodeAttributes = onode['.' + entity].attributeNames
+ var attributes = nodeResults.map( function (row, i) {
+ return {
+ attributeName: nodeAttributes[i],
+ attributeValue: row
+ }
+ })
+ // sort by attributeName
+ attributes.sort( function (a, b) { return a.attributeName.localeCompare(b.attributeName) })
+
+ // move the Name first
+ var nameIndex = attributes.findIndex ( function (attr) {
+ return attr.attributeName === 'name'
+ })
+ if (nameIndex >= 0)
+ attributes.splice(0, 0, attributes.splice(nameIndex, 1)[0]);
+ // get the list of ports this router is listening on
+ if (entity === 'router') {
+ var listeners = onode['.listener'].results;
+ var listenerAttributes = onode['.listener'].attributeNames;
+ var normals = listeners.filter ( function (listener) {
+ return QDRService.valFor( listenerAttributes, listener, 'role') === 'normal';
+ })
+ var ports = []
+ normals.forEach (function (normalListener) {
+ ports.push(QDRService.valFor( listenerAttributes, normalListener, 'port'))
+ })
+ // add as 2nd row
+ if (ports.length)
+ attributes.splice(1, 0, {attributeName: 'Listening on', attributeValue: ports, description: 'The port on which this router is listening for connections'});
+ }
+
+ $scope.$broadcast('showEntityForm', {entity: entity, attributes: attributes})
+ }
+ if (!$scope.$$phase) $scope.$apply()
+ }
+
+ function getContainerIndex(_id) {
+ var nodeIndex = 0;
+ var nodeInfo = QDRService.topology.nodeInfo();
+ for (var id in nodeInfo) {
+ var node = nodeInfo[id]['.router'];
+ // there should be only one router entity for each node, so using results[0] should be fine
+ if (QDRService.valFor( node.attributeNames, node.results[0], "id") === _id)
+ return nodeIndex;
+ if (QDRService.valFor( node.attributeNames, node.results[0], "routerId") === _id)
+ return nodeIndex;
+ nodeIndex++
+ }
+ // there was no router.id that matched, check deprecated router.routerId
+ nodeIndex = 0;
+ for (var id in nodeInfo) {
+ var node = nodeInfo[id]['.container'];
+ if (node) {
+ if (QDRService.valFor ( node.attributeNames, node.results[0], "containerName") === _id)
+ return nodeIndex;
+ }
+ nodeIndex++
+ }
+ //QDR.log.warn("unable to find containerIndex for " + _id);
+ return -1;
+ }
+
+ function getNodeIndex (_id) {
+ var nodeIndex = 0;
+ var nodeInfo = QDRService.topology.nodeInfo();
+ for (var id in nodeInfo) {
+ if (QDRService.nameFromId(id) == _id) return nodeIndex;
+ nodeIndex++
+ }
+ QDR.log.warn("unable to find nodeIndex for " + _id);
+ return -1;
+ }
+
+ function getLink (_source, _target, dir, cls) {
+ for (var i=0; i < links.length; i++) {
+ var s = links[i].source, t = links[i].target;
+ if (typeof links[i].source == "object") {
+ s = s.id;
+ t = t.id;
+ }
+ if (s == _source && t == _target) {
+ return i;
+ }
+ // same link, just reversed
+ if (s == _target && t == _source) {
+ return -i;
+ }
+ }
+
+ //QDR.log.debug("creating new link (" + (links.length) + ") between " + nodes[_source].name + " and " + nodes[_target].name);
+ var link = {
+ source: _source,
+ target: _target,
+ left: dir != "out",
+ right: dir == "out",
+ cls: cls
+ };
+ return links.push(link) - 1;
+ }
+
+
+ function resetMouseVars() {
+ mousedown_node = null;
+ mouseup_node = null;
+ mousedown_link = null;
+ }
+
+ // update force layout (called automatically each iteration)
+ function tick() {
+ circle.attr('transform', function (d) {
+ var cradius;
+ if (d.nodeType == "inter-router") {
+ cradius = d.left ? radius + 8 : radius;
+ } else {
+ cradius = d.left ? radiusNormal + 18 : radiusNormal;
+ }
+ d.x = Math.max(d.x, radiusNormal * 2);
+ d.y = Math.max(d.y, radiusNormal * 2);
+ d.x = Math.max(0, Math.min(width-cradius, d.x))
+ d.y = Math.max(0, Math.min(height-cradius, d.y))
+ return 'translate(' + d.x + ',' + d.y + ')';
+ });
+
+ // draw directed edges with proper padding from node centers
+ path.attr('d', function (d) {
+ //QDR.log.debug("in tick for d");
+ //console.dump(d);
+ var sourcePadding, targetPadding, r;
+
+ if (d.target.nodeType == "inter-router") {
+ r = radius;
+ // right arrow left line start
+ sourcePadding = d.left ? radius + 8 : radius;
+ // left arrow right line start
+ targetPadding = d.right ? radius + 16 : radius;
+ } else {
+ r = radiusNormal - 18;
+ sourcePadding = d.left ? radiusNormal + 18 : radiusNormal;
+ targetPadding = d.right ? radiusNormal + 16 : radiusNormal;
+ }
+ var dtx = Math.max(targetPadding, Math.min(width-r, d.target.x)),
+ dty = Math.max(targetPadding, Math.min(height-r, d.target.y)),
+ dsx = Math.max(sourcePadding, Math.min(width-r, d.source.x)),
+ dsy = Math.max(sourcePadding, Math.min(height-r, d.source.y));
+
+ var deltaX = dtx - dsx,
+ deltaY = dty - dsy,
+ dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY),
+ normX = deltaX / dist,
+ normY = deltaY / dist;
+ var sourceX = dsx + (sourcePadding * normX),
+ sourceY = dsy + (sourcePadding * normY),
+ targetX = dtx - (targetPadding * normX),
+ targetY = dty - (targetPadding * normY);
+ sourceX = Math.max(0, Math.min(width, sourceX))
+ sourceY = Math.max(0, Math.min(width, sourceY))
+ targetX = Math.max(0, Math.min(width, targetX))
+ targetY = Math.max(0, Math.min(width, targetY))
+
+ return 'M' + sourceX + ',' + sourceY + 'L' + targetX + ',' + targetY;
+ });
+
+ if (!animate) {
+ animate = true;
+ force.stop();
+ }
+ }
+
+ // highlight the paths between the selected node and the hovered node
+ function findNextHopNode(from, d) {
+ // d is the node that the mouse is over
+ // from is the selected_node ....
+ if (!from)
+ return null;
+
+ if (from == d)
+ return selected_node;
+
+ //QDR.log.debug("finding nextHop from: " + from.name + " to " + d.name);
+ var sInfo = QDRService.topology.nodeInfo()[from.key];
+
+ if (!sInfo) {
+ QDR.log.warn("unable to find topology node info for " + from.key);
+ return null;
+ }
+
+ // find the hovered name in the selected name's .router.node results
+ if (!sInfo['.router.node'])
+ return null;
+ var aAr = sInfo['.router.node'].attributeNames;
+ var vAr = sInfo['.router.node'].results;
+ for (var hIdx=0; hIdx<vAr.length; ++hIdx) {
+ var addrT = QDRService.valFor(aAr, vAr[hIdx], "id" );
+ if (addrT == d.name) {
+ //QDR.log.debug("found " + d.name + " at " + hIdx);
+ var nextHop = QDRService.valFor(aAr, vAr[hIdx], "nextHop");
+ //QDR.log.debug("nextHop was " + nextHop);
+ return (nextHop == null) ? nodeFor(addrT) : nodeFor(nextHop);
+ }
+ }
+ return null;
+ }
+
+ function nodeFor(name) {
+ for (var i=0; i<nodes.length; ++i) {
+ if (nodes[i].name == name)
+ return nodes[i];
+ }
+ return null;
+ }
+
+ function linkFor(source, target) {
+ for (var i=0; i<links.length; ++i) {
+ if ((links[i].source == source) && (links[i].target == target))
+ return links[i];
+ if ((links[i].source == target) && (links[i].target == source))
+ return links[i];
+ }
+ // the selected node was a client/broker
+ //QDR.log.debug("failed to find a link between ");
+ //console.dump(source);
+ //QDR.log.debug(" and ");
+ //console.dump(target);
+ return null;
+ }
+
+ function clearPopups() {
+ d3.select("#crosssection").style("display", "none");
+ $('.hastip').empty();
+ d3.select("#multiple_details").style("visibility", "hidden")
+ d3.select("#link_details").style("visibility", "hidden")
+ d3.select('#node_context_menu').style('display', 'none');
+ }
+ function removeCrosssection() {
+ setTimeout(function () {
+ d3.select("[id^=tooltipsy]").remove()
+ $('.hastip').empty();
+ }, 1010);
+ d3.select("#crosssection svg g").transition()
+ .duration(1000)
+ .attr("transform", "scale(0)")
+ .style("opacity", 0)
+ .each("end", function (d) {
+ d3.select("#crosssection svg").remove();
+ d3.select("#crosssection").style("display","none");
+ });
+ d3.select("#multiple_details").transition()
+ .duration(500)
+ .style("opacity", 0)
+ .each("end", function (d) {
+ d3.select("#multiple_details").style("visibility", "hidden")
+ stopUpdateConnectionsGrid();
+ })
+ hideLinkDetails();
+ }
+
+ // takes the nodes and links array of objects and adds svg elements for everything that hasn't already
+ // been added
+ function restart(start) {
+ circle.call(force.drag);
+
+ // path (link) group
+ path = path.data(links);
+
+ // update existing links
+ path.classed('selected', function(d) { return d === selected_link; })
+ .classed('highlighted', function(d) { return d.highlighted; } )
+ .classed('temp', function(d) { return d.cls == 'temp'; } )
+ .attr('marker-start', function(d) {
+ var sel = d===selected_link ? '-selected' : '';
+ return d.left ? 'url('+urlPrefix+'#start-arrow' + sel + ')' : ''; })
+ .attr('marker-end', function(d) {
+ var sel = d===selected_link ? '-selected' : '';
+ return d.right ? 'url('+urlPrefix+'#end-arrow' + sel +')' : ''; })
+
+
+ // add new links. if links[] is longer than the existing paths, add a new path for each new element
+ path.enter().append('svg:path')
+ .attr('class', 'link')
+ .attr('marker-start', function(d) {
+ var sel = d===selected_link ? '-selected' : '';
+ return d.left ? 'url('+urlPrefix+'#start-arrow' + sel + ')' : ''; })
+ .attr('marker-end', function(d) {
+ var sel = d===selected_link ? '-selected' : '';
+ return d.right ? 'url('+urlPrefix+'#end-arrow' + sel + ')' : ''; })
+ .classed('temp', function(d) { return d.cls == 'temp'; } )
+ // mouseover a line
+ .on('mouseover', function (d) {
+ if($scope.addingNode.step > 0) {
+ if (d.cls == 'temp') {
+ d3.select(this).classed('over', true);
+ }
+ return;
+ }
+ //QDR.log.debug("showing connections form");
+ var resultIndex = 0; // the connection to use
+ var left = d.left ? d.target : d.source;
+ // right is the node that the arrow points to, left is the other node
+ var right = d.left ? d.source : d.target;
+ var onode = QDRService.topology.nodeInfo()[left.key];
+ // loop through all the connections for left, and find the one for right
+ if (!onode || !onode['.connection'])
+ return;
+ // update the info dialog for the link the mouse is over
+ if (!selected_node && !selected_link) {
+ for (resultIndex=0; resultIndex < onode['.connection'].results.length; ++resultIndex) {
+ var conn = onode['.connection'].results[resultIndex];
+ /// find the connection whose container is the right's name
+ var name = QDRService.valFor(onode['.connection'].attributeNames, conn, "container");
+ if (name == right.routerId) {
+ break;
+ }
+ }
+ // did not find connection. this is a connection to a non-interrouter node
+ if (resultIndex === onode['.connection'].results.length) {
+ // use the non-interrouter node's connection info
+ left = d.target;
+ resultIndex = left.resultIndex;
+ }
+ if (resultIndex)
+ updateForm(left.key, 'connection', resultIndex);
+ }
+
+ mousedown_link = d;
+ selected_link = mousedown_link;
+ restart();
+ })
+ // mouseout a line
+ .on('mouseout', function (d) {
+ if($scope.addingNode.step > 0) {
+ if (d.cls == 'temp') {
+ d3.select(this).classed('over', false);
+ }
+ return;
+ }
+ //QDR.log.debug("showing connections form");
+ selected_link = null;
+ restart();
+ })
+ // contextmenu for a line
+ .on("contextmenu", function(d) {
+ $(document).click();
+ d3.event.preventDefault();
+ if (d.cls !== "temp")
+ return;
+
+ mousedown_link = d;
+ d3.select('#link_context_menu')
+ .style('left', (mouseX + $(document).scrollLeft()) + "px")
+ .style('top', (mouseY + $(document).scrollTop()) + "px")
+ .style('display', 'block');
+ })
+ // clicked on a line
+ .on("click", function (d) {
+ var clickPos = d3.mouse(this);
+ d3.event.stopPropagation();
+ clearPopups();
+ var diameter = 400;
+ var format = d3.format(",d");
+ var pack = d3.layout.pack()
+ .size([diameter - 4, diameter - 4])
+ .padding(-10)
+ .value(function(d) { return d.size; });
+
+ d3.select("#crosssection svg").remove();
+ var svg = d3.select("#crosssection").append("svg")
+ .attr("width", diameter)
+ .attr("height", diameter)
+ var svgg = svg.append("g")
+ .attr("transform", "translate(2,2)");
+
+ var root = {
+ name: " Links between " + d.source.name + " and " + d.target.name,
+ children: []
+ }
+ var nodeInfo = QDRService.topology.nodeInfo();
+ var connections = nodeInfo[d.source.key]['.connection'];
+ var containerIndex = connections.attributeNames.indexOf('container');
+ connections.results.some ( function (connection) {
+ if (connection[containerIndex] == d.target.routerId) {
+ root.attributeNames = connections.attributeNames;
+ root.obj = connection;
+ root.desc = "Connection";
+ return true; // stop looping after 1 match
+ }
+ return false;
+ })
+
+ // find router.links where link.remoteContainer is d.source.name
+ var links = nodeInfo[d.source.key]['.router.link'];
+ var identityIndex = connections.attributeNames.indexOf('identity')
+ var roleIndex = connections.attributeNames.indexOf('role')
+ var connectionIdIndex = links.attributeNames.indexOf('connectionId');
+ var linkTypeIndex = links.attributeNames.indexOf('linkType');
+ var nameIndex = links.attributeNames.indexOf('name');
+ var linkDirIndex = links.attributeNames.indexOf('linkDir');
+
+ if (roleIndex < 0 || identityIndex < 0 || connectionIdIndex < 0
+ || linkTypeIndex < 0 || nameIndex < 0 || linkDirIndex < 0)
+ return;
+ links.results.forEach ( function (link) {
+ if (root.obj && link[connectionIdIndex] == root.obj[identityIndex] && link[linkTypeIndex] == root.obj[roleIndex])
+ root.children.push (
+ { name: " " + link[linkDirIndex] + " ",
+ size: 100,
+ obj: link,
+ desc: "Link",
+ attributeNames: links.attributeNames
+ })
+ })
+ if (root.children.length == 0)
+ return;
+ var node = svgg.datum(root).selectAll(".node")
+ .data(pack.nodes)
+ .enter().append("g")
+ .attr("class", function(d) { return d.children ? "parent node hastip" : "leaf node hastip"; })
+ .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")" + (!d.children ? "scale(0.9)" : ""); })
+ .attr("title", function (d) {
+ var title = "<h4>" + d.desc + "</h4><table class='tiptable'><tbody>";
+ if (d.attributeNames)
+ d.attributeNames.forEach( function (n, i) {
+ title += "<tr><td>" + n + "</td><td>";
+ title += d.obj[i] != null ? d.obj[i] : '';
+ title += '</td></tr>';
+ })
+ title += "</tbody></table>"
+ return title
+ })
+ node.append("circle")
+ .attr("r", function(d) { return d.r; });
+
+// node.filter(function(d) { return !d.children; }).append("text")
+ node.append("text")
+ .attr("dy", function (d) { return d.children ? "-10em" : ".5em"})
+ .style("text-anchor", "middle")
+ .text(function(d) {
+ return d.name.substring(0, d.r / 3);
+ });
+ $('.hastip').tooltipsy({ alignTo: 'cursor'});
+ svgg.attr("transform", "translate(2,2) scale(0.01)")
+
+ var bounds = $("#topology").position()
+ d3.select("#crosssection")
+ .style("display", "block")
+ .style("left", (clickPos[0] + bounds.left) + "px")
+ .style("top", (clickPos[1] + bounds.top) + "px")
+
+ svgg.transition()
+ .attr("transform", "translate(2,2) scale(1)")
+ .each("end", function () {
+ d3.selectAll("#crosssection g.leaf text").attr("dy", ".3em")
+ })
+ })
+
+ // remove old links
+ path.exit().remove();
+
+
+ // circle (node) group
+ // nodes are known by id
+ circle = circle.data(nodes, function (d) {
+ return d.id;
+ });
+
+ // update existing nodes visual states
+ circle.selectAll('circle')
+ .classed('selected', function (d) { return (d === selected_node) })
+ .classed('fixed', function (d) { return (d.fixed & 0b1) })
+
+ // add new circle nodes. if nodes[] is longer than the existing paths, add a new path for each new element
+ var g = circle.enter().append('svg:g')
+ .classed('multiple', function(d) { return (d.normals && d.normals.length > 1) } )
+
+ var appendCircle = function (g) {
+ // add new circles and set their attr/class/behavior
+ return g.append('svg:circle')
+ .attr('class', 'node')
+ .attr('r', function (d) { return radii[d.nodeType] } )
+ .classed('fixed', function (d) {return d.fixed})
+ .classed('temp', function(d) { return QDRService.nameFromId(d.key) == '__internal__'; } )
+ .classed('normal', function(d) { return d.nodeType == 'normal' } )
+ .classed('inter-router', function(d) { return d.nodeType == 'inter-router' } )
+ .classed('on-demand', function(d) { return d.nodeType == 'on-demand' } )
+ .classed('console', function(d) { return QDRService.isConsole(d) } )
+ .classed('artemis', function(d) { return QDRService.isArtemis(d) } )
+ .classed('qpid-cpp', function(d) { return QDRService.isQpid(d) } )
+ .classed('client', function(d) { return d.nodeType === 'normal' && !d.properties.console_identifier } )
+ }
+ appendCircle(g)
+ .on('mouseover', function (d) { // mouseover a circle
+ if ($scope.addingNode.step > 0) {
+ d3.select(this).attr('transform', 'scale(1.1)');
+ return;
+ }
+ if (!selected_node) {
+ if (d.nodeType === 'inter-router') {
+ //QDR.log.debug("showing general form");
+ updateForm(d.key, 'router', 0);
+ } else if (d.nodeType === 'normal' || d.nodeType === 'on-demand') {
+ //QDR.log.debug("showing connections form");
+ updateForm(d.key, 'connection', d.resultIndex);
+ }
+ }
+
+ if (d === mousedown_node)
+ return;
+ //if (d === selected_node)
+ // return;
+ // enlarge target node
+ d3.select(this).attr('transform', 'scale(1.1)');
+ // highlight the next-hop route from the selected node to this node
+ mousedown_node = null;
+ if (!selected_node) {
+ return;
+ }
+ setTimeout(nextHop, 1, selected_node, d);
+ })
+ .on('mouseout', function (d) { // mouseout a circle
+ // unenlarge target node
+ d3.select(this).attr('transform', '');
+ for (var i=0; i<links.length; ++i) {
+ links[i]['highlighted'] = false;
+ }
+ restart();
+ })
+ .on('mousedown', function (d) { // mousedown a circle
+ if (d3.event.button !== 0) { // ignore all but left button
+ return;
+ }
+ mousedown_node = d;
+ // mouse position relative to svg
+ initial_mouse_down_position = d3.mouse(this.parentElement.parentElement.parentElement).slice();
+ })
+ .on('mouseup', function (d) { // mouseup a circle
+ if (!mousedown_node)
+ return;
+
+ selected_link = null;
+ // unenlarge target node
+ d3.select(this).attr('transform', '');
+
+ // check for drag
+ mouseup_node = d;
+ var mySvg = this.parentElement.parentElement.parentElement;
+ // if we dragged the node, make it fixed
+ var cur_mouse = d3.mouse(mySvg);
+ if (cur_mouse[0] != initial_mouse_down_position[0] ||
+ cur_mouse[1] != initial_mouse_down_position[1]) {
+ console.log("mouse pos changed. making this node fixed")
+ d3.select(this).classed("fixed", d.fixed = true);
+ resetMouseVars();
+ return;
+ }
+
+ // we didn't drag, we just clicked on the node
+ if ($scope.addingNode.step > 0) {
+ if (d.nodeType !== 'inter-router')
+ return;
+ if (QDRService.nameFromId(d.key) == '__internal__')
+ return;
+
+ // add a link from the clicked node to the new node
+ getLink(d.id, nodes.length-1, "in", "temp");
+ $scope.addingNode.hasLink = true;
+ if (!$scope.$$phase) $scope.$apply()
+ // add new elements to the svg
+ force.links(links).start();
+ restart();
+ return;
+ }
+
+ // if this node was selected, unselect it
+ if (mousedown_node === selected_node) {
+ selected_node = null;
+ }
+ else {
+ if (d.nodeType !== 'normal' && d.nodeType !== 'on-demand')
+ selected_node = mousedown_node;
+ }
+ for (var i=0; i<links.length; ++i) {
+ links[i]['highlighted'] = false;
+ }
+ mousedown_node = null;
+ if (!$scope.$$phase) $scope.$apply()
+ restart(false);
+ })
+ .on("dblclick", function (d) { // dblclick a circle
+ if (d.fixed) {
+ d3.select(this).classed("fixed", d.fixed = false);
+ force.start(); // let the nodes move to a new position
+ }
+ if (QDRService.nameFromId(d.key) == '__internal__') {
+ editNode();
+ if (!$scope.$$phase) $scope.$apply()
+ }
+ })
+ .on("contextmenu", function(d) { // rightclick a circle
+ $(document).click();
+ d3.event.preventDefault();
+ $scope.contextNode = d;
+ if (!$scope.$$phase) $scope.$apply() // we just changed a scope valiable during an async event
+ var bounds = $(QDR.offsetParent).offset()
+ d3.select('#node_context_menu')
+ .style('left', (mouseX - bounds.left + $(document).scrollLeft()) + "px")
+ .style('top', (mouseY - bounds.top + $(document).scrollTop()) + "px")
+ .style('display', 'block');
+ })
+ .on("click", function (d) { // leftclick a circle
+ var clickPos = d3.mouse(this);
+ clearPopups();
+ if (!d.normals) {
+ // circle was a router or a broker
+ if ( QDRService.isArtemis(d) && Core.ConnectionName === 'Artemis' ) {
+ $location.path('/jmx/attributes?tab=artemis&con=Artemis')
+ }
+ return;
+ }
+ // circle was a client or console
+ d3.event.stopPropagation();
+ startUpdateConnectionsGrid(d, clickPos);
+ })
+
+ var appendContent = function (g) {
+ // show node IDs
+ g.append('svg:text')
+ .attr('x', 0)
+ .attr('y', function (d) {
+ var y = 6;
+ if (QDRService.isArtemis(d))
+ y = 8;
+ else if (QDRService.isQpid(d))
+ y = 9;
+ else if (d.nodeType === 'inter-router')
+ y = 4;
+ return y;})
+ .attr('class', 'id')
+ .classed('console', function(d) { return QDRService.isConsole(d) } )
+ .classed('normal', function(d) { return d.nodeType === 'normal' } )
+ .classed('on-demand', function(d) { return d.nodeType === 'on-demand' } )
+ .classed('artemis', function(d) { return QDRService.isArtemis(d) } )
+ .classed('qpid-cpp', function(d) { return QDRService.isQpid(d) } )
+ .text(function (d) {
+ if (QDRService.isConsole(d)) {
+ return '\uf108'; // icon-desktop for this console
+ }
+ if (QDRService.isArtemis(d)) {
+ return '\ue900'
+ }
+ if (QDRService.isQpid(d)) {
+ return '\ue901';
+ }
+ if (d.nodeType === 'normal')
+ return '\uf109'; // icon-laptop for clients
+ return d.name.length>7 ? d.name.substr(0,6)+'...' : d.name;
+ });
+ }
+ appendContent(g)
+
+ var appendTitle = function (g) {
+ g.append("svg:title").text(function (d) {
+ var x = '';
+ if (d.normals && d.normals.length > 1)
+ x = " x " + d.normals.length;
+ if (QDRService.isConsole(d)) {
+ return 'Dispatch console' + x
+ }
+ if (d.properties.product == 'qpid-cpp') {
+ return 'Broker - qpid-cpp' + x
+ }
+ if ( QDRService.isArtemis(d) ) {
+ return 'Broker - Artemis' + x
+ }
+ return d.nodeType == 'normal' ? 'client' + x : (d.nodeType == 'on-demand' ? 'broker' : 'Router ' + d.name)
+ })
+ }
+ appendTitle(g);
+
+ // remove old nodes
+ circle.exit().remove();
+
+ // add subcircles
+ svg.selectAll('.subcircle').remove();
+ var multiples = svg.selectAll('.multiple')
+ multiples.each( function (d) {
+ d.normals.forEach( function (n, i) {
+ if (i<d.normals.length-1 && i<3) // only show a few shadow circles
+ this.insert('svg:circle', ":first-child")
+ .attr('class', 'subcircle node')
+ .attr('r', 15 - i)
+ .attr('transform', "translate("+ 4 * (i+1) +", 0)")
+ }, d3.select(this))
+ })
+
+ // dynamically create the legend based on which node types are present
+ var legendNodes = [];
+ legendNodes.push(aNode("Router", "", "inter-router", undefined, 0, 0, 0, 0, false, {}))
+
+ if (!svg.selectAll('circle.console').empty()) {
+ legendNodes.push(aNode("Dispatch console", "", "normal", undefined, 1, 0, 0, 0, false, {console_identifier: 'Dispatch console'}))
+ }
+ if (!svg.selectAll('circle.client').empty()) {
+ legendNodes.push(aNode("Client", "", "normal", undefined, 2, 0, 0, 0, false, {}))
+ }
+ if (!svg.selectAll('circle.qpid-cpp').empty()) {
+ legendNodes.push(aNode("Qpid cpp broker", "", "on-demand", undefined, 3, 0, 0, 0, false, {product: 'qpid-cpp'}))
+ }
+ if (!svg.selectAll('circle.artemis').empty()) {
+ legendNodes.push(aNode("Artemis broker", "", "on-demand", undefined, 4, 0, 0, 0, false, {}))
+ }
+ lsvg = lsvg.data(legendNodes, function (d) {
+ return d.id;
+ });
+ var lg = lsvg.enter().append('svg:g')
+ .attr('transform', function (d, i) {
+ // 45px between lines and add 10px space after 1st line
+ return "translate(0, "+(45*i+(i>0?10:0))+")"
+ })
+
+ appendCircle(lg)
+ appendContent(lg)
+ appendTitle(lg)
+ lg.append('svg:text')
+ .attr('x', 35)
+ .attr('y', 6)
+ .attr('class', "label")
+ .text(function (d) {return d.key })
+ lsvg.exit().remove();
+ var svgEl = document.getElementById('svglegend')
+ if (svgEl) {
+ var bb;
+ // firefox can throw an exception on getBBox on an svg element
+ try {
+ bb = svgEl.getBBox();
+ } catch (e) {
+ bb = {y: 0, height: 200, x: 0, width: 200}
+ }
+ svgEl.style.height = (bb.y + bb.height) + 'px';
+ svgEl.style.width = (bb.x + bb.width) + 'px';
+ }
+
+ if (!mousedown_node || !selected_node)
+ return;
+
+ if (!start)
+ return;
+ // set the graph in motion
+ //QDR.log.debug("mousedown_node is " + mousedown_node);
+ force.start();
+ }
+
+ // show the links popup and update it periodically
+ var startUpdateConnectionsGrid = function (d, clickPos) {
+ // called every update tick
+ var extendConnections = function () {
+ $scope.multiData = []
+ var normals = d.normals;
+ // find updated normals for d
+ d3.selectAll('.normal')
+ .each(function(newd) {
+ if (newd.id == d.id && newd.name == d.name) {
+ normals = newd.normals;
+ }
+ });
+ if (normals) {
+ normals.forEach( function (n) {
+ var nodeInfo = QDRService.topology.nodeInfo();
+ var links = nodeInfo[n.key]['.router.link'];
+ var linkTypeIndex = links.attributeNames.indexOf('linkType');
+ var connectionIdIndex = links.attributeNames.indexOf('connectionId');
+ n.linkData = [];
+ links.results.forEach( function (linkArray) {
+ var link = QDRService.flatten(links.attributeNames, linkArray)
+ if (link.linkType === 'endpoint' && link.connectionId === n.connectionId) {
+ var l = {};
+ l.owningAddr = link.owningAddr;
+ l.dir = link.linkDir;
+ if (l.owningAddr && l.owningAddr.length > 2)
+ if (l.owningAddr[0] === 'M')
+ l.owningAddr = l.owningAddr.substr(2)
+ else
+ l.owningAddr = l.owningAddr.substr(1)
+
+ l.deliveryCount = QDRService.pretty(link.deliveryCount);
+ l.uncounts = QDRService.pretty(link.undeliveredCount + link.unsettledCount)
+ l.adminStatus = link.adminStatus;
+ l.operStatus = link.operStatus;
+ l.identity = link.identity
+ l.connectionId = link.connectionId
+ l.nodeId = n.key
+ l.type = link.type
+ l.name = link.name
+
+ //QDR.log.debug("pushing link state for " + l.owningAddr + " status: "+ l.adminStatus)
+ n.linkData.push(l)
+ }
+ })
+ $scope.multiData.push(n)
+ if (n.connectionId == $scope.connectionId)
+ $scope.linkData = n.linkData;
+ })
+ }
+ $scope.$apply();
+
+ d3.select('#multiple_details')
+ .style({
+ height: ((normals.length + 1) * 30) + 40 + "px",
+ 'overflow-y': normals.length > 10 ? 'scroll' : 'hidden'
+ })
+
+ }
+
+ // call extendConnections whenever the background data is updated
+ QDRService.addUpdatedAction("normalsStats", extendConnections)
+ extendConnections();
+ clearPopups();
+ var visibility = 'visible'
+ var left = mouseX + $(document).scrollLeft()
+ var bounds = $("#topology").position()
+ if (d.normals.length === 1) {
+ visibility = 'hidden'
+ left = left - 30;
+ mouseY = mouseY - 20
+ }
+ d3.select('#multiple_details')
+ .style({
+ visibility: visibility,
+ opacity: 1,
+ left: (clickPos[0] + bounds.left) + "px",
+ top: (clickPos[1] + bounds.top) + "px"})
+ if (d.normals.length === 1) {
+ // simulate a click on the connection to popup the link details
+ $scope.multiDetails.showLinksList( {entity: d} )
+ }
+ }
+
+ var stopUpdateConnectionsGrid = function () {
+ QDRService.delUpdatedAction("normalsStats");
+ }
+
+ function nextHop(thisNode, d) {
+ if ((thisNode) && (thisNode != d)) {
+ var target = findNextHopNode(thisNode, d);
+ //QDR.log.debug("highlight link from node ");
+ //console.dump(nodeFor(selected_node.name));
+ //console.dump(target);
+ if (target) {
+ var hlLink = linkFor(nodeFor(thisNode.name), target);
+ //QDR.log.debug("need to highlight");
+ //console.dump(hlLink);
+ if (hlLink)
+ hlLink['highlighted'] = true;
+ else
+ target = null;
+ }
+ setTimeout(nextHop, 1, target, d);
+ }
+ restart();
+ }
+
+
+ function mousedown() {
+ // prevent I-bar on drag
+ //d3.event.preventDefault();
+
+ // because :active only works in WebKit?
+ svg.classed('active', true);
+ }
+
+ QDRService.addUpdatedAction("topology", function() {
+ //QDR.log.debug("Topology controller was notified that the model was updated");
+ if (hasChanged()) {
+ QDR.log.info("svg graph changed")
+ saveChanged();
+ // TODO: update graph nodes instead of rebuilding entire graph
+ d3.select("#SVG_ID").remove();
+ d3.select("#svg_legend svg").remove();
+ animate = true;
+ initForceGraph();
+ //if ($location.path().startsWith("/topology"))
+ // Core.notification('info', "Qpid dispatch router topology changed");
+
+ } else {
+ //QDR.log.debug("no changes")
+ }
+ });
+
+ function hasChanged () {
+ // Don't update the underlying topology diagram if we are adding a new node.
+ // Once adding is completed, the topology will update automatically if it has changed
+ if ($scope.addingNode.step > 0)
+ return false;
+ var nodeInfo = QDRService.topology.nodeInfo();
+ if (Object.keys(nodeInfo).length != Object.keys(savedKeys).length)
+ return true;
+ for (var key in nodeInfo) {
+ // if this node isn't in the saved node list
+ if (!savedKeys.hasOwnProperty(key))
+ return true;
+ // if the number of connections for this node chaanged
+ if (nodeInfo[key]['.connection'].results.length != savedKeys[key]) {
+ /*
+ QDR.log.debug("number of connections changed for " + key);
+ QDR.log.debug("QDRService.topology._nodeInfo[key]['.connection'].results.length");
+ console.dump(QDRService.topology._nodeInfo[key]['.connection'].results.length);
+ QDR.log.debug("savedKeys[key]");
+ console.dump(savedKeys[key]);
+ */
+ return true;
+ }
+ }
+ return false;
+ };
+ function saveChanged () {
+ savedKeys = {};
+ var nodeInfo = QDRService.topology.nodeInfo();
+ // save the number of connections per node
+ for (var key in nodeInfo) {
+ savedKeys[key] = nodeInfo[key]['.connection'].results.length;
+ }
+ //QDR.log.debug("saving current keys");
+ //console.dump(savedKeys);
+ };
+ // we are about to leave the page, save the node positions
+ $rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
+ //QDR.log.debug("locationChangeStart");
+ nodes.forEach( function (d) {
+ localStorage[d.name] = angular.toJson({x: d.x, y: d.y, fixed: d.fixed});
+ });
+ $scope.addingNode.step = 0;
+
+ });
+ // When the DOM element is removed from the page,
+ // AngularJS will trigger the $destroy event on
+ // the scope
+ $scope.$on("$destroy", function( event ) {
+ //QDR.log.debug("scope on destroy");
+ QDRService.stopUpdating();
+ QDRService.delUpdatedAction("topology");
+ d3.select("#SVG_ID").remove();
+ window.removeEventListener('resize', resize);
+ });
+
+ initForceGraph();
+ saveChanged();
+ QDRService.startUpdating();
+
+ function doAddDialog(NewRouterName) {
+ var d = $modal.dialog({
+ dialogClass: "modal dlg-large",
+ backdrop: true,
+ keyboard: true,
+ backdropClick: true,
+ controller: 'QDR.NodeDialogController',
+ templateUrl: 'node-config-template.html',
+ resolve: {
+ newname: function () {
+ return NewRouterName;
+ }
+ }
+ });
+ d.open().then(function (result) {
+ if (result)
+ doDownloadDialog(result);
+ });
+ };
+
+ function doDownloadDialog(result) {
+ d = modal.dialog({
+ backdrop: true,
+ keyboard: true,
+ backdropClick: true,
+ controller: 'QDR.DownloadDialogController',
+ templateUrl: 'download-dialog-template.html',
+ resolve: {
+ results: function () {
+ return result;
+ }
+ }
+ });
+ d.open().then(function (result) {
+ //QDR.log.debug("download dialog done")
+ })
+ if (!$scope.$$phase) $scope.$apply()
+ };
+ };
+
+ return QDR;
+}(QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.download-controller.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.download-controller.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.download-controller.js
new file mode 100644
index 0000000..2eb812f
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.download-controller.js
@@ -0,0 +1,150 @@
+/*
+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) {
+ 'use strict';
+
+ angular
+ .module('horizon.dashboard.dispatch.topology')
+ .controller('horizon.dashboard.dispatch.topology.TopologyDownloadController', TopologyDownloadController);
+
+ TopologyDownloadController.$inject = [
+ '$scope',
+ 'horizon.dashboard.dispatch.comService'
+ ]
+
+ function TopologyDownloadController($scope, QDRService, dialog, results) {
+ var result = results.entities;
+ var annotations = results.annotations;
+ var annotationKeys = Object.keys(annotations);
+ var annotationSections = {};
+
+ // use the router's name as the file name if present
+ $scope.newRouterName = 'router';
+ result.forEach( function (e) {
+ if (e.actualName == 'router') {
+ e.attributes.forEach( function (a) {
+ if (a.name == 'name') {
+ $scope.newRouterName = a.value;
+ }
+ })
+ }
+ })
+ $scope.newRouterName = $scope.newRouterName + ".conf";
+
+ var template = $templateCache.get('config-file-header.html');
+ $scope.verbose = true;
+ $scope.$watch('verbose', function (newVal) {
+ if (newVal !== undefined) {
+ // recreate output using current verbose setting
+ getOutput();
+ }
+ })
+
+ var getOutput = function () {
+ $scope.output = template + '\n';
+ $scope.parts = [];
+ var commentChar = '#'
+ result.forEach(function (entity) {
+ // don't output a section for annotations, they get flattened into the entities
+ var section = "";
+ if (entity.icon) {
+ section += "##\n## Add to " + entity.link.__data__.source.name + "'s configuration file\n##\n";
+ }
+ section += "##\n## " + QDRService.humanify(entity.actualName) + " - " + entity.description + "\n##\n";
+ section += entity.actualName + " {\n";
+ entity.attributes.forEach(function (attribute) {
+ if (attribute.input == 'select')
+ attribute.value = attribute.selected;
+
+ // treat values with all spaces and empty strings as undefined
+ attribute.value = String(attribute.value).trim();
+ if (attribute.value === 'undefined' || attribute.value === '')
+ attribute.value = undefined;
+
+ if ($scope.verbose) {
+ commentChar = attribute.required || attribute.value != attribute['default'] ? ' ' : '#';
+ if (!attribute.value) {
+ commentChar = '#';
+ attribute.value = '';
+ }
+ section += commentChar + " "
+ + attribute.name + ":" + Array(Math.max(20 - attribute.name.length, 1)).join(" ")
+ + attribute.value
+ + Array(Math.max(20 - ((attribute.value)+"").length, 1)).join(" ")
+ + '# ' + attribute.description
+ + "\n";
+ } else {
+ if (attribute.value) {
+ if (attribute.value != attribute['default'] || attribute.required)
+ section += " "
+ + attribute.name + ":" + Array(20 - attribute.name.length).join(" ")
+ + attribute.value + "\n";
+
+ }
+ }
+ })
+ section += "}\n\n";
+ // if entity.icon is true, this is a connector intended for another router
+ if (entity.icon)
+ $scope.parts.push({output: section,
+ link: entity.link,
+ name: entity.link.__data__.source.name,
+ references: entity.references});
+ else
+ $scope.output += section;
+
+ // if this section is actually an annotation
+ if (annotationKeys.indexOf(entity.actualName) > -1) {
+ annotationSections[entity.actualName] = section;
+ }
+ })
+ // go back and add annotation sections to the parts
+ $scope.parts.forEach (function (part) {
+ for (var section in annotationSections) {
+ if (part.references.indexOf(section) > -1) {
+ part.output += annotationSections[section];
+ }
+ }
+ })
+ QDR.log.debug($scope.output);
+ }
+
+ // handle the download button click
+ $scope.download = function () {
+ var output = $scope.output + "\n\n"
+ var blob = new Blob([output], { type: 'text/plain;charset=utf-16' });
+ saveAs(blob, $scope.newRouterName);
+ }
+
+ $scope.downloadPart = function (part) {
+ var linkName = part.link.__data__.source.name + 'additional.conf';
+ var blob = new Blob([part.output], { type: 'text/plain;charset=utf-16' });
+ saveAs(blob, linkName);
+ }
+
+ $scope.done = function () {
+ dialog.close();
+ }
+ };
+
+ return QDR;
+}(QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.form-controller.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.form-controller.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.form-controller.js
new file mode 100644
index 0000000..19af366
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.form-controller.js
@@ -0,0 +1,73 @@
+/*
+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) {
+ 'use strict';
+
+ angular
+ .module('horizon.dashboard.dispatch.topology')
+ .controller('horizon.dashboard.dispatch.topology.TopologyFormController', TopologyFormController);
+
+ TopologyFormController.$inject = [
+ '$scope',
+ 'horizon.dashboard.dispatch.comService'
+ ];
+
+ function TopologyFormController($scope, QDRService) {
+ var fctrl = this;
+ $scope.attributes = []
+ var nameTemplate = '<div title="{{row.entity.description}}" class="ngCellText"><span>{{row.entity.attributeName}}</span></div>';
+ var valueTemplate = '<div title="{{row.entity.attributeValue}}" class="ngCellText"><span>{{row.entity.attributeValue}}</span></div>';
+ $scope.topoGridOptions = {
+ data: 'attributes',
+ enableColumnResize: true,
+ multiSelect: false,
+ columnDefs: [
+ {
+ field: 'attributeName',
+// cellTemplate: nameTemplate,
+ displayName: 'Attribute'
+ },
+ {
+ field: 'attributeValue',
+// cellTemplate: valueTemplate,
+ displayName: 'Value'
+ }
+ ]
+ };
+ $scope.form = ''
+ $scope.$on('showEntityForm', function (event, args) {
+ var attributes = args.attributes;
+ var entityTypes = QDRService.schema.entityTypes[args.entity].attributes;
+ attributes.forEach( function (attr) {
+ if (entityTypes[attr.attributeName] && entityTypes[attr.attributeName].description)
+ attr.description = entityTypes[attr.attributeName].description
+ })
+ $scope.attributes = attributes;
+ $scope.form = args.entity;
+ })
+ $scope.$on('showAddForm', function (event) {
+ $scope.form = 'add';
+ })
+ }
+
+ return QDR;
+}(QDR || {}));
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.module.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.module.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.module.js
new file mode 100644
index 0000000..e5a5242
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/topology.module.js
@@ -0,0 +1,112 @@
+/*
+ * Licensed 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.
+ */
+(function () {
+ 'use strict';
+
+ angular
+ .module('horizon.dashboard.dispatch.topology', [])
+ .config(config)
+ .run(addTemplates)
+
+ config.$inject = [
+ '$provide',
+ '$windowProvider'
+ ];
+
+ addTemplates.$inject = [
+ '$templateCache',
+ ];
+
+ /**
+ * @name config
+ * @param {Object} $provide
+ * @param {Object} $windowProvider
+ * @description Base path for the overview code
+ * @returns {undefined} No return value
+ */
+ function config($provide, $windowProvider) {
+ var path = $windowProvider.$get().STATIC_URL + 'dashboard/dispatch/topology/';
+ $provide.constant('horizon.dashboard.dispatch.topology.basePath', path);
+ }
+
+ function addTemplates($templateCache) {
+ $templateCache.put("dispatch/topology.html",
+ "<div class=\"qdrTopology\" ng-controller=\"horizon.dashboard.dispatch.topology.TopologyController as ctrl\">" +
+ " <div>" +
+ "<!--" +
+ " <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=\"geology\" ng-show=\"mode == 'Globe'\"><!-- d3 globe 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 id=\"svg_legend\"></div>" +
+ " <div id=\"multiple_details\">" +
+ " <h4 class=\"grid-title\">Connections</h4>" +
+ " <div class=\"grid\" ui-grid=\"multiDetails\" ui-grid-selection></div>" +
+ " </div>" +
+ " <div id=\"link_details\">" +
+ " <h4 class=\"grid-title\">Links</h4>" +
+ " <div class=\"grid\" ui-grid=\"linkDetails\" ui-grid-selection></div>" +
+ " </div>" +
+ " </div>" +
+ " <div ng-controller=\"horizon.dashboard.dispatch.topology.TopologyFormController as fctrl\">" +
+ " <div id=\"topologyForm\" ng-class=\"{selected : isSelected()}\">" +
+ " <!-- <div ng-repeat=\"form in forms\" ng-show=\"isVisible(form)\" ng-class='{selected : isSelected(form)}'> -->" +
+ " <div ng-if=\"form == 'router'\">" +
+ " <h3>Router Info</h3>" +
+ " <div class=\"grid\" ui-grid=\"topoGridOptions\"></div>" +
+ " </div>" +
+ " <div ng-if=\"form == 'connection'\">" +
+ " <h3>Connection Info</h3>" +
+ " <div class=\"grid\" ui-grid=\"topoGridOptions\"></div>" +
+ " </div>" +
+ " <div id=\"addNodeForm\" ng-show=\"form == 'add'\">" +
+ " <h3>Add a new router</h3>" +
+ " <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>"
+ );
+ }
+})();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[10/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
DISPATCH-531 Initial version of openstack horizon plugin
Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/0c58c381
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/0c58c381
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/0c58c381
Branch: refs/heads/master
Commit: 0c58c3814866cbe60f13ff69ad73c74a4d8692aa
Parents: 9790303
Author: Ernest Allen <ea...@redhat.com>
Authored: Wed Oct 19 08:48:34 2016 -0400
Committer: Ernest Allen <ea...@redhat.com>
Committed: Wed Oct 19 08:48:34 2016 -0400
----------------------------------------------------------------------
console/dispatch-dashboard/MANIFEST.in | 3 +
console/dispatch-dashboard/README.rst | 41 +
console/dispatch-dashboard/dispatch/__init__.py | 0
.../dispatch-dashboard/dispatch/dashboard.py | 23 +
.../dispatch/overv/__init__.py | 0
.../dispatch-dashboard/dispatch/overv/panel.py | 20 +
.../dispatch/overv/templates/overv/index.html | 13 +
.../dispatch-dashboard/dispatch/overv/tests.py | 19 +
.../dispatch-dashboard/dispatch/overv/urls.py | 20 +
.../dispatch-dashboard/dispatch/overv/views.py | 22 +
.../static/dashboard/dispatch/connect.json | 2 +
.../dashboard/dispatch/dispatch.comService.js | 935 +
.../dashboard/dispatch/dispatch.module.js | 256 +
.../static/dashboard/dispatch/dispatch.scss | 2135 ++
.../dashboard/dispatch/jquery.dynatree.min.js | 4 +
.../static/dashboard/dispatch/lib/d3.v3.min.js | 5 +
.../static/dashboard/dispatch/lib/rhea-min.js | 4 +
.../static/dashboard/dispatch/lib/slider.js | 233 +
.../dashboard/dispatch/lib/tooltipsy.min.js | 20 +
.../static/dashboard/dispatch/lib/ui-grid.js | 28540 +++++++++++++++++
.../dispatch/overv/overview.controller.js | 1428 +
.../dashboard/dispatch/overv/overview.module.js | 178 +
.../dashboard/dispatch/qdrChartService.js | 1109 +
.../dispatch/topology/config-file-header.html | 17 +
.../topology/download-dialog-template.html | 23 +
.../dispatch/topology/node-config-template.html | 51 +
.../dispatch/topology/topology.controller.js | 1703 +
.../topology/topology.download-controller.js | 150 +
.../topology/topology.form-controller.js | 73 +
.../dispatch/topology/topology.module.js | 112 +
.../topology/topology.node-controller.js | 294 +
.../dispatch/templates/dispatch/base.html | 10 +
.../dispatch/topology/__init__.py | 0
.../dispatch/topology/panel.py | 20 +
.../topology/templates/topology/index.html | 35 +
.../dispatch/topology/tests.py | 19 +
.../dispatch/topology/urls.py | 20 +
.../dispatch/topology/views.py | 22 +
.../enabled/_4000_dispatch.py | 33 +
.../enabled/_4030_dispatch_overv_panel.py | 9 +
.../enabled/_4050_dispatch_topology_panel.py | 9 +
console/dispatch-dashboard/setup.py | 42 +
42 files changed, 37652 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/MANIFEST.in
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/MANIFEST.in b/console/dispatch-dashboard/MANIFEST.in
new file mode 100644
index 0000000..1d1b591
--- /dev/null
+++ b/console/dispatch-dashboard/MANIFEST.in
@@ -0,0 +1,3 @@
+include setup.py
+
+recursive-include dispatch *
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/README.rst
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/README.rst b/console/dispatch-dashboard/README.rst
new file mode 100644
index 0000000..ac3b17c
--- /dev/null
+++ b/console/dispatch-dashboard/README.rst
@@ -0,0 +1,41 @@
+=========
+dispatch_dashboard
+=========
+
+Qpid Dispatch Router Horizon plugin
+
+Manual Installation
+-------------------
+
+Copy the contents of this directoty to /opt/stack/dispatch_plugin and setup the plugin::
+
+ cd /opt/stack/dispatch_plugin/
+ python setup.py sdist
+
+If needed, create a virtual environment and install Horizon dependencies::
+
+ cd /opt/stack/horizon
+ python tools/install_venv.py
+
+If needed, set up your ``local_settings.py`` file::
+
+ cp openstack_dashboard/local/local_settings.py.example openstack_dashboard/local/local_settings.py
+
+
+Install the dispatch dashboard in your horizon virtual environment::
+
+ ./tools/with_venv.sh pip install ../dispatch-plugin/dist/dispatch-0.0.1.tar.gz
+
+And enable it in Horizon::
+
+ cp ../dispatch-plugin/enabled/_4*.py openstack_dashboard/local/enabled
+
+If needed, compress the files::
+
+ ./tools/with-venv.sh python manage.py compress
+
+Run a server in the virtual environment::
+
+ ./tools/with-venv.sh python manage.py runserver 0.0.0.0:8080
+
+The horizon dashboard will be available in your browser at http://localhost:8080/
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/__init__.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/__init__.py b/console/dispatch-dashboard/dispatch/__init__.py
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/dashboard.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/dashboard.py b/console/dispatch-dashboard/dispatch/dashboard.py
new file mode 100644
index 0000000..9fad953
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/dashboard.py
@@ -0,0 +1,23 @@
+# Licensed 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.
+
+from django.utils.translation import ugettext_lazy as _
+
+import horizon
+
+
+class Dispatch(horizon.Dashboard):
+ name = _("Qpid Dispatch")
+ slug = "dispatch"
+ default_panel = 'overv' # slug of the dashboard's default panel.
+
+horizon.register(Dispatch)
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/overv/__init__.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/overv/__init__.py b/console/dispatch-dashboard/dispatch/overv/__init__.py
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/overv/panel.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/overv/panel.py b/console/dispatch-dashboard/dispatch/overv/panel.py
new file mode 100644
index 0000000..315c7e0
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/overv/panel.py
@@ -0,0 +1,20 @@
+# Licensed 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.
+
+from django.utils.translation import ugettext_lazy as _
+
+import horizon
+
+
+class Overv(horizon.Panel):
+ name = _("Overview")
+ slug = "overv"
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/overv/templates/overv/index.html
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/overv/templates/overv/index.html b/console/dispatch-dashboard/dispatch/overv/templates/overv/index.html
new file mode 100644
index 0000000..afe47bd
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/overv/templates/overv/index.html
@@ -0,0 +1,13 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Overv" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Overview") %}
+{% endblock page_header %}
+
+{% block main %}
+ <ng-include src="'dispatch/overview.html'"></ng-include>
+{% endblock %}
+
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/overv/tests.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/overv/tests.py b/console/dispatch-dashboard/dispatch/overv/tests.py
new file mode 100644
index 0000000..47816a3
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/overv/tests.py
@@ -0,0 +1,19 @@
+# Licensed 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.
+
+from horizon.test import helpers as test
+
+
+class OvervTests(test.TestCase):
+ # Unit tests for overv.
+ def test_me(self):
+ self.assertTrue(1 + 1 == 2)
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/overv/urls.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/overv/urls.py b/console/dispatch-dashboard/dispatch/overv/urls.py
new file mode 100644
index 0000000..6debf00
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/overv/urls.py
@@ -0,0 +1,20 @@
+# Licensed 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.
+
+from django.conf.urls import url
+
+from dispatch.overv import views
+
+
+urlpatterns = [
+ url(r'^$', views.IndexView.as_view(), name='index'),
+]
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/overv/views.py
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/overv/views.py b/console/dispatch-dashboard/dispatch/overv/views.py
new file mode 100644
index 0000000..235a0d5
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/overv/views.py
@@ -0,0 +1,22 @@
+# Licensed 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.
+
+from horizon import views
+
+
+class IndexView(views.APIView):
+ # A very simple class-based view...
+ template_name = 'dispatch/overv/index.html'
+
+ def get_data(self, request, context, *args, **kwargs):
+ # Add data to the context here...
+ return context
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/connect.json
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/connect.json b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/connect.json
new file mode 100644
index 0000000..2be876d
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/connect.json
@@ -0,0 +1,2 @@
+)]}',
+{"address": "0.0.0.0", "port": 5673}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.comService.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.comService.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.comService.js
new file mode 100644
index 0000000..ace792a
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.comService.js
@@ -0,0 +1,935 @@
+/*
+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.
+*/
+
+(function() {
+ console.dump = function(object) {
+ if (window.JSON && window.JSON.stringify)
+ QDR.log.info(JSON.stringify(object,undefined,2));
+ else
+ console.log(object);
+ };
+})();
+
+var QDR = (function(QDR) {
+ 'use strict';
+
+ // The QDR service handles the connection to
+ // the server in the background
+ angular
+ .module('horizon.dashboard.dispatch')
+ .factory('horizon.dashboard.dispatch.comService', QDRService);
+
+ QDRService.$inject = [
+ '$rootScope',
+ '$http',
+ '$timeout',
+ '$location',
+ 'horizon.dashboard.dispatch.basePath',
+ ];
+
+ function QDRService($rootScope, $http, $timeout, $location, basePath) {
+ var self = {
+
+ rhea: require("rhea"),
+
+ timeout: 10,
+ connectActions: [],
+ disconnectActions: [],
+ updatedActions: {},
+ stop: undefined, // update interval handle
+
+ addConnectAction: function(action) {
+ if (angular.isFunction(action)) {
+ self.connectActions.push(action);
+ }
+ },
+ addDisconnectAction: function(action) {
+ if (angular.isFunction(action)) {
+ self.disconnectActions.push(action);
+ }
+ },
+ addUpdatedAction: function(key, action) {
+ if (angular.isFunction(action)) {
+ self.updatedActions[key] = action;
+ }
+ },
+ delUpdatedAction: function(key) {
+ if (key in self.updatedActions)
+ delete self.updatedActions[key];
+ },
+
+ executeConnectActions: function() {
+ self.connectActions.forEach(function(action) {
+ //QDR.log.debug("executing connect action " + action);
+ try {
+ action.apply();
+ } catch (e) {
+ // in case the page that registered the handler has been unloaded
+ }
+ });
+ self.connectActions = [];
+
+ },
+ executeDisconnectActions: function() {
+ self.disconnectActions.forEach(function(action) {
+ try {
+ action.apply();
+ } catch (e) {
+ // in case the page that registered the handler has been unloaded
+ }
+ });
+ self.disconnectActions = [];
+ },
+ executeUpdatedActions: function() {
+ for (var action in self.updatedActions) {
+ try {
+ self.updatedActions[action].apply();
+ } catch (e) {
+ delete self.updatedActions[action]
+ }
+ }
+ },
+ redirectWhenConnected: function (org) {
+ //$location.path(basePath + "/connect")
+ //$location.search('org', org);
+ window.location.replace("/connect/");
+ },
+
+ notifyTopologyDone: function() {
+ //QDR.log.debug("got Toplogy done notice");
+
+ if (!angular.isDefined(self.schema))
+ return;
+ else if (self.topology._gettingTopo)
+ return;
+ if (!self.gotTopology) {
+ QDR.log.debug("topology was just initialized");
+ self.gotTopology = true;
+ self.executeConnectActions();
+ $rootScope.$apply();
+ } else {
+ //QDR.log.debug("topology model was just updated");
+ self.executeUpdatedActions();
+ }
+
+ },
+ /**
+ * @property options
+ * Holds a reference to the connection options when
+ * a connection is started
+ */
+ options: undefined,
+
+ /*
+ * @property message
+ * The proton message that is used to send commands
+ * and receive responses
+ */
+ sender: undefined,
+ receiver: undefined,
+ sendable: false,
+
+ schema: undefined,
+
+ toAddress: undefined,
+ connected: false,
+ gotTopology: false,
+ errorText: undefined,
+ connectionError: undefined,
+
+ isConnected: function() {
+ return self.connected;
+ },
+
+ correlator: {
+ _objects: {},
+ _corremationID: 0,
+
+ corr: function () {
+ var id = ++this._corremationID + "";
+ this._objects[id] = {resolver: null}
+ return id;
+ },
+ request: function() {
+ //QDR.log.debug("correlator:request");
+ return this;
+ },
+ then: function(id, resolver, error) {
+ //QDR.log.debug("registered then resolver for correlationID: " + id);
+ if (error) {
+ delete this._objects[id];
+ return;
+ }
+ this._objects[id].resolver = resolver;
+ },
+ // called by receiver's on('message') handler when a response arrives
+ resolve: function(context) {
+ var correlationID = context.message.properties.correlation_id;
+ this._objects[correlationID].resolver(context.message.body, context);
+ delete this._objects[correlationID];
+ }
+ },
+
+ onSubscription: function() {
+ self.getSchema();
+ },
+
+ startUpdating: function () {
+ self.stopUpdating();
+ QDR.log.info("startUpdating called")
+ self.topology.get();
+ self.stop = setInterval(function() {
+ self.topology.get();
+ }, 2000);
+ },
+ stopUpdating: function () {
+ if (angular.isDefined(self.stop)) {
+ QDR.log.info("stopUpdating called")
+ clearInterval(self.stop);
+ self.stop = undefined;
+ }
+ },
+
+ initProton: function() {
+ //self.loadConnectOptions()
+ },
+ cleanUp: function() {
+ },
+ error: function(line) {
+ if (line.num) {
+ QDR.log.debug("error - num: ", line.num, " message: ", line.message);
+ } else {
+ QDR.log.debug("error - message: ", line.message);
+ }
+ },
+ disconnected: function(line) {
+ QDR.log.debug("Disconnected from QDR server");
+ self.executeDisconnectActions();
+ },
+
+ nameFromId: function (id) {
+ return id.split('/')[3];
+ },
+
+ humanify: function (s) {
+ if (!s || s.length === 0)
+ return s;
+ var t = s.charAt(0).toUpperCase() + s.substr(1).replace(/[A-Z]/g, ' $&');
+ return t.replace(".", " ");
+ },
+ pretty: function(v) {
+ var formatComma = d3.format(",");
+ if (!isNaN(parseFloat(v)) && isFinite(v))
+ return formatComma(v);
+ return v;
+ },
+
+ nodeNameList: function() {
+ var nl = [];
+ // if we are in the middel of updating the topology
+ // then use the last known node info
+ var ni = self.topology._nodeInfo;
+ if (self.topology._gettingTopo)
+ ni = self.topology._lastNodeInfo;
+ for (var id in ni) {
+ nl.push(self.nameFromId(id));
+ }
+ return nl.sort();
+ },
+
+ nodeIdList: function() {
+ var nl = [];
+ // if we are in the middel of updating the topology
+ // then use the last known node info
+ var ni = self.topology._nodeInfo;
+ if (self.topology._gettingTopo)
+ ni = self.topology._lastNodeInfo;
+ for (var id in ni) {
+ nl.push(id);
+ }
+ return nl.sort();
+ },
+
+ nodeList: function () {
+ var nl = [];
+ var ni = self.topology._nodeInfo;
+ if (self.topology._gettingTopo)
+ ni = self.topology._lastNodeInfo;
+ for (var id in ni) {
+ nl.push({name: self.nameFromId(id), id: id});
+ }
+ return nl;
+ },
+
+ // given an attribute name array, find the value at the same index in the values array
+ valFor: function (aAr, vAr, key) {
+ var idx = aAr.indexOf(key);
+ if ((idx > -1) && (idx < vAr.length)) {
+ return vAr[idx];
+ }
+ return null;
+ },
+
+ isArtemis: function (d) {
+ return d.nodeType ==='on-demand' && !d.properties.product;
+ },
+
+ isQpid: function (d) {
+ return d.nodeType ==='on-demand' && (d.properties && d.properties.product === 'qpid-cpp');
+ },
+
+ isAConsole: function (properties, connectionId, nodeType, key) {
+ return self.isConsole({properties: properties, connectionId: connectionId, nodeType: nodeType, key: key})
+ },
+ isConsole: function (d) {
+ // use connection properties if available
+ if (d && d['properties'] && d['properties']['console_identifier'] == 'Dispatch console')
+ return true;
+ return false;
+ },
+
+ flatten: function (attributes, result) {
+ var flat = {}
+ attributes.forEach( function (attr, i) {
+ if (result && result.length > i)
+ flat[attr] = result[i]
+ })
+ return flat;
+ },
+ isConsoleLink: function (link) {
+ // find the connection for this link
+ var conns = self.topology.nodeInfo()[link.nodeId]['.connection']
+ var connIndex = conns.attributeNames.indexOf("identity")
+ var linkCons = conns.results.filter ( function (conn) {
+ return conn[connIndex] === link.connectionId;
+ })
+ var conn = self.flatten(conns.attributeNames, linkCons[0]);
+
+ return self.isConsole(conn)
+ },
+
+ quiesceLink: function (nodeId, name) {
+ function gotMethodResponse (nodeName, entity, response, context) {
+ var statusCode = context.message.application_properties.statusCode;
+ if (statusCode < 200 || statusCode >= 300) {
+ Core.notification('error', context.message.application_properties.statusDescription);
+ }
+ }
+ var attributes = {adminStatus: 'disabled', name: name};
+ self.sendMethod(nodeId, "router.link", attributes, "UPDATE", undefined, gotMethodResponse)
+ },
+
+ connectionOptions: {address: '0.0.0.0', port: 5673},
+ loadConnectOptions: function (callback) {
+ $http.get(basePath + 'connect.json').
+ success(function(data, status, headers, config) {
+ //QDR.log.debug("got connect info from file")
+ //console.dump(data)
+ self.connectionOptions = data;
+ if (callback)
+ callback()
+ }).
+ error(function(data, status, headers, config) {
+ //QDR.log.debug("did not get connect info from file")
+ //console.dump(status)
+ if (callback)
+ callback()
+ });
+ },
+
+ addr_text: function (addr) {
+ if (!addr)
+ return "-"
+ if (addr[0] == 'M')
+ return addr.substring(2)
+ else
+ return addr.substring(1)
+ },
+ addr_class: function (addr) {
+ if (!addr) return "-"
+ if (addr[0] == 'M') return "mobile"
+ if (addr[0] == 'R') return "router"
+ if (addr[0] == 'A') return "area"
+ if (addr[0] == 'L') return "local"
+ if (addr[0] == 'C') return "link-incoming"
+ if (addr[0] == 'D') return "link-outgoing"
+ if (addr[0] == 'T') return "topo"
+ return "unknown: " + addr[0]
+ },
+ identity_clean: function (identity) {
+ if (!identity)
+ return "-"
+ var pos = identity.indexOf('/')
+ if (pos >= 0)
+ return identity.substring(pos + 1)
+ return identity
+ },
+
+ /*
+ * send the management messages that build up the topology
+ *
+ *
+ */
+ topology: {
+ _gettingTopo: false,
+ _nodeInfo: {},
+ _lastNodeInfo: {},
+ _expected: {},
+ _timerHandle: null,
+
+ nodeInfo: function () {
+ return this._gettingTopo ? this._lastNodeInfo : this._nodeInfo;
+ },
+
+ get: function () {
+ if (this._gettingTopo)
+ return;
+ if (!self.connected) {
+ QDR.log.debug("topology get failed because !self.connected")
+ return;
+ }
+ this._lastNodeInfo = angular.copy(this._nodeInfo);
+ this._gettingTopo = true;
+
+ self.errorText = undefined;
+ this.cleanUp(this._nodeInfo);
+ this._nodeInfo = {};
+ this._expected = {};
+
+ // get the list of nodes to query.
+ // once this completes, we will get the info for each node returned
+ self.getRemoteNodeInfo( function (response, context) {
+ //QDR.log.debug("got remote node list of ");
+ //console.dump(response);
+ if( Object.prototype.toString.call( response ) === '[object Array]' ) {
+ if (response.length === 0) {
+ // there is only one router, get its node id from the reeciiver
+ //"amqp:/_topo/0/Router.A/temp.aSO3+WGaoNUgGVx"
+ var address = context.receiver.remote.attach.source.address;
+ var addrParts = address.split('/')
+ addrParts.splice(addrParts.length-1, 1, '$management')
+ response = [addrParts.join('/')]
+ }
+ // we expect a response for each of these nodes
+ self.topology.wait(self.timeout);
+ for (var i=0; i<response.length; ++i) {
+ self.makeMgmtCalls(response[i]);
+ }
+ };
+ });
+ },
+
+ cleanUp: function (obj) {
+ //if (obj)
+ // delete obj;
+ },
+ wait: function (timeout) {
+ this.timerHandle = setTimeout(this.timedOut, timeout * 1000);
+ },
+ timedOut: function () {
+ // a node dropped out. this happens when the get-mgmt-nodex
+ // results contains more nodes than actually respond within
+ // the timeout. However, if the responses we get don't contain
+ // the missing node, assume we are done.
+ QDR.log.info("timed out waiting for management responses");
+ // note: can't use 'this' in a timeout handler
+ self.topology.miniDump("state at timeout");
+ // check if _nodeInfo is consistent
+ if (self.topology.isConsistent()) {
+ //TODO: notify controllers which node was dropped
+ // so they can keep an event log
+ self.topology.ondone();
+ return;
+ }
+ self.topology.onerror(Error("Timed out waiting for management responses"));
+ },
+ isConsistent: function () {
+ // see if the responses we have so far reference any nodes
+ // for which we don't have a response
+ var gotKeys = {};
+ for (var id in this._nodeInfo) {
+ var onode = this._nodeInfo[id];
+ var conn = onode['.connection'];
+ // get list of node names in the connection data
+ if (conn) {
+ var containerIndex = conn.attributeNames.indexOf('container');
+ var connectionResults = conn.results;
+ if (containerIndex >= 0)
+ for (var j=0; j < connectionResults.length; ++j) {
+ // inter-router connection to a valid dispatch connection name
+ gotKeys[connectionResults[j][containerIndex]] = ""; // just add the key
+ }
+ }
+ }
+ // gotKeys now contains all the container names that we have received
+ // Are any of the keys that are still expected in the gotKeys list?
+ var keys = Object.keys(gotKeys);
+ for (var id in this._expected) {
+ var key = self.nameFromId(id);
+ if (key in keys)
+ return false;
+ }
+ return true;
+ },
+
+ addNodeInfo: function (id, entity, values) {
+ // save the results in the nodeInfo object
+ if (id) {
+ if (!(id in self.topology._nodeInfo)) {
+ self.topology._nodeInfo[id] = {};
+ }
+ self.topology._nodeInfo[id][entity] = values;
+ }
+
+ // remove the id / entity from _expected
+ if (id in self.topology._expected) {
+ var entities = self.topology._expected[id];
+ var idx = entities.indexOf(entity);
+ if (idx > -1) {
+ entities.splice(idx, 1);
+ if (entities.length == 0)
+ delete self.topology._expected[id];
+ }
+ }
+ // see if the expected obj is empty
+ if (Object.getOwnPropertyNames(self.topology._expected).length == 0)
+ self.topology.ondone();
+ self.topology.cleanUp(values);
+ },
+ expect: function (id, key) {
+ if (!key || !id)
+ return;
+ if (!(id in this._expected))
+ this._expected[id] = [];
+ if (this._expected[id].indexOf(key) == -1)
+ this._expected[id].push(key);
+ },
+ ondone: function () {
+ clearTimeout(this.timerHandle);
+ this._gettingTopo = false;
+ //this.miniDump();
+ //this.dump();
+ self.notifyTopologyDone();
+ },
+ dump: function (prefix) {
+ if (prefix)
+ QDR.log.info(prefix);
+ QDR.log.info("---");
+ for (var key in this._nodeInfo) {
+ QDR.log.info(key);
+ console.dump(this._nodeInfo[key]);
+ QDR.log.info("---");
+ }
+ QDR.log.debug("was still expecting:");
+ console.dump(this._expected);
+ },
+ miniDump: function (prefix) {
+ if (prefix)
+ QDR.log.info(prefix);
+ QDR.log.info("---");
+ console.dump(Object.keys(this._nodeInfo));
+ QDR.log.info("---");
+ },
+ onerror: function (err) {
+ this._gettingTopo = false;
+ QDR.log.debug("Err:" + err);
+ self.executeDisconnectActions();
+
+ }
+
+ },
+
+ getRemoteNodeInfo: function (callback) {
+ //QDR.log.debug("getRemoteNodeInfo called");
+ var ret;
+ // first get the list of remote node names
+ self.correlator.request(
+ ret = self.sendMgmtQuery('GET-MGMT-NODES')
+ ).then(ret.id, function(response, context) {
+ callback(response, context);
+ self.topology.cleanUp(response);
+ }, ret.error);
+ },
+
+ makeMgmtCalls: function (id) {
+ var keys = [".router", ".connection", ".container", ".router.node", ".listener", ".router.link"];
+ $.each(keys, function (i, key) {
+ self.topology.expect(id, key);
+ self.getNodeInfo(id, key, [], self.topology.addNodeInfo);
+ });
+ },
+
+ getNodeInfo: function (nodeName, entity, attrs, callback) {
+ //QDR.log.debug("getNodeInfo called with nodeName: " + nodeName + " and entity " + entity);
+ var ret;
+ self.correlator.request(
+ ret = self.sendQuery(nodeName, entity, attrs)
+ ).then(ret.id, function(response) {
+ callback(nodeName, entity, response);
+ //self.topology.addNodeInfo(nodeName, entity, response);
+ //self.topology.cleanUp(response);
+ }, ret.error);
+ },
+
+ getMultipleNodeInfo: function (nodeNames, entity, attrs, callback, selectedNodeId, aggregate) {
+ if (!angular.isDefined(aggregate))
+ aggregate = true;
+ var responses = {};
+ var gotNodesResult = function (nodeName, dotentity, response) {
+ responses[nodeName] = response;
+ if (Object.keys(responses).length == nodeNames.length) {
+ if (aggregate)
+ self.aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback);
+ else {
+ callback(nodeNames, entity, responses)
+ }
+ }
+ }
+
+ nodeNames.forEach( function (id) {
+ self.getNodeInfo(id, '.'+entity, attrs, gotNodesResult);
+ })
+ //TODO: implement a timeout in case not all requests complete
+ },
+
+ aggregateNodeInfo: function (nodeNames, entity, selectedNodeId, responses, callback) {
+ //QDR.log.debug("got all results for " + entity);
+ // aggregate the responses
+ var newResponse = {};
+ var thisNode = responses[selectedNodeId];
+ newResponse['attributeNames'] = thisNode.attributeNames;
+ newResponse['results'] = thisNode.results;
+ newResponse['aggregates'] = [];
+ for (var i=0; i<thisNode.results.length; ++i) {
+ var result = thisNode.results[i];
+ var vals = [];
+ result.forEach( function (val) {
+ vals.push({sum: val, detail: []})
+ })
+ newResponse.aggregates.push(vals);
+ }
+ var nameIndex = thisNode.attributeNames.indexOf("name");
+ var ent = self.schema.entityTypes[entity];
+ var ids = Object.keys(responses);
+ ids.sort();
+ ids.forEach( function (id) {
+ var response = responses[id];
+ var results = response.results;
+ results.forEach( function (result) {
+ // find the matching result in the aggregates
+ var found = newResponse.aggregates.some( function (aggregate, j) {
+ if (aggregate[nameIndex].sum === result[nameIndex]) {
+ // result and aggregate are now the same record, add the graphable values
+ newResponse.attributeNames.forEach( function (key, i) {
+ if (ent.attributes[key] && ent.attributes[key].graph) {
+ if (id != selectedNodeId)
+ aggregate[i].sum += result[i];
+ }
+ aggregate[i].detail.push({node: self.nameFromId(id)+':', val: result[i]})
+ })
+ return true; // stop looping
+ }
+ return false; // continute looking for the aggregate record
+ })
+ if (!found) {
+ // this attribute was not found in the aggregates yet
+ // because it was not in the selectedNodeId's results
+ var vals = [];
+ result.forEach( function (val) {
+ vals.push({sum: val, detail: [{node: self.nameFromId(id), val: val}]})
+ })
+ newResponse.aggregates.push(vals)
+ }
+ })
+ })
+ callback(nodeNames, entity, newResponse);
+ },
+
+
+ getSchema: function () {
+ //QDR.log.debug("getting schema");
+ var ret;
+ self.correlator.request(
+ ret = self.sendMgmtQuery('GET-SCHEMA')
+ ).then(ret.id, function(response) {
+ //QDR.log.debug("Got schema response");
+ // remove deprecated
+ for (var entityName in response.entityTypes) {
+ var entity = response.entityTypes[entityName]
+ if (entity.deprecated) {
+ // deprecated entity
+ delete response.entityTypes[entityName]
+ } else {
+ for (var attributeName in entity.attributes) {
+ var attribute = entity.attributes[attributeName]
+ if (attribute.deprecated) {
+ // deprecated attribute
+ delete response.entityTypes[entityName].attributes[attributeName]
+ }
+ }
+ }
+ }
+ self.schema = response;
+ self.topology.get();
+ }, ret.error);
+ },
+
+ getNodeInfo: function (nodeName, entity, attrs, callback) {
+ //QDR.log.debug("getNodeInfo called with nodeName: " + nodeName + " and entity " + entity);
+ var ret;
+ self.correlator.request(
+ ret = self.sendQuery(nodeName, entity, attrs)
+ ).then(ret.id, function(response) {
+ callback(nodeName, entity, response);
+ //self.topology.addNodeInfo(nodeName, entity, response);
+ //self.topology.cleanUp(response);
+ }, ret.error);
+ },
+
+ sendMethod: function (nodeId, entity, attrs, operation, props, callback) {
+ var ret;
+ self.correlator.request(
+ ret = self._sendMethod(nodeId, entity, attrs, operation, props)
+ ).then(ret.id, function (response, context) {
+ callback(nodeId, entity, response, context);
+ }, ret.error);
+ },
+
+ _fullAddr: function (toAddr) {
+ var toAddrParts = toAddr.split('/');
+ if (toAddrParts.shift() != "amqp:") {
+ self.topology.error(Error("unexpected format for router address: " + toAddr));
+ return;
+ }
+ //var fullAddr = self.toAddress + "/" + toAddrParts.join('/');
+ var fullAddr = toAddrParts.join('/');
+ return fullAddr;
+ },
+
+ _sendMethod: function (toAddr, entity, attrs, operation, props) {
+ var fullAddr = self._fullAddr(toAddr);
+ var ret = {id: self.correlator.corr()};
+ if (!self.sender || !self.sendable) {
+ ret.error = "no sender"
+ return ret;
+ }
+ try {
+ var application_properties = {
+ operation: operation
+ }
+ if (entity) {
+ var ent = self.schema.entityTypes[entity];
+ var fullyQualifiedType = ent ? ent.fullyQualifiedType : entity;
+ application_properties.type = fullyQualifiedType || entity;
+ }
+ if (attrs.name)
+ application_properties.name = attrs.name;
+ if (props) {
+ jQuery.extend(application_properties, props);
+ }
+ var msg = {
+ body: attrs,
+ properties: {
+ to: fullAddr,
+ reply_to: self.receiver.remote.attach.source.address,
+ correlation_id: ret.id
+ },
+ application_properties: application_properties
+ }
+ self.sender.send( msg );
+ console.dump("------- method called -------")
+ console.dump (msg)
+ }
+ catch (e) {
+ error = "error sending: " + e;
+ QDR.log.error(error)
+ ret.error = error;
+ }
+ return ret;
+ },
+
+ sendQuery: function(toAddr, entity, attrs, operation) {
+ operation = operation || "QUERY"
+ var fullAddr = self._fullAddr(toAddr);
+
+ var body;
+ if (attrs)
+ body = {
+ "attributeNames": attrs,
+ }
+ else
+ body = {
+ "attributeNames": [],
+ }
+ if (entity[0] === '.')
+ entity = entity.substr(1, entity.length-1)
+ var prefix = "org.apache.qpid.dispatch."
+ var configs = ["address", "autoLink", "linkRoute"]
+ if (configs.indexOf(entity) > -1)
+ prefix += "router.config."
+ return self._send(body, fullAddr, operation, prefix + entity);
+ },
+
+ sendMgmtQuery: function (operation) {
+ return self._send([], "/$management", operation);
+ },
+
+ _send: function (body, to, operation, entityType) {
+ var ret = {id: self.correlator.corr()};
+ if (!self.sender || !self.sendable) {
+ ret.error = "no sender"
+ return ret;
+ }
+ try {
+ var application_properties = {
+ operation: operation,
+ type: "org.amqp.management",
+ name: "self"
+ };
+ if (entityType)
+ application_properties.entityType = entityType;
+
+ self.sender.send({
+ body: body,
+ properties: {
+ to: to,
+ reply_to: self.receiver.remote.attach.source.address,
+ correlation_id: ret.id
+ },
+ application_properties: application_properties
+ })
+ }
+ catch (e) {
+ error = "error sending: " + e;
+ QDR.log.error(error)
+ ret.error = error;
+ }
+ return ret;
+ },
+
+ disconnect: function() {
+ self.connection.close();
+ self.errorText = "Disconnected."
+ },
+
+ connect: function(overrideConnectOptions) {
+ QDR.log.debug("****** calling rhea.connect ********")
+ var options = self.connectionOptions;
+ if (overrideConnectOptions)
+ options = overrideConnectOptions;
+ self.topologyInitialized = false;
+ if (!self.connected) {
+ var okay = {connection: false, sender: false, receiver: false}
+ var port = options.port || 5673;
+ var baseAddress = options.address + ':' + port;
+ var ws = self.rhea.websocket_connect(WebSocket);
+ self.toAddress = "amqp://" + baseAddress;
+ self.connectionError = undefined;
+
+ var stop = function (context) {
+ //self.stopUpdating();
+ okay.sender = false;
+ okay.receiver = false;
+ okay.connected = false;
+ self.connected = false;
+ self.sender = null;
+ self.receiver = null;
+ self.sendable = false;
+ self.gotTopology = false;
+ }
+ var maybeStart = function () {
+ if (okay.connection && okay.sender && okay.receiver && self.sendable && !self.connected) {
+ QDR.log.info("okay to start")
+ self.connected = true;
+ self.connection = connection;
+ self.sender = sender;
+ self.receiver = receiver;
+ self.onSubscription();
+ self.gotTopology = false;
+ }
+ }
+ var onDisconnect = function () {
+ //QDR.log.warn("Disconnected");
+ self.connectionError = true;
+ stop();
+ self.executeDisconnectActions();
+ }
+
+ var connection;
+ try {
+QDR.log.debug("trying to connect to ws://" + baseAddress)
+ connection = self.rhea.connect({
+ connection_details:ws('ws://' + baseAddress, ["binary", "base64", "AMQWSB10"]),
+ reconnect:true,
+ properties: {console_identifier: 'Dispatch console'}
+ });
+ }
+ catch (e) {
+ QDR.log.debug("exception caught on connect")
+ self.errorText = "Connection failed"
+ onDisconnect();
+ }
+ if (!self.connectionError) {
+ connection.on('connection_open', function (context) {
+ QDR.log.debug("connection_opened")
+ okay.connection = true;
+ okay.receiver = false;
+ okay.sender = false;
+ })
+ connection.on('disconnected', function (context) {
+ QDR.log.debug("connection disconnected")
+ self.errorText = "Unable to connect"
+ onDisconnect();
+ })
+ connection.on('connection_close', function (context) {
+ QDR.log.debug("connection closed")
+ self.errorText = "Disconnected"
+ onDisconnect();
+ })
+
+ var sender = connection.open_sender();
+ sender.on('sender_open', function (context) {
+ QDR.log.debug("sender_opened")
+ okay.sender = true
+ maybeStart()
+ })
+ sender.on('sendable', function (context) {
+ //QDR.log.debug("sendable")
+ self.sendable = true;
+ maybeStart();
+ })
+
+ var receiver = connection.open_receiver({source: {dynamic: true}});
+ receiver.on('receiver_open', function (context) {
+ QDR.log.debug("receiver_opened")
+ okay.receiver = true;
+ maybeStart()
+ })
+ receiver.on("message", function (context) {
+ self.correlator.resolve(context);
+ });
+ }
+ }
+ }
+ }
+ return self;
+ };
+
+ return QDR;
+}(QDR || {}));
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.module.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.module.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.module.js
new file mode 100644
index 0000000..48cc85f
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/dispatch.module.js
@@ -0,0 +1,256 @@
+/*
+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
+ * @main QDR
+ *
+ * The main entry point for the QDR module
+ *
+ */
+var QDR = (function(QDR) {
+
+ /**
+ * @property pluginName
+ * @type {string}
+ *
+ * The name of this plugin
+ */
+ QDR.pluginName = "QDR";
+ QDR.pluginRoot = "";
+ QDR.isStandalone = true;
+ QDR.isHorizon = true;
+ QDR.offsetParent = ".col-xs-12";
+
+ /**
+ * @property log
+ * @type {Logging.Logger}
+ *
+ * This plugin's logger instance
+ */
+ //HIO QDR.log = Logger.get(QDR.pluginName);
+ /**
+ * @property templatePath
+ * @type {string}
+ *
+ * The top level path to this plugin's partials
+ */
+ QDR.srcBase = "plugin/";
+ QDR.templatePath = QDR.srcBase + "html/";
+ QDR.cssPath = QDR.srcBase + "css/";
+ /**
+ * @property SETTINGS_KEY
+ * @type {string}
+ *
+ * The key used to fetch our settings from local storage
+ */
+ QDR.SETTINGS_KEY = 'QDRSettings';
+ QDR.LAST_LOCATION = "QDRLastLocation";
+
+ /**
+ * @property module
+ * @type {object}
+ *
+ * This plugin's angularjs module instance
+ */
+ QDR.module = angular.module('horizon.dashboard.dispatch',
+ [
+ 'ui.grid',
+ 'ui.grid.resizeColumns',
+ 'ui.grid.selection',
+ 'ui.bootstrap',
+ 'ui.slider',
+ 'horizon.dashboard.dispatch.overv',
+ 'horizon.dashboard.dispatch.topology'
+ ])
+
+ Core = {
+ notification: function (severity, msg) {
+ $.notify(msg, severity);
+ }
+ }
+
+ QDR.module.config(['$provide', '$windowProvider',
+ function ($provide, $windowProvider) {
+ var path = $windowProvider.$get().STATIC_URL + 'dashboard/dispatch/';
+ $provide.constant('horizon.dashboard.dispatch.basePath', path);
+ }
+ ]);
+
+ QDR.module.config(['$compileProvider',
+ function($compileProvider) {
+ $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|blob):/);
+ }
+ ]);
+
+ QDR.module.filter('to_trusted', ['$sce', function($sce){
+ return function(text) {
+ return $sce.trustAsHtml(text);
+ };
+ }]);
+
+ QDR.module.filter('humanify', ['horizon.dashboard.dispatch.comService', function (QDRService) {
+ return function (input) {
+ return QDRService.humanify(input);
+ };
+ }]);
+
+ QDR.module.filter('Pascalcase', function () {
+ return function (str) {
+ if (!str)
+ return "";
+ return str.replace(/(\w)(\w*)/g,
+ function(g0,g1,g2){return g1.toUpperCase() + g2.toLowerCase();});
+ }
+ })
+
+ QDR.module.filter('safePlural', function () {
+ return function (str) {
+ var es = ['x', 'ch', 'ss', 'sh']
+ for (var i=0; i<es.length; ++i) {
+ if (str.endsWith(es[i]))
+ return str + 'es'
+ }
+ if (str.endsWith('y'))
+ return str.substr(0, str.length-2) + 'ies'
+ if (str.endsWith('s'))
+ return str;
+ return str + 's'
+ }
+ })
+
+ QDR.logger = function ($log) {
+ var log = $log;
+
+ this.debug = function (msg) { msg = "QDR: " + msg; log.debug(msg)};
+ this.error = function (msg) {msg = "QDR: " + msg; log.error(msg)}
+ this.info = function (msg) {msg = "QDR: " + msg; log.info(msg)}
+ this.warn = function (msg) {msg = "QDR: " + msg; log.warn(msg)}
+
+ return this;
+ }
+ // one-time initialization happens in the run function
+ // of our module
+ QDR.module.run(
+ ["$rootScope",
+ '$route',
+ '$timeout',
+ "$location",
+ "$log",
+ "horizon.dashboard.dispatch.comService",
+ "horizon.dashboard.dispatch.chartService",
+ function (
+ $rootScope,
+ $route,
+ $timeout,
+ $location,
+ $log,
+ QDRService,
+ QDRChartService) {
+ QDR.log = new QDR.logger($log);
+ QDR.log.info("*************creating Dispatch Console************");
+
+ var curPath = $location.path()
+ var org = curPath.substr(1)
+ if (org && org.length > 0 && org !== "connect") {
+ // $location.search('org', org)
+ } else {
+ // $location.search('org', null)
+ }
+
+ QDRService.initProton();
+ 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.addDisconnectAction( function () {
+ $timeout(function () {
+ var lastLocation = localStorage[QDR.LAST_LOCATION] || "/overview";
+ org = lastLocation.substr(1)
+ //$location.path("/connect");
+ //$location.search('org', org)
+debugger;
+ window.location.replace("/dispatch/connect/");
+ })
+ })
+ QDRService.addConnectAction(function() {
+ var searchObject = $location.search();
+ // the redirect will be handled by QDRService when connected
+ if (searchObject.org) {
+ return;
+ }
+ // there was no org= parameter, so redirect to last known location
+ $timeout(function () {
+ var lastLocation = localStorage[QDR.LAST_LOCATION] || "/overview";
+ //$location.path(lastLocation);
+ })
+ });
+ QDRService.connect(settings);
+ } else {
+QDR.log.debug("QDR.module run called with location of " + $location.path());
+ $timeout(function () {
+ //$location.path('/connect')
+ //$location.search('org', org)
+//debugger;
+// window.location.replace("/dispatch/connect/");
+ })
+ }
+
+ $rootScope.$on('$routeChangeSuccess', function() {
+ var path = $location.path();
+ if (path !== "/connect") {
+ localStorage[QDR.LAST_LOCATION] = path;
+ }
+ });
+ }]);
+
+ QDR.module.controller ("QDR.Core", function ($scope, $rootScope) {
+ $scope.alerts = [];
+ $scope.closeAlert = function(index) {
+ $scope.alerts.splice(index, 1);
+ };
+ $scope.$on('newAlert', function(event, data) {
+ $scope.alerts.push(data);
+ $scope.$apply();
+ });
+ $scope.$on("clearAlerts", function () {
+ $scope.alerts = [];
+ $scope.$apply();
+ })
+
+ })
+
+ return QDR;
+}(QDR || {}));
+
+var Folder = (function () {
+ function Folder(title) {
+ this.title = title;
+ this.children = [];
+ this.folder = true;
+ }
+ return Folder;
+})();
+var Leaf = (function () {
+ function Leaf(title) {
+ this.title = title;
+ }
+ return Leaf;
+})();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[05/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/ui-grid.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/ui-grid.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/ui-grid.js
new file mode 100644
index 0000000..545ef70
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/ui-grid.js
@@ -0,0 +1,28540 @@
+/*!
+ * ui-grid - v3.2.9 - 2016-09-21
+ * Copyright (c) 2016 ; License: MIT
+ */
+
+(function () {
+ 'use strict';
+ angular.module('ui.grid.i18n', []);
+ angular.module('ui.grid', ['ui.grid.i18n']);
+})();
+(function () {
+ 'use strict';
+
+ /**
+ * @ngdoc object
+ * @name ui.grid.service:uiGridConstants
+ * @description Constants for use across many grid features
+ *
+ */
+
+
+ angular.module('ui.grid').constant('uiGridConstants', {
+ LOG_DEBUG_MESSAGES: true,
+ LOG_WARN_MESSAGES: true,
+ LOG_ERROR_MESSAGES: true,
+ CUSTOM_FILTERS: /CUSTOM_FILTERS/g,
+ COL_FIELD: /COL_FIELD/g,
+ MODEL_COL_FIELD: /MODEL_COL_FIELD/g,
+ TOOLTIP: /title=\"TOOLTIP\"/g,
+ DISPLAY_CELL_TEMPLATE: /DISPLAY_CELL_TEMPLATE/g,
+ TEMPLATE_REGEXP: /<.+>/,
+ FUNC_REGEXP: /(\([^)]*\))?$/,
+ DOT_REGEXP: /\./g,
+ APOS_REGEXP: /'/g,
+ BRACKET_REGEXP: /^(.*)((?:\s*\[\s*\d+\s*\]\s*)|(?:\s*\[\s*"(?:[^"\\]|\\.)*"\s*\]\s*)|(?:\s*\[\s*'(?:[^'\\]|\\.)*'\s*\]\s*))(.*)$/,
+ COL_CLASS_PREFIX: 'ui-grid-col',
+ ENTITY_BINDING: '$$this',
+ events: {
+ GRID_SCROLL: 'uiGridScroll',
+ COLUMN_MENU_SHOWN: 'uiGridColMenuShown',
+ ITEM_DRAGGING: 'uiGridItemDragStart', // For any item being dragged
+ COLUMN_HEADER_CLICK: 'uiGridColumnHeaderClick'
+ },
+ // copied from http://www.lsauer.com/2011/08/javascript-keymap-keycodes-in-json.html
+ keymap: {
+ TAB: 9,
+ STRG: 17,
+ CAPSLOCK: 20,
+ CTRL: 17,
+ CTRLRIGHT: 18,
+ CTRLR: 18,
+ SHIFT: 16,
+ RETURN: 13,
+ ENTER: 13,
+ BACKSPACE: 8,
+ BCKSP: 8,
+ ALT: 18,
+ ALTR: 17,
+ ALTRIGHT: 17,
+ SPACE: 32,
+ WIN: 91,
+ MAC: 91,
+ FN: null,
+ PG_UP: 33,
+ PG_DOWN: 34,
+ UP: 38,
+ DOWN: 40,
+ LEFT: 37,
+ RIGHT: 39,
+ ESC: 27,
+ DEL: 46,
+ F1: 112,
+ F2: 113,
+ F3: 114,
+ F4: 115,
+ F5: 116,
+ F6: 117,
+ F7: 118,
+ F8: 119,
+ F9: 120,
+ F10: 121,
+ F11: 122,
+ F12: 123
+ },
+ /**
+ * @ngdoc object
+ * @name ASC
+ * @propertyOf ui.grid.service:uiGridConstants
+ * @description Used in {@link ui.grid.class:GridOptions.columnDef#properties_sort columnDef.sort} and
+ * {@link ui.grid.class:GridOptions.columnDef#properties_sortDirectionCycle columnDef.sortDirectionCycle}
+ * to configure the sorting direction of the column
+ */
+ ASC: 'asc',
+ /**
+ * @ngdoc object
+ * @name DESC
+ * @propertyOf ui.grid.service:uiGridConstants
+ * @description Used in {@link ui.grid.class:GridOptions.columnDef#properties_sort columnDef.sort} and
+ * {@link ui.grid.class:GridOptions.columnDef#properties_sortDirectionCycle columnDef.sortDirectionCycle}
+ * to configure the sorting direction of the column
+ */
+ DESC: 'desc',
+
+
+ /**
+ * @ngdoc object
+ * @name filter
+ * @propertyOf ui.grid.service:uiGridConstants
+ * @description Used in {@link ui.grid.class:GridOptions.columnDef#properties_filter columnDef.filter}
+ * to configure filtering on the column
+ *
+ * `SELECT` and `INPUT` are used with the `type` property of the filter, the rest are used to specify
+ * one of the built-in conditions.
+ *
+ * Available `condition` options are:
+ * - `uiGridConstants.filter.STARTS_WITH`
+ * - `uiGridConstants.filter.ENDS_WITH`
+ * - `uiGridConstants.filter.CONTAINS`
+ * - `uiGridConstants.filter.GREATER_THAN`
+ * - `uiGridConstants.filter.GREATER_THAN_OR_EQUAL`
+ * - `uiGridConstants.filter.LESS_THAN`
+ * - `uiGridConstants.filter.LESS_THAN_OR_EQUAL`
+ * - `uiGridConstants.filter.NOT_EQUAL`
+ * - `uiGridConstants.filter.STARTS_WITH`
+ *
+ *
+ * Available `type` options are:
+ * - `uiGridConstants.filter.SELECT` - use a dropdown box for the cell header filter field
+ * - `uiGridConstants.filter.INPUT` - use a text box for the cell header filter field
+ */
+ filter: {
+ STARTS_WITH: 2,
+ ENDS_WITH: 4,
+ EXACT: 8,
+ CONTAINS: 16,
+ GREATER_THAN: 32,
+ GREATER_THAN_OR_EQUAL: 64,
+ LESS_THAN: 128,
+ LESS_THAN_OR_EQUAL: 256,
+ NOT_EQUAL: 512,
+ SELECT: 'select',
+ INPUT: 'input'
+ },
+
+ /**
+ * @ngdoc object
+ * @name aggregationTypes
+ * @propertyOf ui.grid.service:uiGridConstants
+ * @description Used in {@link ui.grid.class:GridOptions.columnDef#properties_aggregationType columnDef.aggregationType}
+ * to specify the type of built-in aggregation the column should use.
+ *
+ * Available options are:
+ * - `uiGridConstants.aggregationTypes.sum` - add the values in this column to produce the aggregated value
+ * - `uiGridConstants.aggregationTypes.count` - count the number of rows to produce the aggregated value
+ * - `uiGridConstants.aggregationTypes.avg` - average the values in this column to produce the aggregated value
+ * - `uiGridConstants.aggregationTypes.min` - use the minimum value in this column as the aggregated value
+ * - `uiGridConstants.aggregationTypes.max` - use the maximum value in this column as the aggregated value
+ */
+ aggregationTypes: {
+ sum: 2,
+ count: 4,
+ avg: 8,
+ min: 16,
+ max: 32
+ },
+
+ // TODO(c0bra): Create full list of these somehow. NOTE: do any allow a space before or after them?
+ CURRENCY_SYMBOLS: ['\u0192', '$', '�', '$', '�', '�', '\u17db', '\u20a9', '\u20b1', '\u0e3f', '\u20ab'],
+
+ /**
+ * @ngdoc object
+ * @name scrollDirection
+ * @propertyOf ui.grid.service:uiGridConstants
+ * @description Set on {@link ui.grid.class:Grid#properties_scrollDirection Grid.scrollDirection},
+ * to indicate the direction the grid is currently scrolling in
+ *
+ * Available options are:
+ * - `uiGridConstants.scrollDirection.UP` - set when the grid is scrolling up
+ * - `uiGridConstants.scrollDirection.DOWN` - set when the grid is scrolling down
+ * - `uiGridConstants.scrollDirection.LEFT` - set when the grid is scrolling left
+ * - `uiGridConstants.scrollDirection.RIGHT` - set when the grid is scrolling right
+ * - `uiGridConstants.scrollDirection.NONE` - set when the grid is not scrolling, this is the default
+ */
+ scrollDirection: {
+ UP: 'up',
+ DOWN: 'down',
+ LEFT: 'left',
+ RIGHT: 'right',
+ NONE: 'none'
+
+ },
+
+ /**
+ * @ngdoc object
+ * @name dataChange
+ * @propertyOf ui.grid.service:uiGridConstants
+ * @description Used with {@link ui.grid.core.api:PublicApi#methods_notifyDataChange PublicApi.notifyDataChange},
+ * {@link ui.grid.class:Grid#methods_callDataChangeCallbacks Grid.callDataChangeCallbacks},
+ * and {@link ui.grid.class:Grid#methods_registerDataChangeCallback Grid.registerDataChangeCallback}
+ * to specify the type of the event(s).
+ *
+ * Available options are:
+ * - `uiGridConstants.dataChange.ALL` - listeners fired on any of these events, fires listeners on all events.
+ * - `uiGridConstants.dataChange.EDIT` - fired when the data in a cell is edited
+ * - `uiGridConstants.dataChange.ROW` - fired when a row is added or removed
+ * - `uiGridConstants.dataChange.COLUMN` - fired when the column definitions are modified
+ * - `uiGridConstants.dataChange.OPTIONS` - fired when the grid options are modified
+ */
+ dataChange: {
+ ALL: 'all',
+ EDIT: 'edit',
+ ROW: 'row',
+ COLUMN: 'column',
+ OPTIONS: 'options'
+ },
+
+ /**
+ * @ngdoc object
+ * @name scrollbars
+ * @propertyOf ui.grid.service:uiGridConstants
+ * @description Used with {@link ui.grid.class:GridOptions#properties_enableHorizontalScrollbar GridOptions.enableHorizontalScrollbar}
+ * and {@link ui.grid.class:GridOptions#properties_enableVerticalScrollbar GridOptions.enableVerticalScrollbar}
+ * to specify the scrollbar policy for that direction.
+ *
+ * Available options are:
+ * - `uiGridConstants.scrollbars.NEVER` - never show scrollbars in this direction
+ * - `uiGridConstants.scrollbars.ALWAYS` - always show scrollbars in this direction
+ */
+
+ scrollbars: {
+ NEVER: 0,
+ ALWAYS: 1
+ //WHEN_NEEDED: 2
+ }
+ });
+
+})();
+
+angular.module('ui.grid').directive('uiGridCell', ['$compile', '$parse', 'gridUtil', 'uiGridConstants', function ($compile, $parse, gridUtil, uiGridConstants) {
+ var uiGridCell = {
+ priority: 0,
+ scope: false,
+ require: '?^uiGrid',
+ compile: function() {
+ return {
+ pre: function($scope, $elm, $attrs, uiGridCtrl) {
+ function compileTemplate() {
+ var compiledElementFn = $scope.col.compiledElementFn;
+
+ compiledElementFn($scope, function(clonedElement, scope) {
+ $elm.append(clonedElement);
+ });
+ }
+
+ // If the grid controller is present, use it to get the compiled cell template function
+ if (uiGridCtrl && $scope.col.compiledElementFn) {
+ compileTemplate();
+ }
+ // No controller, compile the element manually (for unit tests)
+ else {
+ if ( uiGridCtrl && !$scope.col.compiledElementFn ){
+ // gridUtil.logError('Render has been called before precompile. Please log a ui-grid issue');
+
+ $scope.col.getCompiledElementFn()
+ .then(function (compiledElementFn) {
+ compiledElementFn($scope, function(clonedElement, scope) {
+ $elm.append(clonedElement);
+ });
+ });
+ }
+ else {
+ var html = $scope.col.cellTemplate
+ .replace(uiGridConstants.MODEL_COL_FIELD, 'row.entity.' + gridUtil.preEval($scope.col.field))
+ .replace(uiGridConstants.COL_FIELD, 'grid.getCellValue(row, col)');
+
+ var cellElement = $compile(html)($scope);
+ $elm.append(cellElement);
+ }
+ }
+ },
+ post: function($scope, $elm, $attrs, uiGridCtrl) {
+ var initColClass = $scope.col.getColClass(false);
+ $elm.addClass(initColClass);
+
+ var classAdded;
+ var updateClass = function( grid ){
+ var contents = $elm;
+ if ( classAdded ){
+ contents.removeClass( classAdded );
+ classAdded = null;
+ }
+
+ if (angular.isFunction($scope.col.cellClass)) {
+ classAdded = $scope.col.cellClass($scope.grid, $scope.row, $scope.col, $scope.rowRenderIndex, $scope.colRenderIndex);
+ }
+ else {
+ classAdded = $scope.col.cellClass;
+ }
+ contents.addClass(classAdded);
+ };
+
+ if ($scope.col.cellClass) {
+ updateClass();
+ }
+
+ // Register a data change watch that would get triggered whenever someone edits a cell or modifies column defs
+ var dataChangeDereg = $scope.grid.registerDataChangeCallback( updateClass, [uiGridConstants.dataChange.COLUMN, uiGridConstants.dataChange.EDIT]);
+
+ // watch the col and row to see if they change - which would indicate that we've scrolled or sorted or otherwise
+ // changed the row/col that this cell relates to, and we need to re-evaluate cell classes and maybe other things
+ var cellChangeFunction = function( n, o ){
+ if ( n !== o ) {
+ if ( classAdded || $scope.col.cellClass ){
+ updateClass();
+ }
+
+ // See if the column's internal class has changed
+ var newColClass = $scope.col.getColClass(false);
+ if (newColClass !== initColClass) {
+ $elm.removeClass(initColClass);
+ $elm.addClass(newColClass);
+ initColClass = newColClass;
+ }
+ }
+ };
+
+ // TODO(c0bra): Turn this into a deep array watch
+/* shouldn't be needed any more given track by col.name
+ var colWatchDereg = $scope.$watch( 'col', cellChangeFunction );
+*/
+ var rowWatchDereg = $scope.$watch( 'row', cellChangeFunction );
+
+
+ var deregisterFunction = function() {
+ dataChangeDereg();
+// colWatchDereg();
+ rowWatchDereg();
+ };
+
+ $scope.$on( '$destroy', deregisterFunction );
+ $elm.on( '$destroy', deregisterFunction );
+ }
+ };
+ }
+ };
+
+ return uiGridCell;
+}]);
+
+
+(function(){
+
+angular.module('ui.grid')
+.service('uiGridColumnMenuService', [ 'i18nService', 'uiGridConstants', 'gridUtil',
+function ( i18nService, uiGridConstants, gridUtil ) {
+/**
+ * @ngdoc service
+ * @name ui.grid.service:uiGridColumnMenuService
+ *
+ * @description Services for working with column menus, factored out
+ * to make the code easier to understand
+ */
+
+ var service = {
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name initialize
+ * @description Sets defaults, puts a reference to the $scope on
+ * the uiGridController
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ * @param {controller} uiGridCtrl the uiGridController for the grid
+ * we're on
+ *
+ */
+ initialize: function( $scope, uiGridCtrl ){
+ $scope.grid = uiGridCtrl.grid;
+
+ // Store a reference to this link/controller in the main uiGrid controller
+ // to allow showMenu later
+ uiGridCtrl.columnMenuScope = $scope;
+
+ // Save whether we're shown or not so the columns can check
+ $scope.menuShown = false;
+ },
+
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name setColMenuItemWatch
+ * @description Setup a watch on $scope.col.menuItems, and update
+ * menuItems based on this. $scope.col needs to be set by the column
+ * before calling the menu.
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ * @param {controller} uiGridCtrl the uiGridController for the grid
+ * we're on
+ *
+ */
+ setColMenuItemWatch: function ( $scope ){
+ var deregFunction = $scope.$watch('col.menuItems', function (n) {
+ if (typeof(n) !== 'undefined' && n && angular.isArray(n)) {
+ n.forEach(function (item) {
+ if (typeof(item.context) === 'undefined' || !item.context) {
+ item.context = {};
+ }
+ item.context.col = $scope.col;
+ });
+
+ $scope.menuItems = $scope.defaultMenuItems.concat(n);
+ }
+ else {
+ $scope.menuItems = $scope.defaultMenuItems;
+ }
+ });
+
+ $scope.$on( '$destroy', deregFunction );
+ },
+
+
+ /**
+ * @ngdoc boolean
+ * @name enableSorting
+ * @propertyOf ui.grid.class:GridOptions.columnDef
+ * @description (optional) True by default. When enabled, this setting adds sort
+ * widgets to the column header, allowing sorting of the data in the individual column.
+ */
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name sortable
+ * @description determines whether this column is sortable
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ *
+ */
+ sortable: function( $scope ) {
+ if ( $scope.grid.options.enableSorting && typeof($scope.col) !== 'undefined' && $scope.col && $scope.col.enableSorting) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name isActiveSort
+ * @description determines whether the requested sort direction is current active, to
+ * allow highlighting in the menu
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ * @param {string} direction the direction that we'd have selected for us to be active
+ *
+ */
+ isActiveSort: function( $scope, direction ){
+ return (typeof($scope.col) !== 'undefined' && typeof($scope.col.sort) !== 'undefined' &&
+ typeof($scope.col.sort.direction) !== 'undefined' && $scope.col.sort.direction === direction);
+
+ },
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name suppressRemoveSort
+ * @description determines whether we should suppress the removeSort option
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ *
+ */
+ suppressRemoveSort: function( $scope ) {
+ if ($scope.col && $scope.col.suppressRemoveSort) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ },
+
+
+ /**
+ * @ngdoc boolean
+ * @name enableHiding
+ * @propertyOf ui.grid.class:GridOptions.columnDef
+ * @description (optional) True by default. When set to false, this setting prevents a user from hiding the column
+ * using the column menu or the grid menu.
+ */
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name hideable
+ * @description determines whether a column can be hidden, by checking the enableHiding columnDef option
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ *
+ */
+ hideable: function( $scope ) {
+ if (typeof($scope.col) !== 'undefined' && $scope.col && $scope.col.colDef && $scope.col.colDef.enableHiding === false ) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ },
+
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name getDefaultMenuItems
+ * @description returns the default menu items for a column menu
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ *
+ */
+ getDefaultMenuItems: function( $scope ){
+ return [
+ {
+ title: i18nService.getSafeText('sort.ascending'),
+ icon: 'ui-grid-icon-sort-alt-up',
+ action: function($event) {
+ $event.stopPropagation();
+ $scope.sortColumn($event, uiGridConstants.ASC);
+ },
+ shown: function () {
+ return service.sortable( $scope );
+ },
+ active: function() {
+ return service.isActiveSort( $scope, uiGridConstants.ASC);
+ }
+ },
+ {
+ title: i18nService.getSafeText('sort.descending'),
+ icon: 'ui-grid-icon-sort-alt-down',
+ action: function($event) {
+ $event.stopPropagation();
+ $scope.sortColumn($event, uiGridConstants.DESC);
+ },
+ shown: function() {
+ return service.sortable( $scope );
+ },
+ active: function() {
+ return service.isActiveSort( $scope, uiGridConstants.DESC);
+ }
+ },
+ {
+ title: i18nService.getSafeText('sort.remove'),
+ icon: 'ui-grid-icon-cancel',
+ action: function ($event) {
+ $event.stopPropagation();
+ $scope.unsortColumn();
+ },
+ shown: function() {
+ return service.sortable( $scope ) &&
+ typeof($scope.col) !== 'undefined' && (typeof($scope.col.sort) !== 'undefined' &&
+ typeof($scope.col.sort.direction) !== 'undefined') && $scope.col.sort.direction !== null &&
+ !service.suppressRemoveSort( $scope );
+ }
+ },
+ {
+ title: i18nService.getSafeText('column.hide'),
+ icon: 'ui-grid-icon-cancel',
+ shown: function() {
+ return service.hideable( $scope );
+ },
+ action: function ($event) {
+ $event.stopPropagation();
+ $scope.hideColumn();
+ }
+ }
+ ];
+ },
+
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name getColumnElementPosition
+ * @description gets the position information needed to place the column
+ * menu below the column header
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ * @param {GridCol} column the column we want to position below
+ * @param {element} $columnElement the column element we want to position below
+ * @returns {hash} containing left, top, offset, height, width
+ *
+ */
+ getColumnElementPosition: function( $scope, column, $columnElement ){
+ var positionData = {};
+ positionData.left = $columnElement[0].offsetLeft;
+ positionData.top = $columnElement[0].offsetTop;
+ positionData.parentLeft = $columnElement[0].offsetParent.offsetLeft;
+
+ // Get the grid scrollLeft
+ positionData.offset = 0;
+ if (column.grid.options.offsetLeft) {
+ positionData.offset = column.grid.options.offsetLeft;
+ }
+
+ positionData.height = gridUtil.elementHeight($columnElement, true);
+ positionData.width = gridUtil.elementWidth($columnElement, true);
+
+ return positionData;
+ },
+
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.service:uiGridColumnMenuService
+ * @name repositionMenu
+ * @description Reposition the menu below the new column. If the menu has no child nodes
+ * (i.e. it's not currently visible) then we guess it's width at 100, we'll be called again
+ * later to fix it
+ * @param {$scope} $scope the $scope from the uiGridColumnMenu
+ * @param {GridCol} column the column we want to position below
+ * @param {hash} positionData a hash containing left, top, offset, height, width
+ * @param {element} $elm the column menu element that we want to reposition
+ * @param {element} $columnElement the column element that we want to reposition underneath
+ *
+ */
+ repositionMenu: function( $scope, column, positionData, $elm, $columnElement ) {
+ var menu = $elm[0].querySelectorAll('.ui-grid-menu');
+
+ // It's possible that the render container of the column we're attaching to is
+ // offset from the grid (i.e. pinned containers), we need to get the difference in the offsetLeft
+ // between the render container and the grid
+ var renderContainerElm = gridUtil.closestElm($columnElement, '.ui-grid-render-container');
+ var renderContainerOffset = renderContainerElm.getBoundingClientRect().left - $scope.grid.element[0].getBoundingClientRect().left;
+
+ var containerScrollLeft = renderContainerElm.querySelectorAll('.ui-grid-viewport')[0].scrollLeft;
+
+ // default value the last width for _this_ column, otherwise last width for _any_ column, otherwise default to 170
+ var myWidth = column.lastMenuWidth ? column.lastMenuWidth : ( $scope.lastMenuWidth ? $scope.lastMenuWidth : 170);
+ var paddingRight = column.lastMenuPaddingRight ? column.lastMenuPaddingRight : ( $scope.lastMenuPaddingRight ? $scope.lastMenuPaddingRight : 10);
+
+ if ( menu.length !== 0 ){
+ var mid = menu[0].querySelectorAll('.ui-grid-menu-mid');
+ if ( mid.length !== 0 && !angular.element(mid).hasClass('ng-hide') ) {
+ myWidth = gridUtil.elementWidth(menu, true);
+ $scope.lastMenuWidth = myWidth;
+ column.lastMenuWidth = myWidth;
+
+ // TODO(c0bra): use padding-left/padding-right based on document direction (ltr/rtl), place menu on proper side
+ // Get the column menu right padding
+ paddingRight = parseInt(gridUtil.getStyles(angular.element(menu)[0])['paddingRight'], 10);
+ $scope.lastMenuPaddingRight = paddingRight;
+ column.lastMenuPaddingRight = paddingRight;
+ }
+ }
+
+ var left = positionData.left + renderContainerOffset - containerScrollLeft + positionData.parentLeft + positionData.width - myWidth + paddingRight;
+ if (left < positionData.offset){
+ left = positionData.offset;
+ }
+
+ $elm.css('left', left + 'px');
+ $elm.css('top', (positionData.top + positionData.height) + 'px');
+ }
+
+ };
+
+ return service;
+}])
+
+
+.directive('uiGridColumnMenu', ['$timeout', 'gridUtil', 'uiGridConstants', 'uiGridColumnMenuService', '$document',
+function ($timeout, gridUtil, uiGridConstants, uiGridColumnMenuService, $document) {
+/**
+ * @ngdoc directive
+ * @name ui.grid.directive:uiGridColumnMenu
+ * @description Provides the column menu framework, leverages uiGridMenu underneath
+ *
+ */
+
+ var uiGridColumnMenu = {
+ priority: 0,
+ scope: true,
+ require: '^uiGrid',
+ templateUrl: 'ui-grid/uiGridColumnMenu',
+ replace: true,
+ link: function ($scope, $elm, $attrs, uiGridCtrl) {
+ uiGridColumnMenuService.initialize( $scope, uiGridCtrl );
+
+ $scope.defaultMenuItems = uiGridColumnMenuService.getDefaultMenuItems( $scope );
+
+ // Set the menu items for use with the column menu. The user can later add additional items via the watch
+ $scope.menuItems = $scope.defaultMenuItems;
+ uiGridColumnMenuService.setColMenuItemWatch( $scope );
+
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.directive:uiGridColumnMenu
+ * @name showMenu
+ * @description Shows the column menu. If the menu is already displayed it
+ * calls the menu to ask it to hide (it will animate), then it repositions the menu
+ * to the right place whilst hidden (it will make an assumption on menu width),
+ * then it asks the menu to show (it will animate), then it repositions the menu again
+ * once we can calculate it's size.
+ * @param {GridCol} column the column we want to position below
+ * @param {element} $columnElement the column element we want to position below
+ */
+ $scope.showMenu = function(column, $columnElement, event) {
+ // Swap to this column
+ $scope.col = column;
+
+ // Get the position information for the column element
+ var colElementPosition = uiGridColumnMenuService.getColumnElementPosition( $scope, column, $columnElement );
+
+ if ($scope.menuShown) {
+ // we want to hide, then reposition, then show, but we want to wait for animations
+ // we set a variable, and then rely on the menu-hidden event to call the reposition and show
+ $scope.colElement = $columnElement;
+ $scope.colElementPosition = colElementPosition;
+ $scope.hideThenShow = true;
+
+ $scope.$broadcast('hide-menu', { originalEvent: event });
+ } else {
+ $scope.menuShown = true;
+ uiGridColumnMenuService.repositionMenu( $scope, column, colElementPosition, $elm, $columnElement );
+
+ $scope.colElement = $columnElement;
+ $scope.colElementPosition = colElementPosition;
+ $scope.$broadcast('show-menu', { originalEvent: event });
+
+ }
+ };
+
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.directive:uiGridColumnMenu
+ * @name hideMenu
+ * @description Hides the column menu.
+ * @param {boolean} broadcastTrigger true if we were triggered by a broadcast
+ * from the menu itself - in which case don't broadcast again as we'll get
+ * an infinite loop
+ */
+ $scope.hideMenu = function( broadcastTrigger ) {
+ $scope.menuShown = false;
+ if ( !broadcastTrigger ){
+ $scope.$broadcast('hide-menu');
+ }
+ };
+
+
+ $scope.$on('menu-hidden', function() {
+ if ( $scope.hideThenShow ){
+ delete $scope.hideThenShow;
+
+ uiGridColumnMenuService.repositionMenu( $scope, $scope.col, $scope.colElementPosition, $elm, $scope.colElement );
+ $scope.$broadcast('show-menu');
+
+ $scope.menuShown = true;
+ } else {
+ $scope.hideMenu( true );
+
+ if ($scope.col) {
+ //Focus on the menu button
+ gridUtil.focus.bySelector($document, '.ui-grid-header-cell.' + $scope.col.getColClass()+ ' .ui-grid-column-menu-button', $scope.col.grid, false);
+ }
+ }
+ });
+
+ $scope.$on('menu-shown', function() {
+ $timeout( function() {
+ uiGridColumnMenuService.repositionMenu( $scope, $scope.col, $scope.colElementPosition, $elm, $scope.colElement );
+ //Focus on the first item
+ gridUtil.focus.bySelector($document, '.ui-grid-menu-items .ui-grid-menu-item', true);
+ delete $scope.colElementPosition;
+ delete $scope.columnElement;
+ }, 200);
+ });
+
+
+ /* Column methods */
+ $scope.sortColumn = function (event, dir) {
+ event.stopPropagation();
+
+ $scope.grid.sortColumn($scope.col, dir, true)
+ .then(function () {
+ $scope.grid.refresh();
+ $scope.hideMenu();
+ });
+ };
+
+ $scope.unsortColumn = function () {
+ $scope.col.unsort();
+
+ $scope.grid.refresh();
+ $scope.hideMenu();
+ };
+
+ //Since we are hiding this column the default hide action will fail so we need to focus somewhere else.
+ var setFocusOnHideColumn = function(){
+ $timeout(function(){
+ // Get the UID of the first
+ var focusToGridMenu = function(){
+ return gridUtil.focus.byId('grid-menu', $scope.grid);
+ };
+
+ var thisIndex;
+ $scope.grid.columns.some(function(element, index){
+ if (angular.equals(element, $scope.col)) {
+ thisIndex = index;
+ return true;
+ }
+ });
+
+ var previousVisibleCol;
+ // Try and find the next lower or nearest column to focus on
+ $scope.grid.columns.some(function(element, index){
+ if (!element.visible){
+ return false;
+ } // This columns index is below the current column index
+ else if ( index < thisIndex){
+ previousVisibleCol = element;
+ } // This elements index is above this column index and we haven't found one that is lower
+ else if ( index > thisIndex && !previousVisibleCol) {
+ // This is the next best thing
+ previousVisibleCol = element;
+ // We've found one so use it.
+ return true;
+ } // We've reached an element with an index above this column and the previousVisibleCol variable has been set
+ else if (index > thisIndex && previousVisibleCol) {
+ // We are done.
+ return true;
+ }
+ });
+ // If found then focus on it
+ if (previousVisibleCol){
+ var colClass = previousVisibleCol.getColClass();
+ gridUtil.focus.bySelector($document, '.ui-grid-header-cell.' + colClass+ ' .ui-grid-header-cell-primary-focus', true).then(angular.noop, function(reason){
+ if (reason !== 'canceled'){ // If this is canceled then don't perform the action
+ //The fallback action is to focus on the grid menu
+ return focusToGridMenu();
+ }
+ });
+ } else {
+ // Fallback action to focus on the grid menu
+ focusToGridMenu();
+ }
+ });
+ };
+
+ $scope.hideColumn = function () {
+ $scope.col.colDef.visible = false;
+ $scope.col.visible = false;
+
+ $scope.grid.queueGridRefresh();
+ $scope.hideMenu();
+ $scope.grid.api.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
+ $scope.grid.api.core.raise.columnVisibilityChanged( $scope.col );
+
+ // We are hiding so the default action of focusing on the button that opened this menu will fail.
+ setFocusOnHideColumn();
+ };
+ },
+
+
+
+ controller: ['$scope', function ($scope) {
+ var self = this;
+
+ $scope.$watch('menuItems', function (n) {
+ self.menuItems = n;
+ });
+ }]
+ };
+
+ return uiGridColumnMenu;
+
+}]);
+
+})();
+
+(function(){
+ 'use strict';
+
+ angular.module('ui.grid').directive('uiGridFilter', ['$compile', '$templateCache', 'i18nService', 'gridUtil', function ($compile, $templateCache, i18nService, gridUtil) {
+
+ return {
+ compile: function() {
+ return {
+ pre: function ($scope, $elm, $attrs, controllers) {
+ $scope.col.updateFilters = function( filterable ){
+ $elm.children().remove();
+ if ( filterable ){
+ var template = $scope.col.filterHeaderTemplate;
+
+ $elm.append($compile(template)($scope));
+ }
+ };
+
+ $scope.$on( '$destroy', function() {
+ delete $scope.col.updateFilters;
+ });
+ },
+ post: function ($scope, $elm, $attrs, controllers){
+ $scope.aria = i18nService.getSafeText('headerCell.aria');
+ $scope.removeFilter = function(colFilter, index){
+ colFilter.term = null;
+ //Set the focus to the filter input after the action disables the button
+ gridUtil.focus.bySelector($elm, '.ui-grid-filter-input-' + index);
+ };
+ }
+ };
+ }
+ };
+ }]);
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.grid').directive('uiGridFooterCell', ['$timeout', 'gridUtil', 'uiGridConstants', '$compile',
+ function ($timeout, gridUtil, uiGridConstants, $compile) {
+ var uiGridFooterCell = {
+ priority: 0,
+ scope: {
+ col: '=',
+ row: '=',
+ renderIndex: '='
+ },
+ replace: true,
+ require: '^uiGrid',
+ compile: function compile(tElement, tAttrs, transclude) {
+ return {
+ pre: function ($scope, $elm, $attrs, uiGridCtrl) {
+ var cellFooter = $compile($scope.col.footerCellTemplate)($scope);
+ $elm.append(cellFooter);
+ },
+ post: function ($scope, $elm, $attrs, uiGridCtrl) {
+ //$elm.addClass($scope.col.getColClass(false));
+ $scope.grid = uiGridCtrl.grid;
+
+ var initColClass = $scope.col.getColClass(false);
+ $elm.addClass(initColClass);
+
+ // apply any footerCellClass
+ var classAdded;
+ var updateClass = function( grid ){
+ var contents = $elm;
+ if ( classAdded ){
+ contents.removeClass( classAdded );
+ classAdded = null;
+ }
+
+ if (angular.isFunction($scope.col.footerCellClass)) {
+ classAdded = $scope.col.footerCellClass($scope.grid, $scope.row, $scope.col, $scope.rowRenderIndex, $scope.colRenderIndex);
+ }
+ else {
+ classAdded = $scope.col.footerCellClass;
+ }
+ contents.addClass(classAdded);
+ };
+
+ if ($scope.col.footerCellClass) {
+ updateClass();
+ }
+
+ $scope.col.updateAggregationValue();
+
+ // Watch for column changes so we can alter the col cell class properly
+/* shouldn't be needed any more, given track by col.name
+ $scope.$watch('col', function (n, o) {
+ if (n !== o) {
+ // See if the column's internal class has changed
+ var newColClass = $scope.col.getColClass(false);
+ if (newColClass !== initColClass) {
+ $elm.removeClass(initColClass);
+ $elm.addClass(newColClass);
+ initColClass = newColClass;
+ }
+ }
+ });
+*/
+
+
+ // Register a data change watch that would get triggered whenever someone edits a cell or modifies column defs
+ var dataChangeDereg = $scope.grid.registerDataChangeCallback( updateClass, [uiGridConstants.dataChange.COLUMN]);
+ // listen for visible rows change and update aggregation values
+ $scope.grid.api.core.on.rowsRendered( $scope, $scope.col.updateAggregationValue );
+ $scope.grid.api.core.on.rowsRendered( $scope, updateClass );
+ $scope.$on( '$destroy', dataChangeDereg );
+ }
+ };
+ }
+ };
+
+ return uiGridFooterCell;
+ }]);
+
+})();
+
+(function () {
+ 'use strict';
+
+ angular.module('ui.grid').directive('uiGridFooter', ['$templateCache', '$compile', 'uiGridConstants', 'gridUtil', '$timeout', function ($templateCache, $compile, uiGridConstants, gridUtil, $timeout) {
+
+ return {
+ restrict: 'EA',
+ replace: true,
+ // priority: 1000,
+ require: ['^uiGrid', '^uiGridRenderContainer'],
+ scope: true,
+ compile: function ($elm, $attrs) {
+ return {
+ pre: function ($scope, $elm, $attrs, controllers) {
+ var uiGridCtrl = controllers[0];
+ var containerCtrl = controllers[1];
+
+ $scope.grid = uiGridCtrl.grid;
+ $scope.colContainer = containerCtrl.colContainer;
+
+ containerCtrl.footer = $elm;
+
+ var footerTemplate = $scope.grid.options.footerTemplate;
+ gridUtil.getTemplate(footerTemplate)
+ .then(function (contents) {
+ var template = angular.element(contents);
+
+ var newElm = $compile(template)($scope);
+ $elm.append(newElm);
+
+ if (containerCtrl) {
+ // Inject a reference to the footer viewport (if it exists) into the grid controller for use in the horizontal scroll handler below
+ var footerViewport = $elm[0].getElementsByClassName('ui-grid-footer-viewport')[0];
+
+ if (footerViewport) {
+ containerCtrl.footerViewport = footerViewport;
+ }
+ }
+ });
+ },
+
+ post: function ($scope, $elm, $attrs, controllers) {
+ var uiGridCtrl = controllers[0];
+ var containerCtrl = controllers[1];
+
+ // gridUtil.logDebug('ui-grid-footer link');
+
+ var grid = uiGridCtrl.grid;
+
+ // Don't animate footer cells
+ gridUtil.disableAnimations($elm);
+
+ containerCtrl.footer = $elm;
+
+ var footerViewport = $elm[0].getElementsByClassName('ui-grid-footer-viewport')[0];
+ if (footerViewport) {
+ containerCtrl.footerViewport = footerViewport;
+ }
+ }
+ };
+ }
+ };
+ }]);
+
+})();
+(function () {
+ 'use strict';
+
+ angular.module('ui.grid').directive('uiGridGridFooter', ['$templateCache', '$compile', 'uiGridConstants', 'gridUtil', '$timeout', function ($templateCache, $compile, uiGridConstants, gridUtil, $timeout) {
+
+ return {
+ restrict: 'EA',
+ replace: true,
+ // priority: 1000,
+ require: '^uiGrid',
+ scope: true,
+ compile: function ($elm, $attrs) {
+ return {
+ pre: function ($scope, $elm, $attrs, uiGridCtrl) {
+
+ $scope.grid = uiGridCtrl.grid;
+
+
+
+ var footerTemplate = $scope.grid.options.gridFooterTemplate;
+ gridUtil.getTemplate(footerTemplate)
+ .then(function (contents) {
+ var template = angular.element(contents);
+
+ var newElm = $compile(template)($scope);
+ $elm.append(newElm);
+ });
+ },
+
+ post: function ($scope, $elm, $attrs, controllers) {
+
+ }
+ };
+ }
+ };
+ }]);
+
+})();
+(function(){
+ 'use strict';
+
+ angular.module('ui.grid').directive('uiGridGroupPanel', ["$compile", "uiGridConstants", "gridUtil", function($compile, uiGridConstants, gridUtil) {
+ var defaultTemplate = 'ui-grid/ui-grid-group-panel';
+
+ return {
+ restrict: 'EA',
+ replace: true,
+ require: '?^uiGrid',
+ scope: false,
+ compile: function($elm, $attrs) {
+ return {
+ pre: function ($scope, $elm, $attrs, uiGridCtrl) {
+ var groupPanelTemplate = $scope.grid.options.groupPanelTemplate || defaultTemplate;
+
+ gridUtil.getTemplate(groupPanelTemplate)
+ .then(function (contents) {
+ var template = angular.element(contents);
+
+ var newElm = $compile(template)($scope);
+ $elm.append(newElm);
+ });
+ },
+
+ post: function ($scope, $elm, $attrs, uiGridCtrl) {
+ $elm.bind('$destroy', function() {
+ // scrollUnbinder();
+ });
+ }
+ };
+ }
+ };
+ }]);
+
+})();
+(function(){
+ 'use strict';
+
+ angular.module('ui.grid').directive('uiGridHeaderCell', ['$compile', '$timeout', '$window', '$document', 'gridUtil', 'uiGridConstants', 'ScrollEvent', 'i18nService',
+ function ($compile, $timeout, $window, $document, gridUtil, uiGridConstants, ScrollEvent, i18nService) {
+ // Do stuff after mouse has been down this many ms on the header cell
+ var mousedownTimeout = 500;
+ var changeModeTimeout = 500; // length of time between a touch event and a mouse event being recognised again, and vice versa
+
+ var uiGridHeaderCell = {
+ priority: 0,
+ scope: {
+ col: '=',
+ row: '=',
+ renderIndex: '='
+ },
+ require: ['^uiGrid', '^uiGridRenderContainer'],
+ replace: true,
+ compile: function() {
+ return {
+ pre: function ($scope, $elm, $attrs) {
+ var cellHeader = $compile($scope.col.headerCellTemplate)($scope);
+ $elm.append(cellHeader);
+ },
+
+ post: function ($scope, $elm, $attrs, controllers) {
+ var uiGridCtrl = controllers[0];
+ var renderContainerCtrl = controllers[1];
+
+ $scope.i18n = {
+ headerCell: i18nService.getSafeText('headerCell'),
+ sort: i18nService.getSafeText('sort')
+ };
+ $scope.isSortPriorityVisible = function() {
+ //show sort priority if column is sorted and there is at least one other sorted column
+ return angular.isNumber($scope.col.sort.priority) && $scope.grid.columns.some(function(element, index){
+ return angular.isNumber(element.sort.priority) && element !== $scope.col;
+ });
+ };
+ $scope.getSortDirectionAriaLabel = function(){
+ var col = $scope.col;
+ //Trying to recreate this sort of thing but it was getting messy having it in the template.
+ //Sort direction {{col.sort.direction == asc ? 'ascending' : ( col.sort.direction == desc ? 'descending':'none')}}. {{col.sort.priority ? {{columnPriorityText}} {{col.sort.priority}} : ''}
+ var sortDirectionText = col.sort.direction === uiGridConstants.ASC ? $scope.i18n.sort.ascending : ( col.sort.direction === uiGridConstants.DESC ? $scope.i18n.sort.descending : $scope.i18n.sort.none);
+ var label = sortDirectionText;
+
+ if ($scope.isSortPriorityVisible()) {
+ label = label + '. ' + $scope.i18n.headerCell.priority + ' ' + col.sort.priority;
+ }
+ return label;
+ };
+
+ $scope.grid = uiGridCtrl.grid;
+
+ $scope.renderContainer = uiGridCtrl.grid.renderContainers[renderContainerCtrl.containerId];
+
+ var initColClass = $scope.col.getColClass(false);
+ $elm.addClass(initColClass);
+
+ // Hide the menu by default
+ $scope.menuShown = false;
+
+ // Put asc and desc sort directions in scope
+ $scope.asc = uiGridConstants.ASC;
+ $scope.desc = uiGridConstants.DESC;
+
+ // Store a reference to menu element
+ var $colMenu = angular.element( $elm[0].querySelectorAll('.ui-grid-header-cell-menu') );
+
+ var $contentsElm = angular.element( $elm[0].querySelectorAll('.ui-grid-cell-contents') );
+
+
+ // apply any headerCellClass
+ var classAdded;
+ var previousMouseX;
+
+ // filter watchers
+ var filterDeregisters = [];
+
+
+ /*
+ * Our basic approach here for event handlers is that we listen for a down event (mousedown or touchstart).
+ * Once we have a down event, we need to work out whether we have a click, a drag, or a
+ * hold. A click would sort the grid (if sortable). A drag would be used by moveable, so
+ * we ignore it. A hold would open the menu.
+ *
+ * So, on down event, we put in place handlers for move and up events, and a timer. If the
+ * timer expires before we see a move or up, then we have a long press and hence a column menu open.
+ * If the up happens before the timer, then we have a click, and we sort if the column is sortable.
+ * If a move happens before the timer, then we are doing column move, so we do nothing, the moveable feature
+ * will handle it.
+ *
+ * To deal with touch enabled devices that also have mice, we only create our handlers when
+ * we get the down event, and we create the corresponding handlers - if we're touchstart then
+ * we get touchmove and touchend, if we're mousedown then we get mousemove and mouseup.
+ *
+ * We also suppress the click action whilst this is happening - otherwise after the mouseup there
+ * will be a click event and that can cause the column menu to close
+ *
+ */
+
+ $scope.downFn = function( event ){
+ event.stopPropagation();
+
+ if (typeof(event.originalEvent) !== 'undefined' && event.originalEvent !== undefined) {
+ event = event.originalEvent;
+ }
+
+ // Don't show the menu if it's not the left button
+ if (event.button && event.button !== 0) {
+ return;
+ }
+ previousMouseX = event.pageX;
+
+ $scope.mousedownStartTime = (new Date()).getTime();
+ $scope.mousedownTimeout = $timeout(function() { }, mousedownTimeout);
+
+ $scope.mousedownTimeout.then(function () {
+ if ( $scope.colMenu ) {
+ uiGridCtrl.columnMenuScope.showMenu($scope.col, $elm, event);
+ }
+ });
+
+ uiGridCtrl.fireEvent(uiGridConstants.events.COLUMN_HEADER_CLICK, {event: event, columnName: $scope.col.colDef.name});
+
+ $scope.offAllEvents();
+ if ( event.type === 'touchstart'){
+ $document.on('touchend', $scope.upFn);
+ $document.on('touchmove', $scope.moveFn);
+ } else if ( event.type === 'mousedown' ){
+ $document.on('mouseup', $scope.upFn);
+ $document.on('mousemove', $scope.moveFn);
+ }
+ };
+
+ $scope.upFn = function( event ){
+ event.stopPropagation();
+ $timeout.cancel($scope.mousedownTimeout);
+ $scope.offAllEvents();
+ $scope.onDownEvents(event.type);
+
+ var mousedownEndTime = (new Date()).getTime();
+ var mousedownTime = mousedownEndTime - $scope.mousedownStartTime;
+
+ if (mousedownTime > mousedownTimeout) {
+ // long click, handled above with mousedown
+ }
+ else {
+ // short click
+ if ( $scope.sortable ){
+ $scope.handleClick(event);
+ }
+ }
+ };
+
+ $scope.moveFn = function( event ){
+ // Chrome is known to fire some bogus move events.
+ var changeValue = event.pageX - previousMouseX;
+ if ( changeValue === 0 ){ return; }
+
+ // we're a move, so do nothing and leave for column move (if enabled) to take over
+ $timeout.cancel($scope.mousedownTimeout);
+ $scope.offAllEvents();
+ $scope.onDownEvents(event.type);
+ };
+
+ $scope.clickFn = function ( event ){
+ event.stopPropagation();
+ $contentsElm.off('click', $scope.clickFn);
+ };
+
+
+ $scope.offAllEvents = function(){
+ $contentsElm.off('touchstart', $scope.downFn);
+ $contentsElm.off('mousedown', $scope.downFn);
+
+ $document.off('touchend', $scope.upFn);
+ $document.off('mouseup', $scope.upFn);
+
+ $document.off('touchmove', $scope.moveFn);
+ $document.off('mousemove', $scope.moveFn);
+
+ $contentsElm.off('click', $scope.clickFn);
+ };
+
+ $scope.onDownEvents = function( type ){
+ // If there is a previous event, then wait a while before
+ // activating the other mode - i.e. if the last event was a touch event then
+ // don't enable mouse events for a wee while (500ms or so)
+ // Avoids problems with devices that emulate mouse events when you have touch events
+
+ switch (type){
+ case 'touchmove':
+ case 'touchend':
+ $contentsElm.on('click', $scope.clickFn);
+ $contentsElm.on('touchstart', $scope.downFn);
+ $timeout(function(){
+ $contentsElm.on('mousedown', $scope.downFn);
+ }, changeModeTimeout);
+ break;
+ case 'mousemove':
+ case 'mouseup':
+ $contentsElm.on('click', $scope.clickFn);
+ $contentsElm.on('mousedown', $scope.downFn);
+ $timeout(function(){
+ $contentsElm.on('touchstart', $scope.downFn);
+ }, changeModeTimeout);
+ break;
+ default:
+ $contentsElm.on('click', $scope.clickFn);
+ $contentsElm.on('touchstart', $scope.downFn);
+ $contentsElm.on('mousedown', $scope.downFn);
+ }
+ };
+
+
+ var updateHeaderOptions = function( grid ){
+ var contents = $elm;
+ if ( classAdded ){
+ contents.removeClass( classAdded );
+ classAdded = null;
+ }
+
+ if (angular.isFunction($scope.col.headerCellClass)) {
+ classAdded = $scope.col.headerCellClass($scope.grid, $scope.row, $scope.col, $scope.rowRenderIndex, $scope.colRenderIndex);
+ }
+ else {
+ classAdded = $scope.col.headerCellClass;
+ }
+ contents.addClass(classAdded);
+
+ $timeout(function (){
+ var rightMostContainer = $scope.grid.renderContainers['right'] ? $scope.grid.renderContainers['right'] : $scope.grid.renderContainers['body'];
+ $scope.isLastCol = ( $scope.col === rightMostContainer.visibleColumnCache[ rightMostContainer.visibleColumnCache.length - 1 ] );
+ });
+
+ // Figure out whether this column is sortable or not
+ if (uiGridCtrl.grid.options.enableSorting && $scope.col.enableSorting) {
+ $scope.sortable = true;
+ }
+ else {
+ $scope.sortable = false;
+ }
+
+ // Figure out whether this column is filterable or not
+ var oldFilterable = $scope.filterable;
+ if (uiGridCtrl.grid.options.enableFiltering && $scope.col.enableFiltering) {
+ $scope.filterable = true;
+ }
+ else {
+ $scope.filterable = false;
+ }
+
+ if ( oldFilterable !== $scope.filterable){
+ if ( typeof($scope.col.updateFilters) !== 'undefined' ){
+ $scope.col.updateFilters($scope.filterable);
+ }
+
+ // if column is filterable add a filter watcher
+ if ($scope.filterable) {
+ $scope.col.filters.forEach( function(filter, i) {
+ filterDeregisters.push($scope.$watch('col.filters[' + i + '].term', function(n, o) {
+ if (n !== o) {
+ uiGridCtrl.grid.api.core.raise.filterChanged();
+ uiGridCtrl.grid.api.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
+ uiGridCtrl.grid.queueGridRefresh();
+ }
+ }));
+ });
+ $scope.$on('$destroy', function() {
+ filterDeregisters.forEach( function(filterDeregister) {
+ filterDeregister();
+ });
+ });
+ } else {
+ filterDeregisters.forEach( function(filterDeregister) {
+ filterDeregister();
+ });
+ }
+
+ }
+
+ // figure out whether we support column menus
+ if ($scope.col.grid.options && $scope.col.grid.options.enableColumnMenus !== false &&
+ $scope.col.colDef && $scope.col.colDef.enableColumnMenu !== false){
+ $scope.colMenu = true;
+ } else {
+ $scope.colMenu = false;
+ }
+
+ /**
+ * @ngdoc property
+ * @name enableColumnMenu
+ * @propertyOf ui.grid.class:GridOptions.columnDef
+ * @description if column menus are enabled, controls the column menus for this specific
+ * column (i.e. if gridOptions.enableColumnMenus, then you can control column menus
+ * using this option. If gridOptions.enableColumnMenus === false then you get no column
+ * menus irrespective of the value of this option ). Defaults to true.
+ *
+ */
+ /**
+ * @ngdoc property
+ * @name enableColumnMenus
+ * @propertyOf ui.grid.class:GridOptions.columnDef
+ * @description Override for column menus everywhere - if set to false then you get no
+ * column menus. Defaults to true.
+ *
+ */
+
+ $scope.offAllEvents();
+
+ if ($scope.sortable || $scope.colMenu) {
+ $scope.onDownEvents();
+
+ $scope.$on('$destroy', function () {
+ $scope.offAllEvents();
+ });
+ }
+ };
+
+/*
+ $scope.$watch('col', function (n, o) {
+ if (n !== o) {
+ // See if the column's internal class has changed
+ var newColClass = $scope.col.getColClass(false);
+ if (newColClass !== initColClass) {
+ $elm.removeClass(initColClass);
+ $elm.addClass(newColClass);
+ initColClass = newColClass;
+ }
+ }
+ });
+*/
+ updateHeaderOptions();
+
+ // Register a data change watch that would get triggered whenever someone edits a cell or modifies column defs
+ var dataChangeDereg = $scope.grid.registerDataChangeCallback( updateHeaderOptions, [uiGridConstants.dataChange.COLUMN]);
+
+ $scope.$on( '$destroy', dataChangeDereg );
+
+ $scope.handleClick = function(event) {
+ // If the shift key is being held down, add this column to the sort
+ var add = false;
+ if (event.shiftKey) {
+ add = true;
+ }
+
+ // Sort this column then rebuild the grid's rows
+ uiGridCtrl.grid.sortColumn($scope.col, add)
+ .then(function () {
+ if (uiGridCtrl.columnMenuScope) { uiGridCtrl.columnMenuScope.hideMenu(); }
+ uiGridCtrl.grid.refresh();
+ });
+ };
+
+
+ $scope.toggleMenu = function(event) {
+ event.stopPropagation();
+
+ // If the menu is already showing...
+ if (uiGridCtrl.columnMenuScope.menuShown) {
+ // ... and we're the column the menu is on...
+ if (uiGridCtrl.columnMenuScope.col === $scope.col) {
+ // ... hide it
+ uiGridCtrl.columnMenuScope.hideMenu();
+ }
+ // ... and we're NOT the column the menu is on
+ else {
+ // ... move the menu to our column
+ uiGridCtrl.columnMenuScope.showMenu($scope.col, $elm);
+ }
+ }
+ // If the menu is NOT showing
+ else {
+ // ... show it on our column
+ uiGridCtrl.columnMenuScope.showMenu($scope.col, $elm);
+ }
+ };
+ }
+ };
+ }
+ };
+
+ return uiGridHeaderCell;
+ }]);
+
+})();
+
+(function(){
+ 'use strict';
+
+ angular.module('ui.grid').directive('uiGridHeader', ['$templateCache', '$compile', 'uiGridConstants', 'gridUtil', '$timeout', 'ScrollEvent',
+ function($templateCache, $compile, uiGridConstants, gridUtil, $timeout, ScrollEvent) {
+ var defaultTemplate = 'ui-grid/ui-grid-header';
+ var emptyTemplate = 'ui-grid/ui-grid-no-header';
+
+ return {
+ restrict: 'EA',
+ // templateUrl: 'ui-grid/ui-grid-header',
+ replace: true,
+ // priority: 1000,
+ require: ['^uiGrid', '^uiGridRenderContainer'],
+ scope: true,
+ compile: function($elm, $attrs) {
+ return {
+ pre: function ($scope, $elm, $attrs, controllers) {
+ var uiGridCtrl = controllers[0];
+ var containerCtrl = controllers[1];
+
+ $scope.grid = uiGridCtrl.grid;
+ $scope.colContainer = containerCtrl.colContainer;
+
+ updateHeaderReferences();
+
+ var headerTemplate;
+ if (!$scope.grid.options.showHeader) {
+ headerTemplate = emptyTemplate;
+ }
+ else {
+ headerTemplate = ($scope.grid.options.headerTemplate) ? $scope.grid.options.headerTemplate : defaultTemplate;
+ }
+
+ gridUtil.getTemplate(headerTemplate)
+ .then(function (contents) {
+ var template = angular.element(contents);
+
+ var newElm = $compile(template)($scope);
+ $elm.replaceWith(newElm);
+
+ // And update $elm to be the new element
+ $elm = newElm;
+
+ updateHeaderReferences();
+
+ if (containerCtrl) {
+ // Inject a reference to the header viewport (if it exists) into the grid controller for use in the horizontal scroll handler below
+ var headerViewport = $elm[0].getElementsByClassName('ui-grid-header-viewport')[0];
+
+
+ if (headerViewport) {
+ containerCtrl.headerViewport = headerViewport;
+ angular.element(headerViewport).on('scroll', scrollHandler);
+ $scope.$on('$destroy', function () {
+ angular.element(headerViewport).off('scroll', scrollHandler);
+ });
+ }
+ }
+
+ $scope.grid.queueRefresh();
+ });
+
+ function updateHeaderReferences() {
+ containerCtrl.header = containerCtrl.colContainer.header = $elm;
+
+ var headerCanvases = $elm[0].getElementsByClassName('ui-grid-header-canvas');
+
+ if (headerCanvases.length > 0) {
+ containerCtrl.headerCanvas = containerCtrl.colContainer.headerCanvas = headerCanvases[0];
+ }
+ else {
+ containerCtrl.headerCanvas = null;
+ }
+ }
+
+ function scrollHandler(evt) {
+ if (uiGridCtrl.grid.isScrollingHorizontally) {
+ return;
+ }
+ var newScrollLeft = gridUtil.normalizeScrollLeft(containerCtrl.headerViewport, uiGridCtrl.grid);
+ var horizScrollPercentage = containerCtrl.colContainer.scrollHorizontal(newScrollLeft);
+
+ var scrollEvent = new ScrollEvent(uiGridCtrl.grid, null, containerCtrl.colContainer, ScrollEvent.Sources.ViewPortScroll);
+ scrollEvent.newScrollLeft = newScrollLeft;
+ if ( horizScrollPercentage > -1 ){
+ scrollEvent.x = { percentage: horizScrollPercentage };
+ }
+
+ uiGridCtrl.grid.scrollContainers(null, scrollEvent);
+ }
+ },
+
+ post: function ($scope, $elm, $attrs, controllers) {
+ var uiGridCtrl = controllers[0];
+ var containerCtrl = controllers[1];
+
+ // gridUtil.logDebug('ui-grid-header link');
+
+ var grid = uiGridCtrl.grid;
+
+ // Don't animate header cells
+ gridUtil.disableAnimations($elm);
+
+ function updateColumnWidths() {
+ // this styleBuilder always runs after the renderContainer, so we can rely on the column widths
+ // already being populated correctly
+
+ var columnCache = containerCtrl.colContainer.visibleColumnCache;
+
+ // Build the CSS
+ // uiGridCtrl.grid.columns.forEach(function (column) {
+ var ret = '';
+ var canvasWidth = 0;
+ columnCache.forEach(function (column) {
+ ret = ret + column.getColClassDefinition();
+ canvasWidth += column.drawnWidth;
+ });
+
+ containerCtrl.colContainer.canvasWidth = canvasWidth;
+
+ // Return the styles back to buildStyles which pops them into the `customStyles` scope variable
+ return ret;
+ }
+
+ containerCtrl.header = $elm;
+
+ var headerViewport = $elm[0].getElementsByClassName('ui-grid-header-viewport')[0];
+ if (headerViewport) {
+ containerCtrl.headerViewport = headerViewport;
+ }
+
+ //todo: remove this if by injecting gridCtrl into unit tests
+ if (uiGridCtrl) {
+ uiGridCtrl.grid.registerStyleComputation({
+ priority: 15,
+ func: updateColumnWidths
+ });
+ }
+ }
+ };
+ }
+ };
+ }]);
+
+})();
+
+(function(){
+
+angular.module('ui.grid')
+.service('uiGridGridMenuService', [ 'gridUtil', 'i18nService', 'uiGridConstants', function( gridUtil, i18nService, uiGridConstants ) {
+ /**
+ * @ngdoc service
+ * @name ui.grid.gridMenuService
+ *
+ * @description Methods for working with the grid menu
+ */
+
+ var service = {
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.gridMenuService
+ * @name initialize
+ * @description Sets up the gridMenu. Most importantly, sets our
+ * scope onto the grid object as grid.gridMenuScope, allowing us
+ * to operate when passed only the grid. Second most importantly,
+ * we register the 'addToGridMenu' and 'removeFromGridMenu' methods
+ * on the core api.
+ * @param {$scope} $scope the scope of this gridMenu
+ * @param {Grid} grid the grid to which this gridMenu is associated
+ */
+ initialize: function( $scope, grid ){
+ grid.gridMenuScope = $scope;
+ $scope.grid = grid;
+ $scope.registeredMenuItems = [];
+
+ // not certain this is needed, but would be bad to create a memory leak
+ $scope.$on('$destroy', function() {
+ if ( $scope.grid && $scope.grid.gridMenuScope ){
+ $scope.grid.gridMenuScope = null;
+ }
+ if ( $scope.grid ){
+ $scope.grid = null;
+ }
+ if ( $scope.registeredMenuItems ){
+ $scope.registeredMenuItems = null;
+ }
+ });
+
+ $scope.registeredMenuItems = [];
+
+ /**
+ * @ngdoc function
+ * @name addToGridMenu
+ * @methodOf ui.grid.core.api:PublicApi
+ * @description add items to the grid menu. Used by features
+ * to add their menu items if they are enabled, can also be used by
+ * end users to add menu items. This method has the advantage of allowing
+ * remove again, which can simplify management of which items are included
+ * in the menu when. (Noting that in most cases the shown and active functions
+ * provide a better way to handle visibility of menu items)
+ * @param {Grid} grid the grid on which we are acting
+ * @param {array} items menu items in the format as described in the tutorial, with
+ * the added note that if you want to use remove you must also specify an `id` field,
+ * which is provided when you want to remove an item. The id should be unique.
+ *
+ */
+ grid.api.registerMethod( 'core', 'addToGridMenu', service.addToGridMenu );
+
+ /**
+ * @ngdoc function
+ * @name removeFromGridMenu
+ * @methodOf ui.grid.core.api:PublicApi
+ * @description Remove an item from the grid menu based on a provided id. Assumes
+ * that the id is unique, removes only the last instance of that id. Does nothing if
+ * the specified id is not found
+ * @param {Grid} grid the grid on which we are acting
+ * @param {string} id the id we'd like to remove from the menu
+ *
+ */
+ grid.api.registerMethod( 'core', 'removeFromGridMenu', service.removeFromGridMenu );
+ },
+
+
+ /**
+ * @ngdoc function
+ * @name addToGridMenu
+ * @propertyOf ui.grid.gridMenuService
+ * @description add items to the grid menu. Used by features
+ * to add their menu items if they are enabled, can also be used by
+ * end users to add menu items. This method has the advantage of allowing
+ * remove again, which can simplify management of which items are included
+ * in the menu when. (Noting that in most cases the shown and active functions
+ * provide a better way to handle visibility of menu items)
+ * @param {Grid} grid the grid on which we are acting
+ * @param {array} items menu items in the format as described in the tutorial, with
+ * the added note that if you want to use remove you must also specify an `id` field,
+ * which is provided when you want to remove an item. The id should be unique.
+ *
+ */
+ addToGridMenu: function( grid, menuItems ) {
+ if ( !angular.isArray( menuItems ) ) {
+ gridUtil.logError( 'addToGridMenu: menuItems must be an array, and is not, not adding any items');
+ } else {
+ if ( grid.gridMenuScope ){
+ grid.gridMenuScope.registeredMenuItems = grid.gridMenuScope.registeredMenuItems ? grid.gridMenuScope.registeredMenuItems : [];
+ grid.gridMenuScope.registeredMenuItems = grid.gridMenuScope.registeredMenuItems.concat( menuItems );
+ } else {
+ gridUtil.logError( 'Asked to addToGridMenu, but gridMenuScope not present. Timing issue? Please log issue with ui-grid');
+ }
+ }
+ },
+
+
+ /**
+ * @ngdoc function
+ * @name removeFromGridMenu
+ * @methodOf ui.grid.gridMenuService
+ * @description Remove an item from the grid menu based on a provided id. Assumes
+ * that the id is unique, removes only the last instance of that id. Does nothing if
+ * the specified id is not found. If there is no gridMenuScope or registeredMenuItems
+ * then do nothing silently - the desired result is those menu items not be present and they
+ * aren't.
+ * @param {Grid} grid the grid on which we are acting
+ * @param {string} id the id we'd like to remove from the menu
+ *
+ */
+ removeFromGridMenu: function( grid, id ){
+ var foundIndex = -1;
+
+ if ( grid && grid.gridMenuScope ){
+ grid.gridMenuScope.registeredMenuItems.forEach( function( value, index ) {
+ if ( value.id === id ){
+ if (foundIndex > -1) {
+ gridUtil.logError( 'removeFromGridMenu: found multiple items with the same id, removing only the last' );
+ } else {
+
+ foundIndex = index;
+ }
+ }
+ });
+ }
+
+ if ( foundIndex > -1 ){
+ grid.gridMenuScope.registeredMenuItems.splice( foundIndex, 1 );
+ }
+ },
+
+
+ /**
+ * @ngdoc array
+ * @name gridMenuCustomItems
+ * @propertyOf ui.grid.class:GridOptions
+ * @description (optional) An array of menu items that should be added to
+ * the gridMenu. Follow the format documented in the tutorial for column
+ * menu customisation. The context provided to the action function will
+ * include context.grid. An alternative if working with dynamic menus is to use the
+ * provided api - core.addToGridMenu and core.removeFromGridMenu, which handles
+ * some of the management of items for you.
+ *
+ */
+ /**
+ * @ngdoc boolean
+ * @name gridMenuShowHideColumns
+ * @propertyOf ui.grid.class:GridOptions
+ * @description true by default, whether the grid menu should allow hide/show
+ * of columns
+ *
+ */
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.gridMenuService
+ * @name getMenuItems
+ * @description Decides the menu items to show in the menu. This is a
+ * combination of:
+ *
+ * - the default menu items that are always included,
+ * - any menu items that have been provided through the addMenuItem api. These
+ * are typically added by features within the grid
+ * - any menu items included in grid.options.gridMenuCustomItems. These can be
+ * changed dynamically, as they're always recalculated whenever we show the
+ * menu
+ * @param {$scope} $scope the scope of this gridMenu, from which we can find all
+ * the information that we need
+ * @returns {array} an array of menu items that can be shown
+ */
+ getMenuItems: function( $scope ) {
+ var menuItems = [
+ // this is where we add any menu items we want to always include
+ ];
+
+ if ( $scope.grid.options.gridMenuCustomItems ){
+ if ( !angular.isArray( $scope.grid.options.gridMenuCustomItems ) ){
+ gridUtil.logError( 'gridOptions.gridMenuCustomItems must be an array, and is not');
+ } else {
+ menuItems = menuItems.concat( $scope.grid.options.gridMenuCustomItems );
+ }
+ }
+
+ var clearFilters = [{
+ title: i18nService.getSafeText('gridMenu.clearAllFilters'),
+ action: function ($event) {
+ $scope.grid.clearAllFilters(undefined, true, undefined);
+ },
+ shown: function() {
+ return $scope.grid.options.enableFiltering;
+ },
+ order: 100
+ }];
+ menuItems = menuItems.concat( clearFilters );
+
+ menuItems = menuItems.concat( $scope.registeredMenuItems );
+
+ if ( $scope.grid.options.gridMenuShowHideColumns !== false ){
+ menuItems = menuItems.concat( service.showHideColumns( $scope ) );
+ }
+
+ menuItems.sort(function(a, b){
+ return a.order - b.order;
+ });
+
+ return menuItems;
+ },
+
+
+ /**
+ * @ngdoc array
+ * @name gridMenuTitleFilter
+ * @propertyOf ui.grid.class:GridOptions
+ * @description (optional) A function that takes a title string
+ * (usually the col.displayName), and converts it into a display value. The function
+ * must return either a string or a promise.
+ *
+ * Used for internationalization of the grid menu column names - for angular-translate
+ * you can pass $translate as the function, for i18nService you can pass getSafeText as the
+ * function
+ * @example
+ * <pre>
+ * gridOptions = {
+ * gridMenuTitleFilter: $translate
+ * }
+ * </pre>
+ */
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.gridMenuService
+ * @name showHideColumns
+ * @description Adds two menu items for each of the columns in columnDefs. One
+ * menu item for hide, one menu item for show. Each is visible when appropriate
+ * (show when column is not visible, hide when column is visible). Each toggles
+ * the visible property on the columnDef using toggleColumnVisibility
+ * @param {$scope} $scope of a gridMenu, which contains a reference to the grid
+ */
+ showHideColumns: function( $scope ){
+ var showHideColumns = [];
+ if ( !$scope.grid.options.columnDefs || $scope.grid.options.columnDefs.length === 0 || $scope.grid.columns.length === 0 ) {
+ return showHideColumns;
+ }
+
+ // add header for columns
+ showHideColumns.push({
+ title: i18nService.getSafeText('gridMenu.columns'),
+ order: 300
+ });
+
+ $scope.grid.options.gridMenuTitleFilter = $scope.grid.options.gridMenuTitleFilter ? $scope.grid.options.gridMenuTitleFilter : function( title ) { return title; };
+
+ $scope.grid.options.columnDefs.forEach( function( colDef, index ){
+ if ( colDef.enableHiding !== false ){
+ // add hide menu item - shows an OK icon as we only show when column is already visible
+ var menuItem = {
+ icon: 'ui-grid-icon-ok',
+ action: function($event) {
+ $event.stopPropagation();
+ service.toggleColumnVisibility( this.context.gridCol );
+ },
+ shown: function() {
+ return this.context.gridCol.colDef.visible === true || this.context.gridCol.colDef.visible === undefined;
+ },
+ context: { gridCol: $scope.grid.getColumn(colDef.name || colDef.field) },
+ leaveOpen: true,
+ order: 301 + index * 2
+ };
+ service.setMenuItemTitle( menuItem, colDef, $scope.grid );
+ showHideColumns.push( menuItem );
+
+ // add show menu item - shows no icon as we only show when column is invisible
+ menuItem = {
+ icon: 'ui-grid-icon-cancel',
+ action: function($event) {
+ $event.stopPropagation();
+ service.toggleColumnVisibility( this.context.gridCol );
+ },
+ shown: function() {
+ return !(this.context.gridCol.colDef.visible === true || this.context.gridCol.colDef.visible === undefined);
+ },
+ context: { gridCol: $scope.grid.getColumn(colDef.name || colDef.field) },
+ leaveOpen: true,
+ order: 301 + index * 2 + 1
+ };
+ service.setMenuItemTitle( menuItem, colDef, $scope.grid );
+ showHideColumns.push( menuItem );
+ }
+ });
+ return showHideColumns;
+ },
+
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.gridMenuService
+ * @name setMenuItemTitle
+ * @description Handles the response from gridMenuTitleFilter, adding it directly to the menu
+ * item if it returns a string, otherwise waiting for the promise to resolve or reject then
+ * putting the result into the title
+ * @param {object} menuItem the menuItem we want to put the title on
+ * @param {object} colDef the colDef from which we can get displayName, name or field
+ * @param {Grid} grid the grid, from which we can get the options.gridMenuTitleFilter
+ *
+ */
+ setMenuItemTitle: function( menuItem, colDef, grid ){
+ var title = grid.options.gridMenuTitleFilter( colDef.displayName || gridUtil.readableColumnName(colDef.name) || colDef.field );
+
+ if ( typeof(title) === 'string' ){
+ menuItem.title = title;
+ } else if ( title.then ){
+ // must be a promise
+ menuItem.title = "";
+ title.then( function( successValue ) {
+ menuItem.title = successValue;
+ }, function( errorValue ) {
+ menuItem.title = errorValue;
+ });
+ } else {
+ gridUtil.logError('Expected gridMenuTitleFilter to return a string or a promise, it has returned neither, bad config');
+ menuItem.title = 'badconfig';
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @methodOf ui.grid.gridMenuService
+ * @name toggleColumnVisibility
+ * @description Toggles the visibility of an individual column. Expects to be
+ * provided a context that has on it a gridColumn, which is the column that
+ * we'll operate upon. We change the visibility, and refresh the grid as appropriate
+ * @param {GridCol} gridCol the column that we want to toggle
+ *
+ */
+ toggleColumnVisibility: function( gridCol ) {
+ gridCol.colDef.visible = !( gridCol.colDef.visible === true || gridCol.colDef.visible === undefined );
+
+ gridCol.grid.refresh();
+ gridCol.grid.api.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
+ gridCol.grid.api.core.raise.columnVisibilityChanged( gridCol );
+ }
+ };
+
+ return service;
+}])
+
+
+
+.directive('uiGridMenuButton', ['gridUtil', 'uiGridConstants', 'uiGridGridMenuService', 'i18nService',
+function (gridUtil, uiGridConstants, uiGridGridMenuService, i18nService) {
+
+ return {
+ priority: 0,
+ scope: true,
+ require: ['^uiGrid'],
+ templateUrl: 'ui-grid/ui-grid-menu-button',
+ replace: true,
+
+ link: function ($scope, $elm, $attrs, controllers) {
+ var uiGridCtrl = controllers[0];
+
+ // For the aria label
+ $scope.i18n = {
+ aria: i18nService.getSafeText('gridMenu.aria')
+ };
+
+ uiGridGridMenuService.initialize($scope, uiGridCtrl.grid);
+
+ $scope.shown = false;
+
+ $scope.toggleMenu = function () {
+ if ( $scope.shown ){
+ $scope.$broadcast('hide-menu');
+ $scope.shown = false;
+ } else {
+ $scope.menuItems = uiGridGridMenuService.getMenuItems( $scope );
+ $scope.$broadcast('show-menu');
+ $scope.shown = true;
+ }
+ };
+
+ $scope.$on('menu-hidden', function() {
+ $scope.shown = false;
+ gridUtil.focus.bySelector($elm, '.ui-grid-icon-container');
+ });
+ }
+ };
+
+}]);
+
+})();
+
+(function(){
+
+/**
+ * @ngdoc directive
+ * @name ui.grid.directive:uiGridMenu
+ * @element style
+ * @restrict A
+ *
+ * @description
+ * Allows us to interpolate expressions in `<style>` elements. Angular doesn't do this by default as it can/will/might? break in IE8.
+ *
+ * @example
+ <doc:example module="app">
+ <doc:source>
+ <script>
+ var app = angular.module('app', ['ui.grid']);
+
+ app.controller('MainCtrl', ['$scope', function ($scope) {
+
+ }]);
+ </script>
+
+ <div ng-controller="MainCtrl">
+ <div ui-grid-menu shown="true" ></div>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ </doc:scenario>
+ </doc:example>
+ */
+angular.module('ui.grid')
+
+.directive('uiGridMenu', ['$compile', '$timeout', '$window', '$document', 'gridUtil', 'uiGridConstants', 'i18nService',
+function ($compile, $timeout, $window, $document, gridUtil, uiGridConstants, i18nService) {
+ var uiGridMenu = {
+ priority: 0,
+ scope: {
+ // shown: '&',
+ menuItems: '=',
+ autoHide: '=?'
+ },
+ require: '?^uiGrid',
+ templateUrl: 'ui-grid/uiGridMenu',
+ replace: false,
+ link: function ($scope, $elm, $attrs, uiGridCtrl) {
+
+ $scope.dynamicStyles = '';
+
+ var setupHeightStyle = function(gridHeight) {
+ //menu appears under header row, so substract that height from it's total
+ // additional 20px for general padding
+ var gridMenuMaxHeight = gridHeight - uiGridCtrl.grid.headerHeight - 20;
+ $scope.dynamicStyles = [
+ '.grid' + uiGridCtrl.grid.id + ' .ui-grid-menu-mid {',
+ 'max-height: ' + gridMenuMaxHeight + 'px;',
+ '}'
+ ].join(' ');
+ };
+
+ if (uiGridCtrl) {
+ setupHeightStyle(uiGridCtrl.grid.gridHeight);
+ uiGridCtrl.grid.api.core.on.gridDimensionChanged($scope, function(oldGridHeight, oldGridWidth, newGridHeight, newGridWidth) {
+ setupHeightStyle(newGridHeight);
+ });
+ }
+
+ $scope.i18n = {
+ close: i18nService.getSafeText('columnMenu.close')
+ };
+
+ // *** Show/Hide functions ******
+ $scope.showMenu = function(event, args) {
+ if ( !$scope.shown ){
+
+ /*
+ * In order to animate cleanly we remove the ng-if, wait a digest cycle, then
+ * animate the removal of the ng-hide. We can't successfully (so far as I can tell)
+ * animate removal of the ng-if, as the menu items aren't there yet. And we don't want
+ * to rely on ng-show only, as that leaves elements in the DOM that are needlessly evaluated
+ * on scroll events.
+ *
+ * Note when testing animation that animations don't run on the tutorials. When debugging it looks
+ * like they do, but angular has a default $animate provider that is just a stub, and that's what's
+ * being called. ALso don't be fooled by the fact that your browser has actually loaded the
+ * angular-translate.js, it's not using it. You need to test animations in an external application.
+ */
+ $scope.shown = true;
+
+ $timeout( function() {
+ $scope.shownMid = true;
+ $scope.$emit('menu-shown');
+ });
+ } else if ( !$scope.shownMid ) {
+ // we're probably doing a hide then show, so we don't need to wait for ng-if
+ $scope.shownMid = true;
+ $scope.$emit('menu-shown');
+ }
+
+ var docEventType = 'click';
+ if (args && args.originalEvent && args.originalEvent.type && args.originalEvent.type === 'touchstart') {
+ docEventType = args.originalEvent.type;
+ }
+
+ // Turn off an existing document click handler
+ angular.element(document).off('click touchstart', applyHideMenu);
+ $elm.off('keyup', checkKeyUp);
+ $elm.off('keydown', checkKeyDown);
+
+ // Turn on the document click handler, but in a timeout so it doesn't apply to THIS click if there is one
+ $timeout(function() {
+ angular.element(document).on(docEventType, applyHideMenu);
+ $elm.on('keyup', checkKeyUp);
+ $elm.on('keydown', checkKeyDown);
+
+ });
+ //automatically set the focus to the first button element in the now open menu.
+ gridUtil.focus.bySelector($elm, 'button[type=button]', true);
+ };
+
+
+ $scope.hideMenu = function(event) {
+ if ( $scope.shown ){
+ /*
+ * In order to animate cleanly we animate the addition of ng-hide, then use a $timeout to
+ * set the ng-if (shown = false) after the animation runs. In theory we can cascade off the
+ * callback on the addClass method, but it is very unreliable with unit tests for no discernable reason.
+ *
+ * The user may have clicked on the menu again whilst
+ * we're waiting, so we check that the mid isn't shown before applying the ng-if.
+ */
+ $scope.shownMid = false;
+ $timeout( function() {
+ if ( !$scope.shownMid ){
+ $scope.shown = false;
+ $scope.$emit('menu-hidden');
+ }
+ }, 200);
+ }
+
+ angular.element(document).off('click touchstart', applyHideMenu);
+ $elm.off('keyup', checkKeyUp);
+ $elm.off('keydown', checkKeyDown);
+ };
+
+ $scope.$on('hide-menu', function (event, args) {
+ $scope.hideMenu(event, args);
+ });
+
+ $scope.$on('show-menu', function (event, args) {
+ $scope.showMenu(event, args);
+ });
+
+
+ // *** Auto hide when click elsewhere ******
+ var applyHideMenu = function(){
+ if ($scope.shown) {
+ $scope.$apply(function () {
+ $scope.hideMenu();
+ });
+ }
+ };
+
+ // close menu on ESC and keep tab cyclical
+ var checkKeyUp = function(event) {
+ if (event.keyCode === 27) {
+ $scope.hideMenu();
+ }
+ };
+
+ var checkKeyDown = function(event) {
+ var setFocus = function(elm) {
+ elm.focus();
+ event.preventDefault();
+ return false;
+ };
+ if (event.keyCode === 9) {
+ var firstMenuItem, lastMenuItem;
+ var menuItemButtons = $elm[0].querySelectorAll('button:not(.ng-hide)');
+ if (menuItemButtons.length > 0) {
+ firstMenuItem = menuItemButtons[0];
+ lastMenuItem = menuItemButtons[menuItemButtons.length - 1];
+ if (event.target === lastMenuItem && !event.shiftKey) {
+ setFocus(firstMenuItem);
+ } else if (event.target === firstMenuItem && event.shiftKey) {
+ setFocus(lastMenuItem);
+ }
+ }
+ }
+ };
+
+ if (typeof($scope.autoHide) === 'undefined' || $scope.autoHide === undefined) {
+ $scope.autoHide = true;
+ }
+
+ if ($scope.autoHide) {
+ angular.element($window).on('resize', applyHideMenu);
+ }
+
+ $scope.$on('$destroy', function () {
+ angular.element(document).off('click touchstart', applyHideMenu);
+ });
+
+
+ $scope.$on('$destroy', function() {
+ angular.element($window).off('resize', applyHideMenu);
+ });
+
+ if (uiGridCtrl) {
+ $scope.$on('$destroy', uiGridCtrl.grid.api.core.on.scrollBegin($scope, applyHideMenu ));
+ }
+
+ $scope.$on('$destroy', $scope.$on(uiGridConstants.events.ITEM_DRAGGING, applyHideMenu ));
+ }
+ };
+
+ return uiGridMenu;
+}])
+
+.directive('uiGridMenuItem', ['gridUtil', '$compile', 'i18nService', function (gridUtil, $compile, i18nService) {
+ var uiGridMenuItem = {
+ priority: 0,
+ scope: {
+ name: '=',
+ active: '=',
+ action: '=',
+ icon: '=',
+ shown: '=',
+ context: '=',
+ templateUrl: '=',
+ leaveOpen: '=',
+ screenReaderOnly: '='
+ },
+ require: ['?^uiGrid'],
+ templateUrl: 'ui-grid/uiGridMenuItem',
+ replace: false,
+ compile: function() {
+ return {
+ pre: function ($scope, $elm) {
+ if ($scope.templateUrl) {
+ gridUtil.getTemplate($scope.templateUrl)
+ .then(function (contents) {
+ var template = angular.element(contents);
+
+ var newElm = $compile(template)($scope);
+ $elm.replaceWith(newElm);
+ });
+ }
+ },
+ post: function ($scope, $elm, $attrs, controllers) {
+ var uiGridCtrl = controllers[0];
+
+ // TODO(c0bra): validate that shown and active are functions if they're defined. An exception is already thrown above this though
+ // if (typeof($scope.shown) !== 'undefined' && $scope.shown && typeof($scope.shown) !== 'function') {
+ // throw new TypeError("$scope.shown is defined but not a function");
+ // }
+ if (typeof($scope.shown) === 'undefined' || $scope.shown === null) {
+ $scope.shown = function() { return true; };
+ }
+
+ $scope.itemShown = function () {
+ var context = {};
+ if ($scope.context) {
+ context.context = $scope.context;
+ }
+
+ if (typeof(uiGridCtrl) !== 'undefined' && uiGridCtrl) {
+ context.grid = uiGridCtrl.grid;
+ }
+
+ return $scope.shown.call(context);
+ };
+
+ $scope.itemAction = function($event,title) {
+ $event.stopPropagation();
+
+ if (typeof($scope.action) === 'function') {
+ var context = {};
+
+ if ($scope.context) {
+ context.context = $scope.context;
+ }
+
+ // Add the grid to the function call context if the uiGrid controller is present
+ if (typeof(uiGridCtrl) !== 'undefined' && uiGridCtrl) {
+ context.grid = uiGridCtrl.grid;
+ }
+
+
<TRUNCATED>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[03/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/qdrChartService.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/qdrChartService.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/qdrChartService.js
new file mode 100644
index 0000000..992e80d
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/qdrChartService.js
@@ -0,0 +1,1109 @@
+/*
+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) {
+ 'use strict';
+
+ // The QDR chart service handles periodic gathering data for charts and displaying the charts
+ angular
+ .module('horizon.dashboard.dispatch')
+ .factory('horizon.dashboard.dispatch.chartService', QDRChartService);
+
+ QDRChartService.$inject = [
+ '$rootScope',
+ 'horizon.dashboard.dispatch.comService',
+ '$http',
+ '$location'
+ ];
+
+ function QDRChartService(
+ $rootScope,
+ QDRService,
+ $http,
+ $location) {
+ 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(opts, request) { //name, attr, cinstance, request) {
+
+ var base = findBase(opts.name, opts.attr, request);
+ if (!base) {
+ base = new ChartBase(opts.name, opts.attr, request);
+ bases.push(base);
+ }
+ this.base = base;
+ this.instance = angular.isDefined(opts.instance) ? opts.instance : ++instance;
+ this.dashboard = false; // is this chart on the dashboard page
+ this.hdash = false; // is this chart on the hawtio dashboard page
+ this.hreq = false; // has this hdash chart been requested
+ this.type = opts.type ? opts.type: "value"; // value or rate
+ this.rateWindow = opts.rateWindow ? opts.rateWindow : 1000; // calculate the rate of change over this time interval. higher == smother graph
+ this.areaColor = "#cbe7f3"; // the chart's area color when not an empty string
+ this.lineColor = "#058dc7"; // the chart's line color when not an empty string
+ this.visibleDuration = opts.visibleDuration ? opts.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 name = this.name()
+ var nameparts = name.split('/');
+ if (nameparts.length == 2)
+ name = nameparts[1];
+ var key = (QDRService.nameFromId(this.request().nodeId) || '') + this.request().entity + name + this.attr() + "_" + this.instance + "_" + (this.request().aggregate ? "1" : "0");
+ // 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;
+ o.dashboard = this.dashboard;
+ o.hdash = this.hdash;
+ o.instance = this.instance;
+ 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.aggregate = function (_) {
+ if (!arguments.length) return this.base.request.aggregate;
+ this.base.request.aggregate = _;
+ 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()) || 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({
+ nodeId: this.nodeId(),
+ entity: this.entity(),
+ name: this.name(),
+ attr: this.attr(),
+ interval: this.interval(),
+ forceCreate: true,
+ aggregate: this.aggregate(),
+ hdash: this.hdash
+ })
+ 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(opts) { //nodeId, entity, name, attr, interval, aggregate) {
+ this.duration = opts.duration || 10; // number of minutes to keep the data
+ this.nodeId = opts.nodeId; // eg amqp:/_topo/0/QDR.A/$management
+ this.entity = opts.entity; // eg .router.address
+ // sorted since the responses will always be sorted
+ this.aggregate = opts.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 = opts.interval || 1000; // 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(opts.name, opts.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);
+ }
+ };
+
+ // Below here are the properties and methods available on QDRChartService
+ 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();
+ QDRService.addDisconnectAction( function () {
+ self.charts.forEach( function (chart) {
+ self.unRegisterChart(chart, true)
+ })
+ QDRService.addConnectAction(self.init);
+ })
+ },
+
+ 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 (opts) { //name, attr, nodeId, entity, hdash) {
+ if (!opts.hdash)
+ opts.hdash = false; // rather than undefined
+ return self.charts.filter( function (chart) {
+ return (chart.name() == opts.name &&
+ chart.attr() == opts.attr &&
+ chart.nodeId() == opts.nodeId &&
+ chart.entity() == opts.entity &&
+ chart.hdash == opts.hdash)
+ });
+ },
+
+ 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, skipSave) {
+ 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 && !skipSave)
+ self.saveCharts();
+ }
+ }
+ if (foundBases == 1) {
+ var baseIndex = bases.indexOf(chart.base)
+ bases.splice(baseIndex, 1);
+ }
+ },
+
+ // create a chart that doesn't get saved/loaded and has it's own
+ // request scheduler
+ createRequestAndChart: function (opts) {
+ var request = new ChartRequest(opts)
+ var chart = new Chart(opts, request)
+ return chart
+ },
+
+ // opts are nodeId, entity, name, attr, interval, instance, use_instance,
+ // forceCreate, aggregate, hdash
+ registerChart: function (opts) {
+ var request = self.findChartRequest(opts.nodeId, opts.entity, opts.aggregate);
+ if (request) {
+ // add any new attr or name to the list
+ request.addAttrName(opts.name, opts.attr)
+ } else {
+ // the nodeId/entity did not already exist, so add a new request and chart
+ QDR.log.debug("added new request: " + opts.nodeId + " " + opts.entity);
+ request = new ChartRequest(opts); //nodeId, entity, name, attr, interval, aggregate);
+ self.chartRequests.push(request);
+ self.startCollecting(request);
+ }
+ var charts = self.findCharts(opts); //name, attr, nodeId, entity, hdash);
+ var chart;
+ if (charts.length == 0 || opts.forceCreate) {
+ if (!opts.use_instance && opts.instance)
+ delete opts.instance;
+ chart = new Chart(opts, request) //opts.name, opts.attr, opts.instance, 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, skipSave) {
+ // remove the chart
+
+ // TODO: how do we remove charts that were added to the hawtio dashboard but then removed?
+ // We don't get a notification that they were removed. Instead, we could just stop sending
+ // the request in the background and only send the request when the chart's tick() event is triggered
+ //if (chart.hdash) {
+ // chart.dashboard = false;
+ // self.saveCharts();
+ // return;
+ //}
+
+ 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, skipSave);
+ 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);
+ }
+ }
+ }
+ }
+ if (!skipSave)
+ self.saveCharts();
+ },
+
+ 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);
+ },
+
+ shouldRequest: function (request) {
+ // see if any of the charts associated with this request have either dialog, dashboard, or hreq
+ return self.charts.some( function (chart) {
+ return (chart.dashboard || chart.hreq) || (!chart.dashboard && !chart.hdash);
+ });
+ },
+
+ // send the request
+ sendChartRequest: function (request, once) {
+ if (!once && !self.shouldRequest(request)) {
+ request.setTimeoutHandle = setTimeout(self.sendChartRequest, request.interval, request)
+ return;
+ }
+ // 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) {
+ if (!response || !response.attributeNames)
+ return;
+ //QDR.log.debug("got chart results for " + nodeId + " " + entity);
+ // records is 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 schedule another request
+ if (once)
+ return;
+ request.setTimeoutHandle = setTimeout(self.sendChartRequest, request.interval, request)
+ },
+
+ numCharts: function () {
+ return self.charts.filter( function (chart) { return chart.dashboard }).length;
+ //return self.charts.length;
+ },
+
+ isAttrCharted: function (nodeId, entity, name, attr) {
+ var charts = self.findCharts({
+ name: name,
+ attr: attr,
+ nodeId: nodeId,
+ entity: entity
+ })
+ // if any of the matching charts are on the dashboard page, return true
+ return charts.some(function (chart) {
+ return (chart.dashboard)
+ });
+ },
+
+ addHDash: function (chart) {
+ chart.hdash = true;
+ self.saveCharts();
+ },
+
+ delHDash: function (chart) {
+ chart.hdash = false;
+ self.saveCharts();
+ },
+
+ 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.hdash) {
+ chart.copyProps(minChart);
+ minCharts.push(minChart);
+ }
+ })
+ localStorage["QDRCharts"] = angular.toJson(minCharts);
+ },
+
+ loadCharts: function () {
+ var charts = angular.fromJson(localStorage["QDRCharts"]);
+ if (charts) {
+ // get array of known ids
+ var nodeList = QDRService.nodeList().map( function (node) {
+ return node.id;
+ })
+ charts.forEach(function (chart) {
+ // if this chart is not in the current list of nodes, skip
+ if (nodeList.indexOf(chart.nodeId) >= 0) {
+ if (!angular.isDefined(chart.instance)) {
+ chart.instance = ++instance;
+ }
+ if (chart.instance >= instance)
+ instance = chart.instance + 1;
+ if (!chart.duration)
+ chart.duration = 10;
+ if (chart.nodeList)
+ chart.aggregate = true;
+ if (!chart.hdash)
+ chart.hdash = false;
+ if (!chart.dashboard)
+ chart.dashboard = false;
+ if (!chart.hdash && !chart.dashboard)
+ chart.dashboard = true;
+ if (chart.hdash && chart.dashboard)
+ chart.dashboard = false;
+ chart.forceCreate = true;
+ chart.use_instance = true;
+ var newChart = self.registerChart(chart); //chart.nodeId, chart.entity, chart.name, chart.attr, chart.interval, true, chart.aggregate);
+ newChart.dashboard = chart.dashboard;
+ newChart.hdash = chart.hdash;
+ newChart.hreq = false;
+ newChart.type = chart.type;
+ newChart.rateWindow = chart.rateWindow;
+ newChart.areaColor = chart.areaColor ? chart.areaColor : "#cbe7f3";
+ newChart.lineColor = chart.lineColor ? chart.lineColor : "#058dc7";
+ newChart.duration(chart.duration);
+ newChart.visibleDuration = chart.visibleDuration ? chart.visibleDuration : 10;
+ if (chart.userTitle)
+ newChart.title(chart.userTitle);
+ }
+ })
+ }
+ },
+
+ AreaChart: function (chart) {
+ 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;
+ this.url = $location.absUrl();
+
+ // 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;
+ var 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,
+ this.chart.visibleDuration)
+ .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, visibleDuration) {
+ 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
+ visibleDuration: visibleDuration
+ }
+ 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 + ")");
+
+ sv.append("linearGradient")
+ .attr("id", id) //"temperature-gradient")
+ .attr("gradientUnits", "userSpaceOnUse")
+ .attr("x1", 0).attr("y1", height *.5 )
+ .attr("x2", 0).attr("y2", height * 1.2)
+ .selectAll("stop")
+ .data([
+ {offset: "0%", opacity: 1},
+ {offset: "100%", opacity: 0}
+ ])
+ .enter().append("stop")
+ .attr("offset", function(d) { return d.offset; })
+ .attr("stop-opacity", function(d) { return d.opacity; })
+ .attr("stop-color", function(d) { return "#cbe7f3" });
+/*
+ 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");
+/*
+ var transition = d3.select({}).transition()
+ .duration(2000)
+ .ease("linear");
+*/
+ 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];});
+ //var points = data.length;
+ //var futureDate = new Date(data[points-1][0].getTime() - attrs.visibleDuration * 60 * 1000);
+ //extent = [futureDate, data[points-1][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]}) *.99;
+ var max = d3.max(data, function(d) {return d[1]}) * 1.01;
+ 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()
+
+ // 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; })
+
+ var g = series.enter().append("g")
+ .attr("class", "series")
+
+ g.append("path")
+ .attr("class", "area")
+ .style("fill", "url(" + attrs.url + "#" + id + ") " + attrs.areaColor) //temperature-gradient)")
+ .attr("d", area.y0(y.range()[0]))
+ .attr("transform", null);
+
+ g.append("path")
+ .attr("class", "line")
+ .style("stroke", attrs.lineColor)
+ .attr("d", line)
+/*
+debugger;
+ g.transition()
+ .duration(2000)
+ .attr("transform", "translate(-4)");
+*/
+ series.exit().remove()
+
+ sv.selectAll("stop")
+ .attr("stop-color", attrs.areaColor)
+
+ }
+ // 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/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/config-file-header.html
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/config-file-header.html b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/config-file-header.html
new file mode 100644
index 0000000..b3cb671
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/config-file-header.html
@@ -0,0 +1,17 @@
+## 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
+##
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/download-dialog-template.html
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/download-dialog-template.html b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/download-dialog-template.html
new file mode 100644
index 0000000..1848278
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/download-dialog-template.html
@@ -0,0 +1,23 @@
+<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>
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/node-config-template.html
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/node-config-template.html b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/node-config-template.html
new file mode 100644
index 0000000..5816dfc
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/topology/node-config-template.html
@@ -0,0 +1,51 @@
+<!--
+ This is the template for the node edit dialog that is displayed.
+-->
+<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>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[06/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/slider.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/slider.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/slider.js
new file mode 100644
index 0000000..97189ae
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/slider.js
@@ -0,0 +1,233 @@
+/*
+ jQuery UI Slider plugin wrapper
+*/
+angular.module('ui.slider', []).value('uiSliderConfig',{}).directive('uiSlider', ['uiSliderConfig', '$timeout', function(uiSliderConfig, $timeout) {
+ uiSliderConfig = uiSliderConfig || {};
+ return {
+ require: 'ngModel',
+ compile: function() {
+ var preLink = function (scope, elm, attrs, ngModel) {
+
+ function parseNumber(n, decimals) {
+ return (decimals) ? parseFloat(n) : parseInt(n, 10);
+ }
+
+ var directiveOptions = angular.copy(scope.$eval(attrs.uiSlider));
+ var options = angular.extend(directiveOptions || {}, uiSliderConfig);
+ // Object holding range values
+ var prevRangeValues = {
+ min: null,
+ max: null
+ };
+
+ // convenience properties
+ var properties = ['min', 'max', 'step', 'lowerBound', 'upperBound'];
+ var useDecimals = (!angular.isUndefined(attrs.useDecimals)) ? true : false;
+ var updateOn = (angular.isDefined(options['updateOn'])) ? options['updateOn'] : 'slide'
+
+ var init = function() {
+ // When ngModel is assigned an array of values then range is expected to be true.
+ // Warn user and change range to true else an error occurs when trying to drag handle
+ if (angular.isArray(ngModel.$viewValue) && options.range !== true) {
+ console.warn('Change your range option of ui-slider. When assigning ngModel an array of values then the range option should be set to true.');
+ options.range = true;
+ }
+
+ // Ensure the convenience properties are passed as options if they're defined
+ // This avoids init ordering issues where the slider's initial state (eg handle
+ // position) is calculated using widget defaults
+ // Note the properties take precedence over any duplicates in options
+ angular.forEach(properties, function(property) {
+ if (angular.isDefined(attrs[property])) {
+ options[property] = parseNumber(attrs[property], useDecimals);
+ }
+ });
+
+ elm.slider(options);
+ init = angular.noop;
+ };
+
+ // Find out if decimals are to be used for slider
+ angular.forEach(properties, function(property) {
+ // support {{}} and watch for updates
+ attrs.$observe(property, function(newVal) {
+ if (!!newVal) {
+ init();
+ options[property] = parseNumber(newVal, useDecimals);
+ elm.slider('option', property, parseNumber(newVal, useDecimals));
+ ngModel.$render();
+ }
+ });
+ });
+ attrs.$observe('disabled', function(newVal) {
+ init();
+ elm.slider('option', 'disabled', !!newVal);
+ });
+
+ // Watch ui-slider (byVal) for changes and update
+ scope.$watch(attrs.uiSlider, function(newVal) {
+ init();
+ if(newVal !== undefined) {
+ elm.slider('option', newVal);
+ }
+ }, true);
+
+ // Late-bind to prevent compiler clobbering
+ $timeout(init, 0, true);
+
+ // Update model value from slider
+ elm.bind(updateOn, function(event, ui) {
+ var valuesChanged;
+
+ if (ui.values) {
+ var boundedValues = ui.values.slice();
+
+ if (options.lowerBound && boundedValues[0] < options.lowerBound) {
+ boundedValues[0] = Math.max(boundedValues[0], options.lowerBound);
+ }
+ if (options.upperBound && boundedValues[1] > options.upperBound) {
+ boundedValues[1] = Math.min(boundedValues[1], options.upperBound);
+ }
+
+ if (boundedValues[0] !== ui.values[0] || boundedValues[1] !== ui.values[1]) {
+ valuesChanged = true;
+ ui.values = boundedValues;
+ }
+ } else {
+ var boundedValue = ui.value;
+
+ if (options.lowerBound && boundedValue < options.lowerBound) {
+ boundedValue = Math.max(boundedValue, options.lowerBound);
+ }
+ if (options.upperBound && boundedValue > options.upperBound) {
+ boundedValue = Math.min(boundedValue, options.upperBound);
+ }
+
+ if (boundedValue !== ui.value) {
+ valuesChanged = true;
+ ui.value = boundedValue;
+ }
+ }
+
+
+ ngModel.$setViewValue(ui.values || ui.value);
+ $(ui.handle).find('.ui-slider-tip').text(ui.value);
+ scope.$apply();
+
+ if (valuesChanged) {
+ setTimeout(function() {
+ elm.slider('value', ui.values || ui.value);
+ }, 0);
+
+ return false;
+ }
+ });
+
+ // Update slider from model value
+ ngModel.$render = function() {
+ init();
+ var method = options.range === true ? 'values' : 'value';
+
+ if (options.range !== true && isNaN(ngModel.$viewValue) && !(ngModel.$viewValue instanceof Array)) {
+ ngModel.$viewValue = 0;
+ }
+ else if (options.range && !angular.isDefined(ngModel.$viewValue)) {
+ ngModel.$viewValue = [0,0];
+ }
+
+ // Do some sanity check of range values
+ if (options.range === true) {
+ // previously, the model was a string b/c it was in a text input, need to convert to a array.
+ // make sure input exists, comma exists once, and it is a string.
+ if (ngModel.$viewValue && angular.isString(ngModel.$viewValue) && (ngModel.$viewValue.match(/,/g) || []).length === 1) {
+ // transform string model into array.
+ var valueArr = ngModel.$viewValue.split(',');
+ ngModel.$viewValue = [Number(valueArr[0]), Number(valueArr[1])];
+ }
+ // Check outer bounds for min and max values
+ if (angular.isDefined(options.min) && options.min > ngModel.$viewValue[0]) {
+ ngModel.$viewValue[0] = options.min;
+ }
+ if (angular.isDefined(options.max) && options.max < ngModel.$viewValue[1]) {
+ ngModel.$viewValue[1] = options.max;
+ }
+
+ // Check min and max range values
+ if (ngModel.$viewValue[0] > ngModel.$viewValue[1]) {
+ // Min value should be less to equal to max value
+ if (prevRangeValues.min >= ngModel.$viewValue[1]) {
+ ngModel.$viewValue[1] = prevRangeValues.min;
+ }
+ // Max value should be less to equal to min value
+ if (prevRangeValues.max <= ngModel.$viewValue[0]) {
+ ngModel.$viewValue[0] = prevRangeValues.max;
+ }
+ }
+
+ // Store values for later user
+ prevRangeValues.min = ngModel.$viewValue[0];
+ prevRangeValues.max = ngModel.$viewValue[1];
+
+ }
+ elm.slider(method, ngModel.$viewValue);
+ };
+
+ scope.$watch(attrs.ngModel, function() {
+ if (options.range === true) {
+ ngModel.$render();
+
+ $(elm).find('.ui-slider-tip').each(function(i, tipElm) {
+ $(tipElm).text(ngModel.$viewValue[i]);
+ });
+ } else {
+ $(elm).find('.ui-slider-tip').text(ngModel.$viewValue);
+ }
+ }, true);
+
+ function destroy() {
+ if (elm.hasClass('ui-slider')) {
+ elm.slider('destroy');
+ }
+ }
+
+ scope.$on("$destroy", destroy);
+ elm.one('$destroy', destroy);
+ };
+
+ var postLink = function (scope, element, attrs, ngModel) {
+ // Add tick marks if 'tick' and 'step' attributes have been setted on element.
+ // Support horizontal slider bar so far. 'tick' and 'step' attributes are required.
+ var options = angular.extend({}, scope.$eval(attrs.uiSlider));
+ var properties = ['min', 'max', 'step', 'tick', 'tip'];
+ angular.forEach(properties, function(property) {
+ if (angular.isDefined(attrs[property])) {
+ options[property] = attrs[property];
+ }
+ });
+ if (angular.isDefined(options['tick']) && angular.isDefined(options['step'])) {
+ var total = parseInt( (parseInt(options['max']) - parseInt(options['min'])) /parseInt(options['step']));
+ for (var i = total; i >= 0; i--) {
+ var left = ((i / total) * 100) + '%';
+ $("<div/>").addClass("ui-slider-tick").appendTo(element).css({left: left});
+ };
+ }
+ if(angular.isDefined(options['tip'])) {
+ $timeout(function(){
+ var handles = element.find('.ui-slider-handle');
+ if(handles && handles.length>1 && ngModel.$viewValue && angular.isArray(ngModel.$viewValue)){
+ $(handles[0]).append('<div class="ui-slider-tip">'+ngModel.$viewValue[0]+'</div>');
+ $(handles[1]).append('<div class="ui-slider-tip">'+ngModel.$viewValue[1]+'</div>');
+ }else{
+ element.find('.ui-slider-handle').append('<div class="ui-slider-tip">'+ngModel.$viewValue+'</div>');
+ }
+ },10);
+ }
+ }
+
+ return {
+ pre: preLink,
+ post: postLink
+ };
+ }
+ };
+}]);
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/tooltipsy.min.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/tooltipsy.min.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/tooltipsy.min.js
new file mode 100644
index 0000000..ed2c2f8
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/tooltipsy.min.js
@@ -0,0 +1,20 @@
+/* tooltipsy by Brian Cray
+ * Lincensed under GPL2 - http://www.gnu.org/licenses/gpl-2.0.html
+ * Option quick reference:
+ * - alignTo: "element" or "cursor" (Defaults to "element")
+ * - offset: Tooltipsy distance from element or mouse cursor, dependent on alignTo setting. Set as array [x, y] (Defaults to [0, -1])
+ * - content: HTML or text content of tooltip. Defaults to "" (empty string), which pulls content from target element's title attribute
+ * - show: function(event, tooltip) to show the tooltip. Defaults to a show(100) effect
+ * - hide: function(event, tooltip) to hide the tooltip. Defaults to a fadeOut(100) effect
+ * - delay: A delay in milliseconds before showing a tooltip. Set to 0 for no delay. Defaults to 200
+ * - css: object containing CSS properties and values. Defaults to {} to use stylesheet for styles
+ * - className: DOM class for styling tooltips with CSS. Defaults to "tooltipsy"
+ * - showEvent: Set a custom event to bind the show function. Defaults to mouseenter
+ * - hideEvent: Set a custom event to bind the show function. Defaults to mouseleave
+ * Method quick reference:
+ * - $('element').data('tooltipsy').show(): Force the tooltip to show
+ * - $('element').data('tooltipsy').hide(): Force the tooltip to hide
+ * - $('element').data('tooltipsy').destroy(): Remove tooltip from DOM
+ * More information visit http://tooltipsy.com/
+ */
+;(function(a){a.tooltipsy=function(c,b){this.options=b;this.$el=a(c);this.title=this.$el.attr("title")||"";this.$el.attr("title","");this.random=parseInt(Math.random()*10000);this.ready=false;this.shown=false;this.width=0;this.height=0;this.delaytimer=null;this.$el.data("tooltipsy",this);this.init()};a.tooltipsy.prototype={init:function(){var e=this,d,b=e.$el,c=b[0];e.settings=d=a.extend({},e.defaults,e.options);d.delay=+d.delay;if(typeof d.content==="function"){e.readify()}if(d.showEvent===d.hideEvent&&d.showEvent==="click"){b.toggle(function(f){if(d.showEvent==="click"&&c.tagName=="A"){f.preventDefault()}if(d.delay>0){e.delaytimer=window.setTimeout(function(){e.show(f)},d.delay)}else{e.show(f)}},function(f){if(d.showEvent==="click"&&c.tagName=="A"){f.preventDefault()}window.clearTimeout(e.delaytimer);e.delaytimer=null;e.hide(f)})}else{b.bind(d.showEvent,function(f){if(d.showEvent==="click"&&c.tagName=="A"){f.preventDefault()}e.delaytimer=window.setTimeout(function(){e.show(f)},d.d
elay||0)}).bind(d.hideEvent,function(f){if(d.showEvent==="click"&&c.tagName=="A"){f.preventDefault()}window.clearTimeout(e.delaytimer);e.delaytimer=null;e.hide(f)})}},show:function(i){if(this.ready===false){this.readify()}var b=this,f=b.settings,h=b.$tipsy,k=b.$el,d=k[0],g=b.offset(d);if(b.shown===false){if((function(m){var l=0,e;for(e in m){if(m.hasOwnProperty(e)){l++}}return l})(f.css)>0){b.$tip.css(f.css)}b.width=h.outerWidth();b.height=h.outerHeight()}if(f.alignTo==="cursor"&&i){var j=[i.clientX+f.offset[0],i.clientY+f.offset[1]];if(j[0]+b.width>a(window).width()){var c={top:j[1]+"px",right:j[0]+"px",left:"auto"}}else{var c={top:j[1]+"px",left:j[0]+"px",right:"auto"}}}else{var j=[(function(){if(f.offset[0]<0){return g.left-Math.abs(f.offset[0])-b.width}else{if(f.offset[0]===0){return g.left-((b.width-k.outerWidth())/2)}else{return g.left+k.outerWidth()+f.offset[0]}}})(),(function(){if(f.offset[1]<0){return g.top-Math.abs(f.offset[1])-b.height}else{if(f.offset[1]===0){return g.to
p-((b.height-b.$el.outerHeight())/2)}else{return g.top+b.$el.outerHeight()+f.offset[1]}}})()]}h.css({top:j[1]+"px",left:j[0]+"px"});b.settings.show(i,h.stop(true,true))},hide:function(c){var b=this;if(b.ready===false){return}if(c&&c.relatedTarget===b.$tip[0]){b.$tip.bind("mouseleave",function(d){if(d.relatedTarget===b.$el[0]){return}b.settings.hide(d,b.$tipsy.stop(true,true))});return}b.settings.hide(c,b.$tipsy.stop(true,true))},readify:function(){this.ready=true;this.$tipsy=a('<div id="tooltipsy'+this.random+'" style="position:fixed;z-index:2147483647;display:none">').appendTo("body");this.$tip=a('<div class="'+this.settings.className+'">').appendTo(this.$tipsy);this.$tip.data("rootel",this.$el);var c=this.$el;var b=this.$tip;this.$tip.html(this.settings.content!=""?(typeof this.settings.content=="string"?this.settings.content:this.settings.content(c,b)):this.title)},offset:function(b){return this.$el[0].getBoundingClientRect()},destroy:function(){if(this.$tipsy){this.$tipsy.remove
();a.removeData(this.$el,"tooltipsy")}},defaults:{alignTo:"element",offset:[0,-1],content:"",show:function(c,b){b.fadeIn(100)},hide:function(c,b){b.fadeOut(100)},css:{},className:"tooltipsy",delay:200,showEvent:"mouseenter",hideEvent:"mouseleave"}};a.fn.tooltipsy=function(b){return this.each(function(){new a.tooltipsy(this,b)})}})(jQuery);
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[07/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/rhea-min.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/rhea-min.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/rhea-min.js
new file mode 100644
index 0000000..939f401
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/lib/rhea-min.js
@@ -0,0 +1,4 @@
+require=function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){(function(c,d){"use strict";function e(a,b,c){Error.call(this),Error.captureStackTrace(this,this.constructor),this.message=a,this.condition=b,this.description=a,this.connection=c}function f(a){return a.get_id_string?a.get_id_string():a.localAddress+":"+a.localPort+" -> "+a.remoteAddress+":"+a.remotePort}function g(a){var b=null;return{get_session:function(){return b||(b=a.create_session(),b.begin()),b}}}function h(a,b){if(a){var c,d=a;return function(e){return c!==e&&(d=a,c=e),d--?b(e):-1}}return b}function i(a,b){var c,d=a;re
turn function(e){c!==e&&(d=a,c=e);var f=d,g=2*d;return d=b>g?g:b,f}}function j(a){if(void 0===a.transport||"tcp"===a.transport)return u.connect;if("tls"===a.transport||"ssl"===a.transport)return v.connect;throw Error("Unrecognised transport: "+a.transport)}function k(a){var b={};return b.connect=a.connect?a.connect:j(a),b.host=a.host?a.host:"localhost",b.port=a.port?a.port:5672,b.options=a,b}function l(a){return function(){return this.remote.open?this.remote.open[a]:void 0}}function m(a){A.prototype["on_"+a]=function(b){var c=this.remote_channel_map[b.channel];if(!c)throw Error(a+" received on invalid channel "+b.channel);c["on_"+a](b)}}var n=a("./frames.js"),o=a("./log.js"),p=a("./sasl.js"),q=a("./util.js"),r=a("./endpoint.js"),s=a("./session.js"),t=a("./transport.js"),u=a("net"),v=a("tls"),w=a("events").EventEmitter,x=0;a("util").inherits(e,Error);var y=["container_id","hostname","max_frame_size","channel_max","idle_time_out","outgoing_locales","incoming_locales","offered_capabili
ties","desired_capabilities","properties"],z=1,A=function(a,b){if(this.options={},a)for(var c in a)this.options[c]=a[c];if(this.container=b,this.options.id||(this.options.id="connection-"+z++),this.options.container_id||(this.options.container_id=b?b.id:q.generate_uuid()),!this.options.connection_details){var d=this;this.options.connection_details=function(){return k(d.options)}}var e=this.get_option("reconnect",!0);if("boolean"==typeof e&&e){var f=this.get_option("initial_reconnect_delay",100),j=this.get_option("max_reconnect_delay",6e4);this.options.reconnect=h(this.get_option("reconnect_limit"),i(f,j))}else if("number"==typeof e){var m=this.options.reconnect;this.options.reconnect=h(this.get_option("reconnect_limit"),function(){return m})}this.registered=!1,this.state=new r,this.local_channel_map={},this.remote_channel_map={},this.local={},this.remote={},this.local.open=n.open(this.options),this.local.close=n.close({}),this.session_policy=g(this),this.amqp_transport=new t(this.op
tions.id,x,n.TYPE_AMQP,this),this.sasl_transport=void 0,this.transport=this.amqp_transport,this.conn_established_counter=0,this.heartbeat_out=void 0,this.heartbeat_in=void 0,this.abort_idle=!1,this.socket_ready=!1,this.scheduled_reconnect=void 0,this.default_sender=void 0;for(var o in y){var p=y[o];Object.defineProperty(this,p,{get:l(p)})}Object.defineProperty(this,"error",{get:function(){return this.remote.close?this.remote.close.error:void 0}})};A.prototype=Object.create(w.prototype),A.prototype.constructor=A,A.prototype.dispatch=function(a){return o.events("Connection got event: "+a),this.listeners(a).length?(w.prototype.emit.apply(this,arguments),!0):this.container?this.container.dispatch.apply(this.container,arguments):!1},A.prototype.reset=function(){this.abort_idle&&(this.abort_idle=!1,this.local.close.error=void 0,this.state=new r,this.state.open()),this.amqp_transport=new t(this.options.id,x,n.TYPE_AMQP,this),this.sasl_transport=void 0,this.transport=this.amqp_transport,thi
s.state.disconnected(),this.remote={},this.remote_channel_map={};for(var a in this.local_channel_map)this.local_channel_map[a].reset();this.socket_ready=!1},A.prototype.connect=function(){return this.is_server=!1,this._connect(this.options.connection_details(this.conn_established_counter)),this.open(),this},A.prototype.reconnect=function(){return this.scheduled_reconnect=void 0,o.reconnect("reconnecting..."),this.reset(),this._connect(this.options.connection_details(this.conn_established_counter)),c.nextTick(this._process.bind(this)),this},A.prototype._connect=function(a){return a.connect?this.init(a.connect(a.port,a.host,a.options,this.connected.bind(this))):this.init(j(a)(a.port,a.host,a.options,this.connected.bind(this))),this},A.prototype.accept=function(a){return this.is_server=!0,o.io("["+this.id+"] client accepted: "+f(a)),this.socket_ready=!0,this.init(a)},A.prototype.init=function(a){if(this.socket=a,this.socket.on("data",this.input.bind(this)),this.socket.on("error",this.o
n_error.bind(this)),this.socket.on("end",this.eof.bind(this)),this.is_server){var b;this.container&&Object.getOwnPropertyNames(this.container.sasl_server_mechanisms).length&&(b=this.container.sasl_server_mechanisms),this.socket.encrypted&&this.socket.authorized&&this.get_option("enable_sasl_external",!1)&&(b=p.server_add_external(b?q.clone(b):{})),b&&(this.sasl_transport=new p.Server(this,b))}else{var c=this.get_option("sasl_mechanisms");if(!c){var d=this.get_option("username"),e=this.get_option("password");d&&(c=p.client_mechanisms(),e?c.enable_plain(d,e):c.enable_anonymous(d))}this.socket.encrypted&&this.options.cert&&this.get_option("enable_sasl_external",!1)&&(c||(c=p.client_mechanisms()),c.enable_external()),c&&(this.sasl_transport=new p.Client(this,c))}return this.transport=this.sasl_transport?this.sasl_transport:this.amqp_transport,this},A.prototype.attach_sender=function(a){return this.session_policy.get_session().attach_sender(a)},A.prototype.open_sender=A.prototype.attach_
sender,A.prototype.attach_receiver=function(a){return this.session_policy.get_session().attach_receiver(a)},A.prototype.open_receiver=A.prototype.attach_receiver,A.prototype.get_option=function(a,b){return void 0!==this.options[a]?this.options[a]:this.container?this.container.get_option(a,b):b},A.prototype.send=function(a){return void 0===this.default_sender&&(this.default_sender=this.open_sender({target:{}})),this.default_sender.send(a)},A.prototype.connected=function(){this.socket_ready=!0,this.conn_established_counter++,o.io("["+this.options.id+"] connected "+f(this.socket)),this.output()},A.prototype.sasl_failed=function(a){this.transport_error={condition:"amqp:unauthorized-access",description:a},this._handle_error()},A.prototype._handle_error=function(){var a=this.get_error();if(a){var b=this.dispatch("connection_error",this._context());return b=this.dispatch("connection_close",this._context())||b,b||w.prototype.emit.call(this.container,"error",new e(a.description,a.condition,t
his)),!0}return!1},A.prototype.get_error=function(){return this.transport_error?this.transport_error:this.remote.close&&this.remote.close.error?this.remote.close.error:void 0},A.prototype.output=function(){this.socket&&this.socket_ready&&(this.heartbeat_out&&clearTimeout(this.heartbeat_out),this.transport.write(this.socket),(this.is_closed()&&this.state.has_settled()||this.abort_idle||this.transport_error)&&!this.transport.has_writes_pending()?this.socket.end():this.is_open()&&this.remote.open.idle_time_out&&(this.heartbeat_out=setTimeout(this._write_frame.bind(this),this.remote.open.idle_time_out/2)))},A.prototype.input=function(a){this.heartbeat_in&&clearTimeout(this.heartbeat_in),o.io("["+this.options.id+"] read "+a.length+" bytes");var b;this.previous_input?(b=d.concat([this.previous_input,a],this.previous_input.length+a.length),this.previous_input=null):b=a;var c=this.transport.read(b,this);c<b.length&&(this.previous_input=b.slice(c)),this.local.open.idle_time_out&&(this.heartb
eat_in=setTimeout(this.idle.bind(this),this.local.open.idle_time_out)),this.transport.has_writes_pending()?this.output():this.is_closed()&&this.state.has_settled()?this.socket.end():this.is_open()&&this.remote.open.idle_time_out&&!this.heartbeat_out&&(this.heartbeat_out=setTimeout(this._write_frame.bind(this),this.remote.open.idle_time_out/2))},A.prototype.idle=function(){this.is_open()&&(this.abort_idle=!0,this.local.close.error={condition:"amqp:resource-limit-exceeded",description:"max idle time exceeded"},this.close())},A.prototype.on_error=function(a){console.log("["+this.options.id+"] error: "+a),this._disconnected()},A.prototype.eof=function(){this._disconnected()},A.prototype._disconnected=function(){if(this.heartbeat_out&&clearTimeout(this.heartbeat_out),this.heartbeat_in&&clearTimeout(this.heartbeat_in),!this.is_closed()&&void 0===this.scheduled_reconnect&&(this.dispatch("disconnected",this._context())||console.log("["+this.options.id+"] disconnected "),!this.is_server&&!th
is.transport_error&&this.options.reconnect)){var a=this.options.reconnect(this.conn_established_counter);a>=0&&(o.reconnect("Scheduled reconnect in "+a+"ms"),this.scheduled_reconnect=setTimeout(this.reconnect.bind(this),a))}},A.prototype.open=function(){this.state.open()&&this._register()},A.prototype.close=function(a){a&&(this.local.close.error=a),this.state.close()&&this._register()},A.prototype.is_open=function(){return this.state.is_open()},A.prototype.is_closed=function(){return this.state.is_closed()},A.prototype.create_session=function(){for(var a=0;this.local_channel_map[a];)a++;var b=new s(this,a);return this.local_channel_map[a]=b,b},A.prototype.on_open=function(a){if(!this.state.remote_opened())throw Error("Open already received");this.remote.open=a.performative,this.open(),this.dispatch("connection_open",this._context())},A.prototype.on_close=function(a){if(!this.state.remote_closed())throw Error("Close already received");this.remote.close=a.performative,this.close(),thi
s.remote.close.error?this._handle_error():this.dispatch("connection_close",this._context()),this.heartbeat_out&&clearTimeout(this.heartbeat_out)},A.prototype._register=function(){this.registered||(this.registered=!0,c.nextTick(this._process.bind(this)))},A.prototype._process=function(){this.registered=!1;do{this.state.need_open()&&this._write_open();for(var a in this.local_channel_map)this.local_channel_map[a]._process();this.state.need_close()&&this._write_close()}while(!this.state.has_settled())},A.prototype._write_frame=function(a,b,c){this.amqp_transport.encode(n.amqp_frame(a,b,c)),this.output()},A.prototype._write_open=function(){this._write_frame(0,this.local.open.described())},A.prototype._write_close=function(){this._write_frame(0,this.local.close.described())},A.prototype.on_begin=function(a){var b;if(null===a.performative.remote_channel||void 0===a.performative.remote_channel)b=this.create_session(),b.local.begin.remote_channel=a.channel;else if(b=this.local_channel_map[a.
performative.remote_channel],!b)throw Error("Invalid value for remote channel "+a.performative.remote_channel);b.on_begin(a),this.remote_channel_map[a.channel]=b},A.prototype.get_peer_certificate=function(){return this.socket&&this.socket.getPeerCertificate?this.socket.getPeerCertificate():void 0},A.prototype.get_tls_socket=function(){return!this.socket||"tls"!==this.options.transport&&"ssl"!==this.options.transport?void 0:this.socket},A.prototype._context=function(a){var b=a?a:{};return b.connection=this,this.container&&(b.container=this.container),b},m("end"),m("attach"),m("detach"),m("transfer"),m("disposition"),m("flow"),b.exports=A}).call(this,a("_process"),a("buffer").Buffer)},{"./endpoint.js":2,"./frames.js":3,"./log.js":5,"./sasl.js":8,"./session.js":9,"./transport.js":11,"./util.js":13,_process:21,buffer:16,events:20,net:15,tls:15,util:30}],2:[function(a,b,c){"use strict";var d=function(){this.init()};d.prototype.init=function(){this.local_open=!1,this.remote_open=!1,this.o
pen_requests=0,this.close_requests=0,this.initialised=!1},d.prototype.open=function(){return this.initialised=!0,this.local_open?!1:(this.local_open=!0,this.open_requests++,!0)},d.prototype.close=function(){return this.local_open?(this.local_open=!1,this.close_requests++,!0):!1},d.prototype.disconnected=function(){var a=this.local_open;this.init(),a?this.open():this.close()},d.prototype.remote_opened=function(){return this.remote_open?!1:(this.remote_open=!0,!0)},d.prototype.remote_closed=function(){return this.remote_open?(this.remote_open=!1,!0):!1},d.prototype.is_open=function(){return this.local_open&&this.remote_open},d.prototype.is_closed=function(){return this.initialised&&!this.local_open&&!this.remote_open},d.prototype.has_settled=function(){return 0===this.open_requests&&0===this.close_requests},d.prototype.need_open=function(){return this.open_requests>0?(this.open_requests--,!0):!1},d.prototype.need_close=function(){return this.close_requests>0?(this.close_requests--,!0)
:!1},b.exports=d},{}],3:[function(a,b,c){"use strict";function d(a,b){var c=e.define_composite(b);f[b.name]=c.create,g[Number(c.descriptor.numeric).toString(10)]=c,g[c.descriptor.symbolic]=c}var e=a("./types.js"),f={},g={};f.read_header=function(a){var b=4,c={},d=a.toString("ascii",0,b);if("AMQP"!==d)throw Error("Invalid protocol header for AMQP "+d);if(c.protocol_id=a.readUInt8(b++),c.major=a.readUInt8(b++),c.minor=a.readUInt8(b++),c.revision=a.readUInt8(b++),1!==c.major||0!==c.minor)throw Error("Unsupported AMQP version: "+JSON.stringify(c));return c},f.write_header=function(a,b){var c=4;return a.write("AMQP",0,c,"ascii"),a.writeUInt8(b.protocol_id,c++),a.writeUInt8(b.major,c++),a.writeUInt8(b.minor,c++),a.writeUInt8(b.revision,c++),8},f.TYPE_AMQP=0,f.TYPE_SASL=1,f.read_frame=function(a){var b=new e.Reader(a),c={};if(c.size=b.read_uint(4),b.remaining<c.size)return null;var d=b.read_uint(1);if(2>d)throw Error("Invalid data offset, must be at least 2 was "+d);if(c.type=b.read_uint(1
),c.type===f.TYPE_AMQP)c.channel=b.read_uint(2);else{if(c.type!==f.TYPE_SASL)throw Error("Unknown frame type "+c.type);b.skip(2)}if(d>1&&b.skip(4*d-8),b.remaining()){c.performative=b.read();var h=g[c.performative.descriptor.value];h&&(c.performative=new h(c.performative.value)),b.remaining()&&(c.payload=b.read_bytes(b.remaining()))}return c},f.write_frame=function(a){var b=new e.Writer;if(b.skip(4),b.write_uint(2,1),b.write_uint(a.type,1),a.type===f.TYPE_AMQP)b.write_uint(a.channel,2);else{if(a.type!==f.TYPE_SASL)throw Error("Unknown frame type "+a.type);b.write_uint(0,2)}a.performative&&(b.write(a.performative),a.payload&&b.write_bytes(a.payload));var c=b.toBuffer();return c.writeUInt32BE(c.length,0),c},f.amqp_frame=function(a,b,c){return{channel:a||0,type:f.TYPE_AMQP,performative:b,payload:c}},f.sasl_frame=function(a){return{channel:0,type:f.TYPE_SASL,performative:a}};var h={name:"open",code:16,fields:[{name:"container_id",type:"string",mandatory:!0},{name:"hostname",type:"string"
},{name:"max_frame_size",type:"uint",default_value:4294967295},{name:"channel_max",type:"ushort",default_value:65535},{name:"idle_time_out",type:"uint"},{name:"outgoing_locales",type:"symbol",multiple:!0},{name:"incoming_locales",type:"symbol",multiple:!0},{name:"offered_capabilities",type:"symbol",multiple:!0},{name:"desired_capabilities",type:"symbol",multiple:!0},{name:"properties",type:"symbolic_map"}]},i={name:"begin",code:17,fields:[{name:"remote_channel",type:"ushort"},{name:"next_outgoing_id",type:"uint",mandatory:!0},{name:"incoming_window",type:"uint",mandatory:!0},{name:"outgoing_window",type:"uint",mandatory:!0},{name:"handle_max",type:"uint",default_value:"4294967295"},{name:"offered_capabilities",type:"symbol",multiple:!0},{name:"desired_capabilities",type:"symbol",multiple:!0},{name:"properties",type:"symbolic_map"}]},j={name:"attach",code:18,fields:[{name:"name",type:"string",mandatory:!0},{name:"handle",type:"uint",mandatory:!0},{name:"role",type:"boolean",mandatory
:!0},{name:"snd_settle_mode",type:"ubyte",default_value:2},{name:"rcv_settle_mode",type:"ubyte",default_value:0},{name:"source",type:"*"},{name:"target",type:"*"},{name:"unsettled",type:"map"},{name:"incomplete_unsettled",type:"boolean",default_value:!1},{name:"initial_delivery_count",type:"uint"},{name:"max_message_size",type:"ulong"},{name:"offered_capabilities",type:"symbol",multiple:!0},{name:"desired_capabilities",type:"symbol",multiple:!0},{name:"properties",type:"symbolic_map"}]},k={name:"flow",code:19,fields:[{name:"next_incoming_id",type:"uint"},{name:"incoming_window",type:"uint",mandatory:!0},{name:"next_outgoing_id",type:"uint",mandatory:!0},{name:"outgoing_window",type:"uint",mandatory:!0},{name:"handle",type:"uint"},{name:"delivery_count",type:"uint"},{name:"link_credit",type:"uint"},{name:"available",type:"uint"},{name:"drain",type:"boolean",default_value:!1},{name:"echo",type:"boolean",default_value:!1},{name:"properties",type:"symbolic_map"}]},l={name:"transfer",cod
e:20,fields:[{name:"handle",type:"uint",mandatory:!0},{name:"delivery_id",type:"uint"},{name:"delivery_tag",type:"binary"},{name:"message_format",type:"uint"},{name:"settled",type:"boolean"},{name:"more",type:"boolean",default_value:!1},{name:"rcv_settle_mode",type:"ubyte"},{name:"state",type:"delivery_state"},{name:"resume",type:"boolean",default_value:!1},{name:"aborted",type:"boolean",default_value:!1},{name:"batchable",type:"boolean",default_value:!1}]},m={name:"disposition",code:21,fields:[{name:"role",type:"boolean",mandatory:!0},{name:"first",type:"uint",mandatory:!0},{name:"last",type:"uint"},{name:"settled",type:"boolean",default_value:!1},{name:"state",type:"*"},{name:"batchable",type:"boolean",default_value:!1}]},n={name:"detach",code:22,fields:[{name:"handle",type:"uint",mandatory:!0},{name:"closed",type:"boolean",default_value:!1},{name:"error",type:"error"}]},o={name:"end",code:23,fields:[{name:"error",type:"error"}]},p={name:"close",code:24,fields:[{name:"error",type:
"error"}]};d(f.TYPE_AMQP,h),d(f.TYPE_AMQP,i),d(f.TYPE_AMQP,j),d(f.TYPE_AMQP,k),d(f.TYPE_AMQP,l),d(f.TYPE_AMQP,m),d(f.TYPE_AMQP,n),d(f.TYPE_AMQP,o),d(f.TYPE_AMQP,p);var q={name:"sasl_mechanisms",code:64,fields:[{name:"sasl_server_mechanisms",type:"symbol",multiple:!0,mandatory:!0}]},r={name:"sasl_init",code:65,fields:[{name:"mechanism",type:"symbol",mandatory:!0},{name:"initial_response",type:"binary"},{name:"hostname",type:"string"}]},s={name:"sasl_challenge",code:66,fields:[{name:"challenge",type:"binary",mandatory:!0}]},t={name:"sasl_response",code:67,fields:[{name:"response",type:"binary",mandatory:!0}]},u={name:"sasl_outcome",code:68,fields:[{name:"code",type:"ubyte",mandatory:!0},{name:"additional_data",type:"binary"}]};d(f.TYPE_SASL,q),d(f.TYPE_SASL,r),d(f.TYPE_SASL,s),d(f.TYPE_SASL,t),d(f.TYPE_SASL,u),b.exports=f},{"./types.js":12}],4:[function(a,b,c){(function(c){"use strict";function d(a){a.delivery.settled=!0}function e(a){a.delivery.update(void 0,k.accepted().described())
}function f(a,b,c){Error.call(this),Error.captureStackTrace(this,this.constructor),this.message=a,this.condition=b,this.description=a,this.link=c}function g(a){switch(a){case"name":case"handle":case"role":case"initial_delivery_count":return!0;default:return!1}}function h(a){return function(){return this.remote.attach?this.remote.attach[a]:void 0}}var i=a("./frames.js"),j=a("./log.js"),k=a("./message.js"),l=a("./terminus.js"),m=a("./endpoint.js"),n=function(a){this.window=a};n.prototype.update=function(a){var b=this.window-a.receiver.credit;a.receiver.flow(b)},a("util").inherits(f,Error);var o=a("events").EventEmitter,p=Object.create(o.prototype);p.dispatch=function(a){return j.events("Link got event: "+a),o.prototype.emit.apply(this.observers,arguments),this.listeners(a).length?(o.prototype.emit.apply(this,arguments),!0):this.session.dispatch.apply(this.session,arguments)},p.set_source=function(a){this.local.attach.source=l.source(a).described()},p.set_target=function(a){this.local.
attach.target=l.target(a).described()},p.attach=function(){this.state.open()&&this.connection._register()},p.open=p.attach,p.detach=function(){this.local.detach.closed=!1,this.state.close()&&this.connection._register()},p.close=function(a){a&&(this.local.detach.error=a),this.local.detach.closed=!0,this.state.close()&&this.connection._register()},p.is_open=function(){return this.session.is_open()&&this.state.is_open()},p.is_closed=function(){return this.session.is_closed()||this.state.is_closed()},p._process=function(){do this.state.need_open()&&this.session.output(this.local.attach.described()),this.issue_flow&&(this.session._write_flow(this),this.issue_flow=!1),this.state.need_close()&&this.session.output(this.local.detach.described());while(!this.state.has_settled())},p.on_attach=function(a){if(!this.state.remote_opened())throw Error("Attach already received");this.remote.handle||(this.remote.handle=a.handle),a.performative.source=l.unwrap(a.performative.source),a.performative.tar
get=l.unwrap(a.performative.target),this.remote.attach=a.performative,this.open(),this.dispatch(this.is_receiver()?"receiver_open":"sender_open",this._context())},p.prefix_event=function(a){return(this.local.attach.role?"receiver_":"sender_")+a},p.on_detach=function(a){if(!this.state.remote_closed())throw Error("Detach already received");this.remote.detach=a.performative,this.close();var b=this.remote.detach.error;if(b){var c=this.dispatch(this.prefix_event("error"),this._context());c=this.dispatch(this.prefix_event("close"),this._context())||c,c||o.prototype.emit.call(this.connection.container,"error",new f(b.description,b.condition,this))}else this.dispatch(this.prefix_event("close"),this._context())};var q=["snd_settle_mode","rcv_settle_mode","source","target","max_message_size","offered_capabilities","desired_capabilities","properties"];p.init=function(a,b,c,d,e){this.session=a,this.connection=a.connection,this.name=b,this.options=void 0===d?{}:d,this.state=new m,this.issue_flow
=!1,this.local={handle:c},this.local.attach=i.attach({handle:c,name:b,role:e});for(var f in this.local.attach)g(f)||void 0===this.options[f]||(this.local.attach[f]=this.options[f]);this.local.detach=i.detach({handle:c,closed:!0}),this.remote={handle:void 0},this.delivery_count=0,this.credit=0,this.observers=new o;for(var j in q){var k=q[j];Object.defineProperty(this,k,{get:h(k)})}Object.defineProperty(this,"error",{get:function(){return this.remote.detach?this.remote.detach.error:void 0}})},p.reset=function(){this.state.disconnected(),this.remote={handle:void 0},this.delivery_count=0,this.credit=0},p.has_credit=function(){return this.credit>0},p.is_receiver=function(){return this.local.attach.role},p._context=function(a){var b=a?a:{};return this.is_receiver()?b.receiver=this:b.sender=this,this.session._context(b)},p.get_option=function(a,b){return void 0!==this.options[a]?this.options[a]:this.session.get_option(a,b)};var r=function(a,b,c,e){this.init(a,b,c,e,!1),this._draining=!1,th
is._drained=!1,this.local.attach.initial_delivery_count=0,this.tag=0,this.get_option("autosettle",!0)&&this.observers.on("settled",d);var f=this;this.get_option("treat_modified_as_released",!0)&&this.observers.on("modified",function(a){f.dispatch("released",a)})};r.prototype=Object.create(p),r.prototype.constructor=r,r.prototype._get_drain=function(){if(this._draining&&this._drained&&this.credit){for(;this.credit;)++this.delivery_count,--this.credit;return!0}return!1},r.prototype.set_drained=function(a){this._drained=a,this._draining&&this._drained&&(this.issue_flow=!0)},r.prototype.next_tag=function(){return new c(new String(this.tag++))},r.prototype.sendable=function(){return this.credit&&this.session.outgoing.available()},r.prototype.on_flow=function(a){var b=a.performative;this.credit=b.delivery_count+b.link_credit-this.delivery_count,this._draining=b.drain,this._drained=this.credit>0,this.is_open()&&(this.dispatch("sender_flow",this._context()),this._draining&&this.dispatch("se
nder_draining",this._context()),this.sendable()&&this.dispatch("sendable",this._context()))},r.prototype.on_transfer=function(){throw Error("got transfer on sending link")},r.prototype.send=function(a,b){var c=this.session.send(this,b?b:this.next_tag(),k.encode(a),0);return 1===this.local.attach.snd_settle_mode&&(c.settled=!0),c};var s=function(a,b,c,d){this.init(a,b,c,d,!0),this.drain=!1,this.set_credit_window(this.get_option("credit_window",100)),this.get_option("autoaccept",!0)&&this.observers.on("message",e)};s.prototype=Object.create(p),s.prototype.constructor=s,s.prototype.on_flow=function(a){this.dispatch("receiver_flow",this._context()),a.performative.drain&&(a.performative.link_credit>0?console.log("ERROR: received flow with drain set, but non zero credit"):this.dispatch("receiver_drained",this._context()))},s.prototype.flow=function(a){a>0&&(this.credit+=a,this.issue_flow=!0,this.connection._register())},s.prototype.add_credit=s.prototype.flow,s.prototype._get_drain=functi
on(){return this.drain},s.prototype.set_credit_window=function(a){if(a>0){var b=new n(a),c=b.update.bind(b);this.observers.on("message",c),this.observers.on("receiver_open",c)}},b.exports={Sender:r,Receiver:s}}).call(this,a("buffer").Buffer)},{"./endpoint.js":2,"./frames.js":3,"./log.js":5,"./message.js":6,"./terminus.js":10,buffer:16,events:20,util:30}],5:[function(a,b,c){"use strict";var d=a("debug");b.exports={frames:d("rhea:frames"),raw:d("rhea:raw"),reconnect:d("rhea:reconnect"),events:d("rhea:events"),message:d("rhea:message"),flow:d("rhea:flow"),io:d("rhea:io")}},{debug:31}],6:[function(a,b,c){"use strict";function d(a,b,c){b.descriptor=a,p[a.symbolic]=b,p[Number(a.numeric).toString(10)]=b,c&&q.push(c)}function e(a){var b=n.define_composite(a);r[a.name]=b.create,o[Number(b.descriptor.numeric).toString(10)]=b,o[b.descriptor.symbolic]=b;var c=function(c,d){c[a.name]=new b(d.value)},e=function(c,d){d[a.name]&&(d[a.name].described?c.push(d[a.name].described()):c.push(b.create(d[a
.name]).described()))};d(b.descriptor,c,e)}function f(a){var b={numeric:a.code};b.symbolic="amqp:"+a.name.replace(/_/g,"-")+":map";var c=function(b,c){b[a.name]=n.unwrap(c)},e=function(c,d){d[a.name]&&c.push(n.described(n.wrap_ulong(b.numeric),n.wrap_map(d[a.name])))};d(b,c,e)}function g(a){var b={};for(var c in a){var d=a[c];for(var e in d)b[d[e]]=c}return b}function h(a,b){for(var c in a){var d=a[c];"object"==typeof d?h(d,b[c]):b[c]=d}}function i(a,b,c){Object.defineProperty(a,c,{get:function(){return this[b]?this[b][c]:void 0},set:function(a){void 0===this[b]&&(this[b]={}),this[b][c]=a}})}function j(a){var b={toJSON:function(){var a={};for(var b in u){var c=u[b];if("function"!=typeof this[c]){var d=s[c];if(d)for(var e in d)void 0!==this[d[e]]&&(void 0===a[c]&&(a[c]={}),a[c][d[e]]=this[d[e]]);else this[c]&&(a[c]=this[c])}}return a},inspect:function(){return JSON.stringify(this.toJSON())},toString:function(){return JSON.stringify(this.toJSON())}};for(var c in t)i(b,t[c],c);return a
&&h(a,b),b}function k(a){var b=void 0;for(var c in a)if("function"!=typeof a[c]&&u.indexOf(c)<0){void 0===b&&(b={});var d=t[c]||"application_properties";void 0===b[d]&&(b[d]={}),b[d][c]=a[c]}else void 0!==b&&(b[c]=a[c]);return b||a}function l(a){var b=n.define_composite(a);b.composite_type=a.name,r[a.name]=b.create,v[Number(b.descriptor.numeric).toString(10)]=b,v[b.descriptor.symbolic]=b,r["is_"+a.name]=function(b){if(b&&b.descriptor){var c=v[b.descriptor.value];if(c)return c.descriptor.numeric===a.code}return!1}}var m=a("./log.js"),n=a("./types.js"),o={},p={},q=[],r={};e({name:"header",code:112,fields:[{name:"durable",type:"boolean",default_value:!1},{name:"priority",type:"ubyte",default_value:4},{name:"ttl",type:"uint"},{name:"first_acquirer",type:"boolean",default_value:!1},{name:"delivery_count",type:"uint",default_value:0}]}),f({name:"delivery_annotations",code:113}),f({name:"message_annotations",code:114}),e({name:"properties",code:115,fields:[{name:"message_id",type:"message_
id"},{name:"user_id",type:"binary"},{name:"to",type:"string"},{name:"subject",type:"string"},{name:"reply_to",type:"string"},{name:"correlation_id",type:"message_id"},{name:"content_type",type:"symbol"},{name:"content_encoding",type:"symbol"},{name:"absolute_expiry_time",type:"timestamp"},{name:"creation_time",type:"timestamp"},{name:"group_id",type:"string"},{name:"group_sequence",type:"uint"},{name:"reply_to_group_id",type:"string"}]}),f({name:"application_properties",code:116}),d({numeric:119,symbolic:"amqp:value:*"},function(a,b){a.body=n.unwrap(b)},function(a,b){a.push(n.described(n.wrap_ulong(119),n.wrap(b.body)))}),f({name:"footer",code:120});var s={properties:["message_id","user_id","to","subject","reply_to","correlation_id","content_type","content_encoding","absolute_expiry_time","creation_time","group_id","group_sequence","reply_to_group_id"],header:["durable","priority","ttl","first_acquirer","delivery_count"]},t=g(s),u=["header","delivery_annotations","message_annotation
s","properties","application_properties","body","footer"];r.encode=function(a){var b=[],c=k(a);q.forEach(function(a){a(b,c)});for(var d=new n.Writer,e=0;e<b.length;e++)m.message("Encoding section "+(e+1)+" of "+b.length+": "+b[e]),d.write(b[e]);var f=d.toBuffer();return m.message("encoded "+f.length+" bytes"),f},r.decode=function(a){for(var b=j(),c=new n.Reader(a);c.remaining();){var d=c.read();if(m.message("decoding section: "+JSON.stringify(d)+" of type: "+JSON.stringify(d.descriptor)),d.descriptor){var e=p[d.descriptor.value];e?e(b,d):console.log("WARNING: did not recognise message section with descriptor "+d.descriptor)}else console.log("WARNING: expected described message section got "+JSON.stringify(d))}if(b.application_properties)for(var f in b.application_properties)i(b,"application_properties",f);return b};var v={};r.unwrap_outcome=function(a){if(a&&a.descriptor){var b=v[a.descriptor.value];if(b)return new b(n.unwrap(a))}return console.log("unrecognised outcome: "+JSON.stri
ngify(a)),a},r.are_outcomes_equivalent=function(a,b){return void 0===a&&void 0===b?!0:void 0===a||void 0===b?!1:a.descriptor.value===b.descriptor.value&&JSON.stringify(a)===JSON.stringify(b)},l({name:"received",code:35,fields:[{name:"section_number",type:"uint",mandatory:!0},{name:"section_offset",type:"ulong",mandatory:!0}]}),l({name:"accepted",code:36,fields:[]}),l({name:"rejected",code:37,fields:[{name:"error",type:"error"}]}),l({name:"released",code:38,fields:[]}),l({name:"modified",code:39,fields:[{name:"delivery_failed",type:"boolean"},{name:"undeliverable_here",type:"boolean"},{name:"message_annotations",type:"map"}]}),b.exports=r},{"./log.js":5,"./types.js":12}],7:[function(a,b,c){"use strict";function d(a,b){return b?Array.isArray(b)?b.indexOf(a)>-1:a===b:!1}var e=a("url"),f={counter:1,next:function(){return this.counter++}},g=function(a,b){var c=e.parse(b);this.connection=a.connect({host:c.hostname,port:c.port}),this.connection.on("message",this._response.bind(this)),this.
connection.on("receiver_open",this._ready.bind(this)),this.sender=this.connection.attach_sender(c.path.substr(1)),this.receiver=this.connection.attach_receiver({source:{dynamic:!0}}),this.id_generator=f,this.pending=[],this.outstanding={}};g.prototype._request=function(a,b,c,d){var e={properties:{}};e.properties.subject=b,e.body=c,e.properties.message_id=a,e.properties.reply_to=this.receiver.remote.attach.source.address,this.outstanding[a]=d,this.sender.send(e)},g.prototype._response=function(a){var b=a.message.properties.correlation_id,c=this.outstanding[b];c?"ok"===a.message.properties.subject?c(a.message.body):c(void 0,{name:a.message.properties.subject,description:a.message.body}):console.log("no request pending for "+b+", ignoring response")},g.prototype._ready=function(){this._process_pending()},g.prototype._process_pending=function(){for(var a=0;a<this.pending.length;a++){var b=this.pending[a];this._request(b.id,b.name,b.args,b.callback)}this.pending=[]},g.prototype.call=func
tion(a,b,c){var d=this.id_generator.next();this.receiver.is_open()&&0===this.pending.length?this._request(d,a,b,c):this.pending.push({
+name:a,args:b,callback:c,id:d})},g.prototype.close=function(){this.receiver.close(),this.sender.close(),this.connection.close()},g.prototype.define=function(a){this[a]=function(b,c){this.call(a,b,c)}};var h=function(a,b){this.ttl=a,this.purged=b,this.entries={},this.timeout=void 0};h.prototype.clear=function(){this.timeout&&clearTimeout(this.timeout),this.entries={}},h.prototype.put=function(a,b){this.entries[a]={value:b,last_accessed:Date.now()},this.timeout||(this.timeout=setTimeout(this.purge.bind(this),this.ttl))},h.prototype.get=function(a){var b=this.entries[a];return b?(b.last_accessed=Date.now(),b.value):void 0},h.prototype.purge=function(){var a=Date.now(),b=[],c=0;for(var d in this.entries)a-this.entries[d].last_accessed>=this.ttl?b.push(d):c++;for(var e=0;e<b.length;e++){var f=this.entries[b[e]];delete this.entries[b[e]],this.purged(f.value)}c&&!this.timeout&&(this.timeout=setTimeout(this.purge.bind(this),this.ttl))};var i=function(a,b){this.factory=a,this.cache=new h(b,f
unction(a){a.close()})};i.prototype.clear=function(){this.cache.clear()},i.prototype.get=function(a){var b=this.cache.get(a);return void 0===b&&(b=this.factory(a),this.cache.put(a,b)),b};var j=function(a,b,c){this.options=c||{};var d=e.parse(b);this.connection=a.connect({host:d.hostname,port:d.port}),this.connection.on("connection_open",this._connection_open.bind(this)),this.connection.on("message",this._request.bind(this)),this.receiver=this.connection.attach_receiver(d.path.substr(1)),this.callbacks={},this._send=void 0,this._clear=void 0};j.prototype._connection_open=function(){if(d("ANONYMOUS-RELAY",this.connection.remote.open.offered_capabilities)){var a=this.connection.attach_sender({target:{}});this._send=function(b){a.send(b)}}else{var b=new i(this.connection.attach_sender.bind(this.connection),this.options.cache_ttl||6e4);this._send=function(a){var c=b.get(a.properties.to);c&&c.send(a)},this._clear=function(){b.clear()}}},j.prototype._respond=function(a){var b=this;return f
unction(c,d){d?(a.properties.subject=d.name||"error",a.body=d.description||d):(a.properties.subject="ok",a.body=c),b._send(a)}},j.prototype._request=function(a){var b=a.message,c={properties:{}};c.properties.to=b.properties.reply_to,c.properties.correlation_id=b.properties.message_id;var d=this.callbacks[b.properties.subject];d?d(b.body,this._respond(c)):(c.properties.subject="bad-method",c.body="Unrecognised method "+b.properties.subject,this._send(c))},j.prototype.bind_sync=function(a,b){this.callbacks[b||a.name]=function(b,c){var d=a(b);c(d)}},j.prototype.bind=function(a,b){this.callbacks[b||a.name]=a},j.prototype.close=function(){this._clear&&this._clear(),this.receiver.close(),this.connection.close()},b.exports={server:function(a,b,c){return new j(a,b,c)},client:function(a,b){return new g(a,b)}}},{url:26}],8:[function(a,b,c){(function(c){"use strict";function d(a){for(var b=[],c=0,d=0;d<a.length;)0===a[d]?(d>c?b.push(a.toString("utf8",c,d)):b.push(null),c=++d):++d;return d>c?b.
push(a.toString("utf8",c,d)):b.push(null),b}var e=a("./frames.js"),f=a("./transport.js"),g={OK:0,AUTH:1,SYS:2,SYS_PERM:3,SYS_TEMP:4},h=3,i=function(a){this.callback=a,this.outcome=void 0,this.username=void 0};i.prototype.start=function(a){var b=d(a);3!==b.length&&this.connection.sasl_failed("Unexpected response in PLAIN, got "+b.length+" fields, expected 3"),this.callback(b[1],b[2])?(this.outcome=!0,this.username=b[1]):this.outcome=!1};var j=function(a,b){this.username=a,this.password=b};j.prototype.start=function(){var a=new c(1+this.username.length+1+this.password.length);return a.writeUInt8(0,0),a.write(this.username,1),a.writeUInt8(0,1+this.username.length),a.write(this.password,1+this.username.length+1),a};var k=function(){this.outcome=void 0,this.username=void 0};k.prototype.start=function(a){this.outcome=!0,this.username=a?a.toString("utf8"):"anonymous"};var l=function(a){this.username=a?a:"anonymous"};l.prototype.start=function(){var a=new c(1+this.username.length);return a.
writeUInt8(0,0),a.write(this.username,1),a};var m=function(){this.outcome=void 0,this.username=void 0};m.prototype.start=function(){this.outcome=!0};var n=function(){this.username=void 0};n.prototype.start=function(){return null};var o=function(a,b){this.connection=a,this.transport=new f(a.amqp_transport.identifier,h,e.TYPE_SASL,this),this.next=a.amqp_transport,this.mechanisms=b,this.mechanism=void 0,this.outcome=void 0,this.username=void 0;var c=Object.getOwnPropertyNames(b);this.transport.encode(e.sasl_frame(e.sasl_mechanisms({sasl_server_mechanisms:c}).described()))};o.prototype.do_step=function(a){void 0===this.mechanism.outcome?this.transport.encode(e.sasl_frame(e.sasl_challenge({challenge:a}).described())):(this.outcome=this.mechanism.outcome?g.OK:g.AUTH,this.transport.encode(e.sasl_frame(e.sasl_outcome({code:this.outcome}).described())),this.outcome===g.OK&&(this.username=this.mechanism.username,this.transport.write_complete=!0,this.transport.read_complete=!0))},o.prototype.o
n_sasl_init=function(a){var b=this.mechanisms[a.performative.mechanism];if(b){this.mechanism=b();var c=this.mechanism.start(a.performative.initial_response);this.do_step(c)}else this.outcome=g.AUTH,this.transport.encode(e.sasl_frame(e.sasl_outcome({code:this.outcome}).described()))},o.prototype.on_sasl_response=function(a){this.do_step(this.mechanism.step(a.performative.response))},o.prototype.has_writes_pending=function(){return this.transport.has_writes_pending()||this.next.has_writes_pending()},o.prototype.write=function(a){return this.transport.write_complete&&0===this.transport.pending.length?this.next.write(a):this.transport.write(a)},o.prototype.read=function(a){return this.transport.read_complete?this.next.read(a):this.transport.read(a)};var p=function(a,b){this.connection=a,this.transport=new f(a.amqp_transport.identifier,h,e.TYPE_SASL,this),this.next=a.amqp_transport,this.mechanisms=b,this.mechanism=void 0,this.mechanism_name=void 0,this.failed=!1};p.prototype.on_sasl_mech
anisms=function(a){for(var b=0;void 0===this.mechanism&&b<a.performative.sasl_server_mechanisms.length;b++){var c=a.performative.sasl_server_mechanisms[b],d=this.mechanisms[c];d&&(this.mechanism=d(),this.mechanism_name=c)}if(this.mechanism){var f=this.mechanism.start();this.transport.encode(e.sasl_frame(e.sasl_init({mechanism:this.mechanism_name,initial_response:f}).described()))}else this.failed=!0,this.connection.sasl_failed("No suitable mechanism; server supports "+a.performative.sasl_server_mechanisms)},p.prototype.on_sasl_challenge=function(a){var b=this.mechanism.step(a.performative.challenge);this.transport.encode(e.sasl_frame(e.sasl_response({response:b}).described()))},p.prototype.on_sasl_outcome=function(a){switch(a.performative.code){case g.OK:this.transport.read_complete=!0,this.transport.write_complete=!0;break;default:this.transport.write_complete=!0,this.connection.sasl_failed("Failed to authenticate: "+a.performative.code)}},p.prototype.has_writes_pending=function(){
return this.transport.has_writes_pending()||this.next.has_writes_pending()},p.prototype.write=function(a){return this.transport.write_complete?this.next.write(a):this.transport.write(a)},p.prototype.read=function(a){return this.transport.read_complete?this.next.read(a):this.transport.read(a)};var q={enable_anonymous:function(){this.ANONYMOUS=function(){return new k}},enable_plain:function(a){this.PLAIN=function(){return new i(a)}}},r={enable_anonymous:function(a){this.ANONYMOUS=function(){return new l(a)}},enable_plain:function(a,b){this.PLAIN=function(){return new j(a,b)}},enable_external:function(){this.EXTERNAL=function(){return new n}}};b.exports={Client:p,Server:o,server_mechanisms:function(){return Object.create(q)},client_mechanisms:function(){return Object.create(r)},server_add_external:function(a){return a.EXTERNAL=function(){return new m},a}}}).call(this,a("buffer").Buffer)},{"./frames.js":3,"./transport.js":11,buffer:16}],9:[function(a,b,c){(function(c){"use strict";funct
ion d(a){var b,c,d,e,g;for(e=0;e<a.length;e++)g=a[e],void 0===b&&(b=g,c=g,d=g.id),i.are_outcomes_equivalent(c.state,g.state)&&c.settled===g.settled&&d===g.id?(c.id!==g.id&&(c=g),d++):(b.link.session.output(f.disposition({role:b.link.is_receiver(),first:b.id,last:c.id,state:b.state,settled:b.settled}).described()),b=g,c=g,d=g.id);void 0!==b&&void 0!==c&&b.link.session.output(f.disposition({role:b.link.is_receiver(),first:b.id,last:c.id,state:b.state,settled:b.settled}).described())}function e(a,b,c){var d=b?b:{};"string"==typeof b&&(d={},d[c]=b),d.name||(d.name=k.generate_uuid());var e=a(d.name,d);for(var f in{source:0,target:0})d[f]&&("string"==typeof d[f]&&(d[f]={address:d[f]}),e["set_"+f](d[f]));return e.attach(),e}var f=a("./frames.js"),g=a("./link.js"),h=a("./log.js"),i=a("./message.js"),j=a("./types.js"),k=a("./util.js"),l=a("./endpoint.js"),m=a("events").EventEmitter,n=function(a){this.capacity=a,this.size=0,this.head=0,this.tail=0,this.entries=[]};n.prototype.available=functi
on(){return this.capacity-this.size},n.prototype.push=function(a){if(!(this.size<this.capacity))throw Error("circular buffer overflow: head="+this.head+" tail="+this.tail+" size="+this.size+" capacity="+this.capacity);this.entries[this.tail]=a,this.tail=(this.tail+1)%this.capacity,this.size++},n.prototype.pop_if=function(a){for(var b=0;this.size&&a(this.entries[this.head]);)this.entries[this.head]=void 0,this.head=(this.head+1)%this.capacity,this.size--,b++;return b},n.prototype.by_id=function(a){if(this.size>0){var b=a-this.entries[this.head].id;if(b<this.size)return this.entries[(this.head+b)%this.capacity]}return void 0},n.prototype.get_head=function(){return this.size>0?this.entries[this.head]:void 0};var o=function(){this.deliveries=new n(2048),this.updated=[],this.pending_dispositions=[],this.next_delivery_id=0,this.next_pending_delivery=0,this.next_transfer_id=0,this.window=j.MAX_UINT,this.remote_next_transfer_id=void 0,this.remote_window=void 0};o.prototype.available=functio
n(){return this.deliveries.available()},o.prototype.send=function(a,b,c,d){var e={id:this.next_delivery_id++,tag:b,link:a,data:c,format:d?d:0,sent:!1,settled:!1,state:void 0,remote_settled:!1,remote_state:void 0},f=this;return e.update=function(a,b){f.update(e,a,b)},this.deliveries.push(e),e},o.prototype.on_begin=function(a){this.remote_window=a.incoming_window},o.prototype.on_flow=function(a){this.remote_next_transfer_id=a.next_incoming_id,this.remote_window=a.incoming_window},o.prototype.on_disposition=function(a){for(var b=a.last?a.last:a.first,c=a.first;b>=c;c++){var d=this.deliveries.by_id(c);if(d&&!d.remote_settled){var e=!1;a.settled&&(d.remote_settled=a.settled,e=!0),a.state&&a.state!==d.remote_state&&(d.remote_state=i.unwrap_outcome(a.state),e=!0),e&&this.updated.push(d)}}},o.prototype.update=function(a,b,c){a&&(a.settled=b,void 0!==c&&(a.state=c),a.remote_settled||this.pending_dispositions.push(a),a.link.connection._register())},o.prototype.transfer_window=function(){retur
n this.remote_window?this.remote_window-(this.next_transfer_id-this.remote_next_transfer_id):0},o.prototype.process=function(){for(var a;this.next_pending_delivery<this.next_delivery_id;){if(a=this.deliveries.by_id(this.next_pending_delivery),!a){console.log("ERROR: Next pending delivery not found: "+this.next_pending_delivery);break}if(!a.link.has_credit()){h.flow("Link has no credit");break}if(a.link.delivery_count++,a.transfers_required=1,!(this.transfer_window()>=a.transfers_required)){h.flow("Incoming window of peer preventing sending further transfers: remote_window="+this.remote_window+", remote_next_transfer_id="+this.remote_next_transfer_id+", next_transfer_id="+this.next_transfer_id);break}this.next_transfer_id+=a.transfers_required,this.window-=a.transfers_required,a.link.session.output(f.transfer({handle:a.link.local.handle,message_format:a.format,delivery_id:a.id,delivery_tag:a.tag,settled:a.settled}).described(),a.data),a.link.credit--,this.next_pending_delivery++}for(
var b=0;b<this.updated.length;b++)a=this.updated[b],a.remote_state&&a.remote_state.constructor.composite_type&&a.link.dispatch(a.remote_state.constructor.composite_type,a.link._context({delivery:a})),a.remote_settled&&a.link.dispatch("settled",a.link._context({delivery:a}));this.updated=[],this.pending_dispositions.length&&(d(this.pending_dispositions),this.pending_dispositions=[]),this.deliveries.pop_if(function(a){return a.settled&&a.remote_settled})};var p=function(){this.deliveries=new n(2048),this.updated=[],this.next_transfer_id=0,this.next_delivery_id=void 0,this.window=2048,this.remote_next_transfer_id=void 0,this.remote_window=void 0};p.prototype.update=function(a,b,c){a&&(a.settled=b,void 0!==c&&(a.state=c),a.remote_settled||this.updated.push(a),a.link.connection._register())},p.prototype.on_transfer=function(a,b){if(this.next_transfer_id++,b.is_open()){void 0===this.next_delivery_id&&(this.next_delivery_id=a.performative.delivery_id);var d,e,f=this.deliveries.get_head();i
f(f&&f.incomplete){if(void 0!==a.performative.delivery_id&&this.next_delivery_id!==a.performative.delivery_id)throw Error("frame sequence error: delivery "+this.next_delivery_id+" not complete, got "+a.performative.delivery_id);d=f,e=c.concat([d.data,a.payload],d.data.size()+a.payload.size())}else{if(this.next_delivery_id!==a.performative.delivery_id)throw Error("frame sequence error: expected "+this.next_delivery_id+", got "+a.performative.delivery_id);d={id:a.performative.delivery_id,tag:a.performative.delivery_tag,link:b,settled:!1,state:void 0,remote_settled:void 0===a.performative.settled?!1:a.performative.settled,remote_state:a.performative.state};var g=this;d.update=function(a,c){var e=a;void 0===e&&(e=1!==b.local.attach.rcv_settle_mode),g.update(d,e,c)},d.accept=function(){this.update(void 0,i.accepted().described())},d.release=function(a){a?this.update(void 0,i.modified(a).described()):this.update(void 0,i.released().described())},d.reject=function(a){this.update(!0,i.rejec
ted({error:a}).described())},d.modified=function(a){this.update(!0,i.modified(a).described())},this.deliveries.push(d),e=a.payload}d.incomplete=a.performative.more,d.incomplete?d.data=e:(b.credit--,b.delivery_count++,this.next_delivery_id++,b.dispatch("message",b._context({message:i.decode(e),delivery:d})))}},p.prototype.process=function(){this.updated.length>0&&(d(this.updated),this.updated=[]),this.deliveries.pop_if(function(a){return a.settled})},p.prototype.on_begin=function(a){this.remote_window=a.outgoing_window},p.prototype.on_flow=function(a){this.remote_next_transfer_id=a.next_outgoing_id,this.remote_window=a.outgoing_window},p.prototype.on_disposition=function(a){for(var b=a.last?a.last:a.first,c=a.first;b>=c;c++){var d=this.deliveries.by_id(c);d&&!d.remote_settled&&a.settled&&(d.remote_settled=a.settled,d.link.dispatch("settled",d.link._context({delivery:d})))}};var q=function(a,b){this.connection=a,this.outgoing=new o,this.incoming=new p,this.state=new l,this.local={chan
nel:b,handles:{}},this.local.begin=f.begin({next_outgoing_id:this.outgoing.next_transfer_id,incoming_window:this.incoming.window,outgoing_window:this.outgoing.window}),this.local.end=f.end(),this.remote={handles:{}},this.links={},this.options={}};q.prototype=Object.create(m.prototype),q.prototype.constructor=q,q.prototype.reset=function(){this.state.disconnected(),this.outgoing=new o,this.incoming=new p,this.remote={handles:{}};for(var a in this.links)this.links[a].reset()},q.prototype.dispatch=function(a){return h.events("Session got event: "+a),this.listeners(a).length?(m.prototype.emit.apply(this,arguments),!0):this.connection.dispatch.apply(this.connection,arguments)},q.prototype.output=function(a,b){this.connection._write_frame(this.local.channel,a,b)},q.prototype.create_sender=function(a,b){return this.create_link(a,g.Sender,b)},q.prototype.create_receiver=function(a,b){return this.create_link(a,g.Receiver,b)},q.prototype.get_option=function(a,b){return void 0!==this.options[a
]?this.options[a]:this.connection.get_option(a,b)},q.prototype.attach_sender=function(a){return e(this.create_sender.bind(this),a,"target")},q.prototype.open_sender=q.prototype.attach_sender,q.prototype.attach_receiver=function(a){return e(this.create_receiver.bind(this),a,"source")},q.prototype.open_receiver=q.prototype.attach_receiver,q.prototype.create_link=function(a,b,c){for(var d=0;this.local.handles[d];)d++;var e=new b(this,a,d,c);return this.links[a]=e,this.local.handles[d]=e,e},q.prototype.begin=function(){this.state.open()&&this.connection._register()},q.prototype.open=q.prototype.begin,q.prototype.end=function(a){a&&(this.local.end.error=a),this.state.close()&&this.connection._register()},q.prototype.close=q.prototype.end,q.prototype.is_open=function(){return this.connection.is_open()&&this.state.is_open()},q.prototype.is_closed=function(){return this.connection.is_closed()||this.state.is_closed()},q.prototype._process=function(){do{this.state.need_open()&&this.output(thi
s.local.begin.described()),this.outgoing.process(),this.incoming.process();for(var a in this.links)this.links[a]._process();this.state.need_close()&&this.output(this.local.end.described())}while(!this.state.has_settled())},q.prototype.send=function(a,b,c,d){var e=this.outgoing.send(a,b,c,d);return this.connection._register(),e},q.prototype._write_flow=function(a){var b={next_incoming_id:this.incoming.next_transfer_id,incoming_window:this.incoming.window,next_outgoing_id:this.outgoing.next_transfer_id,outgoing_window:this.outgoing.window};a&&(a._get_drain()&&(b.drain=!0),b.delivery_count=a.delivery_count,b.handle=a.local.handle,b.link_credit=a.credit),this.output(f.flow(b).described())},q.prototype.on_begin=function(a){if(!this.state.remote_opened())throw Error("Begin already received");this.remote.channel||(this.remote.channel=a.channel),this.remote.begin=a.performative,this.outgoing.on_begin(a.performative),this.incoming.on_begin(a.performative),this.open(),this.dispatch("session_o
pen",this._context())},q.prototype.on_end=function(a){if(!this.state.remote_closed())throw Error("End already received");this.remote.end=a.performative,this.close(),this.dispatch("session_close",this._context())},q.prototype.on_attach=function(a){var b=a.performative.name,c=this.links[b];c||(c=a.performative.role?this.create_sender(b):this.create_receiver(b)),this.remote.handles[a.performative.handle]=c,c.on_attach(a),c.remote.attach=a.performative},q.prototype.on_disposition=function(a){a.performative.role?(h.events("Received disposition for outgoing transfers"),this.outgoing.on_disposition(a.performative)):(h.events("Received disposition for incoming transfers"),this.incoming.on_disposition(a.performative)),this.connection._register()},q.prototype.on_flow=function(a){this.outgoing.on_flow(a.performative),this.incoming.on_flow(a.performative),void 0!==a.performative.handle&&this._get_link(a).on_flow(a),this.connection._register()},q.prototype._context=function(a){var b=a?a:{};retur
n b.session=this,this.connection._context(b)},q.prototype._get_link=function(a){var b=a.performative.handle,c=this.remote.handles[b];if(!c)throw Error("Invalid handle "+b);return c},q.prototype.on_detach=function(a){this._get_link(a).on_detach(a);var b=a.performative.handle,c=this.remote.handles[b];delete this.remote.handles[b],delete this.local.handles[c.local.handle],delete this.links[c.name]},q.prototype.on_transfer=function(a){this.incoming.on_transfer(a,this._get_link(a))},b.exports=q}).call(this,a("buffer").Buffer)},{"./endpoint.js":2,"./frames.js":3,"./link.js":4,"./log.js":5,"./message.js":6,"./types.js":12,"./util.js":13,buffer:16,events:20}],10:[function(a,b,c){"use strict";function d(a){var b=e.define_composite(a);f[a.name]=b.create,g[Number(b.descriptor.numeric).toString(10)]=b,g[b.descriptor.symbolic]=b}var e=a("./types.js"),f={},g={};f.unwrap=function(a){if(a&&a.descriptor){var b=g[a.descriptor.value];if(b)return new b(a.value);console.log("Unknown terminus: "+a.descri
ptor)}return null},d({name:"source",code:40,fields:[{name:"address",type:"string"},{name:"durable",type:"uint",default_value:0},{name:"expiry_policy",type:"symbol",default_value:"session-end"},{name:"timeout",type:"uint",default_value:0},{name:"dynamic",type:"boolean",default_value:!1},{name:"dynamic_node_properties",type:"symbolic_map"},{name:"distribution_mode",type:"symbol"},{name:"filter",type:"symbolic_map"},{name:"default_outcome",type:"*"},{name:"outcomes",type:"symbol",multiple:!0},{name:"capabilities",type:"symbol",multiple:!0}]}),d({name:"target",code:41,fields:[{name:"address",type:"string"},{name:"durable",type:"uint",default_value:0},{name:"expiry_policy",type:"symbol",default_value:"session-end"},{name:"timeout",type:"uint",default_value:0},{name:"dynamic",type:"boolean",default_value:!1},{name:"dynamic_node_properties",type:"symbolic_map"},{name:"capabilities",type:"symbol",multiple:!0}]}),b.exports=f},{"./types.js":12}],11:[function(a,b,c){(function(c){"use strict";v
ar d=a("./frames.js"),e=a("./log.js"),f=function(a,b,c,d){this.identifier=a,this.protocol_id=b,this.frame_type=c,this.handler=d,this.pending=[],this.header_sent=void 0,this.header_received=void 0,this.write_complete=!1,this.read_complete=!1};f.prototype.has_writes_pending=function(){return this.pending.length>0},f.prototype.encode=function(a){var b=d.write_frame(a);e.frames("["+this.identifier+"] PENDING: "+JSON.stringify(a)),this.pending.push(b)},f.prototype.write=function(a){if(!this.header_sent){var b=new c(8),f={protocol_id:this.protocol_id,major:1,minor:0,revision:0};d.write_header(b,f),a.write(b),this.header_sent=f}for(var g=0;g<this.pending.length;g++)a.write(this.pending[g]),e.raw("["+this.identifier+"] SENT: "+JSON.stringify(this.pending[g]));this.pending=[]},f.prototype.read=function(a){var b=0;if(!this.header_received){if(a.length<8)return b;if(this.header_received=d.read_header(a),e.frames("["+this.identifier+"] RECV: "+JSON.stringify(this.header_received)),this.header_r
eceived.protocol_id!==this.protocol_id)throw Error("Invalid AMQP protocol id "+this.header_received.protocol_id+" expecting: "+this.protocol_id);b=8}for(;b<a.length&&!this.read_complete;){var c=a.readUInt32BE(b);if(e.io("["+this.identifier+"] got frame of size "+c),a.length<b+c){e.io("["+this.identifier+"] incomplete frame; have only "+(a.length-b)+" of "+c);break}var f=d.read_frame(a.slice(b,b+c));if(e.frames("["+this.identifier+"] RECV: "+JSON.stringify(f)),f.type!==this.frame_type)throw Error("Invalid frame type: "+f.type);b+=c,f.performative&&f.performative.dispatch(this.handler,f)}return b},b.exports=f}).call(this,a("buffer").Buffer)},{"./frames.js":3,"./log.js":5,buffer:16}],12:[function(a,b,c){(function(a){"use strict";function c(a,b){this.type=a,this.value=b}function d(a){return Number(a).toString(16)}function e(a,b,e,f){var g,h=b>>>4;switch(g=4===h?function(){this.type=g,this.value=f}:14===h||15===h?function(a,b,c){this.type=g,this.value=a,this.array_constructor={typecode:b
},c&&(this.array_constructor.descriptor=c)}:function(a){this.type=g,this.value=a},g.typecode=b,g.prototype=Object.create(c.prototype),g.toString=function(){return a+"#"+d(b)},h){case 4:g.width=0,g.category="fixed";break;case 5:g.width=1,g.category="fixed";break;case 6:g.width=2,g.category="fixed";break;case 7:g.width=4,g.category="fixed";break;case 8:g.width=8,g.category="fixed";break;case 9:g.width=16,g.category="fixed";break;case 10:g.width=1,g.category="variable";break;case 11:g.width=4,g.category="variable";break;case 12:g.width=1,g.category="compound";break;case 13:g.width=4,g.category="compound";break;case 14:g.width=1,g.category="array";break;case 15:g.width=4,g.category="array"}if(e)for(var i in e)g[i]=e[i];return t.by_code[g.typecode]=g,t[a]=g,g}function f(a){return{read:function(b,c){return b["read"+a](c)},write:function(b,c,d){b["write"+a](c,d)}}}function g(a){return{read:function(b,c){return b["read"+a+"BE"](c)},write:function(b,c,d){b["write"+a+"BE"](c,d)}}}function h(a
,b,c){if("number"==typeof b||b instanceof Number){var d=Math.floor(b/u),e=b%u;a.writeUInt32BE(d,c),a.writeUInt32BE(e,c+4)}else b.copy(a,c)}function i(a,b){var c=a.readUInt32BE(b),d=a.readUInt32BE(b+4);return 2097153>c?c*u+d:a.slice(b,b+8)}function j(a,b,c){if("number"==typeof b||b instanceof Number){var d=Math.abs(b),e=Math.floor(d/u),f=d%u;if(a.writeInt32BE(e,c),a.writeUInt32BE(f,c+4),0>b)for(var g=1,h=0;8>h;h++){var i=c+(7-h),j=(255^a[i])+g;a[i]=255&j,g=j>>8}}else b.copy(a,c)}function k(a,b){var c=a.readInt32BE(b),d=a.readUInt32BE(b+4);return 2097153>c&&c>-2097153?c*u+d:a.slice(b,b+8)}function l(a,b){for(var c=0;c<b.length;c++)if(a.type.typecode===b[c].typecode)return!0;return!1}function m(a){for(var b={},c=0;c+1<a.length;)b[a[c++]]=a[c++];return b}function n(a){var b=t.by_code[a];if(!b)throw Error("Unrecognised typecode: "+d(a));return b}function o(a,b){return a>b?a:b}function p(a){if("symbol"===a)return{typecode:t.Sym8.typecode};throw Error("TODO: Array of type "+a+" not yet sup
ported")}function q(a,b){if(void 0!==b&&null!==b){if(Array.isArray(b)){if(!a.multiple)throw Error("Field "+a.name+" does not support multiple values, got "+JSON.stringify(b));var c=p(a.type);return t.wrap_array(b,c.typecode,c.descriptor)}if("*"===a.type)return b;var d=t["wrap_"+a.type];if(d)return d(b);throw Error("No wrapper for field "+a.name+" of type "+a.type)}if(a.mandatory)throw Error("Field "+a.name+" is mandatory");return new t.Null}function r(a,b){var c=function(){return"*"===b.type?this.value[a]:t.unwrap(this.value[a])},d=function(c){this.value[a]=q(b,c)};return{get:c,set:d,enumerable:!0,configurable:!1}}function s(a){var b=t.define_composite(a);t["wrap_"+a.name]=function(a){return b.create(a).described()},w[Number(b.descriptor.numeric).toString(10)]=b,w[b.descriptor.symbolic]=b}c.prototype.toString=function(){return this.value?this.value.toString():null},c.prototype.toLocaleString=function(){return this.value?this.value.toLocaleString():null},c.prototype.valueOf=function(
){return this.value},c.prototype.toJSON=function(){return this.value&&this.value.toJSON?this.value.toJSON():this.value};var t={by_code:{}};Object.defineProperty(t,"MAX_UINT",{value:4294967295,writable:!1,configurable:!1}),Object.defineProperty(t,"MAX_USHORT",{value:65535,writable:!1,configurable:!1});var u=4294967296,v=-2147483647;e("Null",64,void 0,null),e("Boolean",86,f("UInt8")),e("True",65,void 0,!0),e("False",66,void 0,!1),e("Ubyte",80,f("UInt8")),e("Ushort",96,f("UInt16BE")),e("Uint",112,f("UInt32BE")),e("SmallUint",82,f("UInt8")),e("Uint0",67,void 0,0),e("Ulong",128,{write:h,read:i}),e("SmallUlong",83,f("UInt8")),e("Ulong0",68,void 0,0),e("Byte",81,f("Int8")),e("Short",97,f("Int16BE")),e("Int",113,f("Int32BE")),e("SmallInt",84,f("Int8")),e("Long",129,{write:j,read:k}),e("SmallLong",85,f("Int8")),e("Float",114,g("Float")),e("Double",130,g("Double")),e("Decimal32",116),e("Decimal64",132),e("Decimal128",148),e("CharUTF32",115),e("Timestamp",131,{write:j,read:k}),e("Uuid",152),e(
"Vbin8",160),e("Vbin32",176),e("Str8",161,{encoding:"utf8"}),e("Str32",177,{encoding:"utf8"}),e("Sym8",163,{encoding:"ascii"}),e("Sym32",179,{encoding:"ascii"}),e("List0",69,void 0,[]),e("List8",192),e("List32",208),e("Map8",193),e("Map32",209),e("Array8",224),e("Array32",240),t.is_ulong=function(a){return l(a,[t.Ulong,t.Ulong0,t.SmallUlong])},t.is_string=function(a){return l(a,[t.Str8,t.Str32])},t.is_symbol=function(a){return l(a,[t.Sym8,t.Sym32])},t.is_list=function(a){return l(a,[t.List0,t.List8,t.List32])},t.is_map=function(a){return l(a,[t.Map8,t.Map32])},t.wrap_boolean=function(a){return a?new t.True:new t.False},t.wrap_ulong=function(a){return 0===a?new t.Ulong0:a>255?new t.Ulong(a):new t.SmallUlong(a)},t.wrap_uint=function(a){return 0===a?new t.Uint0:a>255?new t.Uint(a):new t.SmallUint(a)},t.wrap_ushort=function(a){return new t.Ushort(a)},t.wrap_ubyte=function(a){return new t.Ubyte(a)},t.wrap_long=function(a){return a>127||-128>a?new t.Long(a):new t.SmallLong(a)},t.wrap_int=
function(a){return a>127||-128>a?new t.Int(a):new t.SmallInt(a)},t.wrap_short=function(a){return new t.Short(a)},t.wrap_byte=function(a){return new t.Byte(a)},t.wrap_float=function(a){return new t.Float(a)},t.wrap_double=function(a){return new t.Double(a)},t.wrap_timestamp=function(a){return new t.Timestamp(a)},t.wrap_binary=function(a){return a.length>255?new t.Vbin32(a):new t.Vbin8(a)},t.wrap_string=function(a){return a.length>255?new t.Str32(a):new t.Str8(a)},t.wrap_symbol=function(a){return a.length>255?new t.Sym32(a):new t.Sym8(a)},t.wrap_list=function(a){if(0===a.length)return new t.List0;var b=a.map(t.wrap);return new t.List32(b)},t.wrap_map=function(a,b){var c=[];for(var d in a)c.push(b?b(d):t.wrap(d)),c.push(t.wrap(a[d]));return new t.Map32(c)},t.wrap_symbolic_map=function(a){return t.wrap_map(a,t.wrap_symbol)},t.wrap_array=function(a,b,c){if(b)return new t.Array32(a,b,c);throw Error("An array must specify a type for its elements")},t.wrap=function(a){var b=typeof a;if("str
ing"===b)return t.wrap_string(a);if("boolean"===b)return a?new t.True:new t.False;if("number"===b||a instanceof Number){if(isNaN(a))throw Error("Cannot wrap NaN! "+a);return Math.floor(a)-a!==0?new t.Double(a):a>0?u>a?t.wrap_uint(a):t.wrap_ulong(a):a>v?t.wrap_int(a):t.wrap_long(a)}return a instanceof Date?t.wrap_timestamp(a.getTime()):a instanceof c?a:"undefined"===b||null===a?new t.Null:Array.isArray(a)?t.wrap_list(a):t.wrap_map(a)},t.wrap_described=function(a,b){var c=t.wrap(a);return b&&("string"==typeof b?c=t.described(t.wrap_string(b),c):("number"==typeof b||b instanceof Number)&&(c=t.described(t.wrap_ulong(b),c))),c},t.wrap_message_id=function(a){var b=typeof a;if("string"===b)return t.wrap_string(a);if("number"===b||a instanceof Number)return t.wrap_ulong(a);throw Error("invalid message id:"+a)};var w={};t.unwrap=function(a,b){if(a instanceof c){if(a.descriptor){var d=w[a.descriptor.value];if(d)return new d(a.value);if(b)return a}var e=t.unwrap(a.value,!0);return t.is_map(a)?
m(e):e}return Array.isArray(a)?a.map(function(a){return t.unwrap(a,!0)}):a},t.described=function(a,b){var c=Object.create(b);return a.length?(c.descriptor=a.shift(),t.described(a,c)):(c.descriptor=a,c)},t.Reader=function(a){this.buffer=a,this.position=0},t.Reader.prototype.read_typecode=function(){return this.read_uint(1)},t.Reader.prototype.read_uint=function(a){var b=this.position;this.position+=a;var c=a>1?"readUInt"+8*a+"BE":"readUInt8";return this.buffer[c](b)},t.Reader.prototype.read_fixed_width=function(a){var b=this.position;return this.position+=a.width,a.read?a.read(this.buffer,b):this.buffer.slice(b,this.position)},t.Reader.prototype.read_variable_width=function(a){var b=this.read_uint(a.width),c=this.read_bytes(b);return a.encoding?c.toString(a.encoding):c},t.Reader.prototype.read=function(){var a=this.read_constructor(),b=this.read_value(n(a.typecode));return a.descriptor?t.described(a.descriptor,b):b},t.Reader.prototype.read_constructor=function(){var a=this.read_typec
ode();if(0===a){var b=[];b.push(this.read());for(var c=this.read_constructor();c.descriptor;)b.push(c.descriptor),c=this.read_constructor();return{typecode:c.typecode,descriptor:1===b.length?b[0]:b}}return{typecode:a}},t.Reader.prototype.read_value=function(a){if(0===a.width)return new a;if("fixed"===a.category)return new a(this.read_fixed_width(a));if("variable"===a.category)return new a(this.read_variable_width(a));if("compound"===a.category)return this.read_compound(a);if("array"===a.category)return this.read_array(a);throw Error("Invalid category for type: "+a)},t.Reader.prototype.read_multiple=function(a,b){for(var c=b?b:this.read.bind(this),d=[];d.length<a;)d.push(c.apply(this));return d},t.Reader.prototype.read_size_count=function(a){return{size:this.read_uint(a),count:this.read_uint(a)}},t.Reader.prototype.read_compound=function(a){var b=this.read_size_count(a.width);return new a(this.read_multiple(b.count))},t.Reader.prototype.read_array=function(a){var b=this.read_size_cou
nt(a.width),c=this.read_constructor(),d=new a(this.read_multiple(b.count,this.read_value.bind(this,n(c.typecode))),c.typecode,c.descriptor);
+return d},t.Reader.prototype.toString=function(){var a="buffer@"+this.position;this.position&&(a+=": ");for(var b=this.position;b<this.buffer.length;b++)b>0&&(a+=","),a+="0x"+Number(this.buffer[b]).toString(16);return a},t.Reader.prototype.reset=function(){this.position=0},t.Reader.prototype.skip=function(a){this.position+=a},t.Reader.prototype.read_bytes=function(a){var b=this.position;return this.position+=a,this.buffer.slice(b,this.position)},t.Reader.prototype.remaining=function(){return this.buffer.length-this.position},t.Writer=function(b){this.buffer=b?b:new a(1024),this.position=0},t.Writer.prototype.toBuffer=function(){return this.buffer.slice(0,this.position)},t.Writer.prototype.ensure=function(b){if(this.buffer.length<b){var c=new a(o(2*this.buffer.length,b));this.buffer.copy(c),this.buffer=c}},t.Writer.prototype.write_typecode=function(a){this.write_uint(a,1)},t.Writer.prototype.write_uint=function(a,b){var c=this.position;this.ensure(this.position+b),this.position+=b;va
r d=b>1?"writeUInt"+8*b+"BE":"writeUInt8";if(!this.buffer[d])throw Error("Buffer does not define "+d);return this.buffer[d](a,c)},t.Writer.prototype.write_fixed_width=function(a,b){var c=this.position;if(this.ensure(this.position+a.width),this.position+=a.width,a.write)a.write(this.buffer,b,c);else{if(!b.copy)throw Error("Cannot handle write for "+a);b.copy(this.buffer,c)}},t.Writer.prototype.write_variable_width=function(b,c){var d=b.encoding?new a(c,b.encoding):new a(c);this.write_uint(d.length,b.width),this.write_bytes(d)},t.Writer.prototype.write_bytes=function(a){var b=this.position;this.ensure(this.position+a.length),this.position+=a.length,a.copy(this.buffer,b)},t.Writer.prototype.write_constructor=function(a,b){b&&(this.write_typecode(0),this.write(b)),this.write_typecode(a)},t.Writer.prototype.write=function(a){if(!(a instanceof c))throw Error("Cannot write "+JSON.stringify(a));this.write_constructor(a.type.typecode,a.descriptor),this.write_value(a.type,a.value,a.array_cons
tructor)},t.Writer.prototype.write_value=function(a,b,c){if(0!==a.width)if("fixed"===a.category)this.write_fixed_width(a,b);else if("variable"===a.category)this.write_variable_width(a,b);else if("compound"===a.category)this.write_compound(a,b);else{if("array"!==a.category)throw Error("Invalid category "+a.category+" for type: "+a);this.write_array(a,b,c)}},t.Writer.prototype.backfill_size=function(a,b){var c=this.position-b;this.position=b,this.write_uint(c-a,a),this.position+=c-a},t.Writer.prototype.write_compound=function(a,b){var c=this.position;this.position+=a.width,this.write_uint(b.length,a.width);for(var d=0;d<b.length;d++)void 0===b[d]||null===b[d]?this.write(new t.Null):this.write(b[d]);this.backfill_size(a.width,c)},t.Writer.prototype.write_array=function(a,b,c){var d=this.position;this.position+=a.width,this.write_uint(b.length,a.width),this.write_constructor(c.typecode,c.descriptor);for(var e=n(c.typecode),f=0;f<b.length;f++)this.write_value(e,b[f]);this.backfill_size(a
.width,d)},t.Writer.prototype.toString=function(){var a="buffer@"+this.position;this.position&&(a+=": ");for(var b=0;b<this.position;b++)b>0&&(a+=","),a+=("00"+Number(this.buffer[b]).toString(16)).slice(-2);return a},t.Writer.prototype.skip=function(a){this.ensure(this.position+a),this.position+=a},t.Writer.prototype.clear=function(){this.buffer.fill(0),this.position=0},t.Writer.prototype.remaining=function(){return this.buffer.length-this.position},t.define_composite=function(a){var b=function(a){this.value=a?a:[]};b.descriptor={numeric:a.code,symbolic:"amqp:"+a.name+":list"},b.prototype.dispatch=function(b,c){b["on_"+a.name](c)};for(var c=0;c<a.fields.length;c++){var d=a.fields[c];Object.defineProperty(b.prototype,d.name,r(c,d))}return b.toString=function(){return a.name+"#"+Number(a.code).toString(16)},b.prototype.toJSON=function(){var a={};for(var b in this)"value"!==b&&this[b]&&(a[b]=this[b]);return a},b.create=function(a){var c=new b;for(var d in a)c[d]=a[d];return c},b.protot
ype.described=function(){return t.described(t.wrap_ulong(b.descriptor.numeric),t.wrap_list(this.value))},b},s({name:"error",code:29,fields:[{name:"condition",type:"symbol",mandatory:!0},{name:"description",type:"string"},{name:"info",type:"map"}]}),b.exports=t}).call(this,a("buffer").Buffer)},{buffer:16}],13:[function(a,b,c){"use strict";var d={};d.generate_uuid=function(){var a="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(a){var b=16*Math.random()|0,c="x"===a?b:3&b|8;return c.toString(16)});return a},d.clone=function(a){for(var b=Object.create(a.prototype||{}),c=Object.getOwnPropertyNames(a),d=0;d<c.length;d++){var e=c[d];b[e]=a[e]}return b},b.exports=d},{}],14:[function(a,b,c){(function(a){"use strict";function c(a){return a}function d(b){return new a(b instanceof ArrayBuffer?new Uint8Array(b):b)}function e(a){return new Uint8Array(a)}function f(a){var b=c,f=c;return a.binaryType&&(a.binaryType="arraybuffer",b=d,f=e),{end:function(){a.close()},write:function(b){
try{a.send(f(b),{binary:!0})}catch(c){a.onerror(c)}},on:function(c,d){"data"===c?a.onmessage=function(a){d(b(a.data))}:"end"===c?a.onclose=d:"error"===c?a.onerror=d:console.log("ERROR: Attempt to set unrecognised handler on websocket wrapper: "+c)},get_id_string:function(){return a.url}}}b.exports={connect:function(a){return function(b,c,d){return function(){return{connect:function(e,g,h,i){var j=new a(b,c,d);return j.onopen=i,f(j)}}}}},wrap:f}}).call(this,a("buffer").Buffer)},{buffer:16}],15:[function(a,b,c){},{}],16:[function(a,b,c){(function(b){"use strict";function d(){try{var a=new Uint8Array(1);return a.__proto__={__proto__:Uint8Array.prototype,foo:function(){return 42}},42===a.foo()&&"function"==typeof a.subarray&&0===a.subarray(1,1).byteLength}catch(b){return!1}}function e(){return g.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function f(a,b){if(e()<b)throw new RangeError("Invalid typed array length");return g.TYPED_ARRAY_SUPPORT?(a=new Uint8Array(b),a.__proto__=g.prototype):(
null===a&&(a=new g(b)),a.length=b),a}function g(a,b,c){if(!(g.TYPED_ARRAY_SUPPORT||this instanceof g))return new g(a,b,c);if("number"==typeof a){if("string"==typeof b)throw new Error("If encoding is specified then the first argument must be a string");return k(this,a)}return h(this,a,b,c)}function h(a,b,c,d){if("number"==typeof b)throw new TypeError('"value" argument must not be a number');return"undefined"!=typeof ArrayBuffer&&b instanceof ArrayBuffer?n(a,b,c,d):"string"==typeof b?l(a,b,c):o(a,b)}function i(a){if("number"!=typeof a)throw new TypeError('"size" argument must be a number');if(0>a)throw new RangeError('"size" argument must not be negative')}function j(a,b,c,d){return i(b),0>=b?f(a,b):void 0!==c?"string"==typeof d?f(a,b).fill(c,d):f(a,b).fill(c):f(a,b)}function k(a,b){if(i(b),a=f(a,0>b?0:0|p(b)),!g.TYPED_ARRAY_SUPPORT)for(var c=0;b>c;++c)a[c]=0;return a}function l(a,b,c){if(("string"!=typeof c||""===c)&&(c="utf8"),!g.isEncoding(c))throw new TypeError('"encoding" must be
a valid string encoding');var d=0|r(b,c);a=f(a,d);var e=a.write(b,c);return e!==d&&(a=a.slice(0,e)),a}function m(a,b){var c=b.length<0?0:0|p(b.length);a=f(a,c);for(var d=0;c>d;d+=1)a[d]=255&b[d];return a}function n(a,b,c,d){if(b.byteLength,0>c||b.byteLength<c)throw new RangeError("'offset' is out of bounds");if(b.byteLength<c+(d||0))throw new RangeError("'length' is out of bounds");return b=void 0===c&&void 0===d?new Uint8Array(b):void 0===d?new Uint8Array(b,c):new Uint8Array(b,c,d),g.TYPED_ARRAY_SUPPORT?(a=b,a.__proto__=g.prototype):a=m(a,b),a}function o(a,b){if(g.isBuffer(b)){var c=0|p(b.length);return a=f(a,c),0===a.length?a:(b.copy(a,0,0,c),a)}if(b){if("undefined"!=typeof ArrayBuffer&&b.buffer instanceof ArrayBuffer||"length"in b)return"number"!=typeof b.length||Y(b.length)?f(a,0):m(a,b);if("Buffer"===b.type&&_(b.data))return m(a,b.data)}throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")}function p(a){if(a>=e())throw new Ra
ngeError("Attempt to allocate Buffer larger than maximum size: 0x"+e().toString(16)+" bytes");return 0|a}function q(a){return+a!=a&&(a=0),g.alloc(+a)}function r(a,b){if(g.isBuffer(a))return a.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(a)||a instanceof ArrayBuffer))return a.byteLength;"string"!=typeof a&&(a=""+a);var c=a.length;if(0===c)return 0;for(var d=!1;;)switch(b){case"ascii":case"latin1":case"binary":return c;case"utf8":case"utf-8":case void 0:return T(a).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*c;case"hex":return c>>>1;case"base64":return W(a).length;default:if(d)return T(a).length;b=(""+b).toLowerCase(),d=!0}}function s(a,b,c){var d=!1;if((void 0===b||0>b)&&(b=0),b>this.length)return"";if((void 0===c||c>this.length)&&(c=this.length),0>=c)return"";if(c>>>=0,b>>>=0,b>=c)return"";for(a||(a="utf8");;)switch(a){case"hex":return H(this,b,c);case"utf8":case"utf-8":return D(this,b,c);case"ascii":re
turn F(this,b,c);case"latin1":case"binary":return G(this,b,c);case"base64":return C(this,b,c);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(this,b,c);default:if(d)throw new TypeError("Unknown encoding: "+a);a=(a+"").toLowerCase(),d=!0}}function t(a,b,c){var d=a[b];a[b]=a[c],a[c]=d}function u(a,b,c,d,e){if(0===a.length)return-1;if("string"==typeof c?(d=c,c=0):c>2147483647?c=2147483647:-2147483648>c&&(c=-2147483648),c=+c,isNaN(c)&&(c=e?0:a.length-1),0>c&&(c=a.length+c),c>=a.length){if(e)return-1;c=a.length-1}else if(0>c){if(!e)return-1;c=0}if("string"==typeof b&&(b=g.from(b,d)),g.isBuffer(b))return 0===b.length?-1:v(a,b,c,d,e);if("number"==typeof b)return b=255&b,g.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?e?Uint8Array.prototype.indexOf.call(a,b,c):Uint8Array.prototype.lastIndexOf.call(a,b,c):v(a,[b],c,d,e);throw new TypeError("val must be string, number or Buffer")}function v(a,b,c,d,e){function f(a,b){return 1===g?a[b]:a.readUInt16BE(b*g)}va
r g=1,h=a.length,i=b.length;if(void 0!==d&&(d=String(d).toLowerCase(),"ucs2"===d||"ucs-2"===d||"utf16le"===d||"utf-16le"===d)){if(a.length<2||b.length<2)return-1;g=2,h/=2,i/=2,c/=2}var j;if(e){var k=-1;for(j=c;h>j;j++)if(f(a,j)===f(b,-1===k?0:j-k)){if(-1===k&&(k=j),j-k+1===i)return k*g}else-1!==k&&(j-=j-k),k=-1}else for(c+i>h&&(c=h-i),j=c;j>=0;j--){for(var l=!0,m=0;i>m;m++)if(f(a,j+m)!==f(b,m)){l=!1;break}if(l)return j}return-1}function w(a,b,c,d){c=Number(c)||0;var e=a.length-c;d?(d=Number(d),d>e&&(d=e)):d=e;var f=b.length;if(f%2!==0)throw new TypeError("Invalid hex string");d>f/2&&(d=f/2);for(var g=0;d>g;++g){var h=parseInt(b.substr(2*g,2),16);if(isNaN(h))return g;a[c+g]=h}return g}function x(a,b,c,d){return X(T(b,a.length-c),a,c,d)}function y(a,b,c,d){return X(U(b),a,c,d)}function z(a,b,c,d){return y(a,b,c,d)}function A(a,b,c,d){return X(W(b),a,c,d)}function B(a,b,c,d){return X(V(b,a.length-c),a,c,d)}function C(a,b,c){return 0===b&&c===a.length?Z.fromByteArray(a):Z.fromByteArray(
a.slice(b,c))}function D(a,b,c){c=Math.min(a.length,c);for(var d=[],e=b;c>e;){var f=a[e],g=null,h=f>239?4:f>223?3:f>191?2:1;if(c>=e+h){var i,j,k,l;switch(h){case 1:128>f&&(g=f);break;case 2:i=a[e+1],128===(192&i)&&(l=(31&f)<<6|63&i,l>127&&(g=l));break;case 3:i=a[e+1],j=a[e+2],128===(192&i)&&128===(192&j)&&(l=(15&f)<<12|(63&i)<<6|63&j,l>2047&&(55296>l||l>57343)&&(g=l));break;case 4:i=a[e+1],j=a[e+2],k=a[e+3],128===(192&i)&&128===(192&j)&&128===(192&k)&&(l=(15&f)<<18|(63&i)<<12|(63&j)<<6|63&k,l>65535&&1114112>l&&(g=l))}}null===g?(g=65533,h=1):g>65535&&(g-=65536,d.push(g>>>10&1023|55296),g=56320|1023&g),d.push(g),e+=h}return E(d)}function E(a){var b=a.length;if(aa>=b)return String.fromCharCode.apply(String,a);for(var c="",d=0;b>d;)c+=String.fromCharCode.apply(String,a.slice(d,d+=aa));return c}function F(a,b,c){var d="";c=Math.min(a.length,c);for(var e=b;c>e;++e)d+=String.fromCharCode(127&a[e]);return d}function G(a,b,c){var d="";c=Math.min(a.length,c);for(var e=b;c>e;++e)d+=String.from
CharCode(a[e]);return d}function H(a,b,c){var d=a.length;(!b||0>b)&&(b=0),(!c||0>c||c>d)&&(c=d);for(var e="",f=b;c>f;++f)e+=S(a[f]);return e}function I(a,b,c){for(var d=a.slice(b,c),e="",f=0;f<d.length;f+=2)e+=String.fromCharCode(d[f]+256*d[f+1]);return e}function J(a,b,c){if(a%1!==0||0>a)throw new RangeError("offset is not uint");if(a+b>c)throw new RangeError("Trying to access beyond buffer length")}function K(a,b,c,d,e,f){if(!g.isBuffer(a))throw new TypeError('"buffer" argument must be a Buffer instance');if(b>e||f>b)throw new RangeError('"value" argument is out of bounds');if(c+d>a.length)throw new RangeError("Index out of range")}function L(a,b,c,d){0>b&&(b=65535+b+1);for(var e=0,f=Math.min(a.length-c,2);f>e;++e)a[c+e]=(b&255<<8*(d?e:1-e))>>>8*(d?e:1-e)}function M(a,b,c,d){0>b&&(b=4294967295+b+1);for(var e=0,f=Math.min(a.length-c,4);f>e;++e)a[c+e]=b>>>8*(d?e:3-e)&255}function N(a,b,c,d,e,f){if(c+d>a.length)throw new RangeError("Index out of range");if(0>c)throw new RangeError("I
ndex out of range")}function O(a,b,c,d,e){return e||N(a,b,c,4,3.4028234663852886e38,-3.4028234663852886e38),$.write(a,b,c,d,23,4),c+4}function P(a,b,c,d,e){return e||N(a,b,c,8,1.7976931348623157e308,-1.7976931348623157e308),$.write(a,b,c,d,52,8),c+8}function Q(a){if(a=R(a).replace(ba,""),a.length<2)return"";for(;a.length%4!==0;)a+="=";return a}function R(a){return a.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}function S(a){return 16>a?"0"+a.toString(16):a.toString(16)}function T(a,b){b=b||1/0;for(var c,d=a.length,e=null,f=[],g=0;d>g;++g){if(c=a.charCodeAt(g),c>55295&&57344>c){if(!e){if(c>56319){(b-=3)>-1&&f.push(239,191,189);continue}if(g+1===d){(b-=3)>-1&&f.push(239,191,189);continue}e=c;continue}if(56320>c){(b-=3)>-1&&f.push(239,191,189),e=c;continue}c=(e-55296<<10|c-56320)+65536}else e&&(b-=3)>-1&&f.push(239,191,189);if(e=null,128>c){if((b-=1)<0)break;f.push(c)}else if(2048>c){if((b-=2)<0)break;f.push(c>>6|192,63&c|128)}else if(65536>c){if((b-=3)<0)break;f.push(c>>12|224,c>>6&63|128
,63&c|128)}else{if(!(1114112>c))throw new Error("Invalid code point");if((b-=4)<0)break;f.push(c>>18|240,c>>12&63|128,c>>6&63|128,63&c|128)}}return f}function U(a){for(var b=[],c=0;c<a.length;++c)b.push(255&a.charCodeAt(c));return b}function V(a,b){for(var c,d,e,f=[],g=0;g<a.length&&!((b-=2)<0);++g)c=a.charCodeAt(g),d=c>>8,e=c%256,f.push(e),f.push(d);return f}function W(a){return Z.toByteArray(Q(a))}function X(a,b,c,d){for(var e=0;d>e&&!(e+c>=b.length||e>=a.length);++e)b[e+c]=a[e];return e}function Y(a){return a!==a}var Z=a("base64-js"),$=a("ieee754"),_=a("isarray");c.Buffer=g,c.SlowBuffer=q,c.INSPECT_MAX_BYTES=50,g.TYPED_ARRAY_SUPPORT=void 0!==b.TYPED_ARRAY_SUPPORT?b.TYPED_ARRAY_SUPPORT:d(),c.kMaxLength=e(),g.poolSize=8192,g._augment=function(a){return a.__proto__=g.prototype,a},g.from=function(a,b,c){return h(null,a,b,c)},g.TYPED_ARRAY_SUPPORT&&(g.prototype.__proto__=Uint8Array.prototype,g.__proto__=Uint8Array,"undefined"!=typeof Symbol&&Symbol.species&&g[Symbol.species]===g&&Obje
ct.defineProperty(g,Symbol.species,{value:null,configurable:!0})),g.alloc=function(a,b,c){return j(null,a,b,c)},g.allocUnsafe=function(a){return k(null,a)},g.allocUnsafeSlow=function(a){return k(null,a)},g.isBuffer=function(a){return!(null==a||!a._isBuffer)},g.compare=function(a,b){if(!g.isBuffer(a)||!g.isBuffer(b))throw new TypeError("Arguments must be Buffers");if(a===b)return 0;for(var c=a.length,d=b.length,e=0,f=Math.min(c,d);f>e;++e)if(a[e]!==b[e]){c=a[e],d=b[e];break}return d>c?-1:c>d?1:0},g.isEncoding=function(a){switch(String(a).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"latin1":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},g.concat=function(a,b){if(!_(a))throw new TypeError('"list" argument must be an Array of Buffers');if(0===a.length)return g.alloc(0);var c;if(void 0===b)for(b=0,c=0;c<a.length;++c)b+=a[c].length;var d=g.allocUnsafe(b),e=0;for(c=0;c<a.length;++c){var f=a[c];if(!g.isBuffer(f))
throw new TypeError('"list" argument must be an Array of Buffers');f.copy(d,e),e+=f.length}return d},g.byteLength=r,g.prototype._isBuffer=!0,g.prototype.swap16=function(){var a=this.length;if(a%2!==0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var b=0;a>b;b+=2)t(this,b,b+1);return this},g.prototype.swap32=function(){var a=this.length;if(a%4!==0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(var b=0;a>b;b+=4)t(this,b,b+3),t(this,b+1,b+2);return this},g.prototype.swap64=function(){var a=this.length;if(a%8!==0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(var b=0;a>b;b+=8)t(this,b,b+7),t(this,b+1,b+6),t(this,b+2,b+5),t(this,b+3,b+4);return this},g.prototype.toString=function(){var a=0|this.length;return 0===a?"":0===arguments.length?D(this,0,a):s.apply(this,arguments)},g.prototype.equals=function(a){if(!g.isBuffer(a))throw new TypeError("Argument must be a Buffer");return this===a?!0:0===g.compare(this,a)},g.proto
type.inspect=function(){var a="",b=c.INSPECT_MAX_BYTES;return this.length>0&&(a=this.toString("hex",0,b).match(/.{2}/g).join(" "),this.length>b&&(a+=" ... ")),"<Buffer "+a+">"},g.prototype.compare=function(a,b,c,d,e){if(!g.isBuffer(a))throw new TypeError("Argument must be a Buffer");if(void 0===b&&(b=0),void 0===c&&(c=a?a.length:0),void 0===d&&(d=0),void 0===e&&(e=this.length),0>b||c>a.length||0>d||e>this.length)throw new RangeError("out of range index");if(d>=e&&b>=c)return 0;if(d>=e)return-1;if(b>=c)return 1;if(b>>>=0,c>>>=0,d>>>=0,e>>>=0,this===a)return 0;for(var f=e-d,h=c-b,i=Math.min(f,h),j=this.slice(d,e),k=a.slice(b,c),l=0;i>l;++l)if(j[l]!==k[l]){f=j[l],h=k[l];break}return h>f?-1:f>h?1:0},g.prototype.includes=function(a,b,c){return-1!==this.indexOf(a,b,c)},g.prototype.indexOf=function(a,b,c){return u(this,a,b,c,!0)},g.prototype.lastIndexOf=function(a,b,c){return u(this,a,b,c,!1)},g.prototype.write=function(a,b,c,d){if(void 0===b)d="utf8",c=this.length,b=0;else if(void 0===c&&
"string"==typeof b)d=b,c=this.length,b=0;else{if(!isFinite(b))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");b=0|b,isFinite(c)?(c=0|c,void 0===d&&(d="utf8")):(d=c,c=void 0)}var e=this.length-b;if((void 0===c||c>e)&&(c=e),a.length>0&&(0>c||0>b)||b>this.length)throw new RangeError("Attempt to write outside buffer bounds");d||(d="utf8");for(var f=!1;;)switch(d){case"hex":return w(this,a,b,c);case"utf8":case"utf-8":return x(this,a,b,c);case"ascii":return y(this,a,b,c);case"latin1":case"binary":return z(this,a,b,c);case"base64":return A(this,a,b,c);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return B(this,a,b,c);default:if(f)throw new TypeError("Unknown encoding: "+d);d=(""+d).toLowerCase(),f=!0}},g.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var aa=4096;g.prototype.slice=function(a,b){var c=this.length;a=~~a,b=void 0===b?c:~~b,0>a?(a+=c,0>a&&(a=0)):a>c&&(a=c),0>b?(b+=c,0>b&&(b=0
)):b>c&&(b=c),a>b&&(b=a);var d;if(g.TYPED_ARRAY_SUPPORT)d=this.subarray(a,b),d.__proto__=g.prototype;else{var e=b-a;d=new g(e,void 0);for(var f=0;e>f;++f)d[f]=this[f+a]}return d},g.prototype.readUIntLE=function(a,b,c){a=0|a,b=0|b,c||J(a,b,this.length);for(var d=this[a],e=1,f=0;++f<b&&(e*=256);)d+=this[a+f]*e;return d},g.prototype.readUIntBE=function(a,b,c){a=0|a,b=0|b,c||J(a,b,this.length);for(var d=this[a+--b],e=1;b>0&&(e*=256);)d+=this[a+--b]*e;return d},g.prototype.readUInt8=function(a,b){return b||J(a,1,this.length),this[a]},g.prototype.readUInt16LE=function(a,b){return b||J(a,2,this.length),this[a]|this[a+1]<<8},g.prototype.readUInt16BE=function(a,b){return b||J(a,2,this.length),this[a]<<8|this[a+1]},g.prototype.readUInt32LE=function(a,b){return b||J(a,4,this.length),(this[a]|this[a+1]<<8|this[a+2]<<16)+16777216*this[a+3]},g.prototype.readUInt32BE=function(a,b){return b||J(a,4,this.length),16777216*this[a]+(this[a+1]<<16|this[a+2]<<8|this[a+3])},g.prototype.readIntLE=function(a
,b,c){a=0|a,b=0|b,c||J(a,b,this.length);for(var d=this[a],e=1,f=0;++f<b&&(e*=256);)d+=this[a+f]*e;return e*=128,d>=e&&(d-=Math.pow(2,8*b)),d},g.prototype.readIntBE=function(a,b,c){a=0|a,b=0|b,c||J(a,b,this.length);for(var d=b,e=1,f=this[a+--d];d>0&&(e*=256);)f+=this[a+--d]*e;return e*=128,f>=e&&(f-=Math.pow(2,8*b)),f},g.prototype.readInt8=function(a,b){return b||J(a,1,this.length),128&this[a]?-1*(255-this[a]+1):this[a]},g.prototype.readInt16LE=function(a,b){b||J(a,2,this.length);var c=this[a]|this[a+1]<<8;return 32768&c?4294901760|c:c},g.prototype.readInt16BE=function(a,b){b||J(a,2,this.length);var c=this[a+1]|this[a]<<8;return 32768&c?4294901760|c:c},g.prototype.readInt32LE=function(a,b){return b||J(a,4,this.length),this[a]|this[a+1]<<8|this[a+2]<<16|this[a+3]<<24},g.prototype.readInt32BE=function(a,b){return b||J(a,4,this.length),this[a]<<24|this[a+1]<<16|this[a+2]<<8|this[a+3]},g.prototype.readFloatLE=function(a,b){return b||J(a,4,this.length),$.read(this,a,!0,23,4)},g.prototype.
readFloatBE=function(a,b){return b||J(a,4,this.length),$.read(this,a,!1,23,4)},g.prototype.readDoubleLE=function(a,b){return b||J(a,8,this.length),$.read(this,a,!0,52,8)},g.prototype.readDoubleBE=function(a,b){return b||J(a,8,this.length),$.read(this,a,!1,52,8)},g.prototype.writeUIntLE=function(a,b,c,d){if(a=+a,b=0|b,c=0|c,!d){var e=Math.pow(2,8*c)-1;K(this,a,b,c,e,0)}var f=1,g=0;for(this[b]=255&a;++g<c&&(f*=256);)this[b+g]=a/f&255;return b+c},g.prototype.writeUIntBE=function(a,b,c,d){if(a=+a,b=0|b,c=0|c,!d){var e=Math.pow(2,8*c)-1;K(this,a,b,c,e,0)}var f=c-1,g=1;for(this[b+f]=255&a;--f>=0&&(g*=256);)this[b+f]=a/g&255;return b+c},g.prototype.writeUInt8=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,1,255,0),g.TYPED_ARRAY_SUPPORT||(a=Math.floor(a)),this[b]=255&a,b+1},g.prototype.writeUInt16LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,2,65535,0),g.TYPED_ARRAY_SUPPORT?(this[b]=255&a,this[b+1]=a>>>8):L(this,a,b,!0),b+2},g.prototype.writeUInt16BE=function(a,b,c){return a=+a,b=0|b,
c||K(this,a,b,2,65535,0),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=255&a):L(this,a,b,!1),b+2},g.prototype.writeUInt32LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,4294967295,0),g.TYPED_ARRAY_SUPPORT?(this[b+3]=a>>>24,this[b+2]=a>>>16,this[b+1]=a>>>8,this[b]=255&a):M(this,a,b,!0),b+4},g.prototype.writeUInt32BE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,4294967295,0),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+2]=a>>>8,this[b+3]=255&a):M(this,a,b,!1),b+4},g.prototype.writeIntLE=function(a,b,c,d){if(a=+a,b=0|b,!d){var e=Math.pow(2,8*c-1);K(this,a,b,c,e-1,-e)}var f=0,g=1,h=0;for(this[b]=255&a;++f<c&&(g*=256);)0>a&&0===h&&0!==this[b+f-1]&&(h=1),this[b+f]=(a/g>>0)-h&255;return b+c},g.prototype.writeIntBE=function(a,b,c,d){if(a=+a,b=0|b,!d){var e=Math.pow(2,8*c-1);K(this,a,b,c,e-1,-e)}var f=c-1,g=1,h=0;for(this[b+f]=255&a;--f>=0&&(g*=256);)0>a&&0===h&&0!==this[b+f+1]&&(h=1),this[b+f]=(a/g>>0)-h&255;return b+c},g.prototype.writeInt8=function(a,b,c){retur
n a=+a,b=0|b,c||K(this,a,b,1,127,-128),g.TYPED_ARRAY_SUPPORT||(a=Math.floor(a)),0>a&&(a=255+a+1),this[b]=255&a,b+1},g.prototype.writeInt16LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,2,32767,-32768),g.TYPED_ARRAY_SUPPORT?(this[b]=255&a,this[b+1]=a>>>8):L(this,a,b,!0),b+2},g.prototype.writeInt16BE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,2,32767,-32768),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>8,this[b+1]=255&a):L(this,a,b,!1),b+2},g.prototype.writeInt32LE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,2147483647,-2147483648),g.TYPED_ARRAY_SUPPORT?(this[b]=255&a,this[b+1]=a>>>8,this[b+2]=a>>>16,this[b+3]=a>>>24):M(this,a,b,!0),b+4},g.prototype.writeInt32BE=function(a,b,c){return a=+a,b=0|b,c||K(this,a,b,4,2147483647,-2147483648),0>a&&(a=4294967295+a+1),g.TYPED_ARRAY_SUPPORT?(this[b]=a>>>24,this[b+1]=a>>>16,this[b+2]=a>>>8,this[b+3]=255&a):M(this,a,b,!1),b+4},g.prototype.writeFloatLE=function(a,b,c){return O(this,a,b,!0,c)},g.prototype.writeFloatBE=function(a,b,c){return O(t
his,a,b,!1,c)},g.prototype.writeDoubleLE=function(a,b,c){return P(this,a,b,!0,c)},g.prototype.writeDoubleBE=function(a,b,c){return P(this,a,b,!1,c)},g.prototype.copy=function(a,b,c,d){if(c||(c=0),d||0===d||(d=this.length),b>=a.length&&(b=a.length),b||(b=0),d>0&&c>d&&(d=c),d===c)return 0;if(0===a.length||0===this.length)return 0;if(0>b)throw new RangeError("targetStart out of bounds");if(0>c||c>=this.length)throw new RangeError("sourceStart out of bounds");if(0>d)throw new RangeError("sourceEnd out of bounds");d>this.length&&(d=this.length),a.length-b<d-c&&(d=a.length-b+c);var e,f=d-c;if(this===a&&b>c&&d>b)for(e=f-1;e>=0;--e)a[e+b]=this[e+c];else if(1e3>f||!g.TYPED_ARRAY_SUPPORT)for(e=0;f>e;++e)a[e+b]=this[e+c];else Uint8Array.prototype.set.call(a,this.subarray(c,c+f),b);return f},g.prototype.fill=function(a,b,c,d){if("string"==typeof a){if("string"==typeof b?(d=b,b=0,c=this.length):"string"==typeof c&&(d=c,c=this.l
<TRUNCATED>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org
[04/10] qpid-dispatch git commit: DISPATCH-531 Initial version of
openstack horizon plugin
Posted by ea...@apache.org.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.controller.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.controller.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.controller.js
new file mode 100644
index 0000000..8ed64a1
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.controller.js
@@ -0,0 +1,1428 @@
+/*
+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) {
+ 'use strict';
+
+ /**
+ * @method OverviewController
+ * @param $scope
+ * @param QDRService
+ * @param QDRChartServer
+ * dialogServer
+ * $location
+ *
+ * Controller that handles the QDR overview page
+ */
+ angular
+ .module('horizon.dashboard.dispatch.overv')
+ .controller('horizon.dashboard.dispatch.overv.OverviewController', OverviewController);
+
+ OverviewController.$inject = [
+ '$scope',
+ 'horizon.dashboard.dispatch.comService',
+ 'horizon.dashboard.dispatch.chartService',
+ '$location',
+ '$timeout',
+ 'horizon.dashboard.dispatch.overv.basePath',
+ 'uiGridConstants',
+ ];
+
+ var FILTERKEY = "QDROverviewFilters"
+ function OverviewController(
+ $scope,
+ QDRService,
+ QDRChartService,
+ $location,
+ $timeout,
+ basePath,
+ uiGridConstants) {
+
+ var ctrl = this;
+
+ QDR.log.debug("QDR.OverviewController started");
+
+ QDRService.addConnectAction( function () {
+ Overview(
+ $scope,
+ QDRService,
+ QDRChartService,
+ $location,
+ $timeout,
+ basePath,
+ uiGridConstants);
+ })
+ QDRService.loadConnectOptions(QDRService.connect);
+
+ $scope.filter = angular.fromJson(localStorage[FILTERKEY]) || {endpointsOnly: true, hideConsoles: true};
+ var showConsoleLinksTitle = function () {
+ return ($scope.filter.hideConsoles ? "Show" : "Hide") + " Console Links"
+ }
+ var showHideConsoleMenuItem = {
+ title: showConsoleLinksTitle(),
+ action: function($event) {
+ $scope.filter.hideConsoles = !$scope.filter.hideConsoles
+ // assumes this is always the 1st custom menu item added
+ this.context.col.colDef.menuItems[0].title = showConsoleLinksTitle()
+ $timeout($scope.allLinkInfo)
+ },
+ }
+ var endpointLinksTitle = function () {
+ return "Show" + ($scope.filter.endpointsOnly ? " all link types" : " endpoints only")
+ }
+
+ $scope.allRouterFields = [];
+ $scope.allRouters = {
+ saveKey: 'allRouters',
+ data: 'allRouterFields',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'routerId',
+ saveKey: 'allRouters',
+ displayName: 'Router'
+ },
+ {
+ field: 'area',
+ displayName: 'Area'
+ },
+ {
+ field: 'mode',
+ displayName: 'Mode'
+ },
+ {
+ field: 'connections',
+ displayName: 'External connections'
+ },
+ {
+ field: 'addrCount',
+ displayName: 'Address count'
+ },
+ {
+ field: 'linkCount',
+ displayName: 'Link count'
+ }
+ ],
+ enableRowSelection: true,
+ enableRowHeaderSelection: false,
+ multiSelect: false,
+ enableColumnResize: true,
+ enableColumnReordering: true,
+ onRegisterApi: function(gridApi){
+ gridApi.selection.on.rowSelectionChanged($scope,function(row){
+ $scope.setActivated('router' , row.entity.nodeId, 'nodeId')
+ });
+ }
+ };
+ $scope.routerFields = []
+ $scope.routerGrid = {
+ saveKey: 'routerGrid',
+ data: 'routerFields',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'attribute',
+ displayName: 'Attribute',
+ saveKey: 'routerGrid',
+ },
+ {
+ field: 'value',
+ displayName: 'Value',
+ }
+ ],
+ enableColumnResize: true,
+ multiSelect: false
+ }
+ $scope.addressesData = []
+ $scope.addressesGrid = {
+ saveKey: 'addressesGrid',
+ data: 'addressesData',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'address',
+ saveKey: 'addressesGrid',
+ displayName: 'address'
+ },
+ {
+ field: 'class',
+ displayName: 'class'
+ },
+ {
+ field: 'phase',
+ displayName: 'phase',
+ width: 80,
+ cellClass: 'grid-align-value'
+ },
+ {
+ field: 'inproc',
+ width: 80,
+ displayName: 'in-proc'
+ },
+ {
+ field: 'local',
+ displayName: 'local',
+ width: 80,
+ cellClass: 'grid-align-value'
+ },
+ {
+ field: 'remote',
+ displayName: 'remote',
+ width: 80,
+ cellClass: 'grid-align-value'
+ },
+ {
+ field: 'in',
+ displayName: 'in',
+ cellClass: 'grid-align-value'
+ },
+ {
+ field: 'out',
+ displayName: 'out',
+ cellClass: 'grid-align-value'
+ }
+ ],
+
+ enableRowSelection: true,
+ enableRowHeaderSelection: false,
+ multiSelect: false,
+ enableColumnResize: true,
+ enableColumnReordering: true,
+ onRegisterApi: function(gridApi){
+ gridApi.selection.on.rowSelectionChanged($scope,function(row){
+ $scope.setActivated('address', row.entity.uid, 'uid')
+ });
+ }
+ };
+ // get info for a all links
+ $scope.linkFields = []
+ $scope.linksGrid = {
+ saveKey: 'linksGrid',
+ data: 'linkFields',
+ enableFiltering: true,
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ saveKey: 'linksGrid',
+ field: 'link',
+ displayName: 'Link',
+ filter: {placeholder: 'filter link name...'},
+ menuItems: [showHideConsoleMenuItem],
+ },
+ {
+ field: 'linkType',
+ displayName: 'Link type',
+ filter: {
+ term: $scope.filter.endpointsOnly ? 'endpoint' : '',
+ placeholder: 'filter link type...',
+ },
+ menuItems: [showHideConsoleMenuItem,
+ {
+ title: endpointLinksTitle(),
+ action: function($event) {
+ $scope.filter.endpointsOnly = !$scope.filter.endpointsOnly
+ // assumes this is the 2nd custom menu item added
+ this.context.col.colDef.menuItems[1].title = endpointLinksTitle()
+ this.context.col.filters[0].term = $scope.filter.endpointsOnly ? 'endpoint' : ''
+ $timeout($scope.allLinkInfo)
+ },
+ }],
+ },
+ {
+ field: 'linkDir',
+ displayName: 'Link dir',
+ filter: {placeholder: 'filter link dir...'},
+ menuItems: [showHideConsoleMenuItem],
+ },
+ {
+ field: 'adminStatus',
+ displayName: 'Admin status',
+ filter: {placeholder: 'filter admin status...'},
+ menuItems: [showHideConsoleMenuItem],
+ },
+ {
+ field: 'operStatus',
+ displayName: 'Oper status',
+ filter: {placeholder: 'filter oper status...'},
+ menuItems: [showHideConsoleMenuItem],
+ },
+ {
+ field: 'deliveryCount',
+ displayName: 'Delivery Count',
+ enableFiltering: false,
+ menuItems: [showHideConsoleMenuItem],
+ },
+ {
+ field: 'rate',
+ displayName: 'Rate',
+ enableFiltering: false,
+ menuItems: [showHideConsoleMenuItem],
+ },
+ {
+ field: 'uncounts',
+ displayName: 'Outstanding',
+ enableFiltering: false,
+ menuItems: [showHideConsoleMenuItem],
+ },
+ {
+ field: 'owningAddr',
+ displayName: 'Address',
+ filter: {placeholder: 'filter address...'},
+ menuItems: [showHideConsoleMenuItem],
+ }/*,
+ {
+ displayName: 'Quiesce',
+ cellClass: 'gridCellButton',
+ cellTemplate: '<button title="{$quiesceLinkText(row)$} this link" type="button" ng-class="quiesceLinkClass(row)" class="btn" ng-click="quiesceLink(row, $event)" ng-disabled="quiesceLinkDisabled(row)">{$quiesceLinkText(row)$}</button>',
+ width: '10%'
+ }*/
+ ],
+ enableRowSelection: true,
+ enableRowHeaderSelection: false,
+ multiSelect: false,
+ enableColumnReordering: true,
+ showColumnMenu: true,
+ rowTemplate: 'dispatch/tplLinkRow.html',
+ onRegisterApi: function(gridApi){
+ gridApi.selection.on.rowSelectionChanged($scope,function(row){
+ $scope.setActivated('link', row.entity.uid, 'uid')
+ });
+ gridApi.core.on.filterChanged( $scope, function() {
+ var column = this.grid.columns[1];
+ $scope.filter.endpointsOnly = (column.filters[0].term === 'endpoint' )
+ column.colDef.menuItems[1].title = endpointLinksTitle()
+ });
+ }
+ };
+ $scope.allConnectionFields = []
+ $scope.allConnectionGrid = {
+ saveKey: 'allConnGrid',
+ data: 'allConnectionFields',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'host',
+ saveKey: 'allConnGrid',
+ displayName: 'host'
+ },
+ {
+ field: 'container',
+ displayName: 'container'
+ },
+ {
+ field: 'role',
+ displayName: 'role'
+ },
+ {
+ field: 'dir',
+ displayName: 'dir'
+ },
+ {
+ field: 'security',
+ displayName: 'security'
+ },
+ {
+ field: 'authentication',
+ displayName: 'authentication'
+ }
+ ],
+ enableRowSelection: true,
+ enableRowHeaderSelection: false,
+ multiSelect: false,
+ enableColumnResize: true,
+ enableColumnReordering: true,
+ onRegisterApi: function(gridApi){
+ gridApi.selection.on.rowSelectionChanged($scope,function(row){
+ $scope.setActivated('connection', row.entity.uid, 'uid')
+ });
+ }
+ };
+ $scope.addressFields = []
+ $scope.addressGrid = {
+ saveKey: 'addGrid',
+ data: 'addressFields',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'attribute',
+ displayName: 'Attribute',
+ saveKey: 'addGrid',
+ },
+ {
+ field: 'value',
+ displayName: 'Value',
+ }
+ ],
+ enableColumnResize: true,
+ multiSelect: false
+ }
+ $scope.singleLinkFields = []
+ $scope.linkGrid = {
+ saveKey: 'linkGrid',
+ data: 'singleLinkFields',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'attribute',
+ displayName: 'Attribute',
+ },
+ {
+ field: 'value',
+ displayName: 'Value',
+ }
+ ],
+ enableColumnResize: true,
+ multiSelect: false
+ }
+ $scope.connectionFields = []
+ $scope.connectionGrid = {
+ saveKey: 'connGrid',
+ data: 'connectionFields',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'attribute',
+ displayName: 'Attribute',
+ saveKey: 'connGrid',
+ },
+ {
+ field: 'value',
+ displayName: 'Value',
+ }
+ ],
+ enableColumnResize: true,
+ multiSelect: false
+ }
+ $scope.allLogFields = []
+ $scope.allLogGrid = {
+ saveKey: 'allLogGrid',
+ data: 'allLogFields',
+ enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
+ columnDefs: [
+ {
+ field: 'module',
+ saveKey: 'allLogGrid',
+ displayName: 'Module'
+ },
+ {
+ field: 'enable',
+ displayName: 'Enable'
+ },
+ {
+ field: 'count',
+ displayName: 'Count'
+ }
+ ],
+ enableRowSelection: true,
+ enableRowHeaderSelection: false,
+ multiSelect: false,
+ enableColumnResize: true,
+ enableColumnReordering: true,
+ onRegisterApi: function(gridApi){
+ gridApi.selection.on.rowSelectionChanged($scope,function(row){
+ $scope.setActivated('log', row.entity.module, 'module')
+ });
+ }
+ };
+ }
+
+ function Overview (
+ $scope,
+ QDRService,
+ QDRChartService,
+ $location,
+ $timeout,
+ basePath,
+ uiGridConstants) {
+
+ var COLUMNSTATEKEY = 'QDRColumnKey.';
+ var OVERVIEWEXPANDEDKEY = "QDROverviewExpanded"
+ var OVERVIEWACTIVATEDKEY = "QDROverviewActivated"
+ var OVERVIEWMODEIDS = "QDROverviewModeIds"
+
+ // we want attributes to be listed first, so add it at index 0
+
+ $scope.subLevelTabs = [{
+ content: '<i class="icon-list"></i> Attributes',
+ title: "View the attribute values on your selection",
+ isValid: function (workspace) { return true; },
+ href: function () { return "#/" + QDR.pluginName + "/attributes"; },
+ index: 0
+ },
+ {
+ content: '<i class="icon-leaf"></i> Operations',
+ title: "Execute operations on your selection",
+ isValid: function (workspace) { return true; },
+ href: function () { return "#/" + QDR.pluginName + "/operations"; },
+ index: 1
+ }]
+
+ $scope.activeTab = $scope.subLevelTabs[0];
+ $scope.setActive = function (nav) {
+ $scope.activeTab = nav;
+ };
+ $scope.isValid = function (nav) {
+ return nav.isValid()
+ }
+ $scope.isActive = function (nav) {
+ return nav == $scope.activeTab;
+ }
+ var refreshInterval = 5000
+ $scope.modes = [
+ {title: 'Overview', name: 'Overview', right: false}
+ ];
+
+ // get info for all routers
+ var allRouterInfo = function () {
+ var nodeIds = QDRService.nodeIdList()
+ var expected = Object.keys(nodeIds).length
+ var received = 0;
+ var allRouterFields = [];
+ var gotNodeInfo = function (nodeName, entity, response) {
+ var results = response.results;
+ var name = QDRService.nameFromId(nodeName)
+ var connections = 0;
+ results.forEach( function (result) {
+ var role = QDRService.valFor(response.attributeNames, result, "role")
+ if (role != 'inter-router') {
+ ++connections
+ }
+ })
+ allRouterFields.push({routerId: name, connections: connections, nodeId: nodeName})
+ ++received
+ if (expected == received) {
+ allRouterFields.sort ( function (a,b) { return a.routerId < b.routerId ? -1 : a.routerId > b.routerId ? 1 : 0})
+ // now get each router's node info
+ QDRService.getMultipleNodeInfo(nodeIds, "router", [], function (nodeIds, entity, responses) {
+ for(var r in responses) {
+ var result = responses[r]
+ var routerId = QDRService.valFor(result.attributeNames, result.results[0], "id")
+ allRouterFields.some( function (connField) {
+ if (routerId === connField.routerId) {
+ result.attributeNames.forEach ( function (attrName) {
+ connField[attrName] = QDRService.valFor(result.attributeNames, result.results[0], attrName)
+ })
+ connField['routerId'] = connField.id; // like QDR.A
+ return true
+ }
+ return false
+ })
+ }
+ $scope.allRouterFields = allRouterFields
+ updateRouterTree(allRouterFields)
+ }, nodeIds[0], false)
+ }
+ }
+ nodeIds.forEach ( function (nodeId, i) {
+ QDRService.getNodeInfo(nodeId, ".connection", ["role"], gotNodeInfo)
+ })
+ loadColState($scope.allRouters)
+ }
+
+ // get info for a single router
+ var routerInfo = function (node) {
+ $scope.router = node
+
+ $scope.routerFields = [];
+ var fields = Object.keys(node.fields)
+ fields.forEach( function (field) {
+ var attr = (field === 'connections') ? 'External connections' : field
+ $scope.routerFields.push({attribute: attr, value: node.fields[field]})
+ })
+ loadColState($scope.routerGrid);
+ }
+
+ // get info for all addresses
+ var allAddressInfo = function () {
+ var gotAllAddressFields = function ( addressFields ) {
+ if (!addressFields || addressFields.length === 0)
+ return;
+ // update the grid's data
+ addressFields.sort ( function (a,b) { return a.address < b.address ? -1 : a.address > b.address ? 1 : 0})
+ addressFields[0].title = addressFields[0].address
+ for (var i=1; i<addressFields.length; ++i) {
+ if (addressFields[i].address === addressFields[i-1].address) {
+ addressFields[i-1].title = addressFields[i-1].address + " (" + addressFields[i-1]['class'] + ")"
+ addressFields[i].title = addressFields[i].address + " (" + addressFields[i]['class'] + ")"
+ } else
+ addressFields[i].title = addressFields[i].address
+ }
+ $scope.addressesData = addressFields
+
+ // repopulate the tree's child nodes
+ updateAddressTree(addressFields)
+ }
+ getAllAddressFields(gotAllAddressFields)
+ loadColState($scope.addressesGrid);
+ }
+
+ var getAllAddressFields = function (callback) {
+ var nodeIds = QDRService.nodeIdList()
+ var addr_class = function (addr) {
+ if (!addr) return "-"
+ if (addr[0] == 'M') return "mobile"
+ if (addr[0] == 'R') return "router"
+ if (addr[0] == 'A') return "area"
+ if (addr[0] == 'L') return "local"
+ if (addr[0] == 'C') return "link-incoming"
+ if (addr[0] == 'D') return "link-outgoing"
+ if (addr[0] == 'T') return "topo"
+ return "unknown: " + addr[0]
+ }
+
+ var addr_phase = function (addr) {
+ if (!addr)
+ return "-"
+ if (addr[0] == 'M')
+ return addr[1]
+ return ''
+ }
+
+ var addressFields = []
+ QDRService.getMultipleNodeInfo(nodeIds, "router.address", [], function (nodeIds, entity, response) {
+ response.aggregates.forEach( function (result) {
+ var prettySum = function (field) {
+ var fieldIndex = response.attributeNames.indexOf(field)
+ if (fieldIndex < 0) {
+ return "-"
+ }
+ var val = result[fieldIndex].sum
+ return QDRService.pretty(val)
+ }
+
+ var uid = QDRService.valFor(response.attributeNames, result, "identity").sum
+ var identity = QDRService.identity_clean(uid)
+
+ addressFields.push({
+ address: QDRService.addr_text(identity),
+ 'class': QDRService.addr_class(identity),
+ phase: addr_phase(identity),
+ inproc: prettySum("inProcess"),
+ local: prettySum("subscriberCount"),
+ remote: prettySum("remoteCount"),
+ 'in': prettySum("deliveriesIngress"),
+ out: prettySum("deliveriesEgress"),
+ thru: prettySum("deliveriesTransit"),
+ toproc: prettySum("deliveriesToContainer"),
+ fromproc:prettySum("deliveriesFromContainer"),
+ uid: uid
+ })
+ })
+ callback(addressFields)
+ }, nodeIds[0])
+ }
+
+ var updateLinkGrid = function ( linkFields ) {
+ // apply the filter
+
+ var filteredLinks = linkFields.filter( function (link) {
+ var include = true;
+/*
+ if ($scope.filter.endpointsOnly === true) {
+ if (link.linkType !== 'endpoint')
+ include = false;
+ }
+*/
+ if ($scope.filter.hideConsoles) {
+ if (QDRService.isConsoleLink(link))
+ include = false;
+ }
+ return include;
+ })
+ $scope.linkFields = filteredLinks;
+ }
+
+ $scope.$watch("filter", function (newValue, oldValue) {
+ if (newValue !== oldValue) {
+ $timeout($scope.allLinkInfo);
+ localStorage[FILTERKEY] = JSON.stringify($scope.filter)
+ }
+ }, true)
+
+ $scope.$on('ngGridEventColumns', function (e, columns) {
+ var saveInfo = columns.map( function (col) {
+ return [col.width, col.visible]
+ })
+ var saveKey = columns[0].colDef.saveKey
+ if (saveKey)
+ localStorage.setItem(COLUMNSTATEKEY+saveKey, JSON.stringify(saveInfo));
+ })
+
+ var loadColState = function (grid) {
+ if (!grid)
+ return;
+ var columns = localStorage.getItem(COLUMNSTATEKEY+grid.saveKey);
+ if (columns) {
+ var cols = JSON.parse(columns);
+ cols.forEach( function (col, index) {
+ if (grid.columnDefs[index]) {
+ grid.columnDefs[index].width = col[0];
+ grid.columnDefs[index].visible = col[1]
+ }
+ })
+ }
+ }
+
+ $scope.allLinkInfo = function () {
+ getAllLinkFields([updateLinkGrid, updateLinkTree])
+// loadColState($scope.linksGrid);
+ }
+
+ var getAllLinkFields = function (completionCallbacks, selectionCallback) {
+ var nodeIds = QDRService.nodeIdList()
+ var linkFields = []
+ var now = Date.now()
+ var rate = function (linkName, response, result) {
+ if (!$scope.linkFields)
+ return 0;
+ var oldname = $scope.linkFields.filter(function (link) {
+ return link.link === linkName
+ })
+ if (oldname.length === 1) {
+ var elapsed = (now - oldname[0].timestamp) / 1000;
+ if (elapsed < 0)
+ return 0
+ var delivered = QDRService.valFor(response.attributeNames, result, "deliveryCount") - oldname[0].rawDeliveryCount
+ //QDR.log.debug("elapsed " + elapsed + " delivered " + delivered)
+ return elapsed > 0 ? parseFloat(Math.round((delivered/elapsed) * 100) / 100).toFixed(2) : 0;
+ } else {
+ //QDR.log.debug("unable to find old linkName")
+ return 0
+ }
+ }
+ var expected = nodeIds.length;
+ var received = 0;
+ var gotLinkInfo = function (nodeName, entity, response) {
+ response.results.forEach( function (result) {
+ var nameIndex = response.attributeNames.indexOf('name')
+ var prettyVal = function (field) {
+ var fieldIndex = response.attributeNames.indexOf(field)
+ if (fieldIndex < 0) {
+ return "-"
+ }
+ var val = result[fieldIndex]
+ return QDRService.pretty(val)
+ }
+ var uncounts = function () {
+ var und = QDRService.valFor(response.attributeNames, result, "undeliveredCount")
+ var uns = QDRService.valFor(response.attributeNames, result, "unsettledCount")
+ return QDRService.pretty(und + uns)
+ }
+ var linkName = function () {
+ var namestr = QDRService.nameFromId(nodeName)
+ return namestr + ':' + QDRService.valFor(response.attributeNames, result, "identity")
+ }
+ var fixAddress = function () {
+ var addresses = []
+ var owningAddr = QDRService.valFor(response.attributeNames, result, "owningAddr") || ""
+ var rawAddress = owningAddr
+ /*
+ - "L*" => "* (local)"
+ - "M0*" => "* (direct)"
+ - "M1*" => "* (dequeue)"
+ - "MX*" => "* (phase X)"
+ */
+ var address = undefined;
+ var starts = {'L': '(local)', 'M0': '(direct)', 'M1': '(dequeue)'}
+ for (var start in starts) {
+ if (owningAddr.startsWith(start)) {
+ var ends = owningAddr.substr(start.length)
+ address = ends + " " + starts[start]
+ rawAddress = ends
+ break;
+ }
+ }
+ if (!address) {
+ // check for MX*
+ if (owningAddr.length > 3) {
+ if (owningAddr[0] === 'M') {
+ var phase = parseInt(owningAddress.substr(1))
+ if (phase && !isNaN(phase)) {
+ var phaseStr = phase + "";
+ var star = owningAddress.substr(2 + phaseStr.length)
+ address = star + " " + "(phase " + phaseStr + ")"
+ }
+ }
+ }
+ }
+ addresses[0] = address || owningAddr
+ addresses[1] = rawAddress
+ return addresses
+ }
+ if (!selectionCallback || selectionCallback(response, result)) {
+ var adminStatus = QDRService.valFor(response.attributeNames, result, "adminStatus")
+ var operStatus = QDRService.valFor(response.attributeNames, result, "operStatus")
+ var linkName = linkName()
+ var linkType = QDRService.valFor(response.attributeNames, result, "linkType")
+ var rawRate = rate(linkName, response, result)
+ var addresses = fixAddress();
+ linkFields.push({
+ link: linkName,
+ title: linkName,
+ uncounts: uncounts(),
+ operStatus: operStatus,
+ adminStatus:adminStatus,
+ owningAddr: addresses[0],
+
+ acceptedCount: prettyVal("acceptedCount"),
+ modifiedCount: prettyVal("modifiedCount"),
+ presettledCount: prettyVal("presettledCount"),
+ rejectedCount: prettyVal("rejectedCount"),
+ releasedCount: prettyVal("releasedCount"),
+ deliveryCount:prettyVal("deliveryCount") + " ",
+
+ rate: QDRService.pretty(rawRate),
+ rawRate: rawRate,
+ capacity: QDRService.valFor(response.attributeNames, result, "capacity"),
+ undeliveredCount: QDRService.valFor(response.attributeNames, result, "undeliveredCount"),
+ unsettledCount: QDRService.valFor(response.attributeNames, result, "unsettledCount"),
+
+ rawAddress: addresses[1],
+ rawDeliveryCount: QDRService.valFor(response.attributeNames, result, "deliveryCount"),
+ name: QDRService.valFor(response.attributeNames, result, "name"),
+ linkName: QDRService.valFor(response.attributeNames, result, "linkName"),
+ connectionId: QDRService.valFor(response.attributeNames, result, "connectionId"),
+ linkDir: QDRService.valFor(response.attributeNames, result, "linkDir"),
+ linkType: linkType,
+ peer: QDRService.valFor(response.attributeNames, result, "peer"),
+ type: QDRService.valFor(response.attributeNames, result, "type"),
+
+ uid: linkName,
+ timestamp: now,
+ nodeId: nodeName,
+ identity: QDRService.valFor(response.attributeNames, result, "identity")
+ })
+ }
+ })
+ if (expected === ++received) {
+ linkFields.sort ( function (a,b) { return a.link < b.link ? -1 : a.link > b.link ? 1 : 0})
+ completionCallbacks.forEach( function (cb) {
+ cb(linkFields)
+ })
+ }
+ }
+ nodeIds.forEach( function (nodeId) {
+ QDRService.getNodeInfo(nodeId, "router.link", [], gotLinkInfo);
+ })
+ }
+
+ // get info for a all connections
+ var allConnectionInfo = function () {
+ getAllConnectionFields([updateConnectionGrid, updateConnectionTree])
+ loadColState($scope.allConnectionGrid);
+ }
+ // called after conection data is available
+ var updateConnectionGrid = function (connectionFields) {
+ $scope.allConnectionFields = connectionFields;
+ }
+
+ // get the connection data for all nodes
+ // called periodically
+ // creates a connectionFields array and calls the callbacks (updateTree and updateGrid)
+ var getAllConnectionFields = function (callbacks) {
+ var nodeIds = QDRService.nodeIdList()
+ var connectionFields = [];
+ var expected = nodeIds.length;
+ var received = 0;
+ var gotConnectionInfo = function (nodeName, entity, response) {
+ response.results.forEach( function (result) {
+
+ var auth = "no_auth"
+ var sasl = QDRService.valFor(response.attributeNames, result, "sasl")
+ if (QDRService.valFor(response.attributeNames, result, "isAuthenticated")) {
+ auth = sasl
+ if (sasl === "ANONYMOUS")
+ auth = "anonymous-user"
+ else {
+ if (sasl === "GSSAPI")
+ sasl = "Kerberos"
+ if (sasl === "EXTERNAL")
+ sasl = "x.509"
+ auth = QDRService.valFor(response.attributeNames, result, "user") + "(" +
+ QDRService.valFor(response.attributeNames, result, "sslCipher") + ")"
+ }
+ }
+
+ var sec = "no-security"
+ if (QDRService.valFor(response.attributeNames, result, "isEncrypted")) {
+ if (sasl === "GSSAPI")
+ sec = "Kerberos"
+ else
+ sec = QDRService.valFor(response.attributeNames, result, "sslProto") + "(" +
+ QDRService.valFor(response.attributeNames, result, "sslCipher") + ")"
+ }
+
+ var host = QDRService.valFor(response.attributeNames, result, "host")
+ var connField = {
+ host: host,
+ security: sec,
+ authentication: auth,
+ routerId: nodeName,
+ uid: host + QDRService.valFor(response.attributeNames, result, "identity")
+ }
+ response.attributeNames.forEach( function (attribute, i) {
+ connField[attribute] = result[i]
+ })
+ connectionFields.push(connField)
+ })
+ if (expected === ++received) {
+ connectionFields.sort ( function (a,b) { return a.host < b.host ? -1 : a.host > b.host ? 1 : 0})
+ callbacks.forEach( function (cb) {
+ cb(connectionFields)
+ })
+ }
+ }
+ nodeIds.forEach( function (nodeId) {
+ QDRService.getNodeInfo(nodeId, ".connection", [], gotConnectionInfo)
+ })
+ }
+
+ // get info for a single address
+ var addressInfo = function (address) {
+ if (!address)
+ return;
+ $scope.address = address
+ var currentEntity = getCurrentLinksEntity();
+ // we are viewing the addressLinks page
+ if (currentEntity === 'Address' && entityModes[currentEntity].currentModeId === 'links') {
+ updateModeLinks()
+ return;
+ }
+
+ $scope.addressFields = [];
+ var fields = Object.keys(address.fields)
+ fields.forEach( function (field) {
+ if (field != "title" && field != "uid")
+ $scope.addressFields.push({attribute: field, value: address.fields[field]})
+ })
+ loadColState($scope.addressGrid);
+ }
+
+ // display the grid detail info for a single link
+ var linkInfo = function (link) {
+ if (!link)
+ return;
+
+ $scope.link = link;
+ $scope.singleLinkFields = [];
+ var fields = Object.keys(link.fields)
+ var excludeFields = ["title", "uid", "uncounts", "rawDeliveryCount", "timestamp", "rawAddress"]
+ fields.forEach( function (field) {
+ if (excludeFields.indexOf(field) == -1)
+ $scope.singleLinkFields.push({attribute: field, value: link.fields[field]})
+ })
+ loadColState($scope.linkGrid);
+ }
+
+ // get info for a single connection
+ $scope.gridModes = [{
+ content: '<a><i class="icon-list"></i> Attriutes</a>',
+ id: 'attributes',
+ title: "View attributes"
+ },
+ {
+ content: '<a><i class="icon-link"></i> Links</a>',
+ id: 'links',
+ title: "Show links"
+ }
+ ];
+ var saveModeIds = function () {
+ var modeIds = {Address: entityModes.Address.currentModeId, Connection: entityModes.Connection.currentModeId}
+ localStorage[OVERVIEWMODEIDS] = JSON.stringify(modeIds)
+ }
+ var loadModeIds = function () {
+ return angular.fromJson(localStorage[OVERVIEWMODEIDS]) ||
+ {Address: 'attributes', Connection: 'attributes'}
+ }
+ var savedModeIds = loadModeIds()
+ var entityModes = {
+ Address: {
+ currentModeId: savedModeIds.Address,
+ filter: function (response, result) {
+ var owningAddr = QDRService.valFor(response.attributeNames, result, "owningAddr")
+ var id = $scope.address.data.fields.uid
+ return (owningAddr === $scope.address.data.fields.uid)
+ }
+ },
+ Connection: {
+ currentModeId: savedModeIds.Connection,
+ filter: function (response, result) {
+ var connectionId = QDRService.valFor(response.attributeNames, result, "connectionId")
+ return (connectionId === $scope.connection.data.fields.identity)
+ }
+ }
+ }
+ $scope.selectMode = function (mode, entity) {
+ if (!mode || !entity)
+ return;
+ entityModes[entity].currentModeId = mode.id;
+ saveModeIds();
+ if (mode.id === 'links') {
+ $scope.linkFields = [];
+ updateModeLinks();
+ }
+ }
+ $scope.isModeSelected = function (mode, entity) {
+ return mode.id === entityModes[entity].currentModeId
+ }
+ $scope.isModeVisible = function (entity, id) {
+ return entityModes[entity].currentModeId === id
+ }
+
+ var updateEntityLinkGrid = function (linkFields) {
+ $timeout(function () {$scope.linkFields = linkFields})
+ }
+ // based on which entity is selected, get and filter the links
+ var updateModeLinks = function () {
+ var currentEntity = getCurrentLinksEntity()
+ if (!currentEntity)
+ return;
+ var selectionCallback = entityModes[currentEntity].filter;
+ getAllLinkFields([updateEntityLinkGrid], selectionCallback)
+ }
+ var getCurrentLinksEntity = function () {
+ var currentEntity;
+ var active = $("#overtree").dynatree("getActiveNode");
+ if (active) {
+ currentEntity = active.data.type;
+ }
+ return currentEntity;
+ }
+
+ $scope.quiesceLinkClass = function (row) {
+ var stateClassMap = {
+ enabled: 'btn-primary',
+ disabled: 'btn-danger'
+ }
+ return stateClassMap[row.entity.adminStatus]
+ }
+
+ $scope.quiesceLink = function (row, $event) {
+ QDRService.quiesceLink(row.entity.nodeId, row.entity.name);
+ $event.stopPropagation()
+ }
+
+ $scope.quiesceLinkDisabled = function (row) {
+ return (row.entity.operStatus !== 'up' && row.entity.operStatus !== 'down')
+ }
+ $scope.quiesceLinkText = function (row) {
+ return row.entity.adminStatus === 'disabled' ? "Revive" : "Quiesce";
+ }
+
+ $scope.expandAll = function () {
+ $("#overtree").dynatree("getRoot").visit(function(node){
+ node.expand(true);
+ });
+ }
+ $scope.contractAll = function () {
+ $("#overtree").dynatree("getRoot").visit(function(node){
+ node.expand(false);
+ })
+ }
+
+ var connectionInfo = function (connection) {
+ if (!connection)
+ return;
+ $scope.connection = connection
+
+ var currentEntity = getCurrentLinksEntity();
+ // we are viewing the connectionLinks page
+ if (currentEntity === 'Connection' && entityModes[currentEntity].currentModeId === 'links') {
+ updateModeLinks()
+ return;
+ }
+
+ $scope.connectionFields = [];
+ var fields = Object.keys(connection.fields)
+ fields.forEach( function (field) {
+ if (field != "title" && field != "uid")
+ $scope.connectionFields.push({attribute: field, value: connection.fields[field]})
+ })
+ $scope.selectMode($scope.currentMode)
+ loadColState($scope.connectionGrid);
+ }
+
+ // get info for a all logs
+ var allLogEntries = []
+ var allLogInfo = function () {
+ var nodeIds = QDRService.nodeIdList()
+ var expected = nodeIds.length;
+ var received = 0;
+ var logResults = []
+ var gotLogInfo = function (nodeId, entity, response, context) {
+ var statusCode = context.message.application_properties.statusCode;
+ if (statusCode < 200 || statusCode >= 300) {
+ Core.notification('error', context.message.application_properties.statusDescription);
+ //QDR.log.debug(context.message.application_properties.statusDescription)
+ return;
+ }
+ var logFields = response.map( function (result) {
+ return {
+ nodeId: QDRService.nameFromId(nodeId),
+ name: result[0],
+ type: result[1],
+ message: result[2],
+ source: result[3],
+ line: result[4],
+ time: Date(result[5]).toString()
+ }
+ })
+ logResults.push.apply(logResults, logFields) // append new array to existing
+ if (expected == ++received) {
+ logResults.sort( function (a, b) {
+ return b.name - a.name
+ })
+
+ $scope.allLogFields = [];
+ var options = $scope.data.log.options;
+ options.forEach( function (option) {
+ if (option.id != 'all') {
+ $scope.allLogFields.push(
+ {module: option.id,
+ enable: option.fields.enable,
+ count: logResults.filter( function (entry) {
+ return entry.name === option.fields.module
+ }).length
+ })
+ }
+ })
+ allLogEntries = logResults
+ }
+ }
+ nodeIds.forEach( function (node) {
+ QDRService.sendMethod(node, undefined, {}, "GET-LOG", {}, gotLogInfo)
+ })
+
+ }
+
+ // get info for a single log
+ var logInfo = function (node) {
+ $scope.log = node
+ $scope.logFields = allLogEntries.filter( function (log) {
+ return node.id === log.name
+ })
+ $scope.$apply();
+ }
+
+ var updateExpanded = function () {
+ if (!$scope.selectedObject)
+ return;
+ // find the parent of the selectedObject and call it's info function
+ if ($scope.selectedObject.pinfo)
+ $scope.selectedObject.pinfo()
+ $scope.selectedObject.info($scope.selectedObject)
+ }
+
+ var sendChartRequest = function (svgCharts) {
+ var gotChartData = function (linkFields) {
+ var now = new Date();
+ svgCharts.forEach( function (svgChart) {
+ var cutOff = new Date(now.getTime() - svgChart.chart.duration() * 60 * 1000);
+ var name = svgChart.chart.name()
+ var attr = svgChart.chart.attr()
+ var data = svgChart.chart.data(name, attr) // get a reference to the data array
+ var val = svgChart.chart.getVal(linkFields)
+ data.push([now, val])
+ // expire the old data
+ while (data[0][0] < cutOff) {
+ data.shift();
+ }
+ })
+ }
+ getAllLinkFields([gotChartData])
+ }
+
+ // loads the tree node name that was last selected
+ var loadActivatedNode = function () {
+ return localStorage[OVERVIEWACTIVATEDKEY] || 'Routers'
+ }
+ // saved the tree node name that is currently selected
+ var saveActivated = function (key) {
+ localStorage[OVERVIEWACTIVATEDKEY] = key;
+ lastKey = key;
+ }
+ // loads list that was saved
+ var loadExpandedNodeList = function () {
+ return angular.fromJson(localStorage[OVERVIEWEXPANDEDKEY]) || [];
+ }
+ // called when a node is expanded
+ // here we save the expanded node so it can be restored when the page reloads
+ var saveExpanded = function () {
+ var list = getExpandedList();
+ localStorage[OVERVIEWEXPANDEDKEY] = JSON.stringify(list)
+ expandedNodeList = list
+ }
+
+ $scope.setActivated = function (dropdown, uid, suid) {
+ $("#sel" + dropdown).val(uid)
+
+ var dd = $scope.data[dropdown];
+ var newOption;
+ if (uid == 'all') {
+ newOption = dd.options[0];
+ } else {
+ dd.options.some( function (option) {
+ if (option.fields && option.fields[suid] === uid) {
+ newOption = option
+ return true;
+ } else
+ return false;
+ })
+ }
+ $scope.activated(newOption)
+ }
+
+ $scope.selectedObject = null;
+ $scope.templateUrl = null;
+ // activated is called each time a dropdown is changed
+ // based on which node is clicked, load the correct data grid template and start getting the data
+ $scope.activated = function (node) {
+ if (!node)
+ return;
+ $scope.selectedObject = node;
+ if (node.id !== "all") {
+ $scope.data[node.type].sel = node
+ }
+ //saveExpanded()
+ //saveActivated(node.data.key)
+
+ $scope.templateUrl = 'dispatch/' + node.type + ".html";
+ // the node's info function will fetch the grids data
+ if (node.info) {
+ $timeout(function () {node.info(node)})
+ }
+ }
+
+ /* --------------------------------------------------
+ *
+ * setup the dropdowns
+ *
+ * -------------------------------------------------
+ */
+ var initDropDown = function (dd, info, type) {
+ $scope.data[dd] = {}
+ $scope.data[dd].options = getAllOption(dd, info, type)
+ $scope.data[dd].sel = $scope.data[dd].options[0];
+ }
+ var getAllOption = function (dd, info, type) {
+ return [{id: 'all',
+ name: 'All ' + type,
+ info: info,
+ type: type}]
+ }
+ var updateDropdown = function (dropdown, allFields, idKey, nameKey, allInfo, allType, info) {
+ var currentId = $scope.data[dropdown].sel.id;
+ $scope.data[dropdown].options = getAllOption(dropdown, allInfo, allType)
+ allFields.forEach( function (fields) {
+ var option = {id: fields[idKey],
+ name: fields[nameKey],
+ info: info,
+ type: dropdown,
+ fields: fields,
+ pinfo: allInfo}
+ $scope.data[dropdown].options.push(option);
+
+ if (currentId === option.id) {
+ $scope.data[dropdown].sel = option
+ }
+ })
+ if ($scope.selectedObject && $scope.selectedObject.type === dropdown) {
+ $scope.selectedObject = $scope.data[dropdown].sel;
+//QDR.log.debug("updated " + dropdown + " to ")
+//console.dump($scope.selectedObject)
+ }
+ }
+
+ // get saved tree state
+ var lastKey = loadActivatedNode();
+ // called when the list of routers changes
+ var updateRouterTree = function (routerFields) {
+ updateDropdown('router', routerFields, 'nodeId', 'routerId', allRouterInfo, 'routers', routerInfo)
+ }
+ var updateAddressTree = function (addressFields) {
+ updateDropdown('address', addressFields, 'uid', 'title', allAddressInfo, 'addresss', addressInfo)
+ }
+ // called whenever a background update is done and an option in the link dropdown is selected
+ var updateLinkTree = function (linkFields) {
+ updateDropdown('link', linkFields, 'uid', 'title', $scope.allLinkInfo, 'links', linkInfo)
+ }
+ var updateConnectionTree = function (connectionFields) {
+ updateDropdown('connection', connectionFields, 'uid', 'host', allConnectionInfo, 'connections', connectionInfo)
+ }
+
+ $scope.data = {}
+ initDropDown('router', allRouterInfo, 'routers')
+ initDropDown('address', allAddressInfo, 'addresss')
+ initDropDown('link', $scope.allLinkInfo, 'links', true)
+ initDropDown('connection', allConnectionInfo, 'connections')
+ initDropDown('log', allLogInfo, 'logs')
+ // called after we are connected to initialize data
+ var initTreeState = function () {
+ allRouterInfo();
+ allAddressInfo();
+ $scope.allLinkInfo();
+ allConnectionInfo()
+
+ var nodeIds = QDRService.nodeIdList()
+ QDRService.getNodeInfo(nodeIds[0], "log", ["module", "enable"], function (nodeName, entity, response) {
+ var moduleIndex = response.attributeNames.indexOf('module')
+ response.results.sort( function (a,b) {return a[moduleIndex] < b[moduleIndex] ? -1 : a[moduleIndex] > b[moduleIndex] ? 1 : 0})
+ response.results.forEach( function (result) {
+ var entry = QDRService.flatten(response.attributeNames, result)
+ $scope.data.log.options.push({id: entry.module, name: entry.module, info: logInfo, type: 'log', fields: entry, pinfo: allLogInfo});
+ })
+ initTreeAndGrid();
+ })
+ }
+
+ $scope.svgCharts = [];
+ var updateTimer;
+ var initCharts = function () {
+ var charts = [];
+ charts.push(QDRChartService.createRequestAndChart(
+ {
+ attr: 'Outstanding deliveries',
+ nodeId: '',
+ name: 'for all endpoints',
+ entity: 'router.link',
+ visibleDuration: 1,
+ duration: 1,
+ }))
+ charts[charts.length-1].getVal = function (linkFields) {
+ var uncountTotal = 0;
+ linkFields.forEach( function (row) {
+ if (row.linkType == 'endpoint' && !QDRService.isConsoleLink(row))
+ uncountTotal += row.undeliveredCount + row.unsettledCount
+ })
+ return uncountTotal;
+ }
+
+ charts.push(QDRChartService.createRequestAndChart(
+ {
+ //type: "rate",
+ attr: 'Outgoing deliveries per second',
+ nodeId: '',
+ name: 'for all endpoints',
+ entity: 'router.link',
+ visibleDuration: 1,
+ duration: 1,
+ }))
+ charts[charts.length-1].getVal = function (linkFields) {
+ var countTotal = 0.0;
+ linkFields.forEach( function (row) {
+ if (row.linkType == 'endpoint' && !QDRService.isConsoleLink(row) && row.linkDir == "out") {
+ countTotal += parseFloat(row.rawRate + "")
+ }
+ })
+ return countTotal;
+ }
+
+ charts.push(QDRChartService.createRequestAndChart(
+ {
+ //type: "rate",
+ attr: 'Incoming deliveries per second',
+ nodeId: '',
+ name: 'for all endpoints',
+ entity: 'router.link',
+ visibleDuration: 1,
+ duration: 1,
+ }))
+ charts[charts.length-1].getVal = function (linkFields) {
+ var countTotal = 0.0;
+ linkFields.forEach( function (row) {
+ if (row.linkType == 'endpoint' && !QDRService.isConsoleLink(row) && row.linkDir == "in")
+ countTotal += parseFloat(row.rawRate + "")
+ })
+ return countTotal;
+ }
+ charts[charts.length-1].areaColor = "#fcd6d6"
+ charts[charts.length-1].lineColor = "#c70404"
+
+ charts.forEach( function (chart) {
+ $scope.svgCharts.push(new QDRChartService.AreaChart(chart));
+ })
+ }
+ initCharts();
+
+ var initTreeAndGrid = function () {
+
+ // show the All routers page
+ $scope.setActivated('link', 'all')
+
+ // populate the data for each expanded node
+ $timeout(updateExpanded);
+ QDRService.addUpdatedAction( "overview", function () {
+ $timeout(updateExpanded);
+ sendChartRequest($scope.svgCharts)
+ })
+ // update the node list
+ QDRService.startUpdating()
+
+ var showChart = function () {
+ // the chart divs are generated by angular and aren't available immediately
+ var div = angular.element("#" + $scope.svgCharts[0].chart.id());
+ if (!div.width()) {
+ setTimeout(showChart, 100);
+ return;
+ }
+ updateDialogChart();
+ }
+
+ var updateDialogChart = function () {
+ $scope.svgCharts.forEach( function ( svgChart) {
+ svgChart.tick(svgChart.chart.id())
+ })
+ if (updateTimer)
+ clearTimeout(updateTimer)
+ updateTimer = setTimeout(updateDialogChart, 1000);
+ }
+ showChart();
+
+ loadColState($scope.allRouters);
+ loadColState($scope.routerGrid);
+ loadColState($scope.addressesGrid);
+ loadColState($scope.addressGrid);
+ loadColState($scope.linksGrid);
+ loadColState($scope.linkGrid);
+ loadColState($scope.allConnectionGrid);
+ loadColState($scope.connectionGrid);
+ } // end of initTreeAndGrid
+
+ $scope.$on("$destroy", function( event ) {
+ QDRService.stopUpdating()
+ QDRService.delUpdatedAction("overview")
+ if (updateTimer)
+ clearTimeout(updateTimer)
+ });
+ initTreeState();
+ QDRService.addDisconnectAction( function () {
+ QDR.log.debug("disconnected from router. show a toast message");
+ if (updateTimer)
+ clearTimeout(updateTimer)
+ })
+ };
+
+ return QDR;
+
+}(QDR || {}));
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/0c58c381/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.module.js
----------------------------------------------------------------------
diff --git a/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.module.js b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.module.js
new file mode 100644
index 0000000..cdde521
--- /dev/null
+++ b/console/dispatch-dashboard/dispatch/static/dashboard/dispatch/overv/overview.module.js
@@ -0,0 +1,178 @@
+/*
+ * Licensed 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.
+ */
+(function () {
+ 'use strict';
+
+ angular
+ .module('horizon.dashboard.dispatch.overv', [])
+ .config(config)
+ .run(addTemplates)
+
+ config.$inject = [
+ '$provide',
+ '$windowProvider'
+ ];
+
+ addTemplates.$inject = [
+ '$templateCache'
+ ]
+
+ /**
+ * @name config
+ * @param {Object} $provide
+ * @param {Object} $windowProvider
+ * @description Base path for the overview code
+ * @returns {undefined} No return value
+ */
+ function config($provide, $windowProvider) {
+ var path = $windowProvider.$get().STATIC_URL + 'dashboard/dispatch/overv/';
+ $provide.constant('horizon.dashboard.dispatch.overv.basePath', path);
+ }
+
+ function addTemplates($templateCache) {
+ $templateCache.put('dispatch/tplLinkRow.html',
+ "<div" +
+ " ng-repeat=\"(colRenderIndex, col) in colContainer.renderedColumns track by col.uid\"" +
+ " ui-grid-one-bind-id-grid=\"rowRenderIndex + '-' + col.uid + '-cell'\"" +
+ " class=\"ui-grid-cell\"" +
+ " ng-class=\"{ 'ui-grid-row-header-cell': col.isRowHeader, linkDirIn: row.entity.linkDir=='in', linkDirOut: row.entity.linkDir=='out' }\"" +
+ " role=\"{{col.isRowHeader ? 'rowheader' : 'gridcell'}}\"" +
+ " ui-grid-cell>" +
+ "</div>"
+ );
+ $templateCache.put('dispatch/links.html',
+ "<h3>Links</h3>" +
+ "<div class='grid' ui-grid='linksGrid' ui-grid-selection></div>"
+ );
+ $templateCache.put('dispatch/link.html',
+ "<h3>Link {$ link.name $}</h3>" +
+ "<div class='grid noHighlight' ui-grid='linkGrid' ui-grid-resize-columns></div>"
+ );
+ $templateCache.put('dispatch/overview.html',
+ "<div id=\"overview-controller\" ng-controller=\"horizon.dashboard.dispatch.overv.OverviewController as ctrl\">" +
+ " <div id=\"overview_charts\" class=\"clearfix\">" +
+ " <div ng-repeat=\"chart in svgCharts\" id=\"{$ chart.chart.id() $}\" class=\"d3Chart\"></div>" +
+ " </div>" +
+ " <hr/>" +
+ " <div id=\"overview_dropdowns\" class=\"clearfix\">" +
+ " <div class=\"overview-dropdown\"" +
+ " ng-class=\"{selected1: selectedObject.type == 'router', selected: selectedObject.type == 'routers'}\">" +
+ " <div class=\"dropdown-entity\">Routers</div>" +
+ " <select id=\"selrouter\" ng-options=\"option.name for option in data.router.options track by option.id\"" +
+ " ng-click=\"activated(data.router.sel)\" ng-model=\"data.router.sel\"></select>" +
+ " </div>" +
+ " <div class=\"overview-dropdown\"" +
+ " ng-class=\"{selected1: selectedObject.type == 'address', selected: selectedObject.type == 'addresses'}\">" +
+ " <div class=\"dropdown-entity\">Addresses</div>" +
+ " <select id=\"seladdress\" ng-options=\"option.name for option in data.address.options track by option.id\"" +
+ " ng-click=\"activated(data.address.sel)\" ng-model=\"data.address.sel\"></select>" +
+ " </div>" +
+ " <div class=\"overview-dropdown\"" +
+ " ng-class=\"{selected1: selectedObject.type == 'link', selected: selectedObject.type == 'links'}\">" +
+ " <div class=\"dropdown-entity\">Links</div>" +
+ " <select id=\"sellink\" ng-options=\"option.name for option in data.link.options track by option.id\"" +
+ " ng-click=\"activated(data.link.sel)\" ng-model=\"data.link.sel\"></select>" +
+ " </div>" +
+ " <div class=\"overview-dropdown\"" +
+ " ng-class=\"{selected1: selectedObject.type == 'connection', selected: selectedObject.type == 'connections'}\">" +
+ " <div class=\"dropdown-entity\">Connections</div>" +
+ " <select id=\"selconnection\" ng-options=\"option.name for option in data.connection.options track by option.id\"" +
+ " ng-click=\"activated(data.connection.sel)\" ng-model=\"data.connection.sel\"></select>" +
+ " </div>" +
+ " <div class=\"overview-dropdown\"" +
+ " ng-class=\"{selected1: selectedObject.type == 'log', selected: selectedObject.type == 'logs'}\">" +
+ " <div class=\"dropdown-entity\">Logs</div>" +
+ " <select id=\"sellog\" ng-options=\"option.name for option in data.log.options track by option.id\"" +
+ " ng-click=\"activated(data.log.sel)\" ng-model=\"data.log.sel\"></select>" +
+ " </div>" +
+ " </div>" +
+ " <div ng-include=\"templateUrl\"></div>" +
+ " <div ng-init=\"overviewLoaded()\"></div>" +
+ "</div>"
+ );
+ $templateCache.put('dispatch/addresss.html',
+ "<h3>Addresses</h3>" +
+ "<div class='grid' ui-grid='addressesGrid' ui-grid-selection></div>"
+ );
+ $templateCache.put('dispatch/address.html',
+ "<ul class=\"nav nav-tabs\">" +
+ " <li ng-repeat=\"mode in gridModes\" ng-click=\"selectMode(mode,'Address')\" ng-class=\"{active : isModeSelected(mode,'Address')}\" title=\"{$ mode.title $}\" ng-bind-html-unsafe=\"mode.content\"> </li>" +
+ "</ul>" +
+ "<div ng-if=\"isModeVisible('Address','attributes')\" class=\"selectedItems\">" +
+ " <h3>Address {$ address.name $}</h3>" +
+ " <div class=\"gridStyle noHighlight\" ui-grid=\"addressGrid\"></div>" +
+ "</div>" +
+ "<div ng-if=\"isModeVisible('Address','links')\" class=\"selectedItems\">" +
+ " <h3>Links for address {$ address.name $}</h3>" +
+ " <div class=\"gridStyle\" ui-grid=\"linksGrid\"></div>" +
+ "</div>"
+ );
+
+ $templateCache.put('dispatch/connection.html',
+ "<ul class=\"nav nav-tabs\">" +
+ " <li ng-repeat=\"mode in gridModes\" ng-click=\"selectMode(mode,'Connection')\" ng-class=\"{active : isModeSelected(mode,'Connection')}\" title=\"{$ mode.title $}\" ng-bind-html-unsafe=\"mode.content\"> </li>" +
+ "</ul>" +
+ "<div ng-if=\"isModeVisible('Connection','attributes')\" class=\"selectedItems\">" +
+ " <h3>Connection {$ connection.name $}</h3>" +
+ " <div class=\"gridStyle noHighlight\" ui-grid=\"connectionGrid\"></div>" +
+ "</div>" +
+ "<div ng-if=\"isModeVisible('Connection','links')\" class=\"selectedItems\">" +
+ " <h3>Links for connection {$ connection.name $}</h3>" +
+ " <div class=\"gridStyle\" ui-grid=\"linksGrid\"></div>" +
+ "</div>"
+ );
+ $templateCache.put('dispatch/connections.html',
+ "<h3>Connections</h3>" +
+ "<div class=\"overview\">" +
+ " <div class=\"grid\" ui-grid=\"allConnectionGrid\" ui-grid-selection></div>" +
+ "</div>"
+ );
+ $templateCache.put('dispatch/log.html',
+ "<h3>{$ log.name $}</h3>" +
+ "<div ng-if=\"logFields.length > 0\">" +
+ " <table class=\"log-entry\" ng-repeat=\"entry in logFields track by $index\">" +
+ " <tr>" +
+ " <td>Router</td><td>{$ entry.nodeId $}</td>" +
+ " </tr>" +
+ " <tr>" +
+ " <td align=\"left\" colspan=\"2\">{$ entry.time $}</td>" +
+ " </tr>" +
+ " <tr>" +
+ " <td>Source</td><td>{$ entry.source $}:{$ entry.line $}</td>" +
+ " </tr>" +
+ " <tr>" +
+ " <td valign=\"middle\">Message</td><td valign=\"middle\"><pre>{$ entry.message $}</pre></td>" +
+ " </tr>" +
+ " </table>" +
+ "</div>" +
+ "<div ng-if=\"logFields.length == 0\">No log entries for {$ log.name $}</div>"
+ );
+ $templateCache.put('dispatch/logs.html',
+ "<h3>Recent log entries</h3>" +
+ "<div class=\"overview\">" +
+ " <div class=\"grid\" ui-grid=\"allLogGrid\" ui-grid-selection></div>" +
+ "</div>"
+ );
+ $templateCache.put('dispatch/router.html',
+ "<h3>Router {$ router.name $}</h3>" +
+ "<div class=\"grid noHighlight\" ui-grid=\"routerGrid\"></div>"
+ );
+ $templateCache.put('dispatch/routers.html',
+ "<h3>Routers</h3>" +
+ "<div class=\"overview\">" +
+ " <div class=\"grid\" ui-grid=\"allRouters\" ui-grid-selection></div>" +
+ "</div>"
+ );
+ }
+})();
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org