Resolved conflict in docker ingore
diff --git a/.dockerignore b/.dockerignore
old mode 100644
new mode 100755
diff --git a/.gitignore b/.gitignore
index 61b07cd..4ec3dcc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@
profile
*.moved-aside
.idea
-xos.iml
xos/configurations/frontend/Dockerfile
xos/core/xoslib/karma-*
xos/core/xoslib/docs
@@ -12,4 +11,5 @@
node_modules
xos/xos_configuration/*
.idea/*
-xos.iml
+*.iml
+npm-debug.log
diff --git a/views/ngXosLib/README.md b/views/ngXosLib/README.md
new file mode 100644
index 0000000..0166efd
--- /dev/null
+++ b/views/ngXosLib/README.md
@@ -0,0 +1,101 @@
+# ngXosLib
+
+This is a collection of helpers to develop views as Angular SPA.
+
+## Tools
+
+This tools are designed to help you developing UI for XOS. As they born for this purpose if often necessary that a XOS instance is running on your sistem and responding at: `localhost:9999`. The `xos/configurations/frontend` is normally enough.
+
+### Apigen
+
+Usage: `npm run apigen`
+
+This tool will automatically generate an angular resource file for each endpoint available in Swagger.
+
+>You can generate api related documentation with: `npm run apidoc`. The output is locate in `api/docs`. You can have a list of available method also trough Swagger at `http://localhost:9999/docs/`
+
+### Vendors
+
+Xos comes with a preset of common libraries, as listed in `bower.json`:
+- angular
+- angular-route
+- angular-resource
+- angular-cookie
+- ng-lodash
+
+This libraries are server through Django, so they will not be included in your minified vendor file. To add a library and generate a new file (that will override the old one), you should:
+- enter `ngXosLib` folder
+- run `bower install [myPackage] --save`
+- rebuild the file with `gulp vendor`
+
+>_NOTE before adding libraries please discuss it to avoid this file to became huge_
+
+### Helpers
+
+XOS comes with an helper library that is automatically loaded in the Django template.
+
+To use it, add `xos.helpers` to your required modules:
+
+```
+angular.module('xos.myView', [
+ 'xos.helpers'
+])
+```
+
+It will automatically ad a `token` to all your request, eventually you can take advantage of some other services:
+
+- **NoHyperlinks Interceptor**: will add a `?no_hyperlinks=1` to your request, to tell Django to return ids instead of links.
+- **XosApi** wrapper for `/xos` endpoints.
+- **XoslibApi** wrapper for `/xoslib` endpoints.
+- **HpcApi** wrapper for `/hpcapi` endpoints.
+
+>_NOTE: for the API related service, check documentation in [Apigen](#apigen) section._
+
+### Yo Xos
+
+We have created a [yeoman](http://yeoman.io/) generator to help you scaffolding views.
+
+>As it is in an early stage of development you should manually link it to your system, to do this enter `xos/core/xoslib/ngXosLib/generator-xos` and run `npm link`.
+
+#### To generate a new view
+
+From `xos/core/xoslib` run `yo xos`. This command will create a new folder with the provided name in: `xos/core/xoslib/ngXosViews` that contain your application.
+
+>If you left empty the view name it should be `xos/core/xoslib/ngXosViews/sampleView`
+
+#### Run a development server
+
+In your `view` folder and run `npm start`.
+
+_This will install required dependencies and start a local server with [BrowserSync](http://www.browsersync.io/)_
+
+#### Publish your view
+
+Once your view is done, from your view root folder, run: `npm run build`.
+
+This will build your application and copy files in the appropriate locations to be used by django.
+
+At this point you can enter: `http://localhost:9999/admin/core/dashboardview/add/` and add your custom view.
+
+>_NOTE url field should be `template:xosSampleView`_
+
+#### Install dependencies in your app
+
+To install a local dependency use bower with `--save`. Common modules are saved in `devDependencies` as they already loaded in the Django template.
+
+The `npm start` command is watching your dependencies and will automatically inject it in your `index.html`.
+
+#### Linting
+
+A styleguide is enforced trough [EsLint](http://eslint.org/) and is checked during the build process. We **highly** suggest to install the linter in your editor to have realtime hint.
+
+#### Test
+
+The generator set up a test environment with a default test.
+To run it execute: `npm test`
+
+## TODO
+
+- Use Angular $resource instead of $http
+- Use ngDoc instead of jsDoc
+- Define styleguide (both visual and js) and if needed define some UI components
\ No newline at end of file
diff --git a/views/ngXosLib/xosHelpers/src/services/csrfToken.interceptor.js b/views/ngXosLib/xosHelpers/src/services/csrfToken.interceptor.js
index 025813c..283e90d 100644
--- a/views/ngXosLib/xosHelpers/src/services/csrfToken.interceptor.js
+++ b/views/ngXosLib/xosHelpers/src/services/csrfToken.interceptor.js
@@ -1,18 +1,18 @@
(function() {
- 'use strict';
+ 'use strict';
- angular
- .module('xos.helpers')
- .factory('SetCSRFToken', setCSRFToken);
+ angular
+ .module('xos.helpers')
+ .factory('SetCSRFToken', setCSRFToken);
- function setCSRFToken($cookies) {
- return {
- request: function(request){
- if(request.method !== 'GET'){
- request.headers['X-CSRFToken'] = $cookies.get('xoscsrftoken');
+ function setCSRFToken($cookies) {
+ return {
+ request: function(request){
+ if(request.method !== 'GET'){
+ request.headers['X-CSRFToken'] = $cookies.get('xoscsrftoken');
+ }
+ return request;
}
- return request;
- }
- };
- }
+ };
+ }
})();
diff --git a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
index 25600aa..d5b53da 100644
--- a/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
+++ b/views/ngXosLib/xosHelpers/src/xosHelpers.module.js
@@ -3,7 +3,12 @@
angular.module('bugSnag', []).factory('$exceptionHandler', function () {
return function (exception, cause) {
- Bugsnag.notifyException(exception, {diagnostics:{cause: cause}});
+ if( window.Bugsnag ){
+ Bugsnag.notifyException(exception, {diagnostics:{cause: cause}});
+ }
+ else{
+ console.error(exception, cause, exception.stack);
+ }
};
});
diff --git a/views/ngXosViews/.gitignore b/views/ngXosViews/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/views/ngXosViews/.gitignore
diff --git a/views/ngXosViews/ceilometerDashboard/src/js/main.js b/views/ngXosViews/ceilometerDashboard/src/js/main.js
index 0bf12db..12f5e72 100644
--- a/views/ngXosViews/ceilometerDashboard/src/js/main.js
+++ b/views/ngXosViews/ceilometerDashboard/src/js/main.js
@@ -110,7 +110,7 @@
// this open the accordion
this.accordion = {
open: {}
- }
+ };
/**
* Open the active panel base on the service stored values
@@ -350,6 +350,7 @@
}
}
})
+ // NOTE reading this on demand for a single
.directive('ceilometerStats', function(){
return {
restrict: 'E',
diff --git a/views/ngXosViews/diagnostic/.bowerrc b/views/ngXosViews/diagnostic/.bowerrc
new file mode 100644
index 0000000..e491038
--- /dev/null
+++ b/views/ngXosViews/diagnostic/.bowerrc
@@ -0,0 +1,3 @@
+{
+ "directory": "src/vendor/"
+}
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/.eslintrc b/views/ngXosViews/diagnostic/.eslintrc
new file mode 100644
index 0000000..ef5b468
--- /dev/null
+++ b/views/ngXosViews/diagnostic/.eslintrc
@@ -0,0 +1,44 @@
+{
+ "ecmaFeatures": {
+ "blockBindings": true,
+ "forOf": true,
+ "destructuring": true,
+ "arrowFunctions": true,
+ "templateStrings": true
+ },
+ "env": {
+ "browser": true,
+ "node": true,
+ "es6": true
+ },
+ "plugins": [
+ //"angular"
+ ],
+ "rules": {
+ "quotes": [2, "single"],
+ "no-undef": 2,
+ "camelcase": [1, {"properties": "always"}],
+ "no-underscore-dangle": 1,
+ "eqeqeq": [2, "smart"],
+ "no-alert": 1,
+ "key-spacing": [1, { "beforeColon": false, "afterColon": true }],
+ "indent": [2, 2],
+ "no-irregular-whitespace": 1,
+ "eol-last": 0,
+ "max-nested-callbacks": [2, 4],
+ "comma-spacing": [1, {"before": false, "after": true}],
+ "no-trailing-spaces": [1, { skipBlankLines: true }],
+ "no-unused-vars": [1, {"vars": "all", "args": "after-used"}],
+ "new-cap": 0,
+
+ //"angular/ng_module_name": [2, '/^xos\.*[a-z]*$/'],
+ //"angular/ng_controller_name": [2, '/^[a-z].*Ctrl$/'],
+ //"angular/ng_service_name": [2, '/^[A-Z].*Service$/'],
+ //"angular/ng_directive_name": [2, '/^[a-z]+[[A-Z].*]*$/'],
+ //"angular/ng_di": [0, "function or array"]
+ },
+ "globals" :{
+ "angular": true,
+ "d3": true
+ }
+}
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/.gitignore b/views/ngXosViews/diagnostic/.gitignore
new file mode 100644
index 0000000..567aee4
--- /dev/null
+++ b/views/ngXosViews/diagnostic/.gitignore
@@ -0,0 +1,6 @@
+dist/
+src/vendor
+.tmp
+node_modules
+npm-debug.log
+dist/
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/bower.json b/views/ngXosViews/diagnostic/bower.json
new file mode 100644
index 0000000..edf419d
--- /dev/null
+++ b/views/ngXosViews/diagnostic/bower.json
@@ -0,0 +1,32 @@
+{
+ "name": "xos-serviceTopology",
+ "version": "0.0.0",
+ "authors": [
+ "Matteo Scandolo <teo@onlab.us>"
+ ],
+ "description": "The serviceTopology view",
+ "license": "MIT",
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "static/js/vendor/",
+ "test",
+ "tests"
+ ],
+ "dependencies": {
+ "d3": "~3.5.13",
+ "lodash": "~4.0.0",
+ "angular-animate": "~1.4.9"
+ },
+ "devDependencies": {
+ "jquery": "~2.1.4",
+ "angular-mocks": "~1.4.7",
+ "angular": "~1.4.7",
+ "angular-ui-router": "~0.2.15",
+ "angular-cookies": "~1.4.7",
+ "angular-resource": "~1.4.7",
+ "ng-lodash": "~0.3.0",
+ "bootstrap-css": "~3.3.6"
+ }
+}
diff --git a/views/ngXosViews/diagnostic/env/default.js b/views/ngXosViews/diagnostic/env/default.js
new file mode 100644
index 0000000..e9ed236
--- /dev/null
+++ b/views/ngXosViews/diagnostic/env/default.js
@@ -0,0 +1,13 @@
+// This is a default configuration for your development environment.
+// You can duplicate this configuration for any of your Backend Environments.
+// Different configurations are loaded setting a NODE_ENV variable that contain the config file name.
+// `NODE_ENV=local npm start`
+//
+// If xoscsrftoken or xossessionid are not specified the browser value are used
+// (works only for local environment as both application are served on the same domain)
+
+module.exports = {
+ host: 'http://clnode067.clemson.cloudlab.us:9999/',
+ xoscsrftoken: 'ZhPiXOVgQH2S0QQtLTkx3DaESR7IOvQc',
+ xossessionid: '49cjbceeg483ife9rocex8h1st360ii3'
+};
diff --git a/views/ngXosViews/diagnostic/env/local.js b/views/ngXosViews/diagnostic/env/local.js
new file mode 100644
index 0000000..bf5af60
--- /dev/null
+++ b/views/ngXosViews/diagnostic/env/local.js
@@ -0,0 +1,13 @@
+// This is a default configuration for your development environment.
+// You can duplicate this configuration for any of your Backend Environments.
+// Different configurations are loaded setting a NODE_ENV variable that contain the config file name.
+// `NODE_ENV=local npm start`
+//
+// If xoscsrftoken or xossessionid are not specified the browser value are used
+// (works only for local environment as both application are served on the same domain)
+
+module.exports = {
+ host: 'http://xos.local:9999/',
+ xoscsrftoken: 'lu7LFTapqkGQqrqjupArNvyfd5LUoOk5',
+ xossessionid: 'mjzzv7u1q68tfsp8s807h0qzv0qdriaj'
+};
diff --git a/views/ngXosViews/diagnostic/env/mock.js b/views/ngXosViews/diagnostic/env/mock.js
new file mode 100644
index 0000000..c65eb2f
--- /dev/null
+++ b/views/ngXosViews/diagnostic/env/mock.js
@@ -0,0 +1,13 @@
+// This is a default configuration for your development environment.
+// You can duplicate this configuration for any of your Backend Environments.
+// Different configurations are loaded setting a NODE_ENV variable that contain the config file name.
+// `NODE_ENV=local npm start`
+//
+// If xoscsrftoken or xossessionid are not specified the browser value are used
+// (works only for local environment as both application are served on the same domain)
+
+module.exports = {
+ host: 'http://localhost:4000/',
+ xoscsrftoken: 'IGSrPSAOmBorK8uxbbscQbn3ODPb9dDW',
+ xossessionid: 'i8hltbdt3po3uxkbvfmzy15o98p9s157'
+};
diff --git a/views/ngXosViews/diagnostic/env/onlab_pod.js b/views/ngXosViews/diagnostic/env/onlab_pod.js
new file mode 100644
index 0000000..413bd04
--- /dev/null
+++ b/views/ngXosViews/diagnostic/env/onlab_pod.js
@@ -0,0 +1,13 @@
+// This is a default configuration for your development environment.
+// You can duplicate this configuration for any of your Backend Environments.
+// Different configurations are loaded setting a NODE_ENV variable that contain the config file name.
+// `NODE_ENV=local npm start`
+//
+// If xoscsrftoken or xossessionid are not specified the browser value are used
+// (works only for local environment as both application are served on the same domain)
+
+module.exports = {
+ host: 'http://10.90.0.132/',
+ xoscsrftoken: 'bDZaZNRDU6BwUanvjfcGfKJHohG3mlqb',
+ xossessionid: '3nww58wgrlscs7boy2xzw11tji8ywal5'
+};
diff --git a/views/ngXosViews/diagnostic/gulp/build.js b/views/ngXosViews/diagnostic/gulp/build.js
new file mode 100644
index 0000000..cf2b40e
--- /dev/null
+++ b/views/ngXosViews/diagnostic/gulp/build.js
@@ -0,0 +1,151 @@
+'use strict';
+
+// BUILD
+//
+// The only purpose of this gulpfile is to build a XOS view and copy the correct files into
+// .html => dashboards
+// .js (minified and concat) => static/js
+//
+// The template are parsed and added to js with angular $templateCache
+
+var gulp = require('gulp');
+var ngAnnotate = require('gulp-ng-annotate');
+var uglify = require('gulp-uglify');
+var templateCache = require('gulp-angular-templatecache');
+var runSequence = require('run-sequence');
+var concat = require('gulp-concat-util');
+var del = require('del');
+var wiredep = require('wiredep');
+var angularFilesort = require('gulp-angular-filesort');
+var _ = require('lodash');
+var eslint = require('gulp-eslint');
+var inject = require('gulp-inject');
+var rename = require('gulp-rename');
+var replace = require('gulp-replace');
+var postcss = require('gulp-postcss');
+var autoprefixer = require('autoprefixer');
+var mqpacker = require('css-mqpacker');
+var csswring = require('csswring');
+
+var TEMPLATE_FOOTER = `
+angular.module('xos.diagnostic').run(function($location){
+ $location.path('/')
+});
+angular.bootstrap(angular.element('#xosDiagnostic'), ['xos.diagnostic']);`;
+
+module.exports = function(options){
+
+ // delete previous builded file
+ gulp.task('clean', function(){
+ return del(
+ [options.dashboards + 'xosDiagnostic.html'],
+ {force: true}
+ );
+ });
+
+ // minify css
+ gulp.task('css', function () {
+ var processors = [
+ autoprefixer({browsers: ['last 1 version']}),
+ mqpacker,
+ csswring
+ ];
+ console.log(options.css);
+ gulp.src([
+ `${options.css}**/*.css`,
+ `!${options.css}dev.css`
+ ])
+ .pipe(postcss(processors))
+ .pipe(gulp.dest(options.tmp + '/css/'));
+ });
+
+ gulp.task('copyCss', ['css'], function(){
+ return gulp.src([`${options.tmp}/css/*.css`])
+ .pipe(concat('xosDiagnostic.css'))
+ .pipe(gulp.dest(options.static + 'css/'))
+ });
+
+ // compile and minify scripts
+ gulp.task('scripts', function() {
+ return gulp.src([
+ options.tmp + '**/*.js',
+ options.tmp + 'templates.js'
+ ])
+ .pipe(ngAnnotate())
+ .pipe(angularFilesort())
+ .pipe(concat('xosDiagnostic.js'))
+ .pipe(concat.header('//Autogenerated, do not edit!!!\n'))
+ .pipe(concat.footer(TEMPLATE_FOOTER))
+ // .pipe(uglify())
+ .pipe(gulp.dest(options.static + 'js/'));
+ });
+
+ // set templates in cache
+ gulp.task('templates', function(){
+ return gulp.src('./src/templates/*.html')
+ .pipe(templateCache({
+ module: 'xos.diagnostic',
+ root: 'templates/',
+ // templateFooter: TEMPLATE_FOOTER
+ }))
+ .pipe(gulp.dest(options.tmp));
+ });
+
+ // copy html index to Django Folder
+ gulp.task('copyHtml', ['clean'], function(){
+ return gulp.src(options.src + 'index.html')
+ // remove dev dependencies from html
+ .pipe(replace(/<!-- bower:css -->(\n.*)*\n<!-- endbower --><!-- endcss -->/, ''))
+ .pipe(replace(/<!-- bower:js -->(\n.*)*\n<!-- endbower --><!-- endjs -->/, ''))
+ .pipe(replace(/ng-app=".*"\s/, ''))
+ // injecting minified files
+ .pipe(
+ inject(
+ gulp.src([
+ options.static + 'js/vendor/xosDiagnosticVendor.js',
+ options.static + 'js/xosDiagnostic.js',
+ options.static + 'css/xosDiagnostic.css'
+ ]),
+ {ignorePath: '/../../../xos/core/xoslib'}
+ )
+ )
+ .pipe(rename('xosDiagnostic.html'))
+ .pipe(gulp.dest(options.dashboards));
+ });
+
+ // minify vendor js files
+ gulp.task('wiredep', function(){
+ var bowerDeps = wiredep().js;
+ if(!bowerDeps){
+ return;
+ }
+
+ // remove angular (it's already loaded)
+ _.remove(bowerDeps, function(dep){
+ return dep.indexOf('angular/angular.js') !== -1;
+ });
+
+ return gulp.src(bowerDeps)
+ .pipe(concat('xosDiagnosticVendor.js'))
+ .pipe(uglify())
+ .pipe(gulp.dest(options.static + 'js/vendor/'));
+ });
+
+ gulp.task('lint', function () {
+ return gulp.src(['src/js/**/*.js'])
+ .pipe(eslint())
+ .pipe(eslint.format())
+ .pipe(eslint.failAfterError());
+ });
+
+ gulp.task('build', function() {
+ runSequence(
+ 'templates',
+ 'babel',
+ 'scripts',
+ 'wiredep',
+ 'copyHtml',
+ 'copyCss'
+ );
+ });
+};
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/gulp/server.js b/views/ngXosViews/diagnostic/gulp/server.js
new file mode 100644
index 0000000..7605294
--- /dev/null
+++ b/views/ngXosViews/diagnostic/gulp/server.js
@@ -0,0 +1,146 @@
+'use strict';
+
+var gulp = require('gulp');
+var browserSync = require('browser-sync').create();
+var inject = require('gulp-inject');
+var runSequence = require('run-sequence');
+var angularFilesort = require('gulp-angular-filesort');
+var babel = require('gulp-babel');
+var wiredep = require('wiredep').stream;
+var httpProxy = require('http-proxy');
+var del = require('del');
+
+const environment = process.env.NODE_ENV;
+
+if (environment){
+ var conf = require(`../env/${environment}.js`);
+}
+else{
+ var conf = require('../env/default.js')
+}
+
+var proxy = httpProxy.createProxyServer({
+ target: conf.host || 'http://0.0.0.0:9999'
+});
+
+
+proxy.on('error', function(error, req, res) {
+ res.writeHead(500, {
+ 'Content-Type': 'text/plain'
+ });
+
+ console.error('[Proxy]', error);
+});
+
+module.exports = function(options){
+
+ // open in browser with sync and proxy to 0.0.0.0
+ gulp.task('browser', function() {
+ browserSync.init({
+ // reloadDelay: 500,
+ // logLevel: 'debug',
+ // logConnections: true,
+ startPath: '#/',
+ snippetOptions: {
+ rule: {
+ match: /<!-- browserSync -->/i
+ }
+ },
+ server: {
+ baseDir: options.src,
+ routes: {
+ '/api': options.api,
+ '/xosHelpers/src': options.helpers
+ },
+ middleware: function(req, res, next){
+ if(
+ req.url.indexOf('/xos/') !== -1 ||
+ req.url.indexOf('/xoslib/') !== -1 ||
+ req.url.indexOf('/hpcapi/') !== -1
+ ){
+ if(conf.xoscsrftoken && conf.xossessionid){
+ req.headers.cookie = `xoscsrftoken=${conf.xoscsrftoken}; xossessionid=${conf.xossessionid}`;
+ req.headers['x-csrftoken'] = conf.xoscsrftoken;
+ }
+ proxy.web(req, res);
+ }
+ else{
+ next();
+ }
+ }
+ }
+ });
+
+ gulp.watch(options.src + 'js/**/*.js', ['js-watch']);
+ gulp.watch(options.src + 'vendor/**/*.js', ['bower'], function(){
+ browserSync.reload();
+ });
+ gulp.watch(options.src + '**/*.html', function(){
+ browserSync.reload();
+ });
+ });
+
+ // transpile js with sourceMaps
+ gulp.task('babel', function(){
+ return gulp.src(options.scripts + '**/*.js')
+ .pipe(babel({sourceMaps: true}))
+ .pipe(gulp.dest(options.tmp));
+ });
+
+ // inject scripts
+ gulp.task('injectScript', ['cleanTmp', 'babel'], function(){
+ return gulp.src(options.src + 'index.html')
+ .pipe(
+ inject(
+ gulp.src([
+ options.tmp + '**/*.js',
+ options.api + '*.js',
+ options.helpers + '**/*.js'
+ ])
+ .pipe(angularFilesort()),
+ {
+ ignorePath: [options.src, '/../../ngXosLib']
+ }
+ )
+ )
+ .pipe(gulp.dest(options.src));
+ });
+
+ // inject CSS
+ gulp.task('injectCss', function(){
+ return gulp.src(options.src + 'index.html')
+ .pipe(
+ inject(
+ gulp.src(options.src + 'css/*.css'),
+ {
+ ignorePath: [options.src]
+ }
+ )
+ )
+ .pipe(gulp.dest(options.src));
+ });
+
+ // inject bower dependencies with wiredep
+ gulp.task('bower', function () {
+ return gulp.src(options.src + 'index.html')
+ .pipe(wiredep({devDependencies: true}))
+ .pipe(gulp.dest(options.src));
+ });
+
+ gulp.task('js-watch', ['injectScript'], function(){
+ browserSync.reload();
+ });
+
+ gulp.task('cleanTmp', function(){
+ return del([options.tmp + '**/*']);
+ });
+
+ gulp.task('serve', function() {
+ runSequence(
+ 'bower',
+ 'injectScript',
+ 'injectCss',
+ ['browser']
+ );
+ });
+};
diff --git a/views/ngXosViews/diagnostic/gulpfile.js b/views/ngXosViews/diagnostic/gulpfile.js
new file mode 100644
index 0000000..7bdc6e0
--- /dev/null
+++ b/views/ngXosViews/diagnostic/gulpfile.js
@@ -0,0 +1,25 @@
+'use strict';
+
+var gulp = require('gulp');
+var wrench = require('wrench');
+
+var options = {
+ src: 'src/',
+ css: 'src/css/',
+ scripts: 'src/js/',
+ tmp: 'src/.tmp',
+ dist: 'dist/',
+ api: '../../ngXosLib/api/',
+ helpers: '../../ngXosLib/xosHelpers/src/',
+ static: '../../../xos/core/xoslib/static/', // this is the django static folder from dev environment
+ dashboards: '../../../xos/core/xoslib/dashboards/' // this is the django html folder
+};
+
+wrench.readdirSyncRecursive('./gulp')
+.map(function(file) {
+ require('./gulp/' + file)(options);
+});
+
+gulp.task('default', function () {
+ gulp.start('build');
+});
diff --git a/views/ngXosViews/diagnostic/karma.conf.js b/views/ngXosViews/diagnostic/karma.conf.js
new file mode 100644
index 0000000..5e312fa
--- /dev/null
+++ b/views/ngXosViews/diagnostic/karma.conf.js
@@ -0,0 +1,90 @@
+// Karma configuration
+// Generated on Tue Oct 06 2015 09:27:10 GMT+0000 (UTC)
+
+/* eslint indent: [2,2], quotes: [2, "single"]*/
+
+/*eslint-disable*/
+var wiredep = require('wiredep');
+var path = require('path');
+
+var bowerComponents = wiredep( {devDependencies: true} )[ 'js' ].map(function( file ){
+ return path.relative(process.cwd(), file);
+});
+
+module.exports = function(config) {
+/*eslint-enable*/
+ config.set({
+
+ // base path that will be used to resolve all patterns (eg. files, exclude)
+ basePath: '',
+
+
+ // frameworks to use
+ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
+ frameworks: ['jasmine'],
+
+
+ // list of files / patterns to load in the browser
+ files: bowerComponents.concat([
+ '../../../xos/core/xoslib/static/js/vendor/ngXosVendor.js',
+ '../../../xos/core/xoslib/static/js/vendor/ngXosHelpers.js',
+ '../../../xos/core/xoslib/static/js/xosApi.js',
+ 'src/js/main.js',
+ 'src/js/**/*.js',
+ 'spec/**/*.mock.js',
+ 'spec/**/*.test.js',
+ 'src/**/*.html'
+ ]),
+
+
+ // list of files to exclude
+ exclude: [
+ ],
+
+
+ // preprocess matching files before serving them to the browser
+ // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
+ preprocessors: {
+ 'src/js/**/*.js': ['babel'],
+ 'spec/**/*.test.js': ['babel'],
+ 'src/**/*.html': ['ng-html2js']
+ },
+
+ ngHtml2JsPreprocessor: {
+ stripPrefix: 'src/', //strip the src path from template url (http://stackoverflow.com/questions/22869668/karma-unexpected-request-when-testing-angular-directive-even-with-ng-html2js)
+ moduleName: 'templates' // define the template module name
+ },
+
+ // test results reporter to use
+ // possible values: 'dots', 'progress'
+ // available reporters: https://npmjs.org/browse/keyword/karma-reporter
+ reporters: ['mocha'],
+
+
+ // web server port
+ port: 9876,
+
+
+ // enable / disable colors in the output (reporters and logs)
+ colors: true,
+
+
+ // level of logging
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+
+ // start these browsers
+ // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
+ browsers: ['PhantomJS'],
+
+
+ // Continuous Integration mode
+ // if true, Karma captures browsers, runs the tests and exits
+ singleRun: false
+ });
+};
diff --git a/views/ngXosViews/diagnostic/mocks/data/cordsubscriber.json b/views/ngXosViews/diagnostic/mocks/data/cordsubscriber.json
new file mode 100644
index 0000000..b0c69bc
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/cordsubscriber.json
@@ -0,0 +1,36 @@
+[
+ {
+ "humanReadableName": "cordSubscriber-1",
+ "id": 1,
+ "service_specific_id": "123",
+ "vlan_id": "432",
+ "s_tag": "222",
+ "c_tag": "432",
+ "vcpe_id": 4,
+ "instance": 1,
+ "instance_name": "mysite_vcpe",
+ "image": 1,
+ "image_name": "trusty-server-multi-nic",
+ "firewall_enable": false,
+ "firewall_rules": "accept all anywhere anywhere",
+ "url_filter_enable": false,
+ "url_filter_rules": "allow all",
+ "url_filter_level": "R",
+ "bbs_account": null,
+ "ssh_command": null,
+ "vcpe_synced": false,
+ "cdn_enable": false,
+ "vbng_id": 5,
+ "routeable_subnet": "",
+ "nat_ip": null,
+ "lan_ip": null,
+ "wan_ip": null,
+ "private_ip": null,
+ "wan_mac": null,
+ "wan_container_ip": "10.0.1.24",
+ "uplink_speed": 1000000000,
+ "downlink_speed": 1000000000,
+ "status": "copyrightviolation",
+ "enable_uverse": true
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/data/instances.json b/views/ngXosViews/diagnostic/mocks/data/instances.json
new file mode 100644
index 0000000..d9814f3
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/instances.json
@@ -0,0 +1,322 @@
+[
+ {
+ "humanReadableName": "sflow_service_instance",
+ "id": 8,
+ "created": "2016-02-17T22:01:02.663Z",
+ "updated": "2016-02-17T22:05:58.270Z",
+ "enacted": "2016-02-17T22:05:58.281Z",
+ "policed": "2016-02-17T22:05:59.137Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746758.281283, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000005",
+ "instance_uuid": "42b75cb7-7205-4a68-9100-b2c1e3ea69b5",
+ "name": "sflow_service_instance",
+ "instance_name": "mysite_sflow-8",
+ "ip": "130.127.133.93",
+ "image": 1,
+ "creator": 1,
+ "slice": 8,
+ "deployment": 1,
+ "node": 2,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 21,
+ 20
+ ]
+ },
+ {
+ "humanReadableName": "client1",
+ "id": 4,
+ "created": "2016-02-17T22:00:58.284Z",
+ "updated": "2016-02-17T22:03:56.863Z",
+ "enacted": "2016-02-17T22:03:56.872Z",
+ "policed": "2016-02-17T22:03:56.953Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746636.872524, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000001",
+ "instance_uuid": "cca264bb-8136-45dd-a5a3-41902a4bcf5b",
+ "name": "client1",
+ "instance_name": "mysite_clients-4",
+ "ip": "130.127.133.90",
+ "image": 1,
+ "creator": 1,
+ "slice": 3,
+ "deployment": 1,
+ "node": 1,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 10,
+ 4,
+ 11
+ ]
+ },
+ {
+ "humanReadableName": "mysite_ceilometer-7",
+ "id": 7,
+ "created": "2016-02-17T22:01:02.550Z",
+ "updated": "2016-02-17T22:06:15.667Z",
+ "enacted": "2016-02-17T22:06:15.672Z",
+ "policed": "2016-02-17T22:06:15.818Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746775.672678, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000006",
+ "instance_uuid": "23d04870-5e0d-4b03-b2e2-032cbfec56fa",
+ "name": "mysite_ceilometer",
+ "instance_name": "mysite_ceilometer-7",
+ "ip": "130.127.133.90",
+ "image": 1,
+ "creator": 1,
+ "slice": 7,
+ "deployment": 1,
+ "node": 1,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 18,
+ 19,
+ 5
+ ]
+ },
+ {
+ "humanReadableName": "onos_app_1",
+ "id": 3,
+ "created": "2016-02-17T22:00:58.258Z",
+ "updated": "2016-02-17T22:04:43.301Z",
+ "enacted": "2016-02-17T22:04:43.309Z",
+ "policed": "2016-02-17T22:04:43.943Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746683.309321, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000002",
+ "instance_uuid": "c21dc52f-363e-47f6-9a2a-489c0af392fb",
+ "name": "onos_app_1",
+ "instance_name": "mysite_onos_vbng-3",
+ "ip": "130.127.133.59",
+ "image": 1,
+ "creator": 1,
+ "slice": 5,
+ "deployment": 1,
+ "node": 3,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 15,
+ 14
+ ]
+ },
+ {
+ "humanReadableName": "ovs_vbng",
+ "id": 6,
+ "created": "2016-02-17T22:00:58.567Z",
+ "updated": "2016-02-17T22:06:33.495Z",
+ "enacted": "2016-02-17T22:06:33.562Z",
+ "policed": "2016-02-17T22:06:34.619Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746793.562678, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000007",
+ "instance_uuid": "81dc0638-c9bc-4396-be17-ea805050e388",
+ "name": "ovs_vbng",
+ "instance_name": "mysite_vbng-6",
+ "ip": "130.127.133.59",
+ "image": 1,
+ "creator": 1,
+ "slice": 2,
+ "deployment": 1,
+ "node": 3,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 8,
+ 3,
+ 9
+ ]
+ },
+ {
+ "humanReadableName": "onos_app_2",
+ "id": 2,
+ "created": "2016-02-17T22:00:58.199Z",
+ "updated": "2016-02-17T22:05:26.512Z",
+ "enacted": "2016-02-17T22:05:26.521Z",
+ "policed": "2016-02-17T22:05:26.730Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746726.521625, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000003",
+ "instance_uuid": "8e3f00c5-0957-4505-ae69-e03e41306435",
+ "name": "onos_app_2",
+ "instance_name": "mysite_onos_volt-2",
+ "ip": "130.127.133.93",
+ "image": 1,
+ "creator": 1,
+ "slice": 4,
+ "deployment": 1,
+ "node": 2,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 12,
+ 13
+ ]
+ },
+ {
+ "humanReadableName": "ovs_volt",
+ "id": 5,
+ "created": "2016-02-17T22:00:58.540Z",
+ "updated": "2016-02-17T22:06:50.666Z",
+ "enacted": "2016-02-17T22:06:50.680Z",
+ "policed": "2016-02-17T22:06:51.342Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746810.680553, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000008",
+ "instance_uuid": "49c08f77-f05a-4c9b-859c-20c2982b19c6",
+ "name": "ovs_volt",
+ "instance_name": "mysite_volt-5",
+ "ip": "130.127.133.93",
+ "image": 1,
+ "creator": 1,
+ "slice": 6,
+ "deployment": 1,
+ "node": 2,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 17,
+ 16,
+ 4
+ ]
+ },
+ {
+ "humanReadableName": "mysite_vsg-1",
+ "id": 1,
+ "created": "2016-02-17T22:00:58.015Z",
+ "updated": "2016-02-17T22:05:44.877Z",
+ "enacted": "2016-02-17T22:05:44.887Z",
+ "policed": "2016-02-17T22:05:45.538Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746744.887768, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000004",
+ "instance_uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "name": "mysite_vsg",
+ "instance_name": "mysite_vsg-1",
+ "ip": "130.127.133.90",
+ "image": 1,
+ "creator": 1,
+ "slice": 1,
+ "deployment": 1,
+ "node": 1,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 1,
+ 6,
+ 7,
+ 2
+ ]
+ },
+ {
+ "humanReadableName": "mysite_vsg-2",
+ "id": 11,
+ "created": "2016-02-17T22:00:58.015Z",
+ "updated": "2016-02-17T22:05:44.877Z",
+ "enacted": "2016-02-17T22:05:44.887Z",
+ "policed": "2016-02-17T22:05:45.538Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746744.887768, \"exponent\": 0}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000004",
+ "instance_uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "name": "mysite_vsg",
+ "instance_name": "mysite_vsg-2",
+ "ip": "130.127.133.90",
+ "image": 1,
+ "creator": 1,
+ "slice": 1,
+ "deployment": 1,
+ "node": 2,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 1,
+ 6,
+ 7,
+ 2
+ ]
+ },
+ {
+ "humanReadableName": "mysite_vsg-3",
+ "id": 12,
+ "created": "2016-02-17T22:00:58.015Z",
+ "updated": "2016-02-17T22:05:44.877Z",
+ "enacted": "2016-02-17T22:05:44.887Z",
+ "policed": "2016-02-17T22:05:45.538Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746744.887768, \"exponent\": 0}",
+ "backend_status": "2 - Error",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000004",
+ "instance_uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "name": "mysite_vsg",
+ "instance_name": "mysite_vsg-3",
+ "ip": "130.127.133.90",
+ "image": 1,
+ "creator": 1,
+ "slice": 1,
+ "deployment": 1,
+ "node": 3,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 1,
+ 6,
+ 7,
+ 2
+ ]
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/data/meterstatistics.json b/views/ngXosViews/diagnostic/mocks/data/meterstatistics.json
new file mode 100644
index 0000000..8678222
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/meterstatistics.json
@@ -0,0 +1,242 @@
+[
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Existence of vcpe instance",
+ "resource_id": "vcpe-222-432",
+ "meter": "vcpe",
+ "unit": "vcpe",
+ "category": "VCPE",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 1.0,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Number of entries in DNS cache",
+ "resource_id": "vcpe-222-432",
+ "meter": "vcpe.dns.cache.size",
+ "unit": "entries",
+ "category": "VCPE",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 150.0,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Unexpired entries that were thrown out of cache",
+ "resource_id": "vcpe-222-432",
+ "meter": "vcpe.dns.replaced_unexpired_entries",
+ "unit": "entries",
+ "category": "VCPE",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Number of cache hits",
+ "resource_id": "vcpe-222-432",
+ "meter": "vcpe.dns.queries_answered_locally",
+ "unit": "queries",
+ "category": "VCPE",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Number of cache misses",
+ "resource_id": "vcpe-222-432",
+ "meter": "vcpe.dns.queries_forwarded",
+ "unit": "queries",
+ "category": "VCPE",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Volume of RAM",
+ "resource_id": "vcpe-222-432",
+ "meter": "memory",
+ "unit": "MB",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 2099.0,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Volume of RAM used",
+ "resource_id": "vcpe-222-432",
+ "meter": "memory.usage",
+ "unit": "MB",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 31.1625,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432",
+ "resource": "vcpe-222-432",
+ "description": "Average CPU utilization",
+ "resource_id": "vcpe-222-432",
+ "meter": "cpu_util",
+ "unit": "%",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 4.845000000000001,
+ "time": "2016-02-24T00:21:28",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth0",
+ "resource": "vcpe-222-432-eth0",
+ "description": "Average rate per sec of incoming bytes on a VM network interface",
+ "resource_id": "vcpe-222-432-eth0",
+ "meter": "network.incoming.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth0",
+ "resource": "vcpe-222-432-eth0",
+ "description": "Average rate per sec of outgoing bytes on a VM network interface",
+ "resource_id": "vcpe-222-432-eth0",
+ "meter": "network.outgoing.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.00030304561114446413,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth0",
+ "resource": "vcpe-222-432-eth0",
+ "description": "Average rate per sec of incoming packets on a VM network interface",
+ "resource_id": "vcpe-222-432-eth0",
+ "meter": "network.incoming.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth0",
+ "resource": "vcpe-222-432-eth0",
+ "description": "Average rate per sec of outgoing packets on a VM network interface",
+ "resource_id": "vcpe-222-432-eth0",
+ "meter": "network.outgoing.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 4.714042840024997e-06,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth1",
+ "resource": "vcpe-222-432-eth1",
+ "description": "Average rate per sec of incoming bytes on a VM network interface",
+ "resource_id": "vcpe-222-432-eth1",
+ "meter": "network.incoming.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth1",
+ "resource": "vcpe-222-432-eth1",
+ "description": "Average rate per sec of outgoing bytes on a VM network interface",
+ "resource_id": "vcpe-222-432-eth1",
+ "meter": "network.outgoing.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.00030304561114446413,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth1",
+ "resource": "vcpe-222-432-eth1",
+ "description": "Average rate per sec of incoming packets on a VM network interface",
+ "resource_id": "vcpe-222-432-eth1",
+ "meter": "network.incoming.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "vcpe-222-432-eth1",
+ "resource": "vcpe-222-432-eth1",
+ "description": "Average rate per sec of outgoing packets on a VM network interface",
+ "resource_id": "vcpe-222-432-eth1",
+ "meter": "network.outgoing.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 4.714042840024997e-06,
+ "time": "2016-02-24T01:19:25",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ }
+]
diff --git a/views/ngXosViews/diagnostic/mocks/data/nodes.json b/views/ngXosViews/diagnostic/mocks/data/nodes.json
new file mode 100644
index 0000000..03ca1da
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/nodes.json
@@ -0,0 +1,53 @@
+[
+ {
+ "humanReadableName": "cp-3.teone-cord.xos-pg0.clemson.cloudlab.us",
+ "id": 3,
+ "created": "2016-02-17T22:00:53.717Z",
+ "updated": "2016-02-17T22:01:24.890Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "cp-3.teone-cord.xos-pg0.clemson.cloudlab.us",
+ "site_deployment": "http://clnode067.clemson.cloudlab.us:9999/xos/sitedeployments/1/",
+ "site": "http://clnode067.clemson.cloudlab.us:9999/xos/sites/1/"
+ },
+ {
+ "humanReadableName": "cp-2.teone-cord.xos-pg0.clemson.cloudlab.us",
+ "id": 2,
+ "created": "2016-02-17T22:00:53.701Z",
+ "updated": "2016-02-17T22:01:24.891Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "cp-2.teone-cord.xos-pg0.clemson.cloudlab.us",
+ "site_deployment": "http://clnode067.clemson.cloudlab.us:9999/xos/sitedeployments/1/",
+ "site": "http://clnode067.clemson.cloudlab.us:9999/xos/sites/1/"
+ },
+ {
+ "humanReadableName": "cp-1.teone-cord.xos-pg0.clemson.cloudlab.us",
+ "id": 1,
+ "created": "2016-02-17T22:00:53.680Z",
+ "updated": "2016-02-17T22:01:24.892Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": true,
+ "name": "cp-1.teone-cord.xos-pg0.clemson.cloudlab.us",
+ "site_deployment": "http://clnode067.clemson.cloudlab.us:9999/xos/sitedeployments/1/",
+ "site": "http://clnode067.clemson.cloudlab.us:9999/xos/sites/1/"
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/data/services.json b/views/ngXosViews/diagnostic/mocks/data/services.json
new file mode 100644
index 0000000..b3ae7b7
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/services.json
@@ -0,0 +1,177 @@
+[
+ {
+ "humanReadableName":"service_vbng",
+ "id":1,
+ "created":"2016-02-17T19:36:04.242Z",
+ "updated":"2016-02-17T19:36:04.242Z",
+ "enacted":null,
+ "policed":null,
+ "backend_register":"{}",
+ "backend_status":"0 - Provisioning in progress",
+ "deleted":false,
+ "write_protect":false,
+ "lazy_blocked":false,
+ "no_sync":false,
+ "description":null,
+ "enabled":true,
+ "kind":"vBNG",
+ "name":"service_vbng",
+ "versionNumber":"",
+ "published":true,
+ "view_url":"/admin/cord/vbngservice/$id$/",
+ "icon_url":null,
+ "public_key":null,
+ "service_specific_id":null,
+ "service_specific_attribute":null
+ },
+ {
+ "humanReadableName":"service_vsg",
+ "id":2,
+ "created":"2016-02-17T19:36:04.249Z",
+ "updated":"2016-02-17T19:36:04.249Z",
+ "enacted":null,
+ "policed":null,
+ "backend_register":"{}",
+ "backend_status":"0 - Provisioning in progress",
+ "deleted":false,
+ "write_protect":false,
+ "lazy_blocked":false,
+ "no_sync":false,
+ "description":null,
+ "enabled":true,
+ "kind":"vCPE",
+ "name":"service_vsg",
+ "versionNumber":"",
+ "published":true,
+ "view_url":"/admin/cord/vsgservice/$id$/",
+ "icon_url":null,
+ "public_key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0bhkFHg3DxtQY1S0bN4nV2USTO2scHIrTO/WhZYFB9cqxKJPPlayMzi7sJxZFjsEPG9+gUJn7942eObs0mWkn7eIbph1rgKDhh2ZT6GFdJojgaRr0E3HhjmdHXF3IkCEz0DZ1aiBRX0dAEcp+B7eHvcg9QmBUN9TWhMlN82EKwMtWlrMwAqycNEcPiKiwMC3SPNPrq5qTrxIlzMG8Q51Z1b0FYx2oP0E44zecquFF8qfwoVbA48qT0fdPIdcQ0otEdsGaOxxurOPA86q1zYg60w+Hsygl8L2UpyUqEuXKm0OuhckCmCkcPlEysMAty0bx/9M3DwSkMJdOj81Jl+Bd teone@ctl.teone.xos-pg0.clemson.cloudlab.us\n",
+ "service_specific_id":null,
+ "service_specific_attribute":"{\"backend_network_label\": \"hpc_client\"}"
+ },
+ {
+ "humanReadableName":"service_volt",
+ "id":3,
+ "created":"2016-02-17T19:36:04.585Z",
+ "updated":"2016-02-17T19:36:04.585Z",
+ "enacted":null,
+ "policed":null,
+ "backend_register":"{}",
+ "backend_status":"0 - Provisioning in progress",
+ "deleted":false,
+ "write_protect":false,
+ "lazy_blocked":false,
+ "no_sync":false,
+ "description":null,
+ "enabled":true,
+ "kind":"vOLT",
+ "name":"service_volt",
+ "versionNumber":"",
+ "published":true,
+ "view_url":"/admin/cord/voltservice/$id$/",
+ "icon_url":null,
+ "public_key":null,
+ "service_specific_id":null,
+ "service_specific_attribute":null
+ },
+ {
+ "humanReadableName":"service_ceilometer",
+ "id":6,
+ "created":"2016-02-17T19:36:09.291Z",
+ "updated":"2016-02-17T19:36:09.291Z",
+ "enacted":null,
+ "policed":null,
+ "backend_register":"{}",
+ "backend_status":"0 - Provisioning in progress",
+ "deleted":false,
+ "write_protect":false,
+ "lazy_blocked":false,
+ "no_sync":false,
+ "description":null,
+ "enabled":true,
+ "kind":"ceilometer",
+ "name":"service_ceilometer",
+ "versionNumber":"",
+ "published":true,
+ "view_url":"/admin/ceilometer/ceilometerservice/$id$/",
+ "icon_url":null,
+ "public_key":null,
+ "service_specific_id":null,
+ "service_specific_attribute":"{\"ceilometer_pub_sub_url\": \"http://10.11.10.1:4455/\"}"
+ },
+ {
+ "humanReadableName":"service_sflow",
+ "id":7,
+ "created":"2016-02-17T19:36:09.339Z",
+ "updated":"2016-02-17T20:01:35.518Z",
+ "enacted":null,
+ "policed":null,
+ "backend_register":"{\"next_run\": 1455768095.518195, \"last_failure\": 1455739295.518198, \"last_success\": 1455737795.306011, \"exponent\": 293, \"failures\": 293}",
+ "backend_status":"2 - Exception('defer object service_sflow due to waiting on instance.instance_name',)",
+ "deleted":false,
+ "write_protect":false,
+ "lazy_blocked":false,
+ "no_sync":false,
+ "description":null,
+ "enabled":true,
+ "kind":"sflow",
+ "name":"service_sflow",
+ "versionNumber":"",
+ "published":true,
+ "view_url":"/admin/ceilometer/sflowservice/$id$/",
+ "icon_url":null,
+ "public_key":null,
+ "service_specific_id":null,
+ "service_specific_attribute":"{\"sflow_api_port\": 33333, \"sflow_port\": 6343}"
+ },
+ {
+ "humanReadableName":"service_ONOS_vOLT",
+ "id":4,
+ "created":"2016-02-17T19:36:04.840Z",
+ "updated":"2016-02-17T20:01:35.987Z",
+ "enacted":null,
+ "policed":null,
+ "backend_register":"{\"next_run\": 1455768095.987071, \"failures\": 282, \"last_success\": 1455737792.664808, \"exponent\": 282, \"last_failure\": 1455739295.987074}",
+ "backend_status":"2 - Exception('defer object service_ONOS_vOLT due to waiting on instance.instance_name',)",
+ "deleted":false,
+ "write_protect":false,
+ "lazy_blocked":false,
+ "no_sync":false,
+ "description":null,
+ "enabled":true,
+ "kind":"onos",
+ "name":"service_ONOS_vOLT",
+ "versionNumber":"",
+ "published":true,
+ "view_url":"/admin/onos/onosservice/$id$/",
+ "icon_url":null,
+ "public_key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0bhkFHg3DxtQY1S0bN4nV2USTO2scHIrTO/WhZYFB9cqxKJPPlayMzi7sJxZFjsEPG9+gUJn7942eObs0mWkn7eIbph1rgKDhh2ZT6GFdJojgaRr0E3HhjmdHXF3IkCEz0DZ1aiBRX0dAEcp+B7eHvcg9QmBUN9TWhMlN82EKwMtWlrMwAqycNEcPiKiwMC3SPNPrq5qTrxIlzMG8Q51Z1b0FYx2oP0E44zecquFF8qfwoVbA48qT0fdPIdcQ0otEdsGaOxxurOPA86q1zYg60w+Hsygl8L2UpyUqEuXKm0OuhckCmCkcPlEysMAty0bx/9M3DwSkMJdOj81Jl+Bd teone@ctl.teone.xos-pg0.clemson.cloudlab.us\n",
+ "service_specific_id":null,
+ "service_specific_attribute":"{\"no_container\": false}"
+ },
+ {
+ "humanReadableName":"service_ONOS_vBNG",
+ "id":5,
+ "created":"2016-02-17T19:36:04.917Z",
+ "updated":"2016-02-17T20:01:36.011Z",
+ "enacted":null,
+ "policed":null,
+ "backend_register":"{\"next_run\": 1455768096.011113, \"failures\": 282, \"last_success\": 1455737792.697535, \"exponent\": 282, \"last_failure\": 1455739296.011116}",
+ "backend_status":"2 - Exception('defer object service_ONOS_vBNG due to waiting on instance.instance_name',)",
+ "deleted":false,
+ "write_protect":false,
+ "lazy_blocked":false,
+ "no_sync":false,
+ "description":null,
+ "enabled":true,
+ "kind":"onos",
+ "name":"service_ONOS_vBNG",
+ "versionNumber":"",
+ "published":true,
+ "view_url":"/admin/onos/onosservice/$id$/",
+ "icon_url":null,
+ "public_key":"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0bhkFHg3DxtQY1S0bN4nV2USTO2scHIrTO/WhZYFB9cqxKJPPlayMzi7sJxZFjsEPG9+gUJn7942eObs0mWkn7eIbph1rgKDhh2ZT6GFdJojgaRr0E3HhjmdHXF3IkCEz0DZ1aiBRX0dAEcp+B7eHvcg9QmBUN9TWhMlN82EKwMtWlrMwAqycNEcPiKiwMC3SPNPrq5qTrxIlzMG8Q51Z1b0FYx2oP0E44zecquFF8qfwoVbA48qT0fdPIdcQ0otEdsGaOxxurOPA86q1zYg60w+Hsygl8L2UpyUqEuXKm0OuhckCmCkcPlEysMAty0bx/9M3DwSkMJdOj81Jl+Bd teone@ctl.teone.xos-pg0.clemson.cloudlab.us\n",
+ "service_specific_id":null,
+ "service_specific_attribute":"{\"no_container\": false}"
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/data/tenants.json b/views/ngXosViews/diagnostic/mocks/data/tenants.json
new file mode 100644
index 0000000..4e7557b
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/tenants.json
@@ -0,0 +1,232 @@
+[
+ {
+ "humanReadableName": "coarse-tenant-1",
+ "id": 1,
+ "created": "2016-02-17T19:36:04.259Z",
+ "updated": "2016-02-17T19:36:04.259Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "coarse",
+ "provider_service": 1,
+ "subscriber_service": 2,
+ "subscriber_tenant": null,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null,
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "coarse-tenant-2",
+ "id": 2,
+ "created": "2016-02-17T19:36:04.600Z",
+ "updated": "2016-02-17T19:36:04.600Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "coarse",
+ "provider_service": 2,
+ "subscriber_service": 3,
+ "subscriber_tenant": null,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null,
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "vCPE-tenant-4",
+ "id": 4,
+ "created": "2016-02-17T19:36:04.650Z",
+ "updated": "2016-02-17T20:55:18.115Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{\"next_run\": 1455771318.072057, \"last_failure\": 1455742518.072061, \"last_success\": 1455737797.006782, \"exponent\": 871, \"failures\": 871}",
+ "backend_status": "2 - Exception('defer object vCPE-tenant-4 due to waiting on instance.instance_name',)",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "vCPE",
+ "provider_service": 2,
+ "subscriber_service": null,
+ "subscriber_tenant": 3,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": "{\"instance_id\": 1, \"creator_id\": 1, \"wan_container_ip\": \"10.0.1.24\"}",
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "ceilometer-tenant-8",
+ "id": 8,
+ "created": "2016-02-17T19:36:09.370Z",
+ "updated": "2016-02-17T20:55:19.823Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{\"next_run\": 1455771319.823556, \"failures\": 874, \"last_success\": 1455737795.314296, \"exponent\": 874, \"last_failure\": 1455742519.823559}",
+ "backend_status": "2 - Exception('defer object ceilometer-tenant-8 due to waiting on instance.instance_name',)",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "ceilometer",
+ "provider_service": 6,
+ "subscriber_service": null,
+ "subscriber_tenant": null,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": "{\"instance_id\": 7, \"creator_id\": 1, \"use_same_instance_for_multiple_tenants\": true}",
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "vBNG-tenant-5",
+ "id": 5,
+ "created": "2016-02-17T19:36:04.769Z",
+ "updated": "2016-02-17T20:55:21.385Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{\"next_run\": 1455771321.384039, \"last_failure\": 1455742521.384043, \"last_success\": 1455737796.18277, \"exponent\": 881, \"failures\": 881}",
+ "backend_status": "2 - Exception('defer object vBNG-tenant-5 due to does not have a WAN IP yet',)",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "vBNG",
+ "provider_service": 1,
+ "subscriber_service": null,
+ "subscriber_tenant": 4,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": null,
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "onos-tenant-6",
+ "id": 6,
+ "created": "2016-02-17T19:36:05.048Z",
+ "updated": "2016-02-17T20:41:16.675Z",
+ "enacted": "2016-02-17T20:41:16.729Z",
+ "policed": null,
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455741676.729897, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "onos",
+ "provider_service": 5,
+ "subscriber_service": 1,
+ "subscriber_tenant": null,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": "{\"creator_id\": 1, \"dependencies\": \"org.onosproject.proxyarp, org.onosproject.virtualbng, org.onosproject.openflow, org.onosproject.fwd\", \"name\": \"vBNG_ONOS_app\"}",
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "vOLT-tenant-3",
+ "id": 3,
+ "created": "2016-02-17T19:36:04.631Z",
+ "updated": "2016-02-17T20:28:30.428Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{}",
+ "backend_status": "0 - Provisioning in progress",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "vOLT",
+ "provider_service": 3,
+ "subscriber_service": null,
+ "subscriber_tenant": null,
+ "subscriber_user": null,
+ "subscriber_root": 1,
+ "service_specific_id": "123",
+ "service_specific_attribute": "{\"creator_id\": 1, \"c_tag\": \"432\", \"s_tag\": \"222\"}",
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "onos-tenant-7",
+ "id": 7,
+ "created": "2016-02-17T19:36:05.089Z",
+ "updated": "2016-02-17T20:40:54.451Z",
+ "enacted": "2016-02-17T20:40:54.468Z",
+ "policed": null,
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455741654.468755, \"exponent\": 0}",
+ "backend_status": "1 - OK",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "onos",
+ "provider_service": 4,
+ "subscriber_service": 3,
+ "subscriber_tenant": null,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": "{\"creator_id\": 1, \"dependencies\": \"org.onosproject.openflow-base, org.onosproject.olt, org.ciena.onos.ext_notifier, org.ciena.onos.volt_event_publisher\", \"name\": \"vOLT_ONOS_app\", \"install_dependencies\": \"onos-ext-notifier-1.0-SNAPSHOT.oar, onos-ext-volt-event-publisher-1.0-SNAPSHOT.oar\"}",
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "vCPE-tenant-4",
+ "id": 14,
+ "created": "2016-02-17T19:36:04.650Z",
+ "updated": "2016-02-17T20:55:18.115Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{\"next_run\": 1455771318.072057, \"last_failure\": 1455742518.072061, \"last_success\": 1455737797.006782, \"exponent\": 871, \"failures\": 871}",
+ "backend_status": "2 - Exception('defer object vCPE-tenant-4 due to waiting on instance.instance_name',)",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "vCPE",
+ "provider_service": 2,
+ "subscriber_service": null,
+ "subscriber_tenant": 3,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": "{\"instance_id\": 11, \"creator_id\": 1}",
+ "connect_method": "na"
+ },
+ {
+ "humanReadableName": "vCPE-tenant-5",
+ "id": 14,
+ "created": "2016-02-17T19:36:04.650Z",
+ "updated": "2016-02-17T20:55:18.115Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{\"next_run\": 1455771318.072057, \"last_failure\": 1455742518.072061, \"last_success\": 1455737797.006782, \"exponent\": 871, \"failures\": 871}",
+ "backend_status": "2 - Exception('defer object vCPE-tenant-4 due to waiting on instance.instance_name',)",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "vCPE",
+ "provider_service": 2,
+ "subscriber_service": null,
+ "subscriber_tenant": 3,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": "{\"instance_id\": 12, \"creator_id\": 1}",
+ "connect_method": "na"
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/data/users.json b/views/ngXosViews/diagnostic/mocks/data/users.json
new file mode 100644
index 0000000..4cf36f7
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/users.json
@@ -0,0 +1,25 @@
+[
+ {
+ "mac": "01:02:03:04:05:06",
+ "level": "PG_13",
+ "id": 0,
+ "name": "Mom's PC"
+ },
+ {
+ "mac": "34:36:3B:C9:B6:A6",
+ "id": 1,
+ "level": "PG_13"
+ },
+ {
+ "mac": "68:5B:35:9D:91:D5",
+ "level": "PG_13",
+ "id": 2,
+ "name": "Jack's Laptop"
+ },
+ {
+ "id": 3,
+ "mac": "90:E2:BA:82:F9:75",
+ "name": "Dad's PC",
+ "level": "PG_13"
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/data/xos-instance-statistics.json b/views/ngXosViews/diagnostic/mocks/data/xos-instance-statistics.json
new file mode 100644
index 0000000..7b83f5d
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/data/xos-instance-statistics.json
@@ -0,0 +1,557 @@
+[
+ {
+ "resource_name": "mysite_vsg-1",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average CPU utilization",
+ "resource_id": "075a3ae4-9e76-4198-8e6b-67c67b996745",
+ "meter": "cpu_util",
+ "unit": "%",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 34.39350127615451,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "mysite_vsg-1",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of VCPUs",
+ "resource_id": "075a3ae4-9e76-4198-8e6b-67c67b996745",
+ "meter": "vcpus",
+ "unit": "vcpu",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 1.0,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "mysite_vsg-1",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Volume of RAM",
+ "resource_id": "075a3ae4-9e76-4198-8e6b-67c67b996745",
+ "meter": "memory",
+ "unit": "MB",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 2048.0,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "mysite_vsg-1",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "CPU time used",
+ "resource_id": "075a3ae4-9e76-4198-8e6b-67c67b996745",
+ "meter": "cpu",
+ "unit": "ns",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 199610924583333.34,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "mysite_vsg-1",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Volume of RAM used",
+ "resource_id": "075a3ae4-9e76-4198-8e6b-67c67b996745",
+ "meter": "memory.usage",
+ "unit": "MB",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 1931.625,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.outgoing.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.0001851851851851852,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.outgoing.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 14088.506944444445,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.incoming.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.015026113884266964,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.outgoing.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.02625256131755716,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.incoming.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 66.71527777777777,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.incoming.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 9.25925925925926e-05,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.incoming.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 8747.979166666666,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapcf5721c4-c6",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapcf5721c4-c6",
+ "meter": "network.outgoing.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 111.22222222222223,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.outgoing.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 1.603090277777778,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.outgoing.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 1486894584.4722223,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.incoming.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 1248.113658402975,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.outgoing.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 143.5401912417864,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.incoming.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 12888749.868055556,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.incoming.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 1.5918200296505445,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.incoming.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 28949696952.67361,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap3f06c6d0-fb",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap3f06c6d0-fb",
+ "meter": "network.outgoing.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 13835573.3125,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.outgoing.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 6.944444444444444e-05,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.outgoing.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 6808.0,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.incoming.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.014062500000000002,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.outgoing.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.013333333333333332,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.incoming.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 42.958333333333336,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.incoming.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 6.944444444444444e-05,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.incoming.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 7230.0625,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tapb7ae5310-e2",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tapb7ae5310-e2",
+ "meter": "network.outgoing.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 40.958333333333336,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.outgoing.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 6.944444444444444e-05,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.outgoing.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 6773.333333333333,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.incoming.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.014062500000000002,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of outgoing bytes on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.outgoing.bytes.rate",
+ "unit": "B/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 0.013325938251063044,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.incoming.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 44.77777777777778,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Average rate per sec of incoming packets on a VM network interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.incoming.packets.rate",
+ "unit": "packet/s",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 6.944444444444444e-05,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of incoming bytes on the network for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.incoming.bytes",
+ "unit": "B",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 7365.5,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ },
+ {
+ "resource_name": "tap38f1f5be-89",
+ "instance-uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "description": "Number of outgoing packets for a VM interface",
+ "resource_id": "instance-00000004-075a3ae4-9e76-4198-8e6b-67c67b996745-tap38f1f5be-89",
+ "meter": "network.outgoing.packets",
+ "unit": "packet",
+ "category": "Nova",
+ "slice": "mysite_vsg",
+ "name": "none",
+ "service": "service_vsg",
+ "value": 40.77777777777778,
+ "time": "2016-02-23T22:30:34",
+ "project_id": "718dd78532a24a74b0491437bbef398c"
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/diagnostic.conf.json b/views/ngXosViews/diagnostic/mocks/diagnostic.conf.json
new file mode 100644
index 0000000..a346dc6
--- /dev/null
+++ b/views/ngXosViews/diagnostic/mocks/diagnostic.conf.json
@@ -0,0 +1,44 @@
+[
+ {
+ "url": "cordsubscriber",
+ "base": "xoslib/",
+ "methods": ["GET", "PUT"],
+ "param": "id"
+ },
+ {
+ "url": "users",
+ "base": "xoslib/rs/subscriber/1/",
+ "methods": ["GET"]
+ },
+ {
+ "url": "services",
+ "base": "xos/",
+ "methods": ["GET"]
+ },
+ {
+ "url": "tenants",
+ "base": "xos/",
+ "methods": ["GET"]
+ },
+ {
+ "url": "nodes",
+ "base": "xos/",
+ "methods": ["GET"]
+ },
+ {
+ "url": "instances",
+ "base": "xos/",
+ "methods": ["GET"],
+ "param": "id"
+ },
+ {
+ "url": "meterstatistics",
+ "base": "xoslib/",
+ "methods": ["GET"]
+ },
+ {
+ "url": "xos-instance-statistics",
+ "base": "xoslib/",
+ "methods": ["GET"]
+ }
+]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/package.json b/views/ngXosViews/diagnostic/package.json
new file mode 100644
index 0000000..d7694bc
--- /dev/null
+++ b/views/ngXosViews/diagnostic/package.json
@@ -0,0 +1,63 @@
+{
+ "name": "xos-serviceTopology",
+ "version": "1.0.0",
+ "description": "Angular Application for XOS, created with generator-xos",
+ "scripts": {
+ "prestart": "npm install && bower install",
+ "start": "gulp serve",
+ "dev": "NODE_ENV=mock gulp serve",
+ "local": "NODE_ENV=local gulp serve",
+ "server": "easy-mocker -c ./mocks/diagnostic.conf.json -d ./mocks/data",
+ "build": "gulp",
+ "test": "karma start",
+ "lint": "eslint src/js/"
+ },
+ "keywords": [
+ "XOS",
+ "Angular",
+ "XOSlib"
+ ],
+ "author": "Matteo Scandolo",
+ "license": "MIT",
+ "dependencies": {},
+ "devDependencies": {
+ "autoprefixer": "^6.3.3",
+ "browser-sync": "^2.9.11",
+ "css-mqpacker": "^4.0.0",
+ "csswring": "^4.2.1",
+ "del": "^2.0.2",
+ "easy-mocker": "^1.2.0",
+ "eslint": "^1.8.0",
+ "eslint-plugin-angular": "linkmesrl/eslint-plugin-angular",
+ "gulp": "^3.9.0",
+ "gulp-angular-filesort": "^1.1.1",
+ "gulp-angular-templatecache": "^1.8.0",
+ "gulp-babel": "^5.3.0",
+ "gulp-concat": "^2.6.0",
+ "gulp-concat-util": "^0.5.5",
+ "gulp-eslint": "^1.0.0",
+ "gulp-inject": "^3.0.0",
+ "gulp-minify-html": "^1.0.4",
+ "gulp-ng-annotate": "^1.1.0",
+ "gulp-postcss": "^6.0.1",
+ "gulp-rename": "^1.2.2",
+ "gulp-replace": "^0.5.4",
+ "gulp-uglify": "^1.4.2",
+ "http-proxy": "^1.12.0",
+ "ink-docstrap": "^0.5.2",
+ "jasmine-core": "~2.3.4",
+ "karma": "^0.13.14",
+ "karma-babel-preprocessor": "~5.2.2",
+ "karma-coverage": "^0.5.3",
+ "karma-jasmine": "~0.3.6",
+ "karma-mocha-reporter": "~1.1.1",
+ "karma-ng-html2js-preprocessor": "^0.2.0",
+ "karma-phantomjs-launcher": "~0.2.1",
+ "lodash": "^3.10.1",
+ "phantomjs": "^1.9.19",
+ "proxy-middleware": "^0.15.0",
+ "run-sequence": "^1.1.4",
+ "wiredep": "^3.0.0-beta",
+ "wrench": "^1.5.8"
+ }
+}
diff --git a/views/ngXosViews/diagnostic/spec/.eslintrc b/views/ngXosViews/diagnostic/spec/.eslintrc
new file mode 100644
index 0000000..c1764a5
--- /dev/null
+++ b/views/ngXosViews/diagnostic/spec/.eslintrc
@@ -0,0 +1,15 @@
+{
+ "globals" :{
+ "describe": true,
+ "xdescribe": true,
+ "beforeEach": true,
+ "it": true,
+ "inject": true,
+ "expect": true,
+ "jasmine": true
+ },
+ "rules": {
+ "max-nested-callbacks": [0, 4],
+ "camelcase": 0
+ }
+}
diff --git a/views/ngXosViews/diagnostic/spec/logicTopologyHelper.test.js b/views/ngXosViews/diagnostic/spec/logicTopologyHelper.test.js
new file mode 100644
index 0000000..94307f5
--- /dev/null
+++ b/views/ngXosViews/diagnostic/spec/logicTopologyHelper.test.js
@@ -0,0 +1,62 @@
+(function () {
+ 'use strict';
+
+ describe('The Logic Topology Helper Service', () => {
+
+ var Service, Config;
+
+ var x0, x1, x2, x3, x4;
+
+ var svgWidth = 900;
+
+ beforeEach(module('xos.serviceTopology'));
+
+ // inject the rackHelper service
+ beforeEach(inject(function (_LogicTopologyHelper_, _serviceTopologyConfig_) {
+ // The injector unwraps the underscores (_) from around the parameter names when matching
+ Service = _LogicTopologyHelper_;
+ Config = _serviceTopologyConfig_;
+
+ // result
+ let totalElWidth = Config.elWidths.reduce((el, val) => val + el, 0);
+ let remainingSpace = svgWidth - totalElWidth - (Config.widthMargin * 2);
+ let step = remainingSpace / (Config.elWidths.length - 1);
+ x0 = Config.widthMargin;
+ x1 = x0 + Config.elWidths[0] + step;
+ x2 = x1 + Config.elWidths[1] + step;
+ x3 = x2 + Config.elWidths[2] + step;
+ x4 = x3 + Config.elWidths[3] + step;
+ }));
+
+ var customMatchers = {
+ toBeSimilar: () => {
+
+ const tolerance = 0.1;
+
+ return {
+ compare: (actual, expected) => {
+ return {
+ pass: (Math.abs(actual - expected) < tolerance),
+ message: `Expected ${actual} to be ${expected}`
+ }
+ }
+ }
+ }
+ };
+
+ beforeEach(function() {
+ jasmine.addMatchers(customMatchers);
+ });
+
+ it('should calculate horizontal position for each element', () => {
+ let [el0x, el1x, el2x, el3x, el4x] = Service.computeElementPosition(svgWidth);
+ console.log(x1, el1x);
+ expect(el0x).toBeSimilar(svgWidth - (x0 + (Config.elWidths[0] / 2)));
+ expect(el1x).toBeSimilar(svgWidth - (x1 + (Config.elWidths[1] / 2)));
+ expect(el2x).toBeSimilar(svgWidth - (x2 + (Config.elWidths[2] / 2)));
+ expect(el3x).toBeSimilar(svgWidth - (x3 + (Config.elWidths[3] / 2)));
+ expect(el4x).toBeSimilar(svgWidth - (x4 + (Config.elWidths[4] / 2)));
+ });
+ });
+
+})();
diff --git a/views/ngXosViews/diagnostic/spec/rackHelper.test.js b/views/ngXosViews/diagnostic/spec/rackHelper.test.js
new file mode 100644
index 0000000..9f11686
--- /dev/null
+++ b/views/ngXosViews/diagnostic/spec/rackHelper.test.js
@@ -0,0 +1,187 @@
+(function () {
+ 'use strict';
+
+ const computeNodes = [
+ {
+ humanReadableName: 'cp-1.teone.xos-pg0.clemson.cloudlab.us',
+ instances: [
+ {
+ instance_name: 'mysite_clients-3'
+ },
+ {
+ instance_name: 'mysite_clients-4'
+ },
+ {
+ instance_name: 'mysite_clients-5'
+ }
+ ]
+ },
+ {
+ humanReadableName: 'cp-2.teone.xos-pg0.clemson.cloudlab.us',
+ instances: [
+ {
+ instance_name: 'mysite_clients-1'
+ },
+ {
+ instance_name: 'mysite_clients-2'
+ }
+ ]
+ },
+ {
+ humanReadableName: 'cp-2.teone.xos-pg0.clemson.cloudlab.us',
+ instances: [
+ {
+ instance_name: 'mysite_clients-1'
+ },
+ {
+ instance_name: 'mysite_clients-2'
+ }
+ ]
+ }
+ ];
+
+ describe('The Rack Helper Service', () => {
+
+ var Service, Config;
+
+ // results
+ var cp1, cp2, cp3, rack, instancePos, nodePos;
+
+ beforeEach(module('xos.serviceTopology'));
+
+ // inject the rackHelper service
+ beforeEach(inject(function (_RackHelper_, _serviceTopologyConfig_) {
+ // The injector unwraps the underscores (_) from around the parameter names when matching
+ Service = _RackHelper_;
+ Config = _serviceTopologyConfig_;
+
+ cp1 = {
+ width: (Config.instance.width * 2) + (Config.instance.margin * 3),
+ height: (Config.instance.height * 2) + (Config.instance.margin * 5) + Config.computeNode.labelHeight
+ };
+
+ cp2 = {
+ width: (Config.instance.width * 2) + (Config.instance.margin * 3),
+ height: Config.instance.height + (Config.instance.margin * 4) + Config.computeNode.labelHeight
+ };
+
+ cp3 = {
+ width: (Config.instance.width * 2) + (Config.instance.margin * 3),
+ height: Config.instance.height + (Config.instance.margin * 4) + Config.computeNode.labelHeight
+ };
+
+ rack = {
+ width: cp1.width + (Config.computeNode.margin * 2),
+ height: cp1.height + cp2.height + cp3.height + (Config.computeNode.margin * 4)
+ }
+
+ instancePos = [
+ {
+ x: Config.instance.margin,
+ y: Config.instance.margin + Service.getComputeNodeLabelSize()
+ },
+ {
+ x: Config.instance.margin + (Config.instance.width * 1) + (Config.instance.margin * 1),
+ y: Config.instance.margin + Service.getComputeNodeLabelSize()
+ },
+ {
+ x: Config.instance.margin,
+ y: Config.instance.margin + Service.getComputeNodeLabelSize() + + (Config.instance.height * 1) + (Config.instance.margin * 1)
+ },
+ {
+ x: Config.instance.margin + (Config.instance.width * 1) + (Config.instance.margin * 1),
+ y: Config.instance.margin + Service.getComputeNodeLabelSize() + + (Config.instance.height * 1) + (Config.instance.margin * 1)
+ }
+ ];
+
+ nodePos = [
+ {
+ x: Config.computeNode.margin,
+ y: Config.computeNode.margin
+ },
+ {
+ x: Config.computeNode.margin,
+ y: (Config.computeNode.margin * 2) + cp1.height
+ },
+ {
+ x: Config.computeNode.margin,
+ y: (Config.computeNode.margin * 3) + cp1.height + cp2.height
+ }
+ ]
+ }));
+
+ describe('Given a list of instances', () => {
+ it('should calculate the first Compute Node Size', () => {
+ const [width, height] = Service.getComputeNodeSize(computeNodes[0].instances);
+ expect(width).toBe(cp1.width);
+ expect(height).toBe(cp1.height);
+ });
+
+ it('should calculate the second Compute Node Size', () => {
+ const [width, height] = Service.getComputeNodeSize(computeNodes[1].instances);
+ expect(width).toBe(cp2.width);
+ expect(height).toBe(cp2.height);
+ });
+
+ it('should calculate the third Compute Node Size', () => {
+ const [width, height] = Service.getComputeNodeSize(computeNodes[1].instances);
+ expect(width).toBe(cp3.width);
+ expect(height).toBe(cp3.height);
+ });
+ });
+
+ describe('Given a list of Compute Nodes', () => {
+ it('should return rack size', () => {
+ const [width, height] = Service.getRackSize(computeNodes);
+ expect(width).toBe(rack.width);
+ expect(height).toBe(rack.height);
+ });
+ });
+
+ describe('Given an instance index', () => {
+ it('should return the position for first instance', () => {
+ const [x, y] = Service.getInstancePosition(0);
+ expect(x).toBe(instancePos[0].x);
+ expect(y).toBe(instancePos[0].y);
+ })
+
+ it('should return the position for second instance', () => {
+ const [x, y] = Service.getInstancePosition(1);
+ expect(x).toBe(instancePos[1].x);
+ expect(y).toBe(instancePos[1].y);
+ });
+
+ it('should return the position for third instance', () => {
+ const [x, y] = Service.getInstancePosition(2);
+ expect(x).toBe(instancePos[2].x);
+ expect(y).toBe(instancePos[2].y);
+ });
+
+ it('should return the position for 4th instance', () => {
+ const [x, y] = Service.getInstancePosition(3);
+ expect(x).toBe(instancePos[3].x);
+ expect(y).toBe(instancePos[3].y);
+ });
+ });
+
+ describe('Given an ComputeNode index', () => {
+ it('should return the position for 1st node', () => {
+ const [x, y] = Service.getComputeNodePosition(computeNodes, 0);
+ expect(x).toBe(nodePos[0].x);
+ expect(y).toBe(nodePos[0].y);
+ })
+
+ it('should return the position for 2st node', () => {
+ const [x, y] = Service.getComputeNodePosition(computeNodes, 1);
+ expect(x).toBe(nodePos[1].x);
+ expect(y).toBe(nodePos[1].y);
+ });
+
+ it('should return the position for 2st node', () => {
+ const [x, y] = Service.getComputeNodePosition(computeNodes, 2);
+ expect(x).toBe(nodePos[2].x);
+ expect(y).toBe(nodePos[2].y);
+ });
+ });
+ });
+})();
diff --git a/views/ngXosViews/diagnostic/spec/serviceChain.test.js b/views/ngXosViews/diagnostic/spec/serviceChain.test.js
new file mode 100644
index 0000000..8dfa02d
--- /dev/null
+++ b/views/ngXosViews/diagnostic/spec/serviceChain.test.js
@@ -0,0 +1,211 @@
+'use strict';
+
+describe('The Service Relation Service', () => {
+
+ var Service;
+
+ beforeEach(module('xos.serviceTopology'));
+ beforeEach(module('templates'));
+
+ // inject the cartService
+ beforeEach(inject(function (_ServiceRelation_) {
+ // The injector unwraps the underscores (_) from around the parameter names when matching
+ Service = _ServiceRelation_;
+ }));
+
+ describe('given a service', () => {
+
+ const levelRelations = [
+ {
+ subscriber_service: 1
+ },
+ {
+ subscriber_service: 1
+ },
+ {
+ subscriber_service: 2
+ }
+ ];
+
+ it('should find all involved relations', () => {
+ expect(typeof Service.findLevelRelation).toBe('function');
+ let levelRelation = Service.findLevelRelation(levelRelations, 1);
+ expect(levelRelation.length).toBe(2);
+ });
+ });
+
+ describe('given a set of relation', () => {
+
+ const levelRelations = [
+ {
+ provider_service: 1,
+ service_specific_attribute: '{"instance_id": "instance1"}',
+ subscriber_tenant: 2
+ },
+ {
+ provider_service: 2
+ }
+ ];
+
+ const services = [
+ {
+ id: 1
+ },
+ {
+ id: 2
+ },
+ {
+ id: 3
+ }
+ ];
+
+ it('should find all the provider service', () => {
+ expect(typeof Service.findLevelServices).toBe('function');
+ let levelServices = Service.findLevelServices(levelRelations, services);
+ expect(levelServices.length).toBe(2);
+ });
+
+ it('should retrieve all service specific information', () => {
+ let info = Service.findSpecificInformation(levelRelations, 1);
+ expect(info.instance_id).toBe('instance1');
+ });
+ });
+
+
+
+ describe('given a list of services and a list of relations', () => {
+
+ const services = [
+ {
+ id: 1,
+ humanReadableName: 'service-1'
+ },
+ {
+ id: 2,
+ humanReadableName: 'service-2'
+ },
+ {
+ id: 3,
+ humanReadableName: 'service-3'
+ },
+ {
+ id: 4,
+ humanReadableName: 'service-4'
+ }
+ ];
+
+ const tenants = [
+ {
+ id: 1,
+ provider_service: 2,
+ subscriber_tenant: 4,
+ subscriber_service: 1,
+ },
+ {
+ id: 2,
+ provider_service: 3,
+ subscriber_tenant: 1,
+ subscriber_service: 2
+ },
+ {
+ id: 3,
+ provider_service: 4,
+ subscriber_tenant: 4,
+ subscriber_service: 1
+ },
+ {
+ id: 4,
+ subscriber_root: 1,
+ provider_service: 1
+ }
+ ];
+
+ it('should return a tree ordered by tenants', () => {
+ let tree = Service.buildSubscriberServiceTree(services, tenants);
+
+ expect(tree.name).toBe('fakeSubs');
+ expect(tree.parent).toBeNull();
+ expect(tree.children.length).toBe(1);
+
+ expect(tree.children[0].name).toBe('service-1');
+ expect(tree.children[0].parent).toBeNull();
+ expect(tree.children[0].tenant).toEqual({id: 4, subscriber_root: 1, provider_service: 1});
+ expect(tree.children[0].children.length).toBe(2);
+
+ expect(tree.children[0].children[0].name).toBe('service-2');
+ expect(tree.children[0].children[0].tenant).toEqual({ id: 1, provider_service: 2, subscriber_tenant: 4, subscriber_service: 1 });;
+ expect(tree.children[0].children[0].children[0].name).toBe('service-3');
+
+ expect(tree.children[0].children[0].children[0].children[0].name).toBe('Router');
+
+ expect(tree.children[0].children[1].name).toBe('service-4');
+ expect(tree.children[0].children[1].children[0].name).toBe('Router');
+ });
+ });
+
+ describe('given an object', () => {
+
+ const sample = {
+ name: '1',
+ children: [
+ {
+ name: '2',
+ children: [
+ {
+ name: '3'
+ }
+ ]
+ }
+ ]
+ };
+
+ it('should return the depth', () => {
+ expect(Service.depthOf(sample)).toBe(3);
+ });
+ });
+
+ describe('Given a list of services and COARSE tenant', () => {
+
+ const coarseTenants = [
+ {
+ humanReadableName: 'coarse-1',
+ provider_service: 1,
+ subscriber_service: 2
+ },
+ {
+ humanReadableName: 'coarse-2',
+ provider_service: 2,
+ subscriber_service: 3
+ }
+ ];
+
+ const services = [
+ {
+ id: 1,
+ name: 'vbng',
+ humanReadableName: 'vbng'
+ },
+ {
+ id: 2,
+ name: 'vsg',
+ humanReadableName: 'vsg'
+ },
+ {
+ id: 3,
+ name: 'volt',
+ humanReadableName: 'volt'
+ }
+ ];
+
+ it('should build the tenancy graph', () => {
+ let tree = Service.buildServiceTree(services, coarseTenants);
+
+ expect(tree.type).toBe('subscriber');
+ expect(tree.children[0].name).toBe('volt');
+ expect(tree.children[0].service).toBeDefined();
+ expect(tree.children[0].children[0].name).toBe('vsg');
+ expect(tree.children[0].children[0].children[0].name).toBe('vbng');
+ });
+ });
+
+});
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/css/dev.css b/views/ngXosViews/diagnostic/src/css/dev.css
new file mode 100644
index 0000000..7ff2305
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/css/dev.css
@@ -0,0 +1,15 @@
+
+html, body {
+ margin: 0;
+ padding: 0;
+ max-height: 100%;
+ height: 100%;
+}
+
+#xosServiceTopology{
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/css/diagnostic.css b/views/ngXosViews/diagnostic/src/css/diagnostic.css
new file mode 100644
index 0000000..4a8a6f9
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/css/diagnostic.css
@@ -0,0 +1,319 @@
+/* CONTAINER */
+#xosDiagnostic, [ui-view] {
+ min-height: 700px;
+ position: relative;
+}
+
+diagnostic-container .half-height {
+ position: relative;
+ height: 50%;
+}
+
+diagnostic-container .onethird-height {
+ position: relative;
+ height: 33%;
+}
+
+diagnostic-container .twothird-height {
+ position: relative;
+ height: 67%;
+}
+
+diagnostic-container .subscriber-select{
+ max-width: 200px;
+ position: absolute;
+ top: 20px;
+ right: 20px;
+ z-index: 1;
+}
+
+
+/* subscriber-status-modal */
+subscriber-status-modal .row + .row {
+ margin-top: 20px;
+}
+
+.half-height + .half-height {
+ border-top: 1px solid black;
+}
+
+service-topology,
+logic-topology {
+ height: 100%;
+ width: 100%;
+ display: block;
+ position: absolute;
+ top: 0;
+}
+
+logic-topology .subscriber circle,
+logic-topology .device circle{
+ fill: #fff;
+ stroke: green;
+ stroke-width: 1px;
+}
+
+logic-topology > svg {
+ position: absolute;
+ top: 0;
+}
+
+/* CLOUDS */
+
+logic-topology .network .cloud {
+ fill: #fff;
+ stroke: green;
+ stroke-width: 1px;
+}
+
+/* RACK */
+logic-topology .node.rack > g > rect{
+ fill: #ccc;
+ stroke: steelblue;
+ stroke-width: 1px;
+}
+
+/* CP NODE */
+
+logic-topology .compute-node > rect{
+ fill: #fff;
+ stroke: steelblue;
+ stroke-width: 1px;
+}
+
+/* INSTANCE */
+
+logic-topology .instance > rect{
+ fill: #eee;
+ stroke: steelblue;
+ stroke-width: 1px;
+}
+
+logic-topology .node .instance.active rect{
+ fill: lightsteelblue;
+ stroke: steelblue;
+ stroke-width: 1px;
+}
+
+logic-topology .node .instance.active.good > rect{
+ fill: green;
+}
+
+logic-topology .node .instance.active.provisioning > rect{
+ fill: yellow;
+}
+
+logic-topology .node .instance.active.bad > rect{
+ fill: red;
+}
+
+/* INSTANCE STATS */
+
+logic-topology .node .instance .stats-container rect {
+ fill: white;
+}
+
+logic-topology .node .instance .stats-container text.name{
+ font-weight: bold;
+}
+
+logic-topology .node .instance .stats-container text.ip{
+ font-style: italic;
+ font-size: 10px;
+}
+
+/* CONTAINERS */
+logic-topology .node .instance .stats-container .container rect {
+ fill: #eee;
+ stroke: steelblue;
+ stroke-width: 1px;
+}
+
+/* LEGEND */
+
+.legend {
+ fill: #fff;
+ stroke: #ccc;
+ stroke-width: 1px;
+ position: relative;
+}
+
+.legend text {
+ stroke: #000;
+}
+
+.node {
+ cursor: pointer;
+}
+
+.node circle,
+.node rect{
+ fill: #fff;
+ stroke: steelblue;
+ stroke-width: 1px;
+}
+
+.node.subscriber circle,
+.node.subscriber rect,
+.node.router circle,
+.node.router rect {
+ stroke: #05ffcb;
+}
+
+.node.slice rect {
+ stroke: #b01dff;
+}
+
+.node.instance rect {
+ stroke: #ccc;
+}
+
+.node.instance rect.active {
+ stroke: #ff8b00;
+}
+
+.node rect.slice-detail{
+ fill: #fff;
+ stroke: steelblue;
+ stroke-width: 3px;
+}
+
+.node text {
+ font: 12px sans-serif;
+}
+
+.link, .device-link {
+ fill: none;
+ stroke: #ccc;
+ stroke-width: 2px;
+}
+
+.link.slice {
+ stroke: rgba(157, 4, 183, 0.29);
+}
+.link.instance{
+ stroke: #ccc;
+}
+
+.link.instance.active{
+ stroke: rgba(255, 138, 0, 0.65);
+}
+
+.service-details{
+ width: 200px;
+ position: absolute;
+ top: 20px;
+ right: 20px;
+}
+
+/* when showing the thing */
+
+.animate.ng-hide-remove {
+ animation:0.5s bounceInRight ease;
+}
+
+/* when hiding the picture */
+.animate.ng-hide-add {
+ animation:0.5s bounceOutRight ease;
+}
+
+/* LOADER */
+.loader {
+ font-size: 10px;
+ margin: 150px auto;
+ text-indent: -9999em;
+ width: 11em;
+ height: 11em;
+ border-radius: 50%;
+ background: #ffffff;
+ background: -moz-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: -webkit-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: -o-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: -ms-linear-gradient(left, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 42%);
+ position: relative;
+ animation: load3 1.4s infinite linear;
+ transform: translateZ(0);
+}
+.loader:before {
+ width: 50%;
+ height: 50%;
+ background: #105E9E;
+ border-radius: 100% 0 0 0;
+ position: absolute;
+ top: 0;
+ left: 0;
+ content: '';
+}
+.loader:after {
+ background: #fff;
+ width: 75%;
+ height: 75%;
+ border-radius: 50%;
+ content: '';
+ margin: auto;
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
+
+@keyframes load3 {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+/* MODALS */
+
+.modal.fade.in {
+ display: block;
+}
+
+/* ANIMATIONS */
+
+@keyframes bounceInRight {
+ from, 60%, 75%, 90%, to {
+ animation-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ from {
+ opacity: 0;
+ transform: translate3d(3000px, 0, 0);
+ }
+
+ 60% {
+ opacity: 1;
+ transform: translate3d(-25px, 0, 0);
+ }
+
+ 75% {
+ transform: translate3d(10px, 0, 0);
+ }
+
+ 90% {
+ transform: translate3d(-5px, 0, 0);
+ }
+
+ to {
+ transform: none;
+ }
+}
+
+@keyframes bounceOutRight {
+ 20% {
+ opacity: 1;
+ transform: translate3d(-20px, 0, 0);
+ }
+
+ to {
+ opacity: 0;
+ transform: translate3d(2000px, 0, 0);
+ }
+}
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/index.html b/views/ngXosViews/diagnostic/src/index.html
new file mode 100644
index 0000000..dcba923
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/index.html
@@ -0,0 +1,48 @@
+<!-- browserSync -->
+<!-- bower:css -->
+<link rel="stylesheet" href="vendor/bootstrap-css/css/bootstrap.min.css" />
+<!-- endbower --><!-- endcss -->
+<!-- inject:css -->
+<link rel="stylesheet" href="/css/dev.css">
+<link rel="stylesheet" href="/css/diagnostic.css">
+<!-- endinject -->
+
+<div ng-app="xos.diagnostic" id="xosDiagnostic">
+ <div ui-view></div>
+</div>
+
+<!-- bower:js -->
+<script src="vendor/d3/d3.js"></script>
+<script src="vendor/lodash/lodash.js"></script>
+<script src="vendor/angular/angular.js"></script>
+<script src="vendor/angular-animate/angular-animate.js"></script>
+<script src="vendor/jquery/dist/jquery.js"></script>
+<script src="vendor/angular-mocks/angular-mocks.js"></script>
+<script src="vendor/angular-ui-router/release/angular-ui-router.js"></script>
+<script src="vendor/angular-cookies/angular-cookies.js"></script>
+<script src="vendor/angular-resource/angular-resource.js"></script>
+<script src="vendor/ng-lodash/build/ng-lodash.js"></script>
+<script src="vendor/bootstrap-css/js/bootstrap.min.js"></script>
+<!-- endbower --><!-- endjs -->
+<!-- inject:js -->
+<script src="/xosHelpers/src/xosHelpers.module.js"></script>
+<script src="/xosHelpers/src/services/noHyperlinks.interceptor.js"></script>
+<script src="/xosHelpers/src/services/csrfToken.interceptor.js"></script>
+<script src="/xosHelpers/src/services/api.services.js"></script>
+<script src="/api/ng-xoslib.js"></script>
+<script src="/api/ng-xos.js"></script>
+<script src="/api/ng-hpcapi.js"></script>
+<script src="/.tmp/main.js"></script>
+<script src="/.tmp/subscriber-modal.js"></script>
+<script src="/.tmp/serviceTopologyHelper.js"></script>
+<script src="/.tmp/serviceTopology.js"></script>
+<script src="/.tmp/rest_services.js"></script>
+<script src="/.tmp/rackHelper.js"></script>
+<script src="/.tmp/nodeDrawer.js"></script>
+<script src="/.tmp/logicTopologyHelper.js"></script>
+<script src="/.tmp/logicTopology.js"></script>
+<script src="/.tmp/diagnostic.js"></script>
+<script src="/.tmp/d3.js"></script>
+<script src="/.tmp/config.js"></script>
+<script src="/.tmp/chart_data_service.js"></script>
+<!-- endinject -->
diff --git a/views/ngXosViews/diagnostic/src/js/chart_data_service.js b/views/ngXosViews/diagnostic/src/js/chart_data_service.js
new file mode 100644
index 0000000..f39a1e2
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/chart_data_service.js
@@ -0,0 +1,205 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic')
+ .service('ChartData', function($rootScope, $q, lodash, Tenant, Node, serviceTopologyConfig, Ceilometer, Instances) {
+ this.currentSubscriber = null;
+ this.currentServiceChain = null;
+
+ this.logicTopologyData = {
+ name: 'Router',
+ type: 'router',
+ children: [
+ {
+ name: 'WAN',
+ type: 'network',
+ children: [
+ {
+ name: 'Rack',
+ type: 'rack',
+ computeNodes: [],
+ children: [
+ {
+ name: 'LAN',
+ type: 'network',
+ children: [{
+ name: 'Subscriber',
+ type: 'subscriber'
+ }] //subscribers goes here
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ this.getLogicTree = () => {
+ const deferred = $q.defer();
+
+ Node.queryWithInstances().$promise
+ .then((computeNodes) => {
+ this.logicTopologyData.children[0].children[0].computeNodes = computeNodes;
+ // LogicTopologyHelper.updateTree(svg);
+ deferred.resolve(this.logicTopologyData);
+ });
+
+ return deferred.promise;
+ };
+
+ /**
+ * Add Subscriber tag to LAN Network
+ */
+ this.addSubscriberTag = (tags) => {
+ this.logicTopologyData.children[0].children[0].children[0].subscriberTag = {
+ cTag: tags.cTag,
+ sTag: tags.sTag
+ };
+ };
+
+ /**
+ * Add Subscribers to the tree
+ */
+ this.addSubscriber = (subscriber) => {
+ subscriber.children = subscriber.devices;
+
+ // add subscriber to data tree
+ this.logicTopologyData.children[0].children[0].children[0].children = [subscriber];
+ return this.logicTopologyData;
+ };
+
+ this.getSubscriberTag = (subscriber) => {
+ const tags = {
+ cTag: subscriber.c_tag,
+ sTag: subscriber.s_tag
+ };
+
+ this.addSubscriberTag(tags);
+ // add tags info to current subscriber
+ this.currentSubscriber.tags = tags;
+
+ };
+
+ this.getSubscriberIP = (subscriber) => {
+ // const ip = JSON.parse(this.currentServiceChain.children[0].children[0].tenant.service_specific_attribute).wan_container_ip;
+ // const ip = this.currentServiceChain.children[0].children[0].tenant.wan_container_ip;
+ this.logicTopologyData.children[0].subscriberIP = subscriber.wan_container_ip;
+ };
+
+ this.selectSubscriber = (subscriber) => {
+ // append the device with to config settings
+ serviceTopologyConfig.elWidths.push(160);
+
+ this.addSubscriber(angular.copy(subscriber));
+
+ //clean selected instances
+ this.highlightInstances([]);
+
+ this.getSubscriberTag(subscriber);
+ this.getSubscriberIP(subscriber);
+
+ };
+
+ this.highlightInstances = (instances) => {
+
+ const computeNodes = this.logicTopologyData.children[0].children[0].computeNodes;
+
+ // unselect all
+ computeNodes.map((node) => {
+ node.instances.map((instance) => {
+ instance.selected = false
+ return instance;
+ });
+ });
+
+ lodash.forEach(instances, (instance) => {
+ computeNodes.map((node) => {
+ node.instances.map((d3instance) => {
+ if(d3instance.id === instance.id){
+ // console.log(d3instance, instance);
+ d3instance.selected = true;
+ d3instance.stats = instance.stats; //add stats to d3 node
+ d3instance.container = instance.container; // container info to d3 node
+ }
+ return d3instance;
+ });
+ });
+ });
+
+ }
+
+ this.getInstanceStatus = (service) => {
+ const deferred = $q.defer();
+
+ let p;
+
+ // subscriber specific
+ if(this.currentSubscriber){
+
+ let attr;
+ try {
+ attr = JSON.parse(service.tenant.service_specific_attribute);
+ }
+ catch(e){
+ attr = null;
+ }
+
+ // if no instances are associated to the subscriber
+ if(!attr || !attr.instance_id){
+ let d = $q.defer();
+ d.resolve([]);
+ p = d.promise;
+ }
+ // if ther is an instance
+ else{
+ let instance = {};
+ p = Instances.get({id: attr.instance_id}).$promise
+ .then(function(_instance){
+ instance = _instance;
+ return Ceilometer.getInstanceStats(instance.instance_uuid);
+ })
+ .then((stats) => {
+ instance.stats = stats;
+ const containerName = `vcpe-${this.currentSubscriber.tags.sTag}-${this.currentSubscriber.tags.cTag}`;
+ // append containers
+ instance.container = {
+ name: containerName
+ };
+
+ // TODO fetch container stats
+ return Ceilometer.getContainerStats(containerName);
+ })
+ .then((containerStats) => {
+ instance.container.stats = containerStats.stats;
+ instance.container.port = containerStats.port;
+ return [instance];
+ });
+ }
+ }
+ // global scope
+ else {
+ const param = {
+ 'service_vsg': {kind: 'vCPE'},
+ 'service_vbng': {kind: 'vBNG'},
+ 'service_volt': {kind: 'vOLT'}
+ };
+
+ p = Tenant.queryVsgInstances(param[service.name]).$promise
+ .then((instances) => {
+
+ return Ceilometer.getInstancesStats(instances);
+ });
+ }
+
+ p.then((instances) => {
+ this.highlightInstances(instances);
+ deferred.resolve(instances);
+ })
+ .catch((e) => {
+ deferred.reject(e);
+ });
+
+ return deferred.promise;
+ };
+ })
+})();
diff --git a/views/ngXosViews/diagnostic/src/js/config.js b/views/ngXosViews/diagnostic/src/js/config.js
new file mode 100644
index 0000000..75b5fe7
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/config.js
@@ -0,0 +1,51 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic')
+ .constant('serviceTopologyConfig', {
+ widthMargin: 20,
+ heightMargin: 30,
+ duration: 750,
+ elWidths: [20, 104, 105, 104, 20], //this is not true
+ circle: {
+ radius: 10,
+ r: 10,
+ selectedRadius: 15
+ },
+ square: {
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10
+ },
+ rack: {
+ width: 105,
+ height: 50,
+ x: -30,
+ y: -25
+ },
+ computeNode: {
+ width: 50,
+ height: 20,
+ margin: 5,
+ labelHeight: 10,
+ x: -25,
+ y: -10
+ },
+ instance: {
+ width: 80,
+ height: 36,
+ margin: 5,
+ x: -40,
+ y: -18
+ },
+ container: {
+ width: 60,
+ height: 130,
+ margin: 5,
+ x: -30,
+ y: -15
+ }
+ })
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/d3.js b/views/ngXosViews/diagnostic/src/js/d3.js
new file mode 100644
index 0000000..b56ee8f
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/d3.js
@@ -0,0 +1,9 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic')
+ .factory('d3', function($window){
+ return $window.d3;
+ })
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/diagnostic.js b/views/ngXosViews/diagnostic/src/js/diagnostic.js
new file mode 100644
index 0000000..6638a95
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/diagnostic.js
@@ -0,0 +1,44 @@
+(function () {
+ 'use strict';
+ angular.module('xos.diagnostic')
+ .directive('diagnosticContainer', function(){
+ return {
+ restrict: 'E',
+ templateUrl: 'templates/diagnostic.tpl.html',
+ controllerAs: 'vm',
+ controller: function(ChartData, Subscribers, ServiceRelation, $rootScope, $log){
+
+ this.loader = true;
+ this.error = false;
+ Subscribers.query().$promise
+ .then((subscribers) => {
+ this.subscribers = subscribers;
+ return ServiceRelation.get();
+ })
+ .then((serviceChain) => {
+ this.serviceChain = serviceChain;
+ })
+ .catch(e => {
+ throw new Error(e);
+ this.error = e;
+ })
+ .finally(() => {
+ this.loader = false;
+ });
+
+ $rootScope.$on('subscriber.selected', (evt, subscriber) => {
+ ServiceRelation.getBySubscriber(subscriber)
+ .then((serviceChain) => {
+ this.serviceChain = serviceChain;
+ ChartData.currentServiceChain = serviceChain;
+ return Subscribers.getWithDevices({id: subscriber.id}).$promise;
+ })
+ .then((subscriber) => {
+ this.selectedSubscriber = subscriber;
+ ChartData.currentSubscriber = subscriber;
+ });
+ });
+ }
+ }
+ });
+})();
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/logicTopology.js b/views/ngXosViews/diagnostic/src/js/logicTopology.js
new file mode 100644
index 0000000..edd64ea
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/logicTopology.js
@@ -0,0 +1,96 @@
+(function () {
+ 'use strict';
+ angular.module('xos.diagnostic')
+ .directive('logicTopology', function(){
+ return {
+ restrict: 'E',
+ scope: {
+ subscribers: '=',
+ selected: '='
+ },
+ bindToController: true,
+ controllerAs: 'vm',
+ templateUrl: 'templates/logicTopology.tpl.html',
+ controller: function($element, $log, $scope, $rootScope, $timeout, d3, LogicTopologyHelper, Node, Tenant, Ceilometer, serviceTopologyConfig, ChartData){
+ $log.info('Logic Plane');
+
+ var svg;
+ this.selectedInstances = [];
+ this.hideInstanceStats = true;
+
+ const handleSvg = (el) => {
+
+ svg = d3.select(el)
+ .append('svg')
+ .style('width', `${el.clientWidth}px`)
+ .style('height', `${el.clientHeight}px`);
+ }
+
+ ChartData.getLogicTree()
+ .then((tree) => {
+ LogicTopologyHelper.updateTree(svg);
+ });
+
+ $scope.$watch(() => this.selected, (selected) => {
+ if(selected){
+ ChartData.selectSubscriber(selected);
+ LogicTopologyHelper.updateTree(svg);
+ }
+ });
+
+ $rootScope.$on('instance.detail.hide', () => {
+ this.hideInstanceStats = true;
+ $timeout(() => {
+ this.selectedInstances = [];
+ ChartData.highlightInstances([]);
+ LogicTopologyHelper.updateTree(svg);
+ }, 500);
+ });
+
+ $rootScope.$on('instance.detail', (evt, service) => {
+ ChartData.getInstanceStatus(service)
+ .then((instances) => {
+ LogicTopologyHelper.updateTree(svg);
+ })
+ })
+
+ handleSvg($element[0]);
+ LogicTopologyHelper.setupTree(svg);
+
+ this.selectSubscriberModal = () => {
+ this.openSelectSubscriberModal = true;
+ $scope.$apply();
+ };
+
+ this.subscriberStatusModal = () => {
+ this.openSubscriberStatusModal = true;
+ $scope.$apply();
+ };
+
+ // listen for subscriber modal event
+ $rootScope.$on('subscriber.modal.open', () => {
+
+ if(ChartData.currentSubscriber){
+ this.subscriberStatusModal();
+ }
+ else{
+ this.selectSubscriberModal();
+ }
+ });
+
+ // listen for subscriber modal event
+ $rootScope.$on('subscriber.modal.open', () => {
+
+ if(ChartData.currentSubscriber){
+ this.currentSubscriber = ChartData.currentSubscriber;
+ this.subscriberStatusModal();
+ }
+ else{
+ this.selectSubscriberModal();
+ }
+ });
+
+ }
+ };
+ });
+})();
diff --git a/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js b/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js
new file mode 100644
index 0000000..51ac0f7
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js
@@ -0,0 +1,177 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic')
+ .service('LogicTopologyHelper', function($window, $log, $rootScope, lodash, serviceTopologyConfig, NodeDrawer, ChartData){
+
+ var diagonal, nodes, links, i = 0, svgWidth, svgHeight, layout;
+
+ const baseData = ChartData.logicTopologyData;
+
+ /**
+ * Calculate the horizontal position for each element.
+ * subsrcribers, devices and routers have the same fixed width 20
+ * network have a fixed width 104
+ * rack have a fixed width 105
+ * build and array of 6 elements representing the position of each element in the svg
+ * to equally space them
+ */
+
+ this.computeElementPosition = (svgWidth) => {
+
+ let xPos = [];
+
+ let totalElWidth = lodash.reduce(serviceTopologyConfig.elWidths, (el, val) => val + el, 0);
+
+ let remainingSpace = svgWidth - totalElWidth - (serviceTopologyConfig.widthMargin * 2);
+
+ let step = remainingSpace / (serviceTopologyConfig.elWidths.length - 1);
+
+ lodash.forEach(serviceTopologyConfig.elWidths, (el, i) => {
+
+ // get half of the previous elements width
+ let previousElWidth = 0;
+ if(i !== 0){
+ previousElWidth = lodash.reduce(serviceTopologyConfig.elWidths.slice(0, i), (el, val) => val + el, 0);
+ }
+
+ let elPos =
+ serviceTopologyConfig.widthMargin // right margin
+ + (step * i) // space between elements
+ + (el / 2) // this el width
+ + previousElWidth; // previous elements width
+
+ xPos.push(svgWidth - elPos);
+ })
+
+ return xPos
+ };
+
+ /**
+ * from a nested data structure,
+ * create nodes and links for a D3 Tree Layout
+ */
+ const computeLayout = (data) => {
+ let nodes = layout.nodes(data);
+
+ // Normalize for fixed-depth.
+ nodes.forEach((d) => {
+ // position the child node horizontally
+ d.y = this.computeElementPosition(svgWidth)[d.depth];
+ });
+
+ let links = layout.links(nodes);
+
+ return [nodes, links];
+ };
+
+ /**
+ * Draw the containing group for any node or update the existing one
+ */
+ const drawNodes = (svg, nodes) => {
+ // Update the nodes…
+ var node = svg.selectAll('g.node')
+ .data(nodes, d => {
+ if(!angular.isString(d.d3Id)){
+ d.d3Id = `tree-${++i}`;
+ }
+ return d.d3Id;
+ });
+
+ // Enter any new nodes
+ var nodeEnter = node.enter().append('g')
+ .attr({
+ class: d => `node ${d.type}`,
+ transform: `translate(${svgWidth / 2}, ${svgHeight / 2})`
+ });
+
+ // create Nodes
+ NodeDrawer.addNetworks(node.filter('.network'));
+ NodeDrawer.addRack(node.filter('.rack'));
+ NodeDrawer.addPhisical(node.filter('.router'));
+ NodeDrawer.addPhisical(node.filter('.subscriber'));
+ NodeDrawer.addDevice(node.filter('.device'));
+
+ // add event listener to subscriber
+ node.filter('.subscriber')
+ .on('click', () => {
+ $rootScope.$emit('subscriber.modal.open');
+ });
+
+ //update nodes
+ // TODO if data change, only update them
+ // NodeDrawer.updateRack(node.filter('.rack'));
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ 'transform': d => `translate(${d.y},${d.x})`
+ });
+
+ // TODO handle node remove
+ var nodeExit = node.exit().remove();
+ };
+
+ /**
+ * Handle links in the tree layout
+ */
+ const drawLinks = (svg, links) => {
+
+ diagonal = d3.svg.diagonal()
+ .projection(d => [d.y, d.x]);
+
+ // Update the links…
+ var link = svg.selectAll('path.link')
+ .data(links, d => {
+ return d.target.d3Id
+ });
+
+ // Enter any new links at the parent's previous position.
+ link.enter().insert('path', 'g')
+ .attr('class', d => `link ${d.target.type}`)
+ .attr('d', function(d) {
+ var o = {x: svgHeight / 2, y: svgWidth / 2};
+ return diagonal({source: o, target: o});
+ });
+
+ // Transition links to their new position.
+ link.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('d', diagonal);
+
+ link.exit().remove();
+ };
+
+ /**
+ * Calculate the svg size and setup tree layout
+ */
+ this.setupTree = (svg) => {
+
+
+ svgWidth = svg.node().getBoundingClientRect().width;
+ svgHeight = svg.node().getBoundingClientRect().height;
+
+ const width = svgWidth - (serviceTopologyConfig.widthMargin * 2);
+ const height = svgHeight - (serviceTopologyConfig.heightMargin * 2);
+
+ layout = d3.layout.tree()
+ .size([height, width]);
+ };
+
+ /**
+ * Update the tree layout
+ */
+
+ this.updateTree = (svg) => {
+ // Compute the new tree layout.
+ [nodes, links] = computeLayout(baseData);
+
+ // console.log(baseData);
+ drawNodes(svg, nodes);
+ drawLinks(svg, links);
+ }
+
+ });
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/main.js b/views/ngXosViews/diagnostic/src/js/main.js
new file mode 100644
index 0000000..73cd3d9
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/main.js
@@ -0,0 +1,26 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic', [
+ 'ngResource',
+ 'ngCookies',
+ 'ngLodash',
+ 'ngAnimate',
+ 'ui.router',
+ 'xos.helpers'
+ ])
+ .config(($stateProvider) => {
+ $stateProvider
+ .state('home', {
+ url: '/',
+ template: '<diagnostic-container></diagnostic-container>'
+ });
+ })
+ .config(function($httpProvider){
+ $httpProvider.interceptors.push('NoHyperlinks');
+ })
+ .run(($log) => {
+ $log.info('Diagnostic Started');
+ });
+
+})();
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/nodeDrawer.js b/views/ngXosViews/diagnostic/src/js/nodeDrawer.js
new file mode 100644
index 0000000..4bb6c98
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/nodeDrawer.js
@@ -0,0 +1,496 @@
+(function () {
+ 'use strict';
+
+ const shapes = {
+ cloud: ' M 79.72 49.60 C 86.00 37.29 98.57 29.01 111.96 26.42 C 124.27 24.11 137.53 26.15 148.18 32.90 C 158.08 38.78 165.39 48.87 167.65 60.20 C 176.20 57.90 185.14 56.01 194.00 57.73 C 206.08 59.59 217.92 66.01 224.37 76.66 C 227.51 81.54 228.85 87.33 229.23 93.06 C 237.59 93.33 246.22 95.10 253.04 100.19 C 256.69 103.13 259.87 107.67 258.91 112.59 C 257.95 118.43 252.78 122.38 247.78 124.82 C 235.27 130.43 220.23 130.09 207.98 123.93 C 199.33 127.88 189.76 129.43 180.30 128.57 C 173.70 139.92 161.70 147.65 148.86 149.93 C 133.10 153.26 116.06 148.15 104.42 137.08 C 92.98 143.04 78.96 143.87 66.97 139.04 C 57.75 135.41 49.70 128.00 46.60 118.43 C 43.87 109.95 45.81 100.29 51.30 93.32 C 57.38 85.18 67.10 80.44 76.99 78.89 C 74.38 69.20 74.87 58.52 79.72 49.60 Z'
+ }
+
+ var computeNodeId = 0;
+ var instanceId = 0;
+
+ angular.module('xos.diagnostic')
+ .service('NodeDrawer', function(d3, serviceTopologyConfig, RackHelper, lodash){
+
+ var _this = this;
+
+ this.addNetworks = (nodes) => {
+ nodes.append('path')
+ .attr({
+ d: shapes.cloud,
+ transform: 'translate(-63, -52), scale(0.5)',
+ class: 'cloud'
+ });
+
+ nodes.append('text')
+ .attr({
+ 'text-anchor': 'middle'
+ })
+ .text(d => d.name)
+
+ nodes.each(function(n){
+ let currentNode = d3.select(this);
+ // cicle trouch node to add Tags and Public IP
+ if(n.name === 'LAN' && angular.isDefined(n.subscriberTag)){
+ currentNode.append('text')
+ .attr({
+ 'text-anchor': 'middle',
+ y: 40
+ })
+ .text(() => `C-Tag: ${n.subscriberTag.cTag}`);
+
+ currentNode.append('text')
+ .attr({
+ 'text-anchor': 'middle',
+ y: 60
+ })
+ .text(() => `S-Tag: ${n.subscriberTag.sTag}`);
+ }
+
+ if(n.name === 'WAN' && angular.isDefined(n.subscriberIP)){
+ currentNode.append('text')
+ .attr({
+ 'text-anchor': 'middle',
+ y: 40
+ })
+ .text(() => `Public IP: ${n.subscriberIP}`);
+ }
+ });
+ }
+
+ this.addRack = (nodes) => {
+
+ // loop because of D3
+ // rack will be only one
+ nodes.each(d => {
+ let [w, h] = RackHelper.getRackSize(d.computeNodes);
+
+ // TODO update instead of delete and redraw
+ nodes.select('g').remove();
+
+ let rack = nodes
+ .append('g');
+
+ rack
+ .attr({
+ transform: `translate(0,0)`
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ transform: () => `translate(${- (w / 2)}, ${- (h / 2)})`
+ });
+
+ rack
+ .append('rect')
+ .attr({
+ width: 0,
+ height: 0
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ width: w,
+ height: h
+ });
+
+ rack.append('text')
+ .attr({
+ 'text-anchor': 'middle',
+ y: - 10,
+ x: w / 2,
+ opacity: 0
+ })
+ .text(d => d.name)
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ })
+
+ this.drawComputeNodes(rack, d.computeNodes);
+
+ });
+
+ };
+
+ this.drawComputeNodes = (container, nodes) => {
+
+ let elements = container.selectAll('.compute-nodes')
+ .data(nodes, d => {
+ if(!angular.isString(d.d3Id)){
+ d.d3Id = `compute-node-${++computeNodeId}`;
+ }
+ return d.d3Id;
+ });
+
+ let {width, height} = container.node().getBoundingClientRect();
+
+ var nodeContainer = elements.enter().append('g');
+
+ nodeContainer
+ .attr({
+ transform: `translate(${width / 2}, ${ height / 2})`,
+ class: 'compute-node',
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ transform: (d) => `translate(${RackHelper.getComputeNodePosition(nodes, d.d3Id.replace('compute-node-', '') - 1)})`
+ });
+
+ nodeContainer.append('rect')
+ .attr({
+ width: 0,
+ height: 0
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ width: d => RackHelper.getComputeNodeSize(d.instances)[0],
+ height: d => RackHelper.getComputeNodeSize(d.instances)[1],
+ });
+
+ nodeContainer.append('text')
+ .attr({
+ 'text-anchor': 'start',
+ y: 15, //FIXME
+ x: 10, //FIXME
+ opacity: 0
+ })
+ .text(d => d.humanReadableName.split('.')[0])
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ })
+
+ // if there are Compute Nodes
+ if(nodeContainer.length > 0){
+ // draw instances for each compute node
+ nodeContainer.each(function(a){
+ _this.drawInstances(d3.select(this), a.instances);
+ })
+ }
+
+ };
+
+ // NOTE Stripping unuseful names to shorten labels.
+ // This is not elegant
+ const formatInstanceName = (name) => {
+ return name
+ .replace('app_', '')
+ .replace('service_', '')
+ // .replace('ovs_', '')
+ .replace('mysite_', '')
+ .replace('_instance', '');
+ };
+
+ const getInstanceStatusColor = (instance) => {
+ function startWith(val, string){
+ return string.substring(0, val.length) === val;
+ }
+
+ if(startWith('0 - ', instance.backend_status)){
+ return 'provisioning';
+ }
+ if(startWith('1 - ', instance.backend_status)){
+ return 'good';
+ }
+ if(startWith('2 - ', instance.backend_status)){
+ return 'bad';
+ }
+ else {
+ return '';
+ }
+ };
+
+ const drawContainer = (container, docker) => {
+
+ const containerBox = container.append('g')
+ .attr({
+ class: 'container',
+ transform: `translate(${serviceTopologyConfig.instance.margin}, 115)`
+ });
+
+ containerBox.append('rect')
+ .attr({
+ width: 250 - (serviceTopologyConfig.container.margin * 2),
+ height: serviceTopologyConfig.container.height,
+ });
+
+ containerBox.append('text')
+ .attr({
+ y: 20,
+ x: serviceTopologyConfig.instance.margin,
+ class: 'name'
+ })
+ .text(docker.name)
+
+ // add stats
+ const interestingMeters = ['memory', 'memory.usage', 'cpu_util'];
+
+ interestingMeters.forEach((m, i) => {
+ const meter = lodash.find(docker.stats, {meter: m});
+ // if there is no meter stats skip rendering
+ if(!angular.isDefined(meter)){
+ return;
+ }
+ containerBox.append('text')
+ .attr({
+ y: 40 + (i * 15),
+ x: serviceTopologyConfig.instance.margin,
+ opacity: 0
+ })
+ .text(`${meter.description}: ${Math.round(meter.value)} ${meter.unit}`)
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ });
+ });
+
+ // add port stats
+ const ports = ['eth0', 'eth1'];
+ const interestingPortMeters = [
+ {
+ meter: 'network.incoming.bytes.rate',
+ label: 'Incoming'
+ },
+ {
+ meter: 'network.outgoing.bytes.rate',
+ label: 'Outgoing'
+ }
+ ];
+
+ ports.forEach((p, j) => {
+
+ // if there are no port stats skip rendering
+ if(docker.port[p].length === 0){
+ return;
+ }
+
+ containerBox.append('text')
+ .attr({
+ y: 90,
+ x: serviceTopologyConfig.instance.margin + (120 * j),
+ class: 'name'
+ })
+ .text(`${docker.name}-${p}`)
+
+ interestingPortMeters.forEach((m, i) => {
+
+ const meter = lodash.find(docker.port[p], {meter: m.meter});
+ // if there is no meter stats skip rendering
+ if(!angular.isDefined(meter)){
+ return;
+ }
+ containerBox.append('text')
+ .attr({
+ y: 105 + (i * 15),
+ x: serviceTopologyConfig.instance.margin + (120 * j),
+ opacity: 0
+ })
+ .text(`${m.label}: ${Math.round(meter.value)} ${meter.unit}`)
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ });
+ });
+ });
+ }
+
+ const showInstanceStats = (container, instance) => {
+
+ // NOTE this should be dinamically positioned
+ // base on the number of element present
+ const statsContainer = container.append('g')
+ .attr({
+ transform: `translate(200, -120)`,
+ class: 'stats-container'
+ });
+
+
+ statsContainer.append('line')
+ .attr({
+ x1: -160,
+ y1: 120,
+ x2: 0,
+ y2: 50,
+ stroke: 'black',
+ opacity: 0
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ })
+
+ // NOTE rect should be dinamically sized base on the presence of a container
+ let statsHeight = 110;
+ let statsWidth = 250;
+
+ if (instance.container){
+ statsHeight += serviceTopologyConfig.container.height + (serviceTopologyConfig.container.margin * 2)
+ }
+
+ statsContainer.append('rect')
+ .attr({
+ width: statsWidth,
+ height: statsHeight,
+ opacity: 0
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ });
+
+ // add instance info
+ statsContainer.append('text')
+ .attr({
+ y: 15,
+ x: serviceTopologyConfig.instance.margin,
+ class: 'name',
+ opacity: 0
+ })
+ .text(instance.humanReadableName)
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ })
+
+ statsContainer.append('text')
+ .attr({
+ y: 30,
+ x: serviceTopologyConfig.instance.margin,
+ class: 'ip',
+ opacity: 0
+ })
+ .text(instance.ip)
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ })
+
+ // add stats
+ const interestingMeters = ['memory', 'memory.usage', 'cpu', 'vcpus'];
+
+ interestingMeters.forEach((m, i) => {
+ const meter = lodash.find(instance.stats, {meter: m});
+ statsContainer.append('text')
+ .attr({
+ y: 55 + (i * 15),
+ x: serviceTopologyConfig.instance.margin,
+ opacity: 0
+ })
+ .text(`${meter.description}: ${Math.round(meter.value)} ${meter.unit}`)
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ });
+ });
+
+ if(instance.container){
+ // draw container
+ drawContainer(statsContainer, instance.container);
+ }
+
+ };
+
+ this.drawInstances = (container, instances) => {
+
+ // TODO check for stats field in instance and draw popup
+
+ let {width, height} = container.node().getBoundingClientRect();
+
+ let elements = container.selectAll('.instances')
+ .data(instances, d => angular.isString(d.d3Id) ? d.d3Id : d.d3Id = `instance-${++instanceId}`)
+
+ var instanceContainer = elements.enter().append('g');
+
+ instanceContainer
+ .attr({
+ transform: `translate(${width / 2}, ${ height / 2})`,
+ class: d => `instance ${d.selected ? 'active' : ''} ${getInstanceStatusColor(d)}`,
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ transform: (d, i) => `translate(${RackHelper.getInstancePosition(i)})`
+ });
+
+ instanceContainer.append('rect')
+ .attr({
+ width: 0,
+ height: 0
+ })
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ width: serviceTopologyConfig.instance.width,
+ height: serviceTopologyConfig.instance.height
+ });
+
+ instanceContainer.append('text')
+ .attr({
+ 'text-anchor': 'middle',
+ y: 23, //FIXME
+ x: 40, //FIXME
+ opacity: 0
+ })
+ .text(d => formatInstanceName(d.humanReadableName))
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ opacity: 1
+ });
+
+ // if stats are attached and instance is active,
+ // draw stats
+ instanceContainer.each(function(instance, i){
+
+ const container = d3.select(this);
+
+ if(angular.isDefined(instance.stats) && instance.selected){
+ showInstanceStats(container, instance, i);
+ }
+ });
+
+ instanceContainer
+ .on('click', function(d){
+ console.log(`Draw vignette with stats for instance: ${d.name}`);
+ });
+ };
+
+ this.addPhisical = (nodes) => {
+ nodes.append('rect')
+ .attr(serviceTopologyConfig.square);
+
+ nodes.append('text')
+ .attr({
+ 'text-anchor': 'middle',
+ y: serviceTopologyConfig.square.y - 10
+ })
+ .text(d => {
+ return d.name || d.humanReadableName
+ });
+ }
+
+ this.addDevice = (nodes) => {
+ nodes.append('circle')
+ .attr(serviceTopologyConfig.circle);
+
+ nodes.append('text')
+ .attr({
+ 'text-anchor': 'end',
+ x: - serviceTopologyConfig.circle.r - 10,
+ y: serviceTopologyConfig.circle.r / 2
+ })
+ .text(d => d.name || d.mac);
+ }
+ });
+})();
diff --git a/views/ngXosViews/diagnostic/src/js/rackHelper.js b/views/ngXosViews/diagnostic/src/js/rackHelper.js
new file mode 100644
index 0000000..17eb0db
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/rackHelper.js
@@ -0,0 +1,85 @@
+(function () {
+ angular.module('xos.diagnostic')
+ .service('RackHelper', function(serviceTopologyConfig, lodash){
+
+ this.getComputeNodeLabelSize = () => {
+ return serviceTopologyConfig.computeNode.labelHeight + (serviceTopologyConfig.instance.margin * 2)
+ }
+
+ /**
+ * Given a list of instance should get the Compute Node size.
+ * They are placed in rows of 2 with 5px margin on each side.
+ */
+
+ this.getComputeNodeSize = lodash.memoize((instances) => {
+ const width = (serviceTopologyConfig.instance.margin * 3) + (serviceTopologyConfig.instance.width *2);
+
+ const rows = Math.round(instances.length / 2);
+
+ const labelSpace = this.getComputeNodeLabelSize();
+
+ const height = (serviceTopologyConfig.instance.height * rows) + (serviceTopologyConfig.instance.margin * (rows + 1)) + labelSpace;
+
+ return [width, height];
+ });
+
+ /**
+ * Give a list on Compute Node should calculate the Rack Size.
+ * Compute nodes are placed in a single column with 5px margin on each side.
+ */
+ this.getRackSize = (nodes) => {
+
+ let width = 0;
+ let height = serviceTopologyConfig.computeNode.margin;
+
+ lodash.forEach(nodes, (node) => {
+ let [nodeWidth, nodeHeight] = this.getComputeNodeSize(node.instances);
+
+ width = nodeWidth + (serviceTopologyConfig.computeNode.margin * 2);
+ height += (nodeHeight + serviceTopologyConfig.computeNode.margin);
+ });
+
+ return [width, height];
+ };
+
+ /**
+ * Given an instance index, return the coordinates
+ */
+
+ this.getInstancePosition = (position) => {
+ const row = Math.floor(position / 2);
+ const column = (position % 2) ? 1 : 0;
+
+ // add ComputeNode label size
+ const labelSpace = this.getComputeNodeLabelSize();
+
+ // x = margin + (width * column) + ( maring * column)
+ const x = serviceTopologyConfig.instance.margin + (serviceTopologyConfig.instance.width * column) + (serviceTopologyConfig.instance.margin * column);
+
+ // y = label + margin + (height * row) + ( maring * row)
+ const y = labelSpace + serviceTopologyConfig.instance.margin + (serviceTopologyConfig.instance.height * row) + (serviceTopologyConfig.instance.margin * row);
+ return [x, y];
+ };
+
+ /**
+ * Given an Compute Node index, return the coordinates
+ */
+
+ this.getComputeNodePosition = (nodes, position) => {
+
+ const x = serviceTopologyConfig.computeNode.margin;
+
+ let previousElEight = lodash.reduce(nodes.slice(0, position), (val, node) => {
+ return val + this.getComputeNodeSize(node.instances)[1]
+ }, 0);
+
+ const y =
+ serviceTopologyConfig.computeNode.margin
+ + (serviceTopologyConfig.computeNode.margin * position)
+ + previousElEight;
+
+ return [x, y];
+ };
+
+ });
+})();
diff --git a/views/ngXosViews/diagnostic/src/js/rest_services.js b/views/ngXosViews/diagnostic/src/js/rest_services.js
new file mode 100644
index 0000000..5677f30
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/rest_services.js
@@ -0,0 +1,449 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic')
+ .service('Services', function($resource){
+ return $resource('/xos/services/:id', {id: '@id'});
+ })
+ .service('Tenant', function($resource){
+ return $resource('/xos/tenants', {id: '@id'}, {
+ queryVsgInstances: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: (res) => {
+
+ // NOTE
+ // Note that VCPETenant is now VSGTenant.
+
+ let instances = [];
+
+ angular.forEach(res.data, (tenant) => {
+ let info = JSON.parse(tenant.service_specific_attribute);
+ if(info && info.instance_id){
+ instances.push(info.instance_id);
+ }
+ });
+
+ return instances;
+ }
+ }
+ },
+ getSubscriberTag: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: (res) => {
+ // NOTE we should receive only one vOLT tenant here
+ return JSON.parse(res.data[0].service_specific_attribute);
+ }
+ }
+ }
+ });
+ })
+ .service('Ceilometer', function($http, $q, Instances) {
+
+ /**
+ * Get stats for a single instance
+ */
+ this.getInstanceStats = (instanceUuid) => {
+ let deferred = $q.defer();
+
+ $http.get('/xoslib/xos-instance-statistics', {params: {'instance-uuid': instanceUuid}})
+ .then((res) => {
+ deferred.resolve(res.data);
+ })
+ .catch((e) => {
+ deferred.reject(e);
+ })
+
+ return deferred.promise;
+ };
+
+ /**
+ * Collect stats for an array of instances
+ */
+ this.getInstancesStats = (instances) => {
+ let deferred = $q.defer();
+ let instancePromises = [];
+ let instanceList = [];
+
+ // retrieve instance details
+ instances.forEach((instanceId) => {
+ instancePromises.push(Instances.get({id: instanceId}).$promise);
+ });
+
+ // get all instance data
+ $q.all(instancePromises)
+ .then((_instanceList) => {
+ instanceList = _instanceList;
+ let promises = [];
+ // foreach instance query stats
+ instanceList.forEach((instance) => {
+ promises.push(this.getInstanceStats(instance.instance_uuid));
+ });
+ return $q.all(promises);
+ })
+ .then(stats => {
+ // augment instance with stats information
+ instanceList.map((instance, i) => {
+ instance.stats = stats[i];
+ });
+ deferred.resolve(instanceList);
+ })
+ .catch(deferred.reject);
+
+ return deferred.promise;
+ };
+
+ this.getContainerStats = (containerName) => {
+ const deferred = $q.defer();
+
+ let res = {};
+
+ $http.get('/xoslib/meterstatistics', {params: {'resource': containerName}})
+ .then((containerStats) => {
+ res.stats = containerStats.data;
+ return $http.get('/xoslib/meterstatistics', {params: {'resource': `${containerName}-eth0`}})
+ })
+ .then((portStats) => {
+ res.port = {
+ eth0: portStats.data
+ };
+ return $http.get('/xoslib/meterstatistics', {params: {'resource': `${containerName}-eth1`}})
+ })
+ .then((portStats) => {
+ res.port.eth1 = portStats.data;
+ deferred.resolve(res);
+ })
+ .catch((e) => {
+ deferred.reject(e);
+ })
+
+ return deferred.promise;
+ }
+ })
+ .service('Slice', function($resource){
+ return $resource('/xos/slices', {id: '@id'});
+ })
+ .service('Instances', function($resource){
+ return $resource('/xos/instances/:id', {id: '@id'});
+ })
+ .service('Node', function($resource, $q, Instances){
+ return $resource('/xos/nodes', {id: '@id'}, {
+ queryWithInstances: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: function(res){
+
+ // TODO update the API to include instances in nodes
+ // http://stackoverflow.com/questions/14573102/how-do-i-include-related-model-fields-using-django-rest-framework
+
+ const deferred = $q.defer();
+
+ let requests = [];
+
+ angular.forEach(res.data, (node) => {
+ requests.push(Instances.query({node: node.id}).$promise);
+ })
+
+ $q.all(requests)
+ .then((list) => {
+ res.data.map((node, i) => {
+ node.instances = list[i];
+ return node;
+ });
+ deferred.resolve(res.data);
+ })
+
+ return deferred.promise;
+ }
+ }
+ }
+ });
+ })
+ .service('Subscribers', function($resource, $q, SubscriberDevice){
+ return $resource('/xoslib/cordsubscriber/:id', {id: '@id'}, {
+ update: {
+ method: 'PUT',
+ isArray: false
+ },
+ queryWithDevices: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: function(res){
+
+ /**
+ * For each subscriber retrieve devices and append them
+ */
+
+ let deferred = $q.defer();
+
+ let requests = [];
+
+ angular.forEach(res.data, (subscriber) => {
+ requests.push(SubscriberDevice.query({id: subscriber.id}).$promise);
+ })
+
+ $q.all(requests)
+ .then((list) => {
+
+ // adding devices
+
+ res.data.map((subscriber, i) => {
+ subscriber.devices = list[i];
+ subscriber.type = 'subscriber';
+
+ subscriber.devices.map(d => d.type = 'device')
+
+ return subscriber;
+ });
+
+ // faking to have 2 subscriber
+ // res.data.push(angular.copy(res.data[0]));
+
+ deferred.resolve(res.data);
+ })
+
+ return deferred.promise;
+ }
+ }
+ },
+ getWithDevices: {
+ method: 'GET',
+ isArray: false,
+ interceptor: {
+ response: (res) => {
+ let d = $q.defer();
+
+ SubscriberDevice.query({id: res.data.id}).$promise
+ .then(devices => {
+ devices.map(d => d.type = 'device');
+ res.data.devices = devices;
+ res.data.type = 'subscriber';
+ d.resolve(res.data);
+ })
+ .catch(err => {
+ d.reject(err);
+ });
+
+ return d.promise;
+ }
+ }
+ }
+ });
+ })
+ .service('SubscriberDevice', function($resource){
+ return $resource('/xoslib/rs/subscriber/:id/users/', {id: '@id'});
+ })
+ .service('ServiceRelation', function($q, lodash, Services, Tenant, Slice, Instances){
+
+ // count the mas depth of an object
+ const depthOf = (obj) => {
+ var depth = 0;
+ if (obj.children) {
+ obj.children.forEach(function (d) {
+ var tmpDepth = depthOf(d);
+ if (tmpDepth > depth) {
+ depth = tmpDepth
+ }
+ })
+ }
+ return 1 + depth
+ };
+
+ // find all the relation defined for a given root
+ const findLevelRelation = (tenants, rootId) => {
+ return lodash.filter(tenants, service => {
+ return service.subscriber_service === rootId;
+ });
+ };
+
+ const findSpecificInformation = (tenants, rootId) => {
+ var tenants = lodash.filter(tenants, service => {
+ return service.provider_service === rootId && service.subscriber_tenant;
+ });
+
+ var info;
+
+ tenants.forEach((tenant) => {
+ if(tenant.service_specific_attribute){
+ info = JSON.parse(tenant.service_specific_attribute);
+ }
+ });
+
+ return info;
+ };
+
+ // find all the service defined by a given array of relations
+ const findLevelServices = (relations, services) => {
+ const levelServices = [];
+ lodash.forEach(relations, (tenant) => {
+ var service = lodash.find(services, {id: tenant.provider_service});
+ levelServices.push(service);
+ });
+ return levelServices;
+ };
+
+ const buildLevel = (tenants, services, rootService, rootTenant, parentName = null) => {
+
+ // build an array of unlinked services
+ // these are the services that should still placed in the tree
+ var unlinkedServices = lodash.difference(services, [rootService]);
+
+ // find all relations relative to this rootElement
+ const levelRelation = findLevelRelation(tenants, rootService.id);
+ // find all items related to rootElement
+ const levelServices = findLevelServices(levelRelation, services);
+
+ // remove this item from the list (performance
+ unlinkedServices = lodash.difference(unlinkedServices, levelServices);
+
+ rootService.service_specific_attribute = findSpecificInformation(tenants, rootService.id);
+
+ const tree = {
+ name: rootService.humanReadableName,
+ parent: parentName,
+ type: 'service',
+ service: rootService,
+ tenant: rootTenant,
+ children: []
+ };
+
+ lodash.forEach(levelServices, (service) => {
+ let tenant = lodash.find(tenants, {subscriber_tenant: rootTenant.id, provider_service: service.id});
+ tree.children.push(buildLevel(tenants, unlinkedServices, service, tenant, rootService.humanReadableName));
+ });
+
+ // if it is the last element append internet
+ if(tree.children.length === 0){
+ tree.children.push({
+ name: 'Router',
+ type: 'router',
+ children: []
+ });
+ }
+
+ return tree;
+ };
+
+ const buildSubscriberServiceTree = (services, tenants, subscriber = {id: 1, name: 'fakeSubs'}) => {
+
+ // find the root service
+ // it is the one attached to subsriber_root
+ // as now we have only one root so this can work
+ const rootTenant = lodash.find(tenants, {subscriber_root: subscriber.id});
+ const rootService = lodash.find(services, {id: rootTenant.provider_service});
+
+ const serviceTree = buildLevel(tenants, services, rootService, rootTenant);
+
+ return {
+ name: subscriber.name || subscriber.humanReadableName,
+ parent: null,
+ type: 'subscriber',
+ children: [serviceTree]
+ };
+
+ };
+
+ // applying domain knowledge to build the global service tree
+ const buildServiceTree = (services, tenants) => {
+
+ // TODO refactor
+ const buildChild = (services, tenants, currentService) => {
+
+ const response = {
+ type: 'service',
+ name: currentService.humanReadableName,
+ service: currentService
+ };
+
+ let tenant = lodash.find(tenants, {subscriber_service: currentService.id});
+ if(tenant){
+ let next = lodash.find(services, {id: tenant.provider_service});
+ response.children = [buildChild(services, tenants, next)];
+ }
+ else {
+ response.children = [
+ {
+ name: 'Router',
+ type: 'router',
+ children: []
+ }
+ ]
+ }
+ delete currentService.id; // conflict with d3
+ return response;
+ }
+
+ let baseService = lodash.find(services, {id: 3});
+
+ if(!angular.isDefined(baseService)){
+ console.error('Missing Base service!');
+ return;
+ }
+
+ const baseData = {
+ name: 'Subscriber',
+ type: 'subscriber',
+ parent: null,
+ children: [buildChild(services, tenants, baseService)]
+ };
+ return baseData;
+ };
+
+ const getBySubscriber = (subscriber) => {
+ var deferred = $q.defer();
+ var services, tenants;
+ Services.query().$promise
+ .then((res) => {
+ services = res;
+ return Tenant.query().$promise;
+ })
+ .then((res) => {
+ tenants = res;
+ deferred.resolve(buildSubscriberServiceTree(services, tenants, subscriber));
+ })
+ .catch((e) => {
+ throw new Error(e);
+ });
+
+ return deferred.promise;
+ };
+
+ const get = () => {
+ var deferred = $q.defer();
+ var services, tenants;
+ Services.query().$promise
+ .then((res) => {
+ services = res;
+ return Tenant.query({kind: 'coarse'}).$promise;
+ })
+ .then((res) => {
+ tenants = res;
+ deferred.resolve(buildServiceTree(services, tenants));
+ })
+ .catch((e) => {
+ throw new Error(e);
+ });
+
+ return deferred.promise;
+ }
+
+ // export APIs
+ return {
+ get: get,
+ buildServiceTree: buildServiceTree,
+ getBySubscriber: getBySubscriber,
+ buildLevel: buildLevel,
+ buildSubscriberServiceTree: buildSubscriberServiceTree,
+ findLevelRelation: findLevelRelation,
+ findLevelServices: findLevelServices,
+ depthOf: depthOf,
+ findSpecificInformation: findSpecificInformation
+ }
+ });
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/serviceTopology.js b/views/ngXosViews/diagnostic/src/js/serviceTopology.js
new file mode 100644
index 0000000..925e7c0
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/serviceTopology.js
@@ -0,0 +1,69 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic')
+ .directive('serviceTopology', function(){
+ return {
+ restrict: 'E',
+ scope: {
+ serviceChain: '='
+ },
+ bindToController: true,
+ controllerAs: 'vm',
+ template: '',
+ controller: function($element, $window, $scope, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers, ServiceTopologyHelper){
+
+ const el = $element[0];
+
+ d3.select(window)
+ .on('resize', () => {
+ draw(this.serviceChain);
+ });
+
+ var root, svg;
+
+ const draw = (tree) => {
+
+ if(!tree){
+ console.error('Tree is missing');
+ return;
+ }
+
+ // TODO update instead clear and redraw
+
+ // clean
+ d3.select($element[0]).select('svg').remove();
+
+
+ const width = el.clientWidth - (serviceTopologyConfig.widthMargin * 2);
+ const height = el.clientHeight - (serviceTopologyConfig.heightMargin * 2);
+
+ const treeLayout = d3.layout.tree()
+ .size([height, width]);
+
+ svg = d3.select($element[0])
+ .append('svg')
+ .style('width', `${el.clientWidth}px`)
+ .style('height', `${el.clientHeight}px`)
+
+ const treeContainer = svg.append('g')
+ .attr('transform', `translate(${serviceTopologyConfig.widthMargin * 4},${serviceTopologyConfig.heightMargin})`);
+
+ root = tree;
+ root.x0 = height / 2;
+ root.y0 = width / 2;
+
+ // ServiceTopologyHelper.drawLegend(svg);
+ ServiceTopologyHelper.updateTree(treeContainer, treeLayout, root, el);
+ };
+
+ $scope.$watch(() => this.serviceChain, (chain) => {
+ if(angular.isDefined(chain)){
+ draw(chain);
+ }
+ });
+ }
+ }
+ });
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js b/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js
new file mode 100644
index 0000000..3759017
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js
@@ -0,0 +1,172 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic')
+ .service('ServiceTopologyHelper', function($rootScope, $window, $log, lodash, ServiceRelation, serviceTopologyConfig, d3){
+
+ var _svg, _layout, _source, _el;
+
+ var i = 0;
+
+ // given a canvas, a layout and a data source, draw a tree layout
+ const updateTree = (svg, layout, source, el = _el) => {
+
+ if(el){
+ _el = el;
+ }
+
+ //cache data
+ _svg = svg;
+ _layout = layout;
+ _source = source;
+
+ const maxDepth = ServiceRelation.depthOf(source);
+
+ const diagonal = d3.svg.diagonal()
+ .projection(d => [d.y, d.x]);
+
+ // Compute the new tree layout.
+ var nodes = layout.nodes(source).reverse(),
+ links = layout.links(nodes);
+
+ // Normalize for fixed-depth.
+ nodes.forEach(function(d) {
+ // position the child node horizontally
+ const step = ((_el.clientWidth - (serviceTopologyConfig.widthMargin * 2)) / maxDepth);
+ d.y = d.depth * step;
+ });
+
+ // Update the nodes…
+ var node = svg.selectAll('g.node')
+ .data(nodes, function(d) { return d.id || (d.id = ++i); });
+
+ // Enter any new nodes at the parent's previous position.
+ var nodeEnter = node.enter().append('g')
+ .attr({
+ class: d => {
+ return `node ${d.type}`
+ },
+ transform: d => (d.x && d.y) ? `translate(${d.y}, ${d.x})` : `translate(${source.y0}, ${source.x0})`
+ });
+
+ const subscriberNodes = nodeEnter.filter('.subscriber');
+ const internetNodes = nodeEnter.filter('.router');
+ const serviceNodes = nodeEnter.filter('.service');
+
+ subscriberNodes.append('rect')
+ .attr(serviceTopologyConfig.square)
+ // add event listener to subscriber
+ .on('click', () => {
+ $rootScope.$emit('subscriber.modal.open');
+ });
+
+ internetNodes.append('rect')
+ .attr(serviceTopologyConfig.square);
+
+ serviceNodes.append('circle')
+ .attr('r', 1e-6)
+ .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
+ .on('click', serviceClick);
+
+ nodeEnter.append('text')
+ .attr({
+ x: d => d.children ? -serviceTopologyConfig.circle.selectedRadius -3 : serviceTopologyConfig.circle.selectedRadius + 3,
+ dy: '.35em',
+ transform: d => {
+ if (d.children && d.parent){
+ if(d.parent.x < d.x){
+ return 'rotate(-30)';
+ }
+ return 'rotate(30)';
+ }
+ },
+ 'text-anchor': d => d.children ? 'end' : 'start'
+ })
+ .text(d => d.name)
+ .style('fill-opacity', 1e-6);
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ 'transform': d => `translate(${d.y},${d.x})`
+ });
+
+ nodeUpdate.select('circle')
+ .attr('r', d => {
+ return d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius
+ })
+ .style('fill', d => d.selected ? 'lightsteelblue' : '#fff');
+
+ nodeUpdate.select('text')
+ .style('fill-opacity', 1);
+
+ // Transition exiting nodes to the parent's new position.
+ var nodeExit = node.exit().transition()
+ .duration(serviceTopologyConfig.duration)
+ .remove();
+
+ nodeExit.select('circle')
+ .attr('r', 1e-6);
+
+ nodeExit.select('text')
+ .style('fill-opacity', 1e-6);
+
+ // Update the links…
+ var link = svg.selectAll('path.link')
+ .data(links, function(d) { return d.target.id; });
+
+ // Enter any new links at the parent's previous position.
+ link.enter().insert('path', 'g')
+ .attr('class', d => `link ${d.target.type} ${d.target.active ? '' : 'active'}`)
+ .attr('d', function(d) {
+ var o = {x: source.x0, y: source.y0};
+ return diagonal({source: o, target: o});
+ });
+
+ // Transition links to their new position.
+ link.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('d', diagonal);
+
+ // Transition exiting nodes to the parent's new position.
+ link.exit().transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('d', function(d) {
+ var o = {x: source.x, y: source.y};
+ return diagonal({source: o, target: o});
+ })
+ .remove();
+
+ // Stash the old positions for transition.
+ nodes.forEach(function(d) {
+ d.x0 = d.x;
+ d.y0 = d.y;
+ });
+ };
+
+ const serviceClick = function(d) {
+
+ // if was selected
+ if(d.selected){
+ d.selected = !d.selected;
+ $rootScope.$emit('instance.detail.hide', {});
+ return updateTree(_svg, _layout, _source);
+ }
+
+ $rootScope.$emit('instance.detail', {name: d.name, service: d.service, tenant: d.tenant});
+
+ // unselect all
+ _svg.selectAll('circle')
+ .each(d => d.selected = false);
+
+ // toggling selected status
+ d.selected = !d.selected;
+
+ updateTree(_svg, _layout, _source);
+ };
+
+ this.updateTree = updateTree;
+ });
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/subscriber-modal.js b/views/ngXosViews/diagnostic/src/js/subscriber-modal.js
new file mode 100644
index 0000000..74bb8a7
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/subscriber-modal.js
@@ -0,0 +1,66 @@
+(function () {
+ 'use strict';
+ angular.module('xos.diagnostic')
+ .directive('selectSubscriberModal', function(){
+ return {
+ scope: {
+ subscribers: '=',
+ open: '='
+ },
+ bindToController: true,
+ restrict: 'E',
+ templateUrl: 'templates/select-subscriber-modal.tpl.html',
+ controllerAs: 'vm',
+ controller: function($rootScope){
+
+ this.close = () => {
+ this.open = false;
+ };
+
+ this.select = (subscriber) => {
+ $rootScope.$emit('subscriber.selected', subscriber);
+ this.close();
+ };
+ }
+ };
+ })
+ .directive('subscriberStatusModal', function(){
+ return {
+ scope: {
+ open: '=',
+ subscriber: '='
+ },
+ bindToController: true,
+ restrict: 'E',
+ templateUrl: 'templates/subscriber-status-modal.tpl.html',
+ controllerAs: 'vm',
+ controller: function($log, $timeout, $scope, Subscribers){
+
+ $scope.$watch(() => this.open, () => {
+ this.success = null;
+ this.formError = null;
+ });
+
+ this.close = () => {
+ this.open = false;
+ };
+
+ this.updateSubscriber = (subscriber) => {
+
+ Subscribers.update(subscriber).$promise
+ .then(() => {
+ this.success = 'Subscriber successfully updated!';
+ })
+ .catch((e) => {
+ this.formError = e;
+ })
+ .finally(() => {
+ $timeout(() => {
+ this.close();
+ }, 1500);
+ });
+ };
+ }
+ };
+ });
+})();
diff --git a/views/ngXosViews/diagnostic/src/templates/diagnostic.tpl.html b/views/ngXosViews/diagnostic/src/templates/diagnostic.tpl.html
new file mode 100644
index 0000000..25e1338
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/templates/diagnostic.tpl.html
@@ -0,0 +1,22 @@
+<div class="container-fluid">
+ <div ng-hide="vm.error && vm.loader" style="height: 900px">
+ <div class="onethird-height">
+ <service-topology service-chain="vm.serviceChain"></service-topology>
+ </div>
+ <div class="twothird-height">
+ <logic-topology ng-if="vm.subscribers" subscribers="vm.subscribers" selected="vm.selectedSubscriber"></logic-topology>
+ </div>
+ </div>
+ <div class="row" ng-if="vm.error">
+ <div class="col-xs-12">
+ <div class="alert alert-danger">
+ {{vm.error}}
+ </div>
+ </div>
+ </div>
+ <div class="row" ng-if="vm.loader">
+ <div class="col-xs-12">
+ <div class="loader">Loading</div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/templates/logicTopology.tpl.html b/views/ngXosViews/diagnostic/src/templates/logicTopology.tpl.html
new file mode 100644
index 0000000..8c81015
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/templates/logicTopology.tpl.html
@@ -0,0 +1,24 @@
+<select-subscriber-modal open="vm.openSelectSubscriberModal" subscribers="vm.subscribers"></select-subscriber-modal>
+<subscriber-status-modal open="vm.openSubscriberStatusModal" subscriber="vm.currentSubscriber"></subscriber-status-modal>
+<div class="instances-stats animate" ng-hide="vm.hideInstanceStats">
+ <div class="row">
+ <div class="col-sm-3 col-sm-offset-8">
+ <div class="panel panel-primary" ng-repeat="instance in vm.selectedInstances">
+ <div class="panel-heading">
+ {{instance.humanReadableName}}
+ </div>
+ <ul class="list-group">
+ <li class="list-group-item">Backend Status: {{instance.backend_status}}</li>
+ <li class="list-group-item">IP Address: {{instance.ip}}</li>
+ </ul>
+ <ul class="list-group">
+ <li class="list-group-item" ng-repeat="stat in instance.stats">
+ <span class="badge">{{stat.value}}</span>
+ {{stat.meter}}
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/templates/select-subscriber-modal.tpl.html b/views/ngXosViews/diagnostic/src/templates/select-subscriber-modal.tpl.html
new file mode 100644
index 0000000..7672a29
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/templates/select-subscriber-modal.tpl.html
@@ -0,0 +1,17 @@
+<div class="modal fade" ng-class="{in: vm.open}" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-sm">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button ng-click="vm.close()" type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 class="modal-title">Select a subscriber:</h4>
+ </div>
+ <div class="modal-body">
+ <select class="form-control" ng-options="s as s.humanReadableName for s in vm.subscribers" ng-model="vm.selected"></select>
+ </div>
+ <div class="modal-footer">
+ <button ng-click="vm.close()" type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+ <button ng-click="vm.select(vm.selected)" type="button" class="btn btn-primary">Select</button>
+ </div>
+ </div><!-- /.modal-content -->
+ </div><!-- /.modal-dialog -->
+</div><!-- /.modal -->
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/templates/subscriber-status-modal.tpl.html b/views/ngXosViews/diagnostic/src/templates/subscriber-status-modal.tpl.html
new file mode 100644
index 0000000..7f87999
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/templates/subscriber-status-modal.tpl.html
@@ -0,0 +1,81 @@
+<div class="modal fade" ng-class="{in: vm.open}" tabindex="-1" role="dialog">
+ <div class="modal-dialog modal-sm">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button ng-click="vm.close()" type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 class="modal-title">Manage subscriber:</h4>
+ </div>
+ <form name="vm.subscriber-detail">
+ <div class="modal-body">
+ <div class="row">
+ <div class="col-xs-12">
+ <label>Status</label>
+ </div>
+ <div class="col-xs-6">
+ <a ng-click="vm.subscriber.status = 'enabled'"
+ class="btn btn-block"
+ ng-class="{'btn-primary': vm.subscriber.status === 'enabled' ,'btn-default': vm.subscriber.status !== 'enabled'}"
+ >Enabled</a>
+ </div>
+ <div class="col-xs-6">
+ <a ng-click="vm.subscriber.status = 'suspended'"
+ class="btn btn-block"
+ ng-class="{'btn-primary': vm.subscriber.status === 'suspended' ,'btn-default': vm.subscriber.status !== 'suspended'}"
+ >Suspended</a>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-6">
+ <a ng-click="vm.subscriber.status = 'delinquent'"
+ class="btn btn-block"
+ ng-class="{'btn-primary': vm.subscriber.status === 'delinquent' ,'btn-default': vm.subscriber.status !== 'delinquent'}"
+ >Delinquent <br> payment</a>
+ </div>
+ <div class="col-xs-6">
+ <a ng-click="vm.subscriber.status = 'copyrightviolation'"
+ class="btn btn-block"
+ ng-class="{'btn-primary': vm.subscriber.status === 'copyrightviolation' ,'btn-default': vm.subscriber.status !== 'copyrightviolation'}"
+ >Copyright <br> violation</a>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-6">
+ <label>Uplink Speed</label>
+ <input type="number" class="form-control" ng-model="vm.subscriber.uplink_speed"/>
+ </div>
+ <div class="col-xs-6">
+ <label>Downlink Speed</label>
+ <input type="number" class="form-control" ng-model="vm.subscriber.downlink_speed"/>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-xs-6">
+ <label>Enable Uverse</label>
+ </div>
+ <div class="col-xs-6">
+ <a
+ ng-click="vm.subscriber.enable_uverse = !vm.subscriber.enable_uverse"
+ ng-class="{'btn-success': vm.subscriber.enable_uverse, 'btn-danger': !vm.subscriber.enable_uverse}"
+ class="btn btn-block">
+ <span ng-show="vm.subscriber.enable_uverse === true">Enabled</span>
+ <span ng-show="vm.subscriber.enable_uverse !== true">Disabled</span>
+ </a>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer" ng-show="vm.success || vm.formError">
+ <div class="alert alert-success" ng-show="vm.success">
+ {{vm.success}}
+ </div>
+ <div class="alert alert-danger" ng-show="vm.formError">
+ {{vm.formError}}
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button ng-click="vm.close()" type="button" class="btn btn-default" data-dismiss="modal">Close</button>
+ <button ng-click="vm.updateSubscriber(vm.subscriber)" type="button" class="btn btn-primary">Save</button>
+ </div>
+ </form>
+ </div><!-- /.modal-content -->
+ </div><!-- /.modal-dialog -->
+</div><!-- /.modal -->
\ No newline at end of file
diff --git a/xos/configurations/cord/ceilometer.yaml b/xos/configurations/cord/ceilometer.yaml
index 6fcd132..4747fed 100644
--- a/xos/configurations/cord/ceilometer.yaml
+++ b/xos/configurations/cord/ceilometer.yaml
@@ -223,6 +223,12 @@
type: tosca.nodes.DashboardView
properties:
url: template:xosCeilometerDashboard
+
+ Diagnostic:
+ type: tosca.nodes.DashboardView
+ properties:
+ url: template:xosDiagnostic
+
Tenant:
type: tosca.nodes.DashboardView
properties:
@@ -243,3 +249,6 @@
- ceilometer_dashboard:
node: Ceilometer
relationship: tosca.relationships.UsesDashboard
+ - diagnostic_dashboard:
+ node: Diagnostic
+ relationship: tosca.relationships.UsesDashboard
diff --git a/xos/configurations/frontend/mocks/cord.yaml b/xos/configurations/frontend/mocks/cord.yaml
index 8c84d8f..d39f43d 100644
--- a/xos/configurations/frontend/mocks/cord.yaml
+++ b/xos/configurations/frontend/mocks/cord.yaml
@@ -532,3 +532,20 @@
- subscriber:
node: My House
relationship: tosca.relationships.BelongsToSubscriber
+
+ # DASHBOARDS
+ Diagnostic:
+ type: tosca.nodes.DashboardView
+ properties:
+ url: template:xosDiagnostic
+
+ padmin@vicci.org:
+ type: tosca.nodes.User
+ properties:
+ firstname: XOS
+ lastname: admin
+ is_admin: true
+ requirements:
+ - diagnostic_dashboard:
+ node: Diagnostic
+ relationship: tosca.relationships.UsesDashboard
diff --git a/xos/configurations/frontend/service_chain.yaml b/xos/configurations/frontend/service_chain.yaml
new file mode 100644
index 0000000..557f98e
--- /dev/null
+++ b/xos/configurations/frontend/service_chain.yaml
@@ -0,0 +1,204 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup two subscriber with related service chain, use for development of serviceTopology view.
+
+imports:
+ - custom_types/xos.yaml
+
+topology_template:
+ node_templates:
+ # CORD Subscribers
+ Night's Watch:
+ type: tosca.nodes.CORDSubscriber
+ properties:
+ service_specific_id: 123
+ firewall_enable: false
+ cdn_enable: false
+ url_filter_enable: false
+ url_filter_level: R
+
+ # CORD Users for Night's Watch
+ Jhon Snow:
+ type: tosca.nodes.CORDUser
+ properties:
+ mac: 01:02:03:04:05:06
+ level: PG_13
+ requirements:
+ - household:
+ node: Night's Watch
+ relationship: tosca.relationships.SubscriberDevice
+
+ House Targaryen:
+ type: tosca.nodes.CORDSubscriber
+ properties:
+ service_specific_id: 321
+ firewall_enable: false
+ cdn_enable: false
+ url_filter_enable: false
+ url_filter_level: R
+
+ # CORD Users for House Targaryen
+ Daenerys:
+ type: tosca.nodes.CORDUser
+ properties:
+ mac: 06:05:04:03:02:01
+ level: PG_13
+ requirements:
+ - household:
+ node: House Targaryen
+ relationship: tosca.relationships.SubscriberDevice
+
+ # vOLT Tenants
+ Night's Watch vOLT:
+ type: tosca.nodes.VOLTTenant
+ properties:
+ service_specific_id: 123
+ s_tag: 123
+ c_tag: 456
+ requirements:
+ - provider_service:
+ node: service_volt
+ relationship: tosca.relationships.MemberOfService
+ - subscriber:
+ node: Night's Watch
+ relationship: tosca.relationships.BelongsToSubscriber
+
+ Targaryen vOLT:
+ type: tosca.nodes.VOLTTenant
+ properties:
+ service_specific_id: 321
+ s_tag: 321
+ c_tag: 654
+ requirements:
+ - provider_service:
+ node: service_volt
+ relationship: tosca.relationships.MemberOfService
+ - subscriber:
+ node: House Targaryen
+ relationship: tosca.relationships.BelongsToSubscriber
+
+ # CORD Services
+ service_volt:
+ type: tosca.nodes.Service
+ requirements:
+ - vcpe_tenant:
+ node: service_vcpe
+ relationship: tosca.relationships.TenantOfService
+ - lan_network:
+ node: lan_network
+ relationship: tosca.relationships.UsesNetwork
+ - wan_network:
+ node: wan_network
+ relationship: tosca.relationships.UsesNetwork
+ properties:
+ view_url: /admin/cord/voltservice/$id$/
+ kind: vOLT
+
+ service_vcpe:
+ type: tosca.nodes.VCPEService
+ requirements:
+ - vbng_tenant:
+ node: service_vbng
+ relationship: tosca.relationships.TenantOfService
+ properties:
+ view_url: /admin/cord/vcpeservice/$id$/
+ backend_network_label: hpc_client
+ public_key: { get_artifact: [ SELF, pubkey, LOCAL_FILE] }
+ private_key_fn: /opt/xos/observers/vcpe/vcpe_private_key
+ artifacts:
+ pubkey: /root/.ssh/id_rsa.pub #is this right?
+
+ service_vbng:
+ type: tosca.nodes.VBNGService
+ properties:
+ view_url: /admin/cord/vbngservice/$id$/
+
+ # Networks required
+ lan_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+ - connection:
+ node: mysite_volt
+ relationship: tosca.relationships.ConnectsToSlice
+
+ wan_network:
+ type: tosca.nodes.network.Network
+ properties:
+ ip_version: 4
+ requirements:
+ - network_template:
+ node: Private
+ relationship: tosca.relationships.UsesNetworkTemplate
+ - owner:
+ node: mysite_vcpe
+ relationship: tosca.relationships.MemberOfSlice
+ - connection:
+ node: mysite_vcpe
+ relationship: tosca.relationships.ConnectsToSlice
+ - connection:
+ node: mysite_vbng
+ relationship: tosca.relationships.ConnectsToSlice
+
+ # Network templates
+ Private:
+ type: tosca.nodes.NetworkTemplate
+
+ # Sites
+ mysite:
+ type: tosca.nodes.Site
+
+ # Slices
+ mysite_vcpe:
+ description: vCPE Controller Slice
+ type: tosca.nodes.Slice
+ requirements:
+ - vcpe_service:
+ node: service_vcpe
+ relationship: tosca.relationships.MemberOfService
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+ - vcpe_docker_image:
+ node: docker-vcpe
+ relationship: tosca.relationships.UsesImage
+ properties:
+ default_isolation: container
+
+ mysite_vbng:
+ description: slice running OVS controlled by vBNG
+ type: tosca.nodes.Slice
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ mysite_volt:
+ description: OVS controlled by vOLT
+ type: tosca.nodes.Slice
+ requirements:
+ - site:
+ node: mysite
+ relationship: tosca.relationships.MemberOfSite
+
+ # docker image for vcpe containers
+ docker-vcpe:
+ # TODO: need to attach this to mydeployment
+ type: tosca.nodes.Image
+ properties:
+ kind: container
+ container_format: na
+ disk_format: na
+ path: andybavier/docker-vcpe
+ tag: develop
+
diff --git a/xos/core/static/xos.css b/xos/core/static/xos.css
index 592bb7c..d00e625 100644
--- a/xos/core/static/xos.css
+++ b/xos/core/static/xos.css
@@ -23,9 +23,9 @@
overflow-x: hidden;
}
-/*#wrap {
+#wrap {
min-height: 100%;
-}*/
+}
/* ************************* LOGIN PAGE ************************* */
@@ -44,6 +44,7 @@
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
+ min-height: 900px;
}
#wrapper.toggled {
@@ -74,6 +75,15 @@
width: 100%;
position: absolute;
padding: 15px;
+ min-height: 900px;
+}
+
+#page-content-wrapper .container-fluid {
+ min-height: 900px;
+}
+
+.ui-tabs-panel {
+ min-height: 700px;
}
#wrapper.toggled #page-content-wrapper {
diff --git a/xos/core/xoslib/dashboards/xosDiagnostic.html b/xos/core/xoslib/dashboards/xosDiagnostic.html
new file mode 100644
index 0000000..f988d9a
--- /dev/null
+++ b/xos/core/xoslib/dashboards/xosDiagnostic.html
@@ -0,0 +1,15 @@
+<!-- browserSync -->
+
+<!-- inject:css -->
+<link rel="stylesheet" href="/static/css/xosDiagnostic.css">
+<!-- endinject -->
+
+<div id="xosDiagnostic">
+ <div ui-view></div>
+</div>
+
+
+<!-- inject:js -->
+<script src="/static/js/vendor/xosDiagnosticVendor.js"></script>
+<script src="/static/js/xosDiagnostic.js"></script>
+<!-- endinject -->
diff --git a/xos/core/xoslib/static/css/xosDiagnostic.css b/xos/core/xoslib/static/css/xosDiagnostic.css
new file mode 100644
index 0000000..0debce0
--- /dev/null
+++ b/xos/core/xoslib/static/css/xosDiagnostic.css
@@ -0,0 +1 @@
+#xosDiagnostic,[ui-view]{min-height:700px;position:relative}diagnostic-container .half-height{position:relative;height:50%}diagnostic-container .onethird-height{position:relative;height:33%}diagnostic-container .twothird-height{position:relative;height:67%}diagnostic-container .subscriber-select{max-width:200px;position:absolute;top:20px;right:20px;z-index:1}subscriber-status-modal .row+.row{margin-top:20px}.half-height+.half-height{border-top:1px solid black}service-topology,logic-topology{height:100%;width:100%;display:block;position:absolute;top:0}logic-topology .subscriber circle,logic-topology .device circle{fill:#fff;stroke:green;stroke-width:1px}logic-topology>svg{position:absolute;top:0}logic-topology .network .cloud{fill:#fff;stroke:green;stroke-width:1px}logic-topology .node.rack>g>rect{fill:#ccc;stroke:steelblue;stroke-width:1px}logic-topology .compute-node>rect{fill:#fff;stroke:steelblue;stroke-width:1px}logic-topology .instance>rect{fill:#eee;stroke:steelblue;stroke-width:1px}logic-topology .node .instance.active rect{fill:lightsteelblue;stroke:steelblue;stroke-width:1px}logic-topology .node .instance.active.good>rect{fill:green}logic-topology .node .instance.active.provisioning>rect{fill:yellow}logic-topology .node .instance.active.bad>rect{fill:red}logic-topology .node .instance .stats-container rect{fill:white}logic-topology .node .instance .stats-container text.name{font-weight:700}logic-topology .node .instance .stats-container text.ip{font-style:italic;font-size:10px}logic-topology .node .instance .stats-container .container rect{fill:#eee;stroke:steelblue;stroke-width:1px}.legend{fill:#fff;stroke:#ccc;stroke-width:1px;position:relative}.legend text{stroke:#000}.node{cursor:pointer}.node circle,.node rect{fill:#fff;stroke:steelblue;stroke-width:1px}.node.subscriber circle,.node.subscriber rect,.node.router circle,.node.router rect{stroke:#05ffcb}.node.slice rect{stroke:#b01dff}.node.instance rect{stroke:#ccc}.node.instance rect.active{stroke:#ff8b00}.node rect.slice-detail{fill:#fff;stroke:steelblue;stroke-width:3px}.node text{font:12px sans-serif}.link,.device-link{fill:none;stroke:#ccc;stroke-width:2px}.link.slice{stroke:rgba(157,4,183,.29)}.link.instance{stroke:#ccc}.link.instance.active{stroke:rgba(255,138,0,.65)}.service-details{width:200px;position:absolute;top:20px;right:20px}.animate.ng-hide-remove{animation:.5s bounceInRight ease}.animate.ng-hide-add{animation:.5s bounceOutRight ease}.loader{font-size:10px;margin:150px auto;text-indent:-9999em;width:11em;height:11em;border-radius:50%;background:#fff;background:linear-gradient(to right,#fff 10%,rgba(255,255,255,0) 42%);position:relative;animation:load3 1.4s infinite linear;transform:translateZ(0)}.loader:before{width:50%;height:50%;background:#105e9e;border-radius:100% 0 0;position:absolute;top:0;left:0;content:''}.loader:after{background:#fff;width:75%;height:75%;border-radius:50%;content:'';margin:auto;position:absolute;top:0;left:0;bottom:0;right:0}@keyframes load3{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.modal.fade.in{display:block}@keyframes bounceInRight{from,60%,75%,90%,to{animation-timing-function:cubic-bezier(.215,.61,.355,1.000)}from{opacity:0;transform:translate3d(3000px,0,0)}60%{opacity:1;transform:translate3d(-25px,0,0)}75%{transform:translate3d(10px,0,0)}90%{transform:translate3d(-5px,0,0)}to{transform:none}}@keyframes bounceOutRight{20%{opacity:1;transform:translate3d(-20px,0,0)}to{opacity:0;transform:translate3d(2000px,0,0)}}
\ No newline at end of file
diff --git a/xos/core/xoslib/static/js/vendor/xosDiagnosticVendor.js b/xos/core/xoslib/static/js/vendor/xosDiagnosticVendor.js
new file mode 100644
index 0000000..9ef6d6d
--- /dev/null
+++ b/xos/core/xoslib/static/js/vendor/xosDiagnosticVendor.js
@@ -0,0 +1,8 @@
+!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 r(n,t){return t>n?-1:n>t?1:n>=t?0:NaN}function e(n){return null===n?NaN:+n}function i(n){return!isNaN(n)}function u(n){return{left:function(t,r,e,i){for(arguments.length<3&&(e=0),arguments.length<4&&(i=t.length);i>e;){var u=e+i>>>1;n(t[u],r)<0?e=u+1:i=u}return e},right:function(t,r,e,i){for(arguments.length<3&&(e=0),arguments.length<4&&(i=t.length);i>e;){var u=e+i>>>1;n(t[u],r)>0?i=u:e=u+1}return e}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var r in t)Object.defineProperty(n.prototype,r,{value:t[r],enumerable:!1})}function l(){this._=Object.create(null)}function f(n){return(n+="")===Mo||n[0]===bo?bo+n:n}function s(n){return(n+="")[0]===bo?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 v(){var n=[];for(var t in this._)n.push(s(t));return n}function g(){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 _(n,t,r){return function(){var e=r.apply(t,arguments);return e===t?n:e}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var r=0,e=wo.length;e>r;++r){var i=wo[r]+t;if(i in n)return i}}function M(){}function b(){}function w(n){function t(){for(var t,e=r,i=-1,u=e.length;++i<u;)(t=e[i].on)&&t.apply(this,arguments);return n}var r=[],e=new l;return t.on=function(t,i){var u,o=e.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,r=r.slice(0,u=r.indexOf(o)).concat(r.slice(u+1)),e.remove(t)),i&&r.push(e.set(t,{on:i})),n)},t}function k(){ao.event.preventDefault()}function C(){for(var n,t=ao.event;n=t.sourceEvent;)t=n;return t}function A(n){for(var t=new b,r=0,e=arguments.length;++r<e;)t[arguments[r]]=w(t);return t.of=function(r,e){return function(i){try{var u=i.sourceEvent=ao.event;i.target=n,ao.event=i,t[i.type].apply(r,e)}finally{ao.event=u}}},t}function S(n){return Co(n,No),n}function E(n){return"function"==typeof n?n:function(){return Ao(n,this)}}function N(n){return"function"==typeof n?n:function(){return So(n,this)}}function $(n,t){function r(){this.removeAttribute(n)}function e(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function o(){var r=t.apply(this,arguments);null==r?this.removeAttribute(n):this.setAttribute(n,r)}function a(){var r=t.apply(this,arguments);null==r?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,r)}return n=ao.ns.qualify(n),null==t?n.local?e:r:"function"==typeof t?n.local?a:o:n.local?u:i}function j(n){return n.trim().replace(/\s+/g," ")}function D(n){return new RegExp("(?:^|\\s+)"+ao.requote(n)+"(?:\\s+|$)","g")}function T(n){return(n+"").trim().split(/^|\s+/)}function L(n,t){function r(){for(var r=-1;++r<i;)n[r](this,t)}function e(){for(var r=-1,e=t.apply(this,arguments);++r<i;)n[r](this,e)}n=T(n).map(R);var i=n.length;return"function"==typeof t?e:r}function R(n){var t=D(n);return function(r,e){if(i=r.classList)return e?i.add(n):i.remove(n);var i=r.getAttribute("class")||"";e?(t.lastIndex=0,t.test(i)||r.setAttribute("class",j(i+" "+n))):r.setAttribute("class",j(i.replace(t," ")))}}function O(n,t,r){function e(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,r)}function u(){var e=t.apply(this,arguments);null==e?this.style.removeProperty(n):this.style.setProperty(n,e,r)}return null==t?e:"function"==typeof t?u:i}function z(n,t){function r(){delete this[n]}function e(){this[n]=t}function i(){var r=t.apply(this,arguments);null==r?delete this[n]:this[n]=r}return null==t?r:"function"==typeof t?i:e}function q(n){function t(){var t=this.ownerDocument,r=this.namespaceURI;return r&&r!==t.documentElement.namespaceURI?t.createElementNS(r,n):t.createElement(n)}function r(){return this.ownerDocument.createElementNS(n.space,n.local)}return"function"==typeof n?n:(n=ao.ns.qualify(n)).local?r:t}function I(){var n=this.parentNode;n&&n.removeChild(this)}function P(n){return{__data__:n}}function U(n){return function(){return Eo(this,n)}}function F(n){return arguments.length||(n=r),function(t,r){return t&&r?n(t.__data__,r.__data__):!t-!r}}function H(n,t){for(var r=0,e=n.length;e>r;r++)for(var i,u=n[r],o=0,a=u.length;a>o;o++)(i=u[o])&&t(i,o,r);return n}function W(n){return Co(n,jo),n}function B(n){var t,r;return function(e,i,u){var o,a=n[u].update,c=a.length;for(u!=r&&(r=u,t=0),i>=t&&(t=i+1);!(o=a[t])&&++t<c;);return o}}function Y(n,t,r){function e(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function i(){var i=c(t,lo(arguments));e.call(this),this.addEventListener(n,this[o]=i,i.$=r),i._=t}function u(){var t,r=new RegExp("^__on([^.]+)"+ao.requote(n)+"$");for(var e in this)if(t=e.match(r)){var i=this[e];this.removeEventListener(t[1],i,i.$),delete this[e]}}var o="__on"+n,a=n.indexOf("."),c=Z;a>0&&(n=n.slice(0,a));var l=Do.get(n);return l&&(n=l,c=V),a?t?i:e:t?M:u}function Z(n,t){return function(r){var e=ao.event;ao.event=r,t[0]=this.__data__;try{n.apply(this,t)}finally{ao.event=e}}}function V(n,t){var r=Z(n,t);return function(n){var t=this,e=n.relatedTarget;e&&(e===t||8&e.compareDocumentPosition(t))||r.call(t,n)}}function X(r){var e=".dragsuppress-"+ ++Lo,i="click"+e,u=ao.select(t(r)).on("touchmove"+e,k).on("dragstart"+e,k).on("selectstart"+e,k);if(null==To&&(To="onselectstart"in r?!1:x(r.style,"userSelect")),To){var o=n(r).style,a=o[To];o[To]="none"}return function(n){if(u.on(e,null),To&&(o[To]=a),n){var t=function(){u.on(i,null)};u.on(i,function(){k(),t()},!0),setTimeout(t,0)}}}function K(n,r){r.changedTouches&&(r=r.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var i=e.createSVGPoint();if(0>Ro){var u=t(n);if(u.scrollX||u.scrollY){e=ao.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=e[0][0].getScreenCTM();Ro=!(o.f||o.e),e.remove()}}return Ro?(i.x=r.pageX,i.y=r.pageY):(i.x=r.clientX,i.y=r.clientY),i=i.matrixTransform(n.getScreenCTM().inverse()),[i.x,i.y]}var a=n.getBoundingClientRect();return[r.clientX-a.left-n.clientLeft,r.clientY-a.top-n.clientTop]}function J(){return ao.event.changedTouches[0].identifier}function Q(n){return n>0?1:0>n?-1:0}function G(n,t,r){return(t[0]-n[0])*(r[1]-n[1])-(t[1]-n[1])*(r[0]-n[0])}function nn(n){return n>1?0:-1>n?qo:Math.acos(n)}function tn(n){return n>1?Uo:-1>n?-Uo:Math.asin(n)}function rn(n){return((n=Math.exp(n))-1/n)/2}function en(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 cn(n,t,r){return this instanceof cn?(this.h=+n,this.s=+t,void(this.l=+r)):arguments.length<2?n instanceof cn?new cn(n.h,n.s,n.l):bn(""+n,wn,cn):new cn(n,t,r)}function ln(n,t,r){function e(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*e(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,r=0>r?0:r>1?1:r,o=.5>=r?r*(1+t):r+t-r*t,u=2*r-o,new mn(i(n+120),i(n),i(n-120))}function fn(n,t,r){return this instanceof fn?(this.h=+n,this.c=+t,void(this.l=+r)):arguments.length<2?n instanceof fn?new fn(n.h,n.c,n.l):n instanceof hn?vn(n.l,n.a,n.b):vn((n=kn((n=ao.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new fn(n,t,r)}function sn(n,t,r){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new hn(r,Math.cos(n*=Fo)*t,Math.sin(n)*t)}function hn(n,t,r){return this instanceof hn?(this.l=+n,this.a=+t,void(this.b=+r)):arguments.length<2?n instanceof hn?new hn(n.l,n.a,n.b):n instanceof fn?sn(n.h,n.c,n.l):kn((n=mn(n)).r,n.g,n.b):new hn(n,t,r)}function pn(n,t,r){var e=(n+16)/116,i=e+t/500,u=e-r/200;return i=gn(i)*Go,e=gn(e)*na,u=gn(u)*ta,new mn(yn(3.2404542*i-1.5371385*e-.4985314*u),yn(-.969266*i+1.8760108*e+.041556*u),yn(.0556434*i-.2040259*e+1.0572252*u))}function vn(n,t,r){return n>0?new fn(Math.atan2(r,t)*Ho,Math.sqrt(t*t+r*r),n):new fn(NaN,NaN,n)}function gn(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,r){return this instanceof mn?(this.r=~~n,this.g=~~t,void(this.b=~~r)):arguments.length<2?n instanceof mn?new mn(n.r,n.g,n.b):bn(""+n,mn,ln):new mn(n,t,r)}function _n(n){return new mn(n>>16,n>>8&255,255&n)}function xn(n){return _n(n)+""}function Mn(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bn(n,t,r){var e,i,u,o=0,a=0,c=0;if(e=/([a-z]+)\((.*)\)/.exec(n=n.toLowerCase()))switch(i=e[2].split(","),e[1]){case"hsl":return r(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(An(i[0]),An(i[1]),An(i[2]))}return(u=ia.get(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,c=15&u,c=c<<4|c):7===n.length&&(o=(16711680&u)>>16,a=(65280&u)>>8,c=255&u)),t(o,a,c))}function wn(n,t,r){var e,i,u=Math.min(n/=255,t/=255,r/=255),o=Math.max(n,t,r),a=o-u,c=(o+u)/2;return a?(i=.5>c?a/(o+u):a/(2-o-u),e=n==o?(t-r)/a+(r>t?6:0):t==o?(r-n)/a+2:(n-t)/a+4,e*=60):(e=NaN,i=c>0&&1>c?0:e),new cn(e,i,c)}function kn(n,t,r){n=Cn(n),t=Cn(t),r=Cn(r);var e=dn((.4124564*n+.3575761*t+.1804375*r)/Go),i=dn((.2126729*n+.7151522*t+.072175*r)/na),u=dn((.0193339*n+.119192*t+.9503041*r)/ta);return hn(116*i-16,500*(e-i),200*(i-u))}function Cn(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function An(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Sn(n){return"function"==typeof n?n:function(){return n}}function En(n){return function(t,r,e){return 2===arguments.length&&"function"==typeof r&&(e=r,r=null),Nn(t,r,n,e)}}function Nn(n,t,r,e){function i(){var n,t=c.status;if(!t&&jn(c)||t>=200&&300>t||304===t){try{n=r.call(u,c)}catch(e){return void o.error.call(u,e)}o.load.call(u,n)}else o.error.call(u,c)}var u={},o=ao.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=i:c.onreadystatechange=function(){c.readyState>3&&i()},c.onprogress=function(n){var t=ao.event;ao.event=n;try{o.progress.call(u,c)}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?(l=n,u):l},u.response=function(n){return r=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(lo(arguments)))}}),u.send=function(r,e,i){if(2===arguments.length&&"function"==typeof e&&(i=e,e=null),c.open(r,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var f in a)c.setRequestHeader(f,a[f]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),o.beforesend.call(u,c),c.send(null==e?null:e),u},u.abort=function(){return c.abort(),u},ao.rebind(u,o,"on"),null==e?u:u.get($n(e))}function $n(n){return 1===n.length?function(t,r){n(null==t?r:null)}:n}function jn(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function Dn(n,t,r){var e=arguments.length;2>e&&(t=0),3>e&&(r=Date.now());var i=r+t,u={c:n,t:i,n:null};return oa?oa.n=u:ua=u,oa=u,aa||(ca=clearTimeout(ca),aa=1,la(Tn)),u}function Tn(){var n=Ln(),t=Rn()-n;t>24?(isFinite(t)&&(clearTimeout(ca),ca=setTimeout(Tn,t)),aa=0):(aa=1,la(Tn))}function Ln(){for(var n=Date.now(),t=ua;t;)n>=t.t&&t.c(n-t.t)&&(t.c=null),t=t.n;return n}function Rn(){for(var n,t=ua,r=1/0;t;)t.c?(t.t<r&&(r=t.t),t=(n=t).n):t=n?n.n=t.n:ua=t.n;return oa=n,r}function On(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function zn(n,t){var r=Math.pow(10,3*xo(8-t));return{scale:t>8?function(n){return n/r}:function(n){return n*r},symbol:n}}function qn(n){var t=n.decimal,r=n.thousands,e=n.grouping,i=n.currency,u=e&&r?function(n,t){for(var i=n.length,u=[],o=0,a=e[0],c=0;i>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),u.push(n.substring(i-=a,i+a)),!((c+=a+1)>t));)a=e[o=(o+1)%e.length];return u.reverse().join(r)}:m;return function(n){var r=sa.exec(n),e=r[1]||" ",o=r[2]||">",a=r[3]||"-",c=r[4]||"",l=r[5],f=+r[6],s=r[7],h=r[8],p=r[9],v=1,g="",d="",y=!1,m=!0;switch(h&&(h=+h.substring(1)),(l||"0"===e&&"="===o)&&(l=e="0",o="="),p){case"n":s=!0,p="g";break;case"%":v=100,d="%",p="f";break;case"p":v=100,d="%",p="r";break;case"b":case"o":case"x":case"X":"#"===c&&(g="0"+p.toLowerCase());case"c":m=!1;case"d":y=!0,h=0;break;case"s":v=-1,p="r"}"$"===c&&(g=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=ha.get(p)||In;var _=l&&s;return function(n){var r=d;if(y&&n%1)return"";var i=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>v){var c=ao.formatPrefix(n,h);n=c.scale(n),r=c.symbol+d}else n*=v;n=p(n,h);var x,M,b=n.lastIndexOf(".");if(0>b){var w=m?n.lastIndexOf("e"):-1;0>w?(x=n,M=""):(x=n.substring(0,w),M=n.substring(w))}else x=n.substring(0,b),M=t+n.substring(b+1);!l&&s&&(x=u(x,1/0));var k=g.length+x.length+M.length+(_?0:i.length),C=f>k?new Array(k=f-k+1).join(e):"";return _&&(x=u(C+x,C.length?f-M.length:1/0)),i+=g,n=x+M,("<"===o?i+n+C:">"===o?C+i+n:"^"===o?C.substring(0,k>>=1)+i+n+C.substring(k):i+(_?n:C+n))+r}}}function In(n){return n+""}function Pn(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Un(n,t,r){function e(t){var r=n(t),e=u(r,1);return e-t>t-r?r:e}function i(r){return t(r=n(new va(r-1)),1),r}function u(n,r){return t(n=new va(+n),r),n}function o(n,e,u){var o=i(n),a=[];if(u>1)for(;e>o;)r(o)%u||a.push(new Date(+o)),t(o,1);else for(;e>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,r){try{va=Pn;var e=new Pn;return e._=n,o(e,t,r)}finally{va=Date}}n.floor=n,n.round=e,n.ceil=i,n.offset=u,n.range=o;var c=n.utc=Fn(n);return c.floor=c,c.round=Fn(e),c.ceil=Fn(i),c.offset=Fn(u),c.range=a,n}function Fn(n){return function(t,r){try{va=Pn;var e=new Pn;return e._=t,n(e,r)._}finally{va=Date}}}function Hn(n){function t(n){function t(t){for(var r,i,u,o=[],a=-1,c=0;++a<e;)37===n.charCodeAt(a)&&(o.push(n.slice(c,a)),null!=(i=da[r=n.charAt(++a)])&&(r=n.charAt(++a)),(u=E[r])&&(r=u(t,null==i?"e"===r?" ":"0":i)),o.push(r),c=a+1);return o.push(n.slice(c,a)),o.join("")}var e=n.length;return t.parse=function(t){var e={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},i=r(e,n,t,0);if(i!=t.length)return null;"p"in e&&(e.H=e.H%12+12*e.p);var u=null!=e.Z&&va!==Pn,o=new(u?Pn:va);return"j"in e?o.setFullYear(e.y,0,e.j):"W"in e||"U"in e?("w"in e||(e.w="W"in e?1:0),o.setFullYear(e.y,0,1),o.setFullYear(e.y,0,"W"in e?(e.w+6)%7+7*e.W-(o.getDay()+5)%7:e.w+7*e.U-(o.getDay()+6)%7)):o.setFullYear(e.y,e.m,e.d),o.setHours(e.H+(e.Z/100|0),e.M+e.Z%100,e.S,e.L),u?o._:o},t.toString=function(){return n},t}function r(n,t,r,e){for(var i,u,o,a=0,c=t.length,l=r.length;c>a;){if(e>=l)return-1;if(i=t.charCodeAt(a++),37===i){if(o=t.charAt(a++),u=N[o in da?t.charAt(a++):o],!u||(e=u(n,r,e))<0)return-1}else if(i!=r.charCodeAt(e++))return-1}return e}function e(n,t,r){b.lastIndex=0;var e=b.exec(t.slice(r));return e?(n.w=w.get(e[0].toLowerCase()),r+e[0].length):-1}function i(n,t,r){x.lastIndex=0;var e=x.exec(t.slice(r));return e?(n.w=M.get(e[0].toLowerCase()),r+e[0].length):-1}function u(n,t,r){A.lastIndex=0;var e=A.exec(t.slice(r));return e?(n.m=S.get(e[0].toLowerCase()),r+e[0].length):-1}function o(n,t,r){k.lastIndex=0;var e=k.exec(t.slice(r));return e?(n.m=C.get(e[0].toLowerCase()),r+e[0].length):-1}function a(n,t,e){return r(n,E.c.toString(),t,e)}function c(n,t,e){return r(n,E.x.toString(),t,e)}function l(n,t,e){return r(n,E.X.toString(),t,e)}function f(n,t,r){var e=_.get(t.slice(r,r+=2).toLowerCase());return null==e?-1:(n.p=e,r)}var s=n.dateTime,h=n.date,p=n.time,v=n.periods,g=n.days,d=n.shortDays,y=n.months,m=n.shortMonths;t.utc=function(n){function r(n){try{va=Pn;var t=new va;return t._=n,e(t)}finally{va=Date}}var e=t(n);return r.parse=function(n){try{va=Pn;var t=e.parse(n);return t&&t._}finally{va=Date}},r.toString=e.toString,r},t.multi=t.utc.multi=lt;var _=ao.map(),x=Bn(g),M=Yn(g),b=Bn(d),w=Yn(d),k=Bn(y),C=Yn(y),A=Bn(m),S=Yn(m);v.forEach(function(n,t){_.set(n.toLowerCase(),t)});var E={a:function(n){return d[n.getDay()]},A:function(n){return g[n.getDay()]},b:function(n){return m[n.getMonth()]},B:function(n){return y[n.getMonth()]},c:t(s),d:function(n,t){return Wn(n.getDate(),t,2)},e:function(n,t){return Wn(n.getDate(),t,2)},H:function(n,t){return Wn(n.getHours(),t,2)},I:function(n,t){return Wn(n.getHours()%12||12,t,2)},j:function(n,t){return Wn(1+pa.dayOfYear(n),t,3)},L:function(n,t){return Wn(n.getMilliseconds(),t,3)},m:function(n,t){return Wn(n.getMonth()+1,t,2)},M:function(n,t){return Wn(n.getMinutes(),t,2)},p:function(n){return v[+(n.getHours()>=12)]},S:function(n,t){return Wn(n.getSeconds(),t,2)},U:function(n,t){return Wn(pa.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Wn(pa.mondayOfYear(n),t,2)},x:t(h),X:t(p),y:function(n,t){return Wn(n.getFullYear()%100,t,2)},Y:function(n,t){return Wn(n.getFullYear()%1e4,t,4)},Z:at,"%":function(){return"%"}},N={a:e,A:i,b:u,B:o,c:a,d:tt,e:tt,H:et,I:et,j:rt,L:ot,m:nt,M:it,p:f,S:ut,U:Vn,w:Zn,W:Xn,x:c,X:l,y:Jn,Y:Kn,Z:Qn,"%":ct};return t}function Wn(n,t,r){var e=0>n?"-":"",i=(e?-n:n)+"",u=i.length;return e+(r>u?new Array(r-u+1).join(t)+i:i)}function Bn(n){return new RegExp("^(?:"+n.map(ao.requote).join("|")+")","i")}function Yn(n){for(var t=new l,r=-1,e=n.length;++r<e;)t.set(n[r].toLowerCase(),r);return t}function Zn(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+1));return e?(n.w=+e[0],r+e[0].length):-1}function Vn(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r));return e?(n.U=+e[0],r+e[0].length):-1}function Xn(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r));return e?(n.W=+e[0],r+e[0].length):-1}function Kn(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+4));return e?(n.y=+e[0],r+e[0].length):-1}function Jn(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+2));return e?(n.y=Gn(+e[0]),r+e[0].length):-1}function Qn(n,t,r){return/^[+-]\d{4}$/.test(t=t.slice(r,r+5))?(n.Z=-t,r+5):-1}function Gn(n){return n+(n>68?1900:2e3)}function nt(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+2));return e?(n.m=e[0]-1,r+e[0].length):-1}function tt(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+2));return e?(n.d=+e[0],r+e[0].length):-1}function rt(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+3));return e?(n.j=+e[0],r+e[0].length):-1}function et(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+2));return e?(n.H=+e[0],r+e[0].length):-1}function it(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+2));return e?(n.M=+e[0],r+e[0].length):-1}function ut(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+2));return e?(n.S=+e[0],r+e[0].length):-1}function ot(n,t,r){ya.lastIndex=0;var e=ya.exec(t.slice(r,r+3));return e?(n.L=+e[0],r+e[0].length):-1}function at(n){var t=n.getTimezoneOffset(),r=t>0?"-":"+",e=xo(t)/60|0,i=xo(t)%60;return r+Wn(e,"0",2)+Wn(i,"0",2)}function ct(n,t,r){ma.lastIndex=0;var e=ma.exec(t.slice(r,r+1));return e?r+e[0].length:-1}function lt(n){for(var t=n.length,r=-1;++r<t;)n[r][0]=this(n[r][0]);return function(t){for(var r=0,e=n[r];!e[1](t);)e=n[++r];return e[0](t)}}function ft(){}function st(n,t,r){var e=r.s=n+t,i=e-n,u=e-i;r.t=n-u+(t-i)}function ht(n,t){n&&ba.hasOwnProperty(n.type)&&ba[n.type](n,t)}function pt(n,t,r){var e,i=-1,u=n.length-r;for(t.lineStart();++i<u;)e=n[i],t.point(e[0],e[1],e[2]);t.lineEnd()}function vt(n,t){var r=-1,e=n.length;for(t.polygonStart();++r<e;)pt(n[r],t,1);t.polygonEnd()}function gt(){function n(n,t){n*=Fo,t=t*Fo/2+qo/4;var r=n-e,o=r>=0?1:-1,a=o*r,c=Math.cos(t),l=Math.sin(t),f=u*l,s=i*c+f*Math.cos(a),h=f*o*Math.sin(a);ka.add(Math.atan2(h,s)),e=n,i=c,u=l}var t,r,e,i,u;Ca.point=function(o,a){Ca.point=n,e=(t=o)*Fo,i=Math.cos(a=(r=a)*Fo/2+qo/4),u=Math.sin(a)},Ca.lineEnd=function(){n(t,r)}}function dt(n){var t=n[0],r=n[1],e=Math.cos(r);return[e*Math.cos(t),e*Math.sin(t),Math.sin(r)]}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 _t(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 Mt(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 bt(n){return[Math.atan2(n[1],n[0]),tn(n[2])]}function wt(n,t){return xo(n[0]-t[0])<Oo&&xo(n[1]-t[1])<Oo}function kt(n,t){n*=Fo;var r=Math.cos(t*=Fo);Ct(r*Math.cos(n),r*Math.sin(n),Math.sin(t))}function Ct(n,t,r){++Aa,Ea+=(n-Ea)/Aa,Na+=(t-Na)/Aa,$a+=(r-$a)/Aa}function At(){function n(n,i){n*=Fo;var u=Math.cos(i*=Fo),o=u*Math.cos(n),a=u*Math.sin(n),c=Math.sin(i),l=Math.atan2(Math.sqrt((l=r*c-e*a)*l+(l=e*o-t*c)*l+(l=t*a-r*o)*l),t*o+r*a+e*c);Sa+=l,ja+=l*(t+(t=o)),Da+=l*(r+(r=a)),Ta+=l*(e+(e=c)),Ct(t,r,e)}var t,r,e;za.point=function(i,u){i*=Fo;var o=Math.cos(u*=Fo);t=o*Math.cos(i),r=o*Math.sin(i),e=Math.sin(u),za.point=n,Ct(t,r,e)}}function St(){za.point=kt}function Et(){function n(n,t){n*=Fo;var r=Math.cos(t*=Fo),o=r*Math.cos(n),a=r*Math.sin(n),c=Math.sin(t),l=i*c-u*a,f=u*o-e*c,s=e*a-i*o,h=Math.sqrt(l*l+f*f+s*s),p=e*o+i*a+u*c,v=h&&-nn(p)/h,g=Math.atan2(h,p);La+=v*l,Ra+=v*f,Oa+=v*s,Sa+=g,ja+=g*(e+(e=o)),Da+=g*(i+(i=a)),Ta+=g*(u+(u=c)),Ct(e,i,u)}var t,r,e,i,u;za.point=function(o,a){t=o,r=a,za.point=n,o*=Fo;var c=Math.cos(a*=Fo);e=c*Math.cos(o),i=c*Math.sin(o),u=Math.sin(a),Ct(e,i,u)},za.lineEnd=function(){n(t,r),za.lineEnd=St,za.point=kt}}function Nt(n,t){function r(r,e){return r=n(r,e),t(r[0],r[1])}return n.invert&&t.invert&&(r.invert=function(r,e){return r=t.invert(r,e),r&&n.invert(r[0],r[1])}),r}function $t(){return!0}function jt(n,t,r,e,i){var u=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,r=n[0],e=n[t];if(wt(r,e)){i.lineStart();for(var a=0;t>a;++a)i.point((r=n[a])[0],r[1]);return void i.lineEnd()}var c=new Tt(r,n,null,!0),l=new Tt(r,null,c,!1);c.o=l,u.push(c),o.push(l),c=new Tt(e,n,null,!1),l=new Tt(e,null,c,!0),c.o=l,u.push(c),o.push(l)}}),o.sort(t),Dt(u),Dt(o),u.length){for(var a=0,c=r,l=o.length;l>a;++a)o[a].e=c=!c;for(var f,s,h=u[0];;){for(var p=h,v=!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(v)for(var a=0,l=f.length;l>a;++a)i.point((s=f[a])[0],s[1]);else e(p.x,p.n.x,1,i);p=p.n}else{if(v){f=p.p.z;for(var a=f.length-1;a>=0;--a)i.point((s=f[a])[0],s[1])}else e(p.x,p.p.x,-1,i);p=p.p}p=p.o,f=p.z,v=!v}while(!p.v);i.lineEnd()}}}function Dt(n){if(t=n.length){for(var t,r,e=0,i=n[0];++e<t;)i.n=r=n[e],r.p=i,i=r;i.n=r=n[0],r.p=i}}function Tt(n,t,r,e){this.x=n,this.z=t,this.o=r,this.e=e,this.v=!1,this.n=this.p=null}function Lt(n,t,r,e){return function(i,u){function o(t,r){var e=i(t,r);n(t=e[0],r=e[1])&&u.point(t,r)}function a(n,t){var r=i(n,t);d.point(r[0],r[1])}function c(){m.point=a,d.lineStart()}function l(){m.point=o,d.lineEnd()}function f(n,t){g.push([n,t]);var r=i(n,t);x.point(r[0],r[1])}function s(){x.lineStart(),g=[]}function h(){f(g[0][0],g[0][1]),x.lineEnd();var n,t=x.clean(),r=_.buffer(),e=r.length;if(g.pop(),v.push(g),g=null,e)if(1&t){n=r[0];var i,e=n.length-1,o=-1;if(e>0){for(M||(u.polygonStart(),M=!0),u.lineStart();++o<e;)u.point((i=n[o])[0],i[1]);u.lineEnd()}}else e>1&&2&t&&r.push(r.pop().concat(r.shift())),p.push(r.filter(Rt))}var p,v,g,d=t(u),y=i.invert(e[0],e[1]),m={point:o,lineStart:c,lineEnd:l,polygonStart:function(){m.point=f,m.lineStart=s,m.lineEnd=h,p=[],v=[]},polygonEnd:function(){m.point=o,m.lineStart=c,m.lineEnd=l,p=ao.merge(p);var n=Ut(y,v);p.length?(M||(u.polygonStart(),M=!0),jt(p,zt,n,r,u)):n&&(M||(u.polygonStart(),M=!0),u.lineStart(),r(null,null,1,u),u.lineEnd()),M&&(u.polygonEnd(),M=!1),p=v=null},sphere:function(){u.polygonStart(),u.lineStart(),r(null,null,1,u),u.lineEnd(),u.polygonEnd()}},_=Ot(),x=t(_),M=!1;return m}}function Rt(n){return n.length>1}function Ot(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,r){n.push([t,r])},lineEnd:M,buffer:function(){var r=t;return t=[],n=null,r},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function zt(n,t){return((n=n.x)[0]<0?n[1]-Uo-Oo:Uo-n[1])-((t=t.x)[0]<0?t[1]-Uo-Oo:Uo-t[1])}function qt(n){var t,r=NaN,e=NaN,i=NaN;return{lineStart:function(){n.lineStart(),t=1},point:function(u,o){var a=u>0?qo:-qo,c=xo(u-r);xo(c-qo)<Oo?(n.point(r,e=(e+o)/2>0?Uo:-Uo),n.point(i,e),n.lineEnd(),n.lineStart(),n.point(a,e),n.point(u,e),t=0):i!==a&&c>=qo&&(xo(r-i)<Oo&&(r-=i*Oo),xo(u-a)<Oo&&(u-=a*Oo),e=It(r,e,u,o),n.point(i,e),n.lineEnd(),n.lineStart(),n.point(a,e),t=0),n.point(r=u,e=o),i=a},lineEnd:function(){n.lineEnd(),r=e=NaN},clean:function(){return 2-t}}}function It(n,t,r,e){var i,u,o=Math.sin(n-r);return xo(o)>Oo?Math.atan((Math.sin(t)*(u=Math.cos(e))*Math.sin(r)-Math.sin(e)*(i=Math.cos(t))*Math.sin(n))/(i*u*o)):(t+e)/2}function Pt(n,t,r,e){var i;if(null==n)i=r*Uo,e.point(-qo,i),e.point(0,i),e.point(qo,i),e.point(qo,0),e.point(qo,-i),e.point(0,-i),e.point(-qo,-i),e.point(-qo,0),e.point(-qo,i);else if(xo(n[0]-t[0])>Oo){var u=n[0]<t[0]?qo:-qo;i=r*u/2,e.point(-u,i),e.point(0,i),e.point(u,i)}else e.point(t[0],t[1])}function Ut(n,t){var r=n[0],e=n[1],i=[Math.sin(r),-Math.cos(r),0],u=0,o=0;ka.reset();for(var a=0,c=t.length;c>a;++a){var l=t[a],f=l.length;if(f)for(var s=l[0],h=s[0],p=s[1]/2+qo/4,v=Math.sin(p),g=Math.cos(p),d=1;;){d===f&&(d=0),n=l[d];var y=n[0],m=n[1]/2+qo/4,_=Math.sin(m),x=Math.cos(m),M=y-h,b=M>=0?1:-1,w=b*M,k=w>qo,C=v*_;if(ka.add(Math.atan2(C*b*Math.sin(w),g*x+C*Math.cos(w))),u+=k?M+b*Io:M,k^h>=r^y>=r){var A=mt(dt(s),dt(n));Mt(A);var S=mt(i,A);Mt(S);var E=(k^M>=0?-1:1)*tn(S[2]);(e>E||e===E&&(A[0]||A[1]))&&(o+=k^M>=0?1:-1)}if(!d++)break;h=y,v=_,g=x,s=n}}return(-Oo>u||Oo>u&&0>ka)^1&o}function Ft(n){function t(n,t){return Math.cos(n)*Math.cos(t)>u}function r(n){var r,u,c,l,f;return{lineStart:function(){l=c=!1,f=1},point:function(s,h){var p,v=[s,h],g=t(s,h),d=o?g?0:i(s,h):g?i(s+(0>s?qo:-qo),h):0;if(!r&&(l=c=g)&&n.lineStart(),g!==c&&(p=e(r,v),(wt(r,p)||wt(v,p))&&(v[0]+=Oo,v[1]+=Oo,g=t(v[0],v[1]))),g!==c)f=0,g?(n.lineStart(),p=e(v,r),n.point(p[0],p[1])):(p=e(r,v),n.point(p[0],p[1]),n.lineEnd()),r=p;else if(a&&r&&o^g){var y;d&u||!(y=e(v,r,!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])))}!g||r&&wt(r,v)||n.point(v[0],v[1]),r=v,c=g,u=d},lineEnd:function(){c&&n.lineEnd(),r=null},clean:function(){return f|(l&&c)<<1}}}function e(n,t,r){var e=dt(n),i=dt(t),o=[1,0,0],a=mt(e,i),c=yt(a,a),l=a[0],f=c-l*l;if(!f)return!r&&n;var s=u*c/f,h=-u*l/f,p=mt(o,a),v=xt(o,s),g=xt(a,h);_t(v,g);var d=p,y=yt(v,d),m=yt(d,d),_=y*y-m*(yt(v,v)-1);if(!(0>_)){var x=Math.sqrt(_),M=xt(d,(-y-x)/m);if(_t(M,v),M=bt(M),!r)return M;var b,w=n[0],k=t[0],C=n[1],A=t[1];w>k&&(b=w,w=k,k=b);var S=k-w,E=xo(S-qo)<Oo,N=E||Oo>S;if(!E&&C>A&&(b=C,C=A,A=b),N?E?C+A>0^M[1]<(xo(M[0]-w)<Oo?C:A):C<=M[1]&&M[1]<=A:S>qo^(w<=M[0]&&M[0]<=k)){var $=xt(d,(-y+x)/m);return _t($,v),[M,bt($)]}}}function i(t,r){var e=o?n:qo-n,i=0;return-e>t?i|=1:t>e&&(i|=2),-e>r?i|=4:r>e&&(i|=8),i}var u=Math.cos(n),o=u>0,a=xo(u)>Oo,c=gr(n,6*Fo);return Lt(t,r,c,o?[0,-n]:[-qo,n-qo])}function Ht(n,t,r,e){return function(i){var u,o=i.a,a=i.b,c=o.x,l=o.y,f=a.x,s=a.y,h=0,p=1,v=f-c,g=s-l;if(u=n-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)}if(u=t-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)}return h>0&&(i.a={x:c+h*v,y:l+h*g}),1>p&&(i.b={x:c+p*v,y:l+p*g}),i}}}}}}function Wt(n,t,r,e){function i(e,i){return xo(e[0]-n)<Oo?i>0?0:3:xo(e[0]-r)<Oo?i>0?2:1:xo(e[1]-t)<Oo?i>0?1:0:i>0?3:2}function u(n,t){return o(n.x,t.x)}function o(n,t){var r=i(n,1),e=i(t,1);return r!==e?r-e:0===r?t[1]-n[1]:1===r?n[0]-t[0]:2===r?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,r=d.length,e=n[1],i=0;r>i;++i)for(var u,o=1,a=d[i],c=a.length,l=a[0];c>o;++o)u=a[o],l[1]<=e?u[1]>e&&G(l,u,n)>0&&++t:u[1]<=e&&G(l,u,n)<0&&--t,l=u;return 0!==t}function l(u,a,c,l){var f=0,s=0;if(null==u||(f=i(u,c))!==(s=i(a,c))||o(u,a)<0^c>0){do l.point(0===f||3===f?n:r,f>1?e:t);while((f=(f+c+4)%4)!==s)}else l.point(a[0],a[1])}function f(i,u){return i>=n&&r>=i&&u>=t&&e>=u}function s(n,t){f(n,t)&&a.point(n,t)}function h(){N.point=v,d&&d.push(y=[]),k=!0,w=!1,M=b=NaN}function p(){g&&(v(m,_),x&&w&&S.rejoin(),g.push(S.buffer())),N.point=s,w&&a.lineEnd()}function v(n,t){n=Math.max(-Ia,Math.min(Ia,n)),t=Math.max(-Ia,Math.min(Ia,t));var r=f(n,t);if(d&&y.push([n,t]),k)m=n,_=t,x=r,k=!1,r&&(a.lineStart(),a.point(n,t));else if(r&&w)a.point(n,t);else{var e={a:{x:M,y:b},b:{x:n,y:t}};E(e)?(w||(a.lineStart(),a.point(e.a.x,e.a.y)),a.point(e.b.x,e.b.y),r||a.lineEnd(),C=!1):r&&(a.lineStart(),a.point(n,t),C=!1)}M=n,b=t,w=r}var g,d,y,m,_,x,M,b,w,k,C,A=a,S=Ot(),E=Ht(n,t,r,e),N={point:s,lineStart:h,lineEnd:p,polygonStart:function(){a=S,g=[],d=[],C=!0},polygonEnd:function(){a=A,g=ao.merge(g);var t=c([n,e]),r=C&&t,i=g.length;(r||i)&&(a.polygonStart(),r&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),i&&jt(g,u,t,l,a),a.polygonEnd()),g=d=y=null}};return N}}function Bt(n){var t=0,r=qo/3,e=ar(n),i=e(t,r);return i.parallels=function(n){return arguments.length?e(t=n[0]*qo/180,r=n[1]*qo/180):[t/qo*180,r/qo*180]},i}function Yt(n,t){function r(n,t){var r=Math.sqrt(u-2*i*Math.sin(t))/i;return[r*Math.sin(n*=i),o-r*Math.cos(n)]}var e=Math.sin(n),i=(e+Math.sin(t))/2,u=1+e*(2*i-e),o=Math.sqrt(u)/i;return r.invert=function(n,t){var r=o-t;return[Math.atan2(n,r)/i,tn((u-(n*n+r*r)*i*i)/(2*i))]},r}function Zt(){function n(n,t){Ua+=i*n-e*t,e=n,i=t}var t,r,e,i;Ya.point=function(u,o){Ya.point=n,t=e=u,r=i=o},Ya.lineEnd=function(){n(t,r)}}function Vt(n,t){Fa>n&&(Fa=n),n>Wa&&(Wa=n),Ha>t&&(Ha=t),t>Ba&&(Ba=t)}function Xt(){function n(n,t){o.push("M",n,",",t,u)}function t(n,t){o.push("M",n,",",t),a.point=r}function r(n,t){o.push("L",n,",",t)}function e(){a.point=n}function i(){o.push("Z")}var u=Kt(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:e,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=e,a.point=n},pointRadius:function(n){return u=Kt(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Kt(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function Jt(n,t){Ea+=n,Na+=t,++$a}function Qt(){function n(n,e){var i=n-t,u=e-r,o=Math.sqrt(i*i+u*u);ja+=o*(t+n)/2,Da+=o*(r+e)/2,Ta+=o,Jt(t=n,r=e)}var t,r;Va.point=function(e,i){Va.point=n,Jt(t=e,r=i)}}function Gt(){Va.point=Jt}function nr(){function n(n,t){var r=n-e,u=t-i,o=Math.sqrt(r*r+u*u);ja+=o*(e+n)/2,Da+=o*(i+t)/2,Ta+=o,o=i*n-e*t,La+=o*(e+n),Ra+=o*(i+t),Oa+=3*o,Jt(e=n,i=t)}var t,r,e,i;Va.point=function(u,o){Va.point=n,Jt(t=e=u,r=i=o)},Va.lineEnd=function(){n(t,r)}}function tr(n){function t(t,r){n.moveTo(t+o,r),n.arc(t,r,o,0,Io)}function r(t,r){n.moveTo(t,r),a.point=e}function e(t,r){n.lineTo(t,r)}function i(){a.point=t}function u(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=r},lineEnd:i,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=i,a.point=t},pointRadius:function(n){return o=n,a},result:M};return a}function rr(n){function t(n){return(a?e:r)(n)}function r(t){return ur(t,function(r,e){r=n(r,e),t.point(r[0],r[1])})}function e(t){function r(r,e){r=n(r,e),t.point(r[0],r[1])}function e(){_=NaN,k.point=u,t.lineStart()}function u(r,e){var u=dt([r,e]),o=n(r,e);i(_,x,m,M,b,w,_=o[0],x=o[1],m=r,M=u[0],b=u[1],w=u[2],a,t),t.point(_,x)}function o(){k.point=r,t.lineEnd()}function c(){
+e(),k.point=l,k.lineEnd=f}function l(n,t){u(s=n,h=t),p=_,v=x,g=M,d=b,y=w,k.point=u}function f(){i(_,x,m,M,b,w,p,v,s,g,d,y,a,t),k.lineEnd=o,o()}var s,h,p,v,g,d,y,m,_,x,M,b,w,k={point:r,lineStart:e,lineEnd:o,polygonStart:function(){t.polygonStart(),k.lineStart=c},polygonEnd:function(){t.polygonEnd(),k.lineStart=e}};return k}function i(t,r,e,a,c,l,f,s,h,p,v,g,d,y){var m=f-t,_=s-r,x=m*m+_*_;if(x>4*u&&d--){var M=a+p,b=c+v,w=l+g,k=Math.sqrt(M*M+b*b+w*w),C=Math.asin(w/=k),A=xo(xo(w)-1)<Oo||xo(e-h)<Oo?(e+h)/2:Math.atan2(b,M),S=n(A,C),E=S[0],N=S[1],$=E-t,j=N-r,D=_*$-m*j;(D*D/x>u||xo((m*$+_*j)/x-.5)>.3||o>a*p+c*v+l*g)&&(i(t,r,e,a,c,l,E,N,A,M/=k,b/=k,w,d,y),y.point(E,N),i(E,N,A,M,b,w,f,s,h,p,v,g,d,y))}}var u=.5,o=Math.cos(30*Fo),a=16;return t.precision=function(n){return arguments.length?(a=(u=n*n)>0&&16,t):Math.sqrt(u)},t}function er(n){var t=rr(function(t,r){return n([t*Ho,r*Ho])});return function(n){return cr(t(n))}}function ir(n){this.stream=n}function ur(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function or(n){return ar(function(){return n})()}function ar(n){function t(n){return n=a(n[0]*Fo,n[1]*Fo),[n[0]*h+c,l-n[1]*h]}function r(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Ho,n[1]*Ho]}function e(){a=Nt(o=sr(y,_,x),u);var n=u(g,d);return c=p-n[0]*h,l=v+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=null),t}var u,o,a,c,l,f,s=rr(function(n,t){return n=u(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,p=480,v=250,g=0,d=0,y=0,_=0,x=0,M=qa,b=m,w=null,k=null;return t.stream=function(n){return f&&(f.valid=!1),f=cr(M(o,s(b(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(M=null==n?(w=n,qa):Ft((w=+n)*Fo),i()):w},t.clipExtent=function(n){return arguments.length?(k=n,b=n?Wt(n[0][0],n[0][1],n[1][0],n[1][1]):m,i()):k},t.scale=function(n){return arguments.length?(h=+n,e()):h},t.translate=function(n){return arguments.length?(p=+n[0],v=+n[1],e()):[p,v]},t.center=function(n){return arguments.length?(g=n[0]%360*Fo,d=n[1]%360*Fo,e()):[g*Ho,d*Ho]},t.rotate=function(n){return arguments.length?(y=n[0]%360*Fo,_=n[1]%360*Fo,x=n.length>2?n[2]%360*Fo:0,e()):[y*Ho,_*Ho,x*Ho]},ao.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&r,e()}}function cr(n){return ur(n,function(t,r){n.point(t*Fo,r*Fo)})}function lr(n,t){return[n,t]}function fr(n,t){return[n>qo?n-Io:-qo>n?n+Io:n,t]}function sr(n,t,r){return n?t||r?Nt(pr(n),vr(t,r)):pr(n):t||r?vr(t,r):fr}function hr(n){return function(t,r){return t+=n,[t>qo?t-Io:-qo>t?t+Io:t,r]}}function pr(n){var t=hr(n);return t.invert=hr(-n),t}function vr(n,t){function r(n,t){var r=Math.cos(t),a=Math.cos(n)*r,c=Math.sin(n)*r,l=Math.sin(t),f=l*e+a*i;return[Math.atan2(c*u-f*o,a*e-l*i),tn(f*u+c*o)]}var e=Math.cos(n),i=Math.sin(n),u=Math.cos(t),o=Math.sin(t);return r.invert=function(n,t){var r=Math.cos(t),a=Math.cos(n)*r,c=Math.sin(n)*r,l=Math.sin(t),f=l*u-c*o;return[Math.atan2(c*u+l*o,a*e+f*i),tn(f*e-a*i)]},r}function gr(n,t){var r=Math.cos(n),e=Math.sin(n);return function(i,u,o,a){var c=o*t;null!=i?(i=dr(r,i),u=dr(r,u),(o>0?u>i:i>u)&&(i+=o*Io)):(i=n+o*Io,u=n-.5*c);for(var l,f=i;o>0?f>u:u>f;f-=c)a.point((l=bt([r,-e*Math.cos(f),-e*Math.sin(f)]))[0],l[1])}}function dr(n,t){var r=dt(t);r[0]-=n,Mt(r);var e=nn(-r[1]);return((-r[2]<0?-e:e)+2*Math.PI-Oo)%(2*Math.PI)}function yr(n,t,r){var e=ao.range(n,t-Oo,r).concat(t);return function(n){return e.map(function(t){return[n,t]})}}function mr(n,t,r){var e=ao.range(n,t-Oo,r).concat(t);return function(n){return e.map(function(t){return[t,n]})}}function _r(n){return n.source}function xr(n){return n.target}function Mr(n,t,r,e){var i=Math.cos(t),u=Math.sin(t),o=Math.cos(e),a=Math.sin(e),c=i*Math.cos(n),l=i*Math.sin(n),f=o*Math.cos(r),s=o*Math.sin(r),h=2*Math.asin(Math.sqrt(on(e-t)+i*o*on(r-n))),p=1/Math.sin(h),v=h?function(n){var t=Math.sin(n*=h)*p,r=Math.sin(h-n)*p,e=r*c+t*f,i=r*l+t*s,o=r*u+t*a;return[Math.atan2(i,e)*Ho,Math.atan2(o,Math.sqrt(e*e+i*i))*Ho]}:function(){return[n*Ho,t*Ho]};return v.distance=h,v}function br(){function n(n,i){var u=Math.sin(i*=Fo),o=Math.cos(i),a=xo((n*=Fo)-t),c=Math.cos(a);Xa+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=e*u-r*o*c)*a),r*u+e*o*c),t=n,r=u,e=o}var t,r,e;Ka.point=function(i,u){t=i*Fo,r=Math.sin(u*=Fo),e=Math.cos(u),Ka.point=n},Ka.lineEnd=function(){Ka.point=Ka.lineEnd=M}}function wr(n,t){function r(t,r){var e=Math.cos(t),i=Math.cos(r),u=n(e*i);return[u*i*Math.sin(t),u*Math.sin(r)]}return r.invert=function(n,r){var e=Math.sqrt(n*n+r*r),i=t(e),u=Math.sin(i),o=Math.cos(i);return[Math.atan2(n*u,e*o),Math.asin(e&&r*u/e)]},r}function kr(n,t){function r(n,t){o>0?-Uo+Oo>t&&(t=-Uo+Oo):t>Uo-Oo&&(t=Uo-Oo);var r=o/Math.pow(i(t),u);return[r*Math.sin(u*n),o-r*Math.cos(u*n)]}var e=Math.cos(n),i=function(n){return Math.tan(qo/4+n/2)},u=n===t?Math.sin(n):Math.log(e/Math.cos(t))/Math.log(i(t)/i(n)),o=e*Math.pow(i(n),u)/u;return u?(r.invert=function(n,t){var r=o-t,e=Q(u)*Math.sqrt(n*n+r*r);return[Math.atan2(n,r)/u,2*Math.atan(Math.pow(o/e,1/u))-Uo]},r):Ar}function Cr(n,t){function r(n,t){var r=u-t;return[r*Math.sin(i*n),u-r*Math.cos(i*n)]}var e=Math.cos(n),i=n===t?Math.sin(n):(e-Math.cos(t))/(t-n),u=e/i+n;return xo(i)<Oo?lr:(r.invert=function(n,t){var r=u-t;return[Math.atan2(n,r)/i,u-Q(i)*Math.sqrt(n*n+r*r)]},r)}function Ar(n,t){return[n,Math.log(Math.tan(qo/4+t/2))]}function Sr(n){var t,r=or(n),e=r.scale,i=r.translate,u=r.clipExtent;return r.scale=function(){var n=e.apply(r,arguments);return n===r?t?r.clipExtent(null):r:n},r.translate=function(){var n=i.apply(r,arguments);return n===r?t?r.clipExtent(null):r:n},r.clipExtent=function(n){var o=u.apply(r,arguments);if(o===r){if(t=null==n){var a=qo*e(),c=i();u([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},r.clipExtent(null)}function Er(n,t){return[Math.log(Math.tan(qo/4+t/2)),-n]}function Nr(n){return n[0]}function $r(n){return n[1]}function jr(n){for(var t=n.length,r=[0,1],e=2,i=2;t>i;i++){for(;e>1&&G(n[r[e-2]],n[r[e-1]],n[i])<=0;)--e;r[e++]=i}return r.slice(0,e)}function Dr(n,t){return n[0]-t[0]||n[1]-t[1]}function Tr(n,t,r){return(r[0]-t[0])*(n[1]-t[1])<(r[1]-t[1])*(n[0]-t[0])}function Lr(n,t,r,e){var i=n[0],u=r[0],o=t[0]-i,a=e[0]-u,c=n[1],l=r[1],f=t[1]-c,s=e[1]-l,h=(a*(c-l)-s*(i-u))/(s*o-a*f);return[i+h*o,c+h*f]}function Rr(n){var t=n[0],r=n[n.length-1];return!(t[0]-r[0]||t[1]-r[1])}function Or(){ee(this),this.edge=this.site=this.circle=null}function zr(n){var t=cc.pop()||new Or;return t.site=n,t}function qr(n){Vr(n),uc.remove(n),cc.push(n),ee(n)}function Ir(n){var t=n.circle,r=t.x,e=t.cy,i={x:r,y:e},u=n.P,o=n.N,a=[n];qr(n);for(var c=u;c.circle&&xo(r-c.circle.x)<Oo&&xo(e-c.circle.cy)<Oo;)u=c.P,a.unshift(c),qr(c),c=u;a.unshift(c),Vr(c);for(var l=o;l.circle&&xo(r-l.circle.x)<Oo&&xo(e-l.circle.cy)<Oo;)o=l.N,a.push(l),qr(l),l=o;a.push(l),Vr(l);var f,s=a.length;for(f=1;s>f;++f)l=a[f],c=a[f-1],ne(l.edge,c.site,l.site,i);c=a[0],l=a[s-1],l.edge=Qr(c.site,l.site,null,i),Zr(c),Zr(l)}function Pr(n){for(var t,r,e,i,u=n.x,o=n.y,a=uc._;a;)if(e=Ur(a,o)-u,e>Oo)a=a.L;else{if(i=u-Fr(a,o),!(i>Oo)){e>-Oo?(t=a.P,r=a):i>-Oo?(t=a,r=a.N):t=r=a;break}if(!a.R){t=a;break}a=a.R}var c=zr(n);if(uc.insert(t,c),t||r){if(t===r)return Vr(t),r=zr(t.site),uc.insert(c,r),c.edge=r.edge=Qr(t.site,c.site),Zr(t),void Zr(r);if(!r)return void(c.edge=Qr(t.site,c.site));Vr(t),Vr(r);var l=t.site,f=l.x,s=l.y,h=n.x-f,p=n.y-s,v=r.site,g=v.x-f,d=v.y-s,y=2*(h*d-p*g),m=h*h+p*p,_=g*g+d*d,x={x:(d*m-p*_)/y+f,y:(h*_-g*m)/y+s};ne(r.edge,l,v,x),c.edge=Qr(l,n,null,x),r.edge=Qr(n,v,null,x),Zr(t),Zr(r)}}function Ur(n,t){var r=n.site,e=r.x,i=r.y,u=i-t;if(!u)return e;var o=n.P;if(!o)return-(1/0);r=o.site;var a=r.x,c=r.y,l=c-t;if(!l)return a;var f=a-e,s=1/u-1/l,h=f/l;return s?(-h+Math.sqrt(h*h-2*s*(f*f/(-2*l)-c+l/2+i-u/2)))/s+e:(e+a)/2}function Fr(n,t){var r=n.N;if(r)return Ur(r,t);var e=n.site;return e.y===t?e.x:1/0}function Hr(n){this.site=n,this.edges=[]}function Wr(n){for(var t,r,e,i,u,o,a,c,l,f,s=n[0][0],h=n[1][0],p=n[0][1],v=n[1][1],g=ic,d=g.length;d--;)if(u=g[d],u&&u.prepare())for(a=u.edges,c=a.length,o=0;c>o;)f=a[o].end(),e=f.x,i=f.y,l=a[++o%c].start(),t=l.x,r=l.y,(xo(e-t)>Oo||xo(i-r)>Oo)&&(a.splice(o,0,new te(Gr(u.site,f,xo(e-s)<Oo&&v-i>Oo?{x:s,y:xo(t-s)<Oo?r:v}:xo(i-v)<Oo&&h-e>Oo?{x:xo(r-v)<Oo?t:h,y:v}:xo(e-h)<Oo&&i-p>Oo?{x:h,y:xo(t-h)<Oo?r:p}:xo(i-p)<Oo&&e-s>Oo?{x:xo(r-p)<Oo?t:s,y:p}:null),u.site,null)),++c)}function Br(n,t){return t.angle-n.angle}function Yr(){ee(this),this.x=this.y=this.arc=this.site=this.cy=null}function Zr(n){var t=n.P,r=n.N;if(t&&r){var e=t.site,i=n.site,u=r.site;if(e!==u){var o=i.x,a=i.y,c=e.x-o,l=e.y-a,f=u.x-o,s=u.y-a,h=2*(c*s-l*f);if(!(h>=-zo)){var p=c*c+l*l,v=f*f+s*s,g=(s*p-l*v)/h,d=(c*v-f*p)/h,s=d+a,y=lc.pop()||new Yr;y.arc=n,y.site=i,y.x=g+o,y.y=s+Math.sqrt(g*g+d*d),y.cy=s,n.circle=y;for(var m=null,_=ac._;_;)if(y.y<_.y||y.y===_.y&&y.x<=_.x){if(!_.L){m=_.P;break}_=_.L}else{if(!_.R){m=_;break}_=_.R}ac.insert(m,y),m||(oc=y)}}}}function Vr(n){var t=n.circle;t&&(t.P||(oc=t.N),ac.remove(t),lc.push(t),ee(t),n.circle=null)}function Xr(n){for(var t,r=ec,e=Ht(n[0][0],n[0][1],n[1][0],n[1][1]),i=r.length;i--;)t=r[i],(!Kr(t,n)||!e(t)||xo(t.a.x-t.b.x)<Oo&&xo(t.a.y-t.b.y)<Oo)&&(t.a=t.b=null,r.splice(i,1))}function Kr(n,t){var r=n.b;if(r)return!0;var e,i,u=n.a,o=t[0][0],a=t[1][0],c=t[0][1],l=t[1][1],f=n.l,s=n.r,h=f.x,p=f.y,v=s.x,g=s.y,d=(h+v)/2,y=(p+g)/2;if(g===p){if(o>d||d>=a)return;if(h>v){if(u){if(u.y>=l)return}else u={x:d,y:c};r={x:d,y:l}}else{if(u){if(u.y<c)return}else u={x:d,y:l};r={x:d,y:c}}}else if(e=(h-v)/(g-p),i=y-e*d,-1>e||e>1)if(h>v){if(u){if(u.y>=l)return}else u={x:(c-i)/e,y:c};r={x:(l-i)/e,y:l}}else{if(u){if(u.y<c)return}else u={x:(l-i)/e,y:l};r={x:(c-i)/e,y:c}}else if(g>p){if(u){if(u.x>=a)return}else u={x:o,y:e*o+i};r={x:a,y:e*a+i}}else{if(u){if(u.x<o)return}else u={x:a,y:e*a+i};r={x:o,y:e*o+i}}return n.a=u,n.b=r,!0}function Jr(n,t){this.l=n,this.r=t,this.a=this.b=null}function Qr(n,t,r,e){var i=new Jr(n,t);return ec.push(i),r&&ne(i,n,t,r),e&&ne(i,t,n,e),ic[n.i].edges.push(new te(i,n,t)),ic[t.i].edges.push(new te(i,t,n)),i}function Gr(n,t,r){var e=new Jr(n,null);return e.a=t,e.b=r,ec.push(e),e}function ne(n,t,r,e){n.a||n.b?n.l===r?n.b=e:n.a=e:(n.a=e,n.l=t,n.r=r)}function te(n,t,r){var e=n.a,i=n.b;this.edge=n,this.site=t,this.angle=r?Math.atan2(r.y-t.y,r.x-t.x):n.l===t?Math.atan2(i.x-e.x,e.y-i.y):Math.atan2(e.x-i.x,i.y-e.y)}function re(){this._=null}function ee(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function ie(n,t){var r=t,e=t.R,i=r.U;i?i.L===r?i.L=e:i.R=e:n._=e,e.U=i,r.U=e,r.R=e.L,r.R&&(r.R.U=r),e.L=r}function ue(n,t){var r=t,e=t.L,i=r.U;i?i.L===r?i.L=e:i.R=e:n._=e,e.U=i,r.U=e,r.L=e.R,r.L&&(r.L.U=r),e.R=r}function oe(n){for(;n.L;)n=n.L;return n}function ae(n,t){var r,e,i,u=n.sort(ce).pop();for(ec=[],ic=new Array(n.length),uc=new re,ac=new re;;)if(i=oc,u&&(!i||u.y<i.y||u.y===i.y&&u.x<i.x))u.x===r&&u.y===e||(ic[u.i]=new Hr(u),Pr(u),r=u.x,e=u.y),u=n.pop();else{if(!i)break;Ir(i.arc)}t&&(Xr(t),Wr(t));var o={cells:ic,edges:ec};return uc=ac=ec=ic=null,o}function ce(n,t){return t.y-n.y||t.x-n.x}function le(n,t,r){return(n.x-r.x)*(t.y-n.y)-(n.x-t.x)*(r.y-n.y)}function fe(n){return n.x}function se(n){return n.y}function he(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function pe(n,t,r,e,i,u){if(!n(t,r,e,i,u)){var o=.5*(r+i),a=.5*(e+u),c=t.nodes;c[0]&&pe(n,c[0],r,e,o,a),c[1]&&pe(n,c[1],o,e,i,a),c[2]&&pe(n,c[2],r,a,o,u),c[3]&&pe(n,c[3],o,a,i,u)}}function ve(n,t,r,e,i,u,o){var a,c=1/0;return function l(n,f,s,h,p){if(!(f>u||s>o||e>h||i>p)){if(v=n.point){var v,g=t-n.x,d=r-n.y,y=g*g+d*d;if(c>y){var m=Math.sqrt(c=y);e=t-m,i=r-m,u=t+m,o=r+m,a=v}}for(var _=n.nodes,x=.5*(f+h),M=.5*(s+p),b=t>=x,w=r>=M,k=w<<1|b,C=k+4;C>k;++k)if(n=_[3&k])switch(3&k){case 0:l(n,f,s,x,M);break;case 1:l(n,x,s,h,M);break;case 2:l(n,f,M,x,p);break;case 3:l(n,x,M,h,p)}}}(n,e,i,u,o),a}function ge(n,t){n=ao.rgb(n),t=ao.rgb(t);var r=n.r,e=n.g,i=n.b,u=t.r-r,o=t.g-e,a=t.b-i;return function(n){return"#"+Mn(Math.round(r+u*n))+Mn(Math.round(e+o*n))+Mn(Math.round(i+a*n))}}function de(n,t){var r,e={},i={};for(r in n)r in t?e[r]=_e(n[r],t[r]):i[r]=n[r];for(r in t)r in n||(i[r]=t[r]);return function(n){for(r in e)i[r]=e[r](n);return i}}function ye(n,t){return n=+n,t=+t,function(r){return n*(1-r)+t*r}}function me(n,t){var r,e,i,u=sc.lastIndex=hc.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(r=sc.exec(n))&&(e=hc.exec(t));)(i=e.index)>u&&(i=t.slice(u,i),a[o]?a[o]+=i:a[++o]=i),(r=r[0])===(e=e[0])?a[o]?a[o]+=e:a[++o]=e:(a[++o]=null,c.push({i:o,x:ye(r,e)})),u=hc.lastIndex;return u<t.length&&(i=t.slice(u),a[o]?a[o]+=i:a[++o]=i),a.length<2?c[0]?(t=c[0].x,function(n){return t(n)+""}):function(){return t}:(t=c.length,function(n){for(var r,e=0;t>e;++e)a[(r=c[e]).i]=r.x(n);return a.join("")})}function _e(n,t){for(var r,e=ao.interpolators.length;--e>=0&&!(r=ao.interpolators[e](n,t)););return r}function xe(n,t){var r,e=[],i=[],u=n.length,o=t.length,a=Math.min(n.length,t.length);for(r=0;a>r;++r)e.push(_e(n[r],t[r]));for(;u>r;++r)i[r]=n[r];for(;o>r;++r)i[r]=t[r];return function(n){for(r=0;a>r;++r)i[r]=e[r](n);return i}}function Me(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function be(n){return function(t){return 1-n(1-t)}}function we(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function ke(n){return n*n}function Ce(n){return n*n*n}function Ae(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,r=t*n;return 4*(.5>n?r:3*(n-t)+r-.75)}function Se(n){return function(t){return Math.pow(t,n)}}function Ee(n){return 1-Math.cos(n*Uo)}function Ne(n){return Math.pow(2,10*(n-1))}function $e(n){return 1-Math.sqrt(1-n*n)}function je(n,t){var r;return arguments.length<2&&(t=.45),arguments.length?r=t/Io*Math.asin(1/n):(n=1,r=t/4),function(e){return 1+n*Math.pow(2,-10*e)*Math.sin((e-r)*Io/t)}}function De(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Te(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 Le(n,t){n=ao.hcl(n),t=ao.hcl(t);var r=n.h,e=n.c,i=n.l,u=t.h-r,o=t.c-e,a=t.l-i;return isNaN(o)&&(o=0,e=isNaN(e)?t.c:e),isNaN(u)?(u=0,r=isNaN(r)?t.h:r):u>180?u-=360:-180>u&&(u+=360),function(n){return sn(r+u*n,e+o*n,i+a*n)+""}}function Re(n,t){n=ao.hsl(n),t=ao.hsl(t);var r=n.h,e=n.s,i=n.l,u=t.h-r,o=t.s-e,a=t.l-i;return isNaN(o)&&(o=0,e=isNaN(e)?t.s:e),isNaN(u)?(u=0,r=isNaN(r)?t.h:r):u>180?u-=360:-180>u&&(u+=360),function(n){return ln(r+u*n,e+o*n,i+a*n)+""}}function Oe(n,t){n=ao.lab(n),t=ao.lab(t);var r=n.l,e=n.a,i=n.b,u=t.l-r,o=t.a-e,a=t.b-i;return function(n){return pn(r+u*n,e+o*n,i+a*n)+""}}function ze(n,t){return t-=n,function(r){return Math.round(n+t*r)}}function qe(n){var t=[n.a,n.b],r=[n.c,n.d],e=Pe(t),i=Ie(t,r),u=Pe(Ue(r,t,-i))||0;t[0]*r[1]<r[0]*t[1]&&(t[0]*=-1,t[1]*=-1,e*=-1,i*=-1),this.rotate=(e?Math.atan2(t[1],t[0]):Math.atan2(-r[0],r[1]))*Ho,this.translate=[n.e,n.f],this.scale=[e,u],this.skew=u?Math.atan2(i,u)*Ho:0}function Ie(n,t){return n[0]*t[0]+n[1]*t[1]}function Pe(n){var t=Math.sqrt(Ie(n,n));return t&&(n[0]/=t,n[1]/=t),t}function Ue(n,t,r){return n[0]+=r*t[0],n[1]+=r*t[1],n}function Fe(n){return n.length?n.pop()+",":""}function He(n,t,r,e){if(n[0]!==t[0]||n[1]!==t[1]){var i=r.push("translate(",null,",",null,")");e.push({i:i-4,x:ye(n[0],t[0])},{i:i-2,x:ye(n[1],t[1])})}else(t[0]||t[1])&&r.push("translate("+t+")")}function We(n,t,r,e){n!==t?(n-t>180?t+=360:t-n>180&&(n+=360),e.push({i:r.push(Fe(r)+"rotate(",null,")")-2,x:ye(n,t)})):t&&r.push(Fe(r)+"rotate("+t+")")}function Be(n,t,r,e){n!==t?e.push({i:r.push(Fe(r)+"skewX(",null,")")-2,x:ye(n,t)}):t&&r.push(Fe(r)+"skewX("+t+")")}function Ye(n,t,r,e){if(n[0]!==t[0]||n[1]!==t[1]){var i=r.push(Fe(r)+"scale(",null,",",null,")");e.push({i:i-4,x:ye(n[0],t[0])},{i:i-2,x:ye(n[1],t[1])})}else 1===t[0]&&1===t[1]||r.push(Fe(r)+"scale("+t+")")}function Ze(n,t){var r=[],e=[];return n=ao.transform(n),t=ao.transform(t),He(n.translate,t.translate,r,e),We(n.rotate,t.rotate,r,e),Be(n.skew,t.skew,r,e),Ye(n.scale,t.scale,r,e),n=t=null,function(n){for(var t,i=-1,u=e.length;++i<u;)r[(t=e[i]).i]=t.x(n);return r.join("")}}function Ve(n,t){return t=(t-=n=+n)||1/t,function(r){return(r-n)/t}}function Xe(n,t){return t=(t-=n=+n)||1/t,function(r){return Math.max(0,Math.min(1,(r-n)/t))}}function Ke(n){for(var t=n.source,r=n.target,e=Qe(t,r),i=[t];t!==e;)t=t.parent,i.push(t);for(var u=i.length;r!==e;)i.splice(u,0,r),r=r.parent;return i}function Je(n){for(var t=[],r=n.parent;null!=r;)t.push(n),n=r,r=r.parent;return t.push(n),t}function Qe(n,t){if(n===t)return n;for(var r=Je(n),e=Je(t),i=r.pop(),u=e.pop(),o=null;i===u;)o=i,i=r.pop(),u=e.pop();return o}function Ge(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 ri(n){n.fixed&=-5}function ei(n,t,r){var e=0,i=0;if(n.charge=0,!n.leaf)for(var u,o=n.nodes,a=o.length,c=-1;++c<a;)u=o[c],null!=u&&(ei(u,t,r),n.charge+=u.charge,e+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var l=t*r[n.point.index];n.charge+=n.pointCharge=l,e+=l*n.point.x,i+=l*n.point.y}n.cx=e/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 r=[n];null!=(n=r.pop());)if(t(n),(i=n.children)&&(e=i.length))for(var e,i;--e>=0;)r.push(i[e])}function oi(n,t){for(var r=[n],e=[];null!=(n=r.pop());)if(e.push(n),(u=n.children)&&(i=u.length))for(var i,u,o=-1;++o<i;)r.push(u[o]);for(;null!=(n=e.pop());)t(n)}function ai(n){return n.children}function ci(n){return n.value}function li(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,r){n.y0=t,n.y=r}function vi(n){return ao.range(n.length)}function gi(n){for(var t=-1,r=n[0].length,e=[];++t<r;)e[t]=0;return e}function di(n){for(var t,r=1,e=0,i=n[0][1],u=n.length;u>r;++r)(t=n[r][1])>i&&(e=r,i=t);return e}function yi(n){return n.reduce(mi,0)}function mi(n,t){return n+t[1]}function _i(n,t){return xi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function xi(n,t){for(var r=-1,e=+n[0],i=(n[1]-e)/t,u=[];++r<=t;)u[r]=i*r+e;return u}function Mi(n){return[ao.min(n),ao.max(n)]}function bi(n,t){return n.value-t.value}function wi(n,t){var r=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=r,r._pack_prev=t}function ki(n,t){n._pack_next=t,t._pack_prev=n}function Ci(n,t){var r=t.x-n.x,e=t.y-n.y,i=n.r+t.r;return.999*i*i>r*r+e*e}function Ai(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((r=n.children)&&(l=r.length)){var r,e,i,u,o,a,c,l,f=1/0,s=-(1/0),h=1/0,p=-(1/0);if(r.forEach(Si),e=r[0],e.x=-e.r,e.y=0,t(e),l>1&&(i=r[1],i.x=i.r,i.y=0,t(i),l>2))for(u=r[2],$i(e,i,u),t(u),wi(e,u),e._pack_prev=u,wi(u,i),i=e._pack_next,o=3;l>o;o++){$i(e,i,u=r[o]);var v=0,g=1,d=1;for(a=i._pack_next;a!==i;a=a._pack_next,g++)if(Ci(a,u)){v=1;break}if(1==v)for(c=e._pack_prev;c!==a._pack_prev&&!Ci(c,u);c=c._pack_prev,d++);v?(d>g||g==d&&i.r<e.r?ki(e,i=a):ki(e=c,i),o--):(wi(e,u),i=u,t(u))}var y=(f+s)/2,m=(h+p)/2,_=0;for(o=0;l>o;o++)u=r[o],u.x-=y,u.y-=m,_=Math.max(_,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=_,r.forEach(Ei)}}function Si(n){n._pack_next=n._pack_prev=n}function Ei(n){delete n._pack_next,delete n._pack_prev}function Ni(n,t,r,e){var i=n.children;if(n.x=t+=e*n.x,n.y=r+=e*n.y,n.r*=e,i)for(var u=-1,o=i.length;++u<o;)Ni(i[u],t,r,e)}function $i(n,t,r){var e=n.r+r.r,i=t.x-n.x,u=t.y-n.y;if(e&&(i||u)){var o=t.r+r.r,a=i*i+u*u;o*=o,e*=e;var c=.5+(e-o)/(2*a),l=Math.sqrt(Math.max(0,2*o*(e+a)-(e-=a)*e-o*o))/(2*a);r.x=n.x+c*i+l*u,r.y=n.y+c*u-l*i}else r.x=n.x+e,r.y=n.y}function ji(n,t){return n.parent==t.parent?1:2}function Di(n){var t=n.children;return t.length?t[0]:n.t}function Ti(n){var t,r=n.children;return(t=r.length)?r[t-1]:n.t}function Li(n,t,r){var e=r/(t.i-n.i);t.c-=e,t.s+=r,n.c+=e,t.z+=r,t.m+=r}function Ri(n){for(var t,r=0,e=0,i=n.children,u=i.length;--u>=0;)t=i[u],t.z+=r,t.m+=r,r+=t.s+(e+=t.c)}function Oi(n,t,r){return n.a.parent===t.parent?n.a:r}function zi(n){return 1+ao.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Ii(n){var t=n.children;return t&&t.length?Ii(t[0]):n}function Pi(n){var t,r=n.children;return r&&(t=r.length)?Pi(r[t-1]):n}function Ui(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Fi(n,t){var r=n.x+t[3],e=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(r+=i/2,i=0),0>u&&(e+=u/2,u=0),{x:r,y:e,dx:i,dy:u}}function Hi(n){var t=n[0],r=n[n.length-1];return r>t?[t,r]:[r,t]}function Wi(n){return n.rangeExtent?n.rangeExtent():Hi(n.range())}function Bi(n,t,r,e){var i=r(n[0],n[1]),u=e(t[0],t[1]);return function(n){return u(i(n))}}function Yi(n,t){var r,e=0,i=n.length-1,u=n[e],o=n[i];return u>o&&(r=e,e=i,i=r,r=u,u=o,o=r),n[e]=t.floor(u),n[i]=t.ceil(o),n}function Zi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:wc}function Vi(n,t,r,e){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(r(n[o-1],n[o])),u.push(e(t[o-1],t[o]));return function(t){var r=ao.bisect(n,t,1,a)-1;return u[r](i[r](t))}}function Xi(n,t,r,e){function i(){var i=Math.min(n.length,t.length)>2?Vi:Bi,c=e?Xe:Ve;return o=i(n,t,c,r),a=i(t,n,c,_e),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(ze)},u.clamp=function(n){return arguments.length?(e=n,i()):e},u.interpolate=function(n){return arguments.length?(r=n,i()):r},u.ticks=function(t){return Gi(n,t)},u.tickFormat=function(t,r){return nu(n,t,r)},u.nice=function(t){return Ji(n,t),i()},u.copy=function(){return Xi(n,t,r,e)},i()}function Ki(n,t){return ao.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Ji(n,t){return Yi(n,Zi(Qi(n,t)[2])),Yi(n,Zi(Qi(n,t)[2])),n}function Qi(n,t){null==t&&(t=10);var r=Hi(n),e=r[1]-r[0],i=Math.pow(10,Math.floor(Math.log(e/t)/Math.LN10)),u=t/e*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),r[0]=Math.ceil(r[0]/i)*i,r[1]=Math.floor(r[1]/i)*i+.5*i,r[2]=i,r}function Gi(n,t){return ao.range.apply(ao,Qi(n,t))}function nu(n,t,r){var e=Qi(n,t);if(r){var i=sa.exec(r);if(i.shift(),"s"===i[8]){var u=ao.formatPrefix(Math.max(xo(e[0]),xo(e[1])));return i[7]||(i[7]="."+tu(u.scale(e[2]))),i[8]="f",r=ao.format(i.join("")),function(n){return r(u.scale(n))+u.symbol}}i[7]||(i[7]="."+ru(i[8],e)),r=i.join("")}else r=",."+tu(e[2])+"f";return ao.format(r)}function tu(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function ru(n,t){var r=tu(t[2]);return n in kc?Math.abs(r-tu(Math.max(xo(t[0]),xo(t[1]))))+ +("e"!==n):r-2*("%"===n)}function eu(n,t,r,e){function i(n){return(r?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function u(n){return r?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?(r=t[0]>=0,n.domain((e=t.map(Number)).map(i)),o):e},o.base=function(r){return arguments.length?(t=+r,n.domain(e.map(i)),o):t},o.nice=function(){var t=Yi(e.map(i),r?Math:Ac);return n.domain(t),e=t.map(u),o},o.ticks=function(){var n=Hi(e),o=[],a=n[0],c=n[1],l=Math.floor(i(a)),f=Math.ceil(i(c)),s=t%1?2:t;if(isFinite(f-l)){if(r){for(;f>l;l++)for(var h=1;s>h;h++)o.push(u(l)*h);o.push(u(l))}else for(o.push(u(l));l++<f;)for(var h=s-1;h>0;h--)o.push(u(l)*h);for(l=0;o[l]<a;l++);for(f=o.length;o[f-1]>c;f--);o=o.slice(l,f)}return o},o.tickFormat=function(n,r){if(!arguments.length)return Cc;arguments.length<2?r=Cc:"function"!=typeof r&&(r=ao.format(r));var e=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),e>=o?r(n):""}},o.copy=function(){return eu(n.copy(),t,r,e)},Ki(o,n)}function iu(n,t,r){function e(t){return n(i(t))}var i=uu(t),u=uu(1/t);return e.invert=function(t){return u(n.invert(t))},e.domain=function(t){return arguments.length?(n.domain((r=t.map(Number)).map(i)),e):r},e.ticks=function(n){return Gi(r,n)},e.tickFormat=function(n,t){return nu(r,n,t)},e.nice=function(n){return e.domain(Ji(r,n))},e.exponent=function(o){return arguments.length?(i=uu(t=o),u=uu(1/t),n.domain(r.map(i)),e):t},e.copy=function(){return iu(n.copy(),t,r)},Ki(e,n)}function uu(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function r(r){return u[((i.get(r)||("range"===t.t?i.set(r,n.push(r)):NaN))-1)%u.length]}function e(t,r){return ao.range(n.length).map(function(n){return t+r*n})}var i,u,o;return r.domain=function(e){if(!arguments.length)return n;n=[],i=new l;for(var u,o=-1,a=e.length;++o<a;)i.has(u=e[o])||i.set(u,n.push(u));return r[t.t].apply(r,t.a)},r.range=function(n){return arguments.length?(u=n,o=0,t={t:"range",a:arguments},r):u},r.rangePoints=function(i,a){arguments.length<2&&(a=0);var c=i[0],l=i[1],f=n.length<2?(c=(c+l)/2,0):(l-c)/(n.length-1+a);return u=e(c+f*a/2,f),o=0,t={t:"rangePoints",a:arguments},r},r.rangeRoundPoints=function(i,a){arguments.length<2&&(a=0);var c=i[0],l=i[1],f=n.length<2?(c=l=Math.round((c+l)/2),0):(l-c)/(n.length-1+a)|0;return u=e(c+Math.round(f*a/2+(l-c-(n.length-1+a)*f)/2),f),o=0,t={t:"rangeRoundPoints",a:arguments},r},r.rangeBands=function(i,a,c){arguments.length<2&&(a=0),arguments.length<3&&(c=a);var l=i[1]<i[0],f=i[l-0],s=i[1-l],h=(s-f)/(n.length-a+2*c);return u=e(f+h*c,h),l&&u.reverse(),o=h*(1-a),t={t:"rangeBands",a:arguments},r},r.rangeRoundBands=function(i,a,c){arguments.length<2&&(a=0),arguments.length<3&&(c=a);var l=i[1]<i[0],f=i[l-0],s=i[1-l],h=Math.floor((s-f)/(n.length-a+2*c));return u=e(f+Math.round((s-f-(n.length-a)*h)/2),h),l&&u.reverse(),o=Math.round(h*(1-a)),t={t:"rangeRoundBands",a:arguments},r},r.rangeBand=function(){return o},r.rangeExtent=function(){return Hi(t.a[0])},r.copy=function(){return ou(n,t)},r.domain(n)}function au(n,t){function u(){var r=0,e=t.length;for(a=[];++r<e;)a[r-1]=ao.quantile(n,r/e);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(e).filter(i).sort(r),u()):n},o.range=function(n){return arguments.length?(t=n,u()):t},o.quantiles=function(){return a},o.invertExtent=function(r){return r=t.indexOf(r),0>r?[NaN,NaN]:[r>0?a[r-1]:n[0],r<a.length?a[r]:n[n.length-1]]},o.copy=function(){return au(n,t)},u()}function cu(n,t,r){function e(t){return r[Math.max(0,Math.min(o,Math.floor(u*(t-n))))]}function i(){return u=r.length/(t-n),o=r.length-1,e}var u,o;return e.domain=function(r){return arguments.length?(n=+r[0],t=+r[r.length-1],i()):[n,t]},e.range=function(n){return arguments.length?(r=n,i()):r},e.invertExtent=function(t){return t=r.indexOf(t),t=0>t?NaN:t/u+n,[t,t+1/u]},e.copy=function(){return cu(n,t,r)},i()}function lu(n,t){function r(r){return r>=r?t[ao.bisect(n,r)]:void 0}return r.domain=function(t){return arguments.length?(n=t,r):n},r.range=function(n){return arguments.length?(t=n,r):t},r.invertExtent=function(r){return r=t.indexOf(r),[n[r-1],n[r]]},r.copy=function(){return lu(n,t)},r}function fu(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(r){return arguments.length?(n=r.map(t),t):n},t.ticks=function(t){return Gi(n,t)},t.tickFormat=function(t,r){return nu(n,t,r)},t.copy=function(){return fu(n)},t}function su(){return 0}function hu(n){return n.innerRadius}function pu(n){return n.outerRadius}function vu(n){return n.startAngle}function gu(n){return n.endAngle}function du(n){return n&&n.padAngle}function yu(n,t,r,e){return(n-r)*t-(t-e)*n>0?0:1}function mu(n,t,r,e,i){var u=n[0]-t[0],o=n[1]-t[1],a=(i?e:-e)/Math.sqrt(u*u+o*o),c=a*o,l=-a*u,f=n[0]+c,s=n[1]+l,h=t[0]+c,p=t[1]+l,v=(f+h)/2,g=(s+p)/2,d=h-f,y=p-s,m=d*d+y*y,_=r-e,x=f*p-h*s,M=(0>y?-1:1)*Math.sqrt(Math.max(0,_*_*m-x*x)),b=(x*y-d*M)/m,w=(-x*d-y*M)/m,k=(x*y+d*M)/m,C=(-x*d+y*M)/m,A=b-v,S=w-g,E=k-v,N=C-g;return A*A+S*S>E*E+N*N&&(b=k,w=C),[[b-c,w-l],[b*r/_,w*r/_]]}function _u(n){function t(t){function o(){l.push("M",u(n(f),a))}for(var c,l=[],f=[],s=-1,h=t.length,p=Sn(r),v=Sn(e);++s<h;)i.call(this,c=t[s],s)?f.push([+p.call(this,c,s),+v.call(this,c,s)]):f.length&&(o(),f=[]);return f.length&&o(),l.length?l.join(""):null}var r=Nr,e=$r,i=$t,u=xu,o=u.key,a=.7;return t.x=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(e=n,t):e},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=Dc.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 Mu(n){return n.join("L")+"Z"}function bu(n){for(var t=0,r=n.length,e=n[0],i=[e[0],",",e[1]];++t<r;)i.push("H",(e[0]+(e=n[t])[0])/2,"V",e[1]);return r>1&&i.push("H",e[0]),i.join("")}function wu(n){for(var t=0,r=n.length,e=n[0],i=[e[0],",",e[1]];++t<r;)i.push("V",(e=n[t])[1],"H",e[0]);return i.join("")}function ku(n){for(var t=0,r=n.length,e=n[0],i=[e[0],",",e[1]];++t<r;)i.push("H",(e=n[t])[0],"V",e[1]);return i.join("")}function Cu(n,t){return n.length<4?xu(n):n[1]+Eu(n.slice(1,-1),Nu(n,t))}function Au(n,t){return n.length<3?Mu(n):n[0]+Eu((n.push(n[0]),n),Nu([n[n.length-2]].concat(n,[n[1]]),t))}function Su(n,t){return n.length<3?xu(n):n[0]+Eu(n,Nu(n,t))}function Eu(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return xu(n);var r=n.length!=t.length,e="",i=n[0],u=n[1],o=t[0],a=o,c=1;if(r&&(e+="Q"+(u[0]-2*o[0]/3)+","+(u[1]-2*o[1]/3)+","+u[0]+","+u[1],i=n[1],c=2),t.length>1){a=t[1],u=n[c],c++,e+="C"+(i[0]+o[0])+","+(i[1]+o[1])+","+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1];for(var l=2;l<t.length;l++,c++)u=n[c],a=t[l],e+="S"+(u[0]-a[0])+","+(u[1]-a[1])+","+u[0]+","+u[1]}if(r){var f=n[c];e+="Q"+(u[0]+2*a[0]/3)+","+(u[1]+2*a[1]/3)+","+f[0]+","+f[1]}return e}function Nu(n,t){for(var r,e=[],i=(1-t)/2,u=n[0],o=n[1],a=1,c=n.length;++a<c;)r=u,u=o,o=n[a],e.push([i*(o[0]-r[0]),i*(o[1]-r[1])]);return e}function $u(n){if(n.length<3)return xu(n);var t=1,r=n.length,e=n[0],i=e[0],u=e[1],o=[i,i,i,(e=n[1])[0]],a=[u,u,u,e[1]],c=[i,",",u,"L",Lu(Rc,o),",",Lu(Rc,a)];for(n.push(n[r-1]);++t<=r;)e=n[t],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Ru(c,o,a);return n.pop(),c.push("L",e),c.join("")}function ju(n){if(n.length<4)return xu(n);for(var t,r=[],e=-1,i=n.length,u=[0],o=[0];++e<3;)t=n[e],u.push(t[0]),o.push(t[1]);for(r.push(Lu(Rc,u)+","+Lu(Rc,o)),--e;++e<i;)t=n[e],u.shift(),u.push(t[0]),o.shift(),o.push(t[1]),Ru(r,u,o);return r.join("")}function Du(n){for(var t,r,e=-1,i=n.length,u=i+4,o=[],a=[];++e<4;)r=n[e%i],o.push(r[0]),a.push(r[1]);for(t=[Lu(Rc,o),",",Lu(Rc,a)],--e;++e<u;)r=n[e%i],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Ru(t,o,a);return t.join("")}function Tu(n,t){var r=n.length-1;if(r)for(var e,i,u=n[0][0],o=n[0][1],a=n[r][0]-u,c=n[r][1]-o,l=-1;++l<=r;)e=n[l],i=l/r,e[0]=t*e[0]+(1-t)*(u+i*a),e[1]=t*e[1]+(1-t)*(o+i*c);return $u(n)}function Lu(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Ru(n,t,r){n.push("C",Lu(Tc,t),",",Lu(Tc,r),",",Lu(Lc,t),",",Lu(Lc,r),",",Lu(Rc,t),",",Lu(Rc,r))}function Ou(n,t){return(t[1]-n[1])/(t[0]-n[0])}function zu(n){for(var t=0,r=n.length-1,e=[],i=n[0],u=n[1],o=e[0]=Ou(i,u);++t<r;)e[t]=(o+(o=Ou(i=u,u=n[t+1])))/2;return e[t]=o,e}function qu(n){for(var t,r,e,i,u=[],o=zu(n),a=-1,c=n.length-1;++a<c;)t=Ou(n[a],n[a+1]),xo(t)<Oo?o[a]=o[a+1]=0:(r=o[a]/t,e=o[a+1]/t,i=r*r+e*e,i>9&&(i=3*t/Math.sqrt(i),o[a]=i*r,o[a+1]=i*e));for(a=-1;++a<=c;)i=(n[Math.min(c,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 Iu(n){return n.length<3?xu(n):n[0]+Eu(n,qu(n))}function Pu(n){for(var t,r,e,i=-1,u=n.length;++i<u;)t=n[i],r=t[0],e=t[1]-Uo,t[0]=r*Math.cos(e),t[1]=r*Math.sin(e);return n}function Uu(n){function t(t){function c(){g.push("M",a(n(y),s),f,l(n(d.reverse()),s),"Z")}for(var h,p,v,g=[],d=[],y=[],m=-1,_=t.length,x=Sn(r),M=Sn(i),b=r===e?function(){
+return p}:Sn(e),w=i===u?function(){return v}:Sn(u);++m<_;)o.call(this,h=t[m],m)?(d.push([p=+x.call(this,h,m),v=+M.call(this,h,m)]),y.push([+b.call(this,h,m),+w.call(this,h,m)])):d.length&&(c(),d=[],y=[]);return d.length&&c(),g.length?g.join(""):null}var r=Nr,e=Nr,i=0,u=$r,o=$t,a=xu,c=a.key,l=a,f="L",s=.7;return t.x=function(n){return arguments.length?(r=e=n,t):e},t.x0=function(n){return arguments.length?(r=n,t):r},t.x1=function(n){return arguments.length?(e=n,t):e},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?(c="function"==typeof n?a=n:(a=Dc.get(n)||xu).key,l=a.reverse||a,f=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(s=n,t):s},t}function Fu(n){return n.radius}function Hu(n){return[n.x,n.y]}function Wu(n){return function(){var t=n.apply(this,arguments),r=t[0],e=t[1]-Uo;return[r*Math.cos(e),r*Math.sin(e)]}}function Bu(){return 64}function Yu(){return"circle"}function Zu(n){var t=Math.sqrt(n/qo);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Vu(n){return function(){var t,r,e;(t=this[n])&&(e=t[r=t.active])&&(e.timer.c=null,e.timer.t=NaN,--t.count?delete t[r]:delete this[n],t.active+=.5,e.event&&e.event.interrupt.call(this,this.__data__,e.index))}}function Xu(n,t,r){return Co(n,Fc),n.namespace=t,n.id=r,n}function Ku(n,t,r,e){var i=n.id,u=n.namespace;return H(n,"function"==typeof r?function(n,o,a){n[u][i].tween.set(t,e(r.call(n,n.__data__,o,a)))}:(r=e(r),function(n){n[u][i].tween.set(t,r)}))}function Ju(n){return null==n&&(n=""),function(){this.textContent=n}}function Qu(n){return null==n?"__transition__":"__transition_"+n+"__"}function Gu(n,t,r,e,i){function u(n){var t=g.delay;return f.t=t+c,n>=t?o(n-t):void(f.c=o)}function o(r){var i=v.active,u=v[i];u&&(u.timer.c=null,u.timer.t=NaN,--v.count,delete v[i],u.event&&u.event.interrupt.call(n,n.__data__,u.index));for(var o in v)if(e>+o){var l=v[o];l.timer.c=null,l.timer.t=NaN,--v.count,delete v[o]}f.c=a,Dn(function(){return f.c&&a(r||1)&&(f.c=null,f.t=NaN),1},0,c),v.active=e,g.event&&g.event.start.call(n,n.__data__,t),p=[],g.tween.forEach(function(r,e){(e=e.call(n,n.__data__,t))&&p.push(e)}),h=g.ease,s=g.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?(g.event&&g.event.end.call(n,n.__data__,t),--v.count?delete v[e]:delete n[r],1):void 0}var c,f,s,h,p,v=n[r]||(n[r]={active:0,count:0}),g=v[e];g||(c=i.time,f=Dn(u,0,c),g=v[e]={tween:new l,time:c,timer:f,delay:i.delay,duration:i.duration,ease:i.ease,index:t},i=null,++v.count)}function no(n,t,r){n.attr("transform",function(n){var e=t(n);return"translate("+(isFinite(e)?e:r(n))+",0)"})}function to(n,t,r){n.attr("transform",function(n){var e=t(n);return"translate(0,"+(isFinite(e)?e:r(n))+")"})}function ro(n){return n.toISOString()}function eo(n,t,r){function e(t){return n(t)}function i(n,r){var e=n[1]-n[0],i=e/r,u=ao.bisect(Jc,i);return u==Jc.length?[t.year,Qi(n.map(function(n){return n/31536e6}),r)[2]]:u?t[i/Jc[u-1]<Jc[u]/i?u-1:u]:[nl,Qi(n,r)[2]]}return e.invert=function(t){return io(n.invert(t))},e.domain=function(t){return arguments.length?(n.domain(t),e):n.domain().map(io)},e.nice=function(n,t){function r(r){return!isNaN(r)&&!n.range(r,io(+r+1),t).length}var u=e.domain(),o=Hi(u),a=null==n?i(o,10):"number"==typeof n&&i(o,n);return a&&(n=a[0],t=a[1]),e.domain(Yi(u,t>1?{floor:function(t){for(;r(t=n.floor(t));)t=io(t-1);return t},ceil:function(t){for(;r(t=n.ceil(t));)t=io(+t+1);return t}}:n))},e.ticks=function(n,t){var r=Hi(e.domain()),u=null==n?i(r,10):"number"==typeof n?i(r,n):!n.range&&[{range:n},t];return u&&(n=u[0],t=u[1]),n.range(r[0],io(+r[1]+1),1>t?1:t)},e.tickFormat=function(){return r},e.copy=function(){return eo(n.copy(),t,r)},Ki(e,n)}function io(n){return new Date(n)}function uo(n){return JSON.parse(n.responseText)}function oo(n){var t=fo.createRange();return t.selectNode(fo.body),t.createContextualFragment(n.responseText)}var ao={version:"3.5.15"},co=[].slice,lo=function(n){return co.call(n)},fo=this.document;if(fo)try{lo(fo.documentElement.childNodes)[0].nodeType}catch(so){lo=function(n){for(var t=n.length,r=new Array(t);t--;)r[t]=n[t];return r}}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,vo=po.setAttribute,go=po.setAttributeNS,yo=this.CSSStyleDeclaration.prototype,mo=yo.setProperty;po.setAttribute=function(n,t){vo.call(this,n,t+"")},po.setAttributeNS=function(n,t,r){go.call(this,n,t,r+"")},yo.setProperty=function(n,t,r){mo.call(this,n,t+"",r)}}ao.ascending=r,ao.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:NaN},ao.min=function(n,t){var r,e,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(e=n[i])&&e>=e){r=e;break}for(;++i<u;)null!=(e=n[i])&&r>e&&(r=e)}else{for(;++i<u;)if(null!=(e=t.call(n,n[i],i))&&e>=e){r=e;break}for(;++i<u;)null!=(e=t.call(n,n[i],i))&&r>e&&(r=e)}return r},ao.max=function(n,t){var r,e,i=-1,u=n.length;if(1===arguments.length){for(;++i<u;)if(null!=(e=n[i])&&e>=e){r=e;break}for(;++i<u;)null!=(e=n[i])&&e>r&&(r=e)}else{for(;++i<u;)if(null!=(e=t.call(n,n[i],i))&&e>=e){r=e;break}for(;++i<u;)null!=(e=t.call(n,n[i],i))&&e>r&&(r=e)}return r},ao.extent=function(n,t){var r,e,i,u=-1,o=n.length;if(1===arguments.length){for(;++u<o;)if(null!=(e=n[u])&&e>=e){r=i=e;break}for(;++u<o;)null!=(e=n[u])&&(r>e&&(r=e),e>i&&(i=e))}else{for(;++u<o;)if(null!=(e=t.call(n,n[u],u))&&e>=e){r=i=e;break}for(;++u<o;)null!=(e=t.call(n,n[u],u))&&(r>e&&(r=e),e>i&&(i=e))}return[r,i]},ao.sum=function(n,t){var r,e=0,u=n.length,o=-1;if(1===arguments.length)for(;++o<u;)i(r=+n[o])&&(e+=r);else for(;++o<u;)i(r=+t.call(n,n[o],o))&&(e+=r);return e},ao.mean=function(n,t){var r,u=0,o=n.length,a=-1,c=o;if(1===arguments.length)for(;++a<o;)i(r=e(n[a]))?u+=r:--c;else for(;++a<o;)i(r=e(t.call(n,n[a],a)))?u+=r:--c;return c?u/c:void 0},ao.quantile=function(n,t){var r=(n.length-1)*t+1,e=Math.floor(r),i=+n[e-1],u=r-e;return u?i+u*(n[e]-i):i},ao.median=function(n,t){var u,o=[],a=n.length,c=-1;if(1===arguments.length)for(;++c<a;)i(u=e(n[c]))&&o.push(u);else for(;++c<a;)i(u=e(t.call(n,n[c],c)))&&o.push(u);return o.length?ao.quantile(o.sort(r),.5):void 0},ao.variance=function(n,t){var r,u,o=n.length,a=0,c=0,l=-1,f=0;if(1===arguments.length)for(;++l<o;)i(r=e(n[l]))&&(u=r-a,a+=u/++f,c+=u*(r-a));else for(;++l<o;)i(r=e(t.call(n,n[l],l)))&&(u=r-a,a+=u/++f,c+=u*(r-a));return f>1?c/(f-1):void 0},ao.deviation=function(){var n=ao.variance.apply(this,arguments);return n?Math.sqrt(n):n};var _o=u(r);ao.bisectLeft=_o.left,ao.bisect=ao.bisectRight=_o.right,ao.bisector=function(n){return u(1===n.length?function(t,e){return r(n(t),e)}:n)},ao.shuffle=function(n,t,r){(u=arguments.length)<3&&(r=n.length,2>u&&(t=0));for(var e,i,u=r-t;u;)i=Math.random()*u--|0,e=n[u+t],n[u+t]=n[i+t],n[i+t]=e;return n},ao.permute=function(n,t){for(var r=t.length,e=new Array(r);r--;)e[r]=n[t[r]];return e},ao.pairs=function(n){for(var t,r=0,e=n.length-1,i=n[0],u=new Array(0>e?0:e);e>r;)u[r]=[t=i,i=n[++r]];return u},ao.transpose=function(n){if(!(i=n.length))return[];for(var t=-1,r=ao.min(n,o),e=new Array(r);++t<r;)for(var i,u=-1,a=e[t]=new Array(i);++u<i;)a[u]=n[u][t];return e},ao.zip=function(){return ao.transpose(arguments)},ao.keys=function(n){var t=[];for(var r in n)t.push(r);return t},ao.values=function(n){var t=[];for(var r in n)t.push(n[r]);return t},ao.entries=function(n){var t=[];for(var r in n)t.push({key:r,value:n[r]});return t},ao.merge=function(n){for(var t,r,e,i=n.length,u=-1,o=0;++u<i;)o+=n[u].length;for(r=new Array(o);--i>=0;)for(e=n[i],t=e.length;--t>=0;)r[--o]=e[t];return r};var xo=Math.abs;ao.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),(t-n)/r===1/0)throw new Error("infinite range");var e,i=[],u=a(xo(r)),o=-1;if(n*=u,t*=u,r*=u,0>r)for(;(e=n+r*++o)>t;)i.push(e/u);else for(;(e=n+r*++o)<t;)i.push(e/u);return i},ao.map=function(n,t){var r=new l;if(n instanceof l)n.forEach(function(n,t){r.set(n,t)});else if(Array.isArray(n)){var e,i=-1,u=n.length;if(1===arguments.length)for(;++i<u;)r.set(i,n[i]);else for(;++i<u;)r.set(t.call(n,e=n[i],i),e)}else for(var o in n)r.set(o,n[o]);return r};var Mo="__proto__",bo="\x00";c(l,{has:h,get:function(n){return this._[f(n)]},set:function(n,t){return this._[f(n)]=t},remove:p,keys:v,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:g,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 e?e.call(i,o):r?o.sort(r):o;for(var c,f,s,h,p=-1,v=o.length,g=u[a++],d=new l;++p<v;)(h=d.get(c=g(f=o[p])))?h.push(f):d.set(c,[f]);return t?(f=t(),s=function(r,e){f.set(r,n(t,e,a))}):(f={},s=function(r,e){f[r]=n(t,e,a)}),d.forEach(s),f}function t(n,r){if(r>=u.length)return n;var e=[],i=o[r++];return n.forEach(function(n,i){e.push({key:n,values:t(i,r)})}),i?e.sort(function(n,t){return i(n.key,t.key)}):e}var r,e,i={},u=[],o=[];return i.map=function(t,r){return n(r,t,0)},i.entries=function(r){return t(n(ao.map,r,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 r=n,i},i.rollup=function(n){return e=n,i},i},ao.set=function(n){var t=new y;if(n)for(var r=0,e=n.length;e>r;++r)t.add(n[r]);return t},c(y,{has:h,add:function(n){return this._[f(n+="")]=!0,n},remove:p,values:v,size:g,empty:d,forEach:function(n){for(var t in this._)n.call(this,s(t))}}),ao.behavior={},ao.rebind=function(n,t){for(var r,e=1,i=arguments.length;++e<i;)n[r=arguments[e]]=_(n,t,t[r]);return n};var wo=["webkit","ms","moz","Moz","o","O"];ao.dispatch=function(){for(var n=new b,t=-1,r=arguments.length;++t<r;)n[arguments[t]]=w(n);return n},b.prototype.on=function(n,t){var r=n.indexOf("."),e="";if(r>=0&&(e=n.slice(r+1),n=n.slice(0,r)),n)return arguments.length<2?this[n].on(e):this[n].on(e,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(e,null);return this}},ao.event=null,ao.requote=function(n){return n.replace(ko,"\\$&")};var ko=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,Co={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var r in t)n[r]=t[r]},Ao=function(n,t){return t.querySelector(n)},So=function(n,t){return t.querySelectorAll(n)},Eo=function(n,t){var r=n.matches||n[x(n,"matchesSelector")];return(Eo=function(n,t){return r.call(n,t)})(n,t)};"function"==typeof Sizzle&&(Ao=function(n,t){return Sizzle(n,t)[0]||null},So=Sizzle,Eo=Sizzle.matchesSelector),ao.selection=function(){return ao.select(fo.documentElement)};var No=ao.selection.prototype=[];No.select=function(n){var t,r,e,i,u=[];n=E(n);for(var o=-1,a=this.length;++o<a;){u.push(t=[]),t.parentNode=(e=this[o]).parentNode;for(var c=-1,l=e.length;++c<l;)(i=e[c])?(t.push(r=n.call(i,i.__data__,c,o)),r&&"__data__"in i&&(r.__data__=i.__data__)):t.push(null)}return S(u)},No.selectAll=function(n){var t,r,e=[];n=N(n);for(var i=-1,u=this.length;++i<u;)for(var o=this[i],a=-1,c=o.length;++a<c;)(r=o[a])&&(e.push(t=lo(n.call(r,r.__data__,a,i))),t.parentNode=r);return S(e)};var $o={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",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:$o,qualify:function(n){var t=n.indexOf(":"),r=n;return t>=0&&"xmlns"!==(r=n.slice(0,t))&&(n=n.slice(t+1)),$o.hasOwnProperty(r)?{space:$o[r],local:n}:n}},No.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var r=this.node();return n=ao.ns.qualify(n),n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}for(t in n)this.each($(t,n[t]));return this}return this.each($(n,t))},No.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var r=this.node(),e=(n=T(n)).length,i=-1;if(t=r.classList){for(;++i<e;)if(!t.contains(n[i]))return!1}else for(t=r.getAttribute("class");++i<e;)if(!D(n[i]).test(t))return!1;return!0}for(t in n)this.each(L(t,n[t]));return this}return this.each(L(n,t))},No.style=function(n,r,e){var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(r="");for(e in n)this.each(O(e,n[e],r));return this}if(2>i){var u=this.node();return t(u).getComputedStyle(u,null).getPropertyValue(n)}e=""}return this.each(O(n,r,e))},No.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},No.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},No.html=function(n){return arguments.length?this.each("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},No.append=function(n){return n=q(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},No.insert=function(n,t){return n=q(n),t=E(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},No.remove=function(){return this.each(I)},No.data=function(n,t){function r(n,r){var e,i,u,o=n.length,s=r.length,h=Math.min(o,s),p=new Array(s),v=new Array(s),g=new Array(o);if(t){var d,y=new l,m=new Array(o);for(e=-1;++e<o;)(i=n[e])&&(y.has(d=t.call(i,i.__data__,e))?g[e]=i:y.set(d,i),m[e]=d);for(e=-1;++e<s;)(i=y.get(d=t.call(r,u=r[e],e)))?i!==!0&&(p[e]=i,i.__data__=u):v[e]=P(u),y.set(d,!0);for(e=-1;++e<o;)e in m&&y.get(m[e])!==!0&&(g[e]=n[e])}else{for(e=-1;++e<h;)i=n[e],u=r[e],i?(i.__data__=u,p[e]=i):v[e]=P(u);for(;s>e;++e)v[e]=P(r[e]);for(;o>e;++e)g[e]=n[e]}v.update=p,v.parentNode=p.parentNode=g.parentNode=n.parentNode,a.push(v),c.push(p),f.push(g)}var e,i,u=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(e=this[0]).length);++u<o;)(i=e[u])&&(n[u]=i.__data__);return n}var a=W([]),c=S([]),f=S([]);if("function"==typeof n)for(;++u<o;)r(e=this[u],n.call(e,e.parentNode.__data__,u));else for(;++u<o;)r(e=this[u],n);return c.enter=function(){return a},c.exit=function(){return f},c},No.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},No.filter=function(n){var t,r,e,i=[];"function"!=typeof n&&(n=U(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]),t.parentNode=(r=this[u]).parentNode;for(var a=0,c=r.length;c>a;a++)(e=r[a])&&n.call(e,e.__data__,a,u)&&t.push(e)}return S(i)},No.order=function(){for(var n=-1,t=this.length;++n<t;)for(var r,e=this[n],i=e.length-1,u=e[i];--i>=0;)(r=e[i])&&(u&&u!==r.nextSibling&&u.parentNode.insertBefore(r,u),u=r);return this},No.sort=function(n){n=F.apply(this,arguments);for(var t=-1,r=this.length;++t<r;)this[t].sort(n);return this.order()},No.each=function(n){return H(this,function(t,r,e){n.call(t,t.__data__,r,e)})},No.call=function(n){var t=lo(arguments);return n.apply(t[0]=this,t),this},No.empty=function(){return!this.node()},No.node=function(){for(var n=0,t=this.length;t>n;n++)for(var r=this[n],e=0,i=r.length;i>e;e++){var u=r[e];if(u)return u}return null},No.size=function(){var n=0;return H(this,function(){++n}),n};var jo=[];ao.selection.enter=W,ao.selection.enter.prototype=jo,jo.append=No.append,jo.empty=No.empty,jo.node=No.node,jo.call=No.call,jo.size=No.size,jo.select=function(n){for(var t,r,e,i,u,o=[],a=-1,c=this.length;++a<c;){e=(i=this[a]).update,o.push(t=[]),t.parentNode=i.parentNode;for(var l=-1,f=i.length;++l<f;)(u=i[l])?(t.push(e[l]=r=n.call(i.parentNode,u.__data__,l,a)),r.__data__=u.__data__):t.push(null)}return S(o)},jo.insert=function(n,t){return arguments.length<2&&(t=B(this)),No.insert.call(this,n,t)},ao.select=function(t){var r;return"string"==typeof t?(r=[Ao(t,fo)],r.parentNode=fo.documentElement):(r=[t],r.parentNode=n(t)),S([r])},ao.selectAll=function(n){var t;return"string"==typeof n?(t=lo(So(n,fo)),t.parentNode=fo.documentElement):(t=lo(n),t.parentNode=null),S([t])},No.on=function(n,t,r){var e=arguments.length;if(3>e){if("string"!=typeof n){2>e&&(t=!1);for(r in n)this.each(Y(r,n[r],t));return this}if(2>e)return(e=this.node()["__on"+n])&&e._;r=!1}return this.each(Y(n,t,r))};var Do=ao.map({mouseenter:"mouseover",mouseleave:"mouseout"});fo&&Do.forEach(function(n){"on"+n in fo&&Do.remove(n)});var To,Lo=0;ao.mouse=function(n){return K(n,C())};var Ro=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ao.touch=function(n,t,r){if(arguments.length<3&&(r=t,t=C().changedTouches),t)for(var e,i=0,u=t.length;u>i;++i)if((e=t[i]).identifier===r)return K(n,e)},ao.behavior.drag=function(){function n(){this.on("mousedown.drag",u).on("touchstart.drag",o)}function r(n,t,r,u,o){return function(){function a(){var n,r,e=t(h,g);e&&(n=e[0]-_[0],r=e[1]-_[1],v|=n|r,_=e,p({type:"drag",x:e[0]+l[0],y:e[1]+l[1],dx:n,dy:r}))}function c(){t(h,g)&&(y.on(u+d,null).on(o+d,null),m(v),p({type:"dragend"}))}var l,f=this,s=ao.event.target.correspondingElement||ao.event.target,h=f.parentNode,p=e.of(f,arguments),v=0,g=n(),d=".drag"+(null==g?"":"-"+g),y=ao.select(r(s)).on(u+d,a).on(o+d,c),m=X(s),_=t(h,g);i?(l=i.apply(f,arguments),l=[l.x-_[0],l.y-_[1]]):l=[0,0],p({type:"dragstart"})}}var e=A(n,"drag","dragstart","dragend"),i=null,u=r(M,ao.mouse,t,"mousemove","mouseup"),o=r(J,ao.touch,m,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},ao.rebind(n,e,"on")},ao.touches=function(n,t){return arguments.length<2&&(t=C().touches),t?lo(t).map(function(t){var r=K(n,t);return r.identifier=t.identifier,r}):[]};var Oo=1e-6,zo=Oo*Oo,qo=Math.PI,Io=2*qo,Po=Io-Oo,Uo=qo/2,Fo=qo/180,Ho=180/qo,Wo=Math.SQRT2,Bo=2,Yo=4;ao.interpolateZoom=function(n,t){var r,e,i=n[0],u=n[1],o=n[2],a=t[0],c=t[1],l=t[2],f=a-i,s=c-u,h=f*f+s*s;if(zo>h)e=Math.log(l/o)/Wo,r=function(n){return[i+n*f,u+n*s,o*Math.exp(Wo*n*e)]};else{var p=Math.sqrt(h),v=(l*l-o*o+Yo*h)/(2*o*Bo*p),g=(l*l-o*o-Yo*h)/(2*l*Bo*p),d=Math.log(Math.sqrt(v*v+1)-v),y=Math.log(Math.sqrt(g*g+1)-g);e=(y-d)/Wo,r=function(n){var t=n*e,r=en(d),a=o/(Bo*p)*(r*un(Wo*t+d)-rn(d));return[i+a*f,u+a*s,o*r/en(Wo*t+d)]}}return r.duration=1e3*e,r},ao.behavior.zoom=function(){function n(n){n.on(j,s).on(Vo+".zoom",p).on("dblclick.zoom",v).on(L,h)}function r(n){return[(n[0]-C.x)/C.k,(n[1]-C.y)/C.k]}function e(n){return[n[0]*C.k+C.x,n[1]*C.k+C.y]}function i(n){C.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),C.x+=n[0]-t[0],C.y+=n[1]-t[1]}function o(t,r,e,o){t.__chart__={x:C.x,y:C.y,k:C.k},i(Math.pow(2,o)),u(d=r,e),t=ao.select(t),N>0&&(t=t.transition().duration(N)),t.call(n.event)}function a(){M&&M.domain(x.range().map(function(n){return(n-C.x)/C.k}).map(x.invert)),w&&w.domain(b.range().map(function(n){return(n-C.y)/C.k}).map(b.invert))}function c(n){$++||n({type:"zoomstart"})}function l(n){a(),n({type:"zoom",scale:C.k,translate:[C.x,C.y]})}function f(n){--$||(n({type:"zoomend"}),d=null)}function s(){function n(){a=1,u(ao.mouse(i),h),l(o)}function e(){s.on(D,null).on(T,null),p(a),f(o)}var i=this,o=R.of(i,arguments),a=0,s=ao.select(t(i)).on(D,n).on(T,e),h=r(ao.mouse(i)),p=X(i);Uc.call(i),c(o)}function h(){function n(){var n=ao.touches(v);return p=C.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=r(n))}),n}function t(){var t=ao.event.target;ao.select(t).on(x,e).on(M,a),b.push(t);for(var r=ao.event.changedTouches,i=0,u=r.length;u>i;++i)d[r[i].identifier]=null;var c=n(),l=Date.now();if(1===c.length){if(500>l-_){var f=c[0];o(v,f,d[f.identifier],Math.floor(Math.log(C.k)/Math.LN2)+1),k()}_=l}else if(c.length>1){var f=c[0],s=c[1],h=f[0]-s[0],p=f[1]-s[1];y=h*h+p*p}}function e(){var n,t,r,e,o=ao.touches(v);Uc.call(v);for(var a=0,c=o.length;c>a;++a,e=null)if(r=o[a],e=d[r.identifier]){if(t)break;n=r,t=e}if(e){var f=(f=r[0]-n[0])*f+(f=r[1]-n[1])*f,s=y&&Math.sqrt(f/y);n=[(n[0]+r[0])/2,(n[1]+r[1])/2],t=[(t[0]+e[0])/2,(t[1]+e[1])/2],i(s*p)}_=null,u(n,t),l(g)}function a(){if(ao.event.touches.length){for(var t=ao.event.changedTouches,r=0,e=t.length;e>r;++r)delete d[t[r].identifier];for(var i in d)return void n()}ao.selectAll(b).on(m,null),w.on(j,s).on(L,h),A(),f(g)}var p,v=this,g=R.of(v,arguments),d={},y=0,m=".zoom-"+ao.event.changedTouches[0].identifier,x="touchmove"+m,M="touchend"+m,b=[],w=ao.select(v),A=X(v);t(),c(g),w.on(j,null).on(L,t)}function p(){var n=R.of(this,arguments);m?clearTimeout(m):(Uc.call(this),g=r(d=y||ao.mouse(this)),c(n)),m=setTimeout(function(){m=null,f(n)},50),k(),i(Math.pow(2,.002*Zo())*C.k),u(d,g),l(n)}function v(){var n=ao.mouse(this),t=Math.log(C.k)/Math.LN2;o(this,n,r(n),ao.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var g,d,y,m,_,x,M,b,w,C={x:0,y:0,k:1},S=[960,500],E=Xo,N=250,$=0,j="mousedown.zoom",D="mousemove.zoom",T="mouseup.zoom",L="touchstart.zoom",R=A(n,"zoomstart","zoom","zoomend");return Vo||(Vo="onwheel"in fo?(Zo=function(){return-ao.event.deltaY*(ao.event.deltaMode?120:1)},"wheel"):"onmousewheel"in fo?(Zo=function(){return ao.event.wheelDelta},"mousewheel"):(Zo=function(){return-ao.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=R.of(this,arguments),t=C;Ic?ao.select(this).transition().each("start.zoom",function(){C=this.__chart__||{x:0,y:0,k:1},c(n)}).tween("zoom:zoom",function(){var r=S[0],e=S[1],i=d?d[0]:r/2,u=d?d[1]:e/2,o=ao.interpolateZoom([(i-C.x)/C.k,(u-C.y)/C.k,r/C.k],[(i-t.x)/t.k,(u-t.y)/t.k,r/t.k]);return function(t){var e=o(t),a=r/e[2];this.__chart__=C={x:i-e[0]*a,y:u-e[1]*a,k:a},l(n)}}).each("interrupt.zoom",function(){f(n)}).each("end.zoom",function(){f(n)}):(this.__chart__=C,c(n),l(n),f(n))})},n.translate=function(t){return arguments.length?(C={x:+t[0],y:+t[1],k:C.k},a(),n):[C.x,C.y]},n.scale=function(t){return arguments.length?(C={x:C.x,y:C.y,k:null},i(+t),a(),n):C.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Xo:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(y=t&&[+t[0],+t[1]],n):y},n.size=function(t){return arguments.length?(S=t&&[+t[0],+t[1]],n):S},n.duration=function(t){return arguments.length?(N=+t,n):N},n.x=function(t){return arguments.length?(M=t,x=t.copy(),C={x:0,y:0,k:1},n):M},n.y=function(t){return arguments.length?(w=t,b=t.copy(),C={x:0,y:0,k:1},n):w},ao.rebind(n,R,"on")};var Zo,Vo,Xo=[0,1/0];ao.color=an,an.prototype.toString=function(){return this.rgb()+""},ao.hsl=cn;var Ko=cn.prototype=new an;Ko.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new cn(this.h,this.s,this.l/n)},Ko.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new cn(this.h,this.s,n*this.l)},Ko.rgb=function(){return ln(this.h,this.s,this.l)},ao.hcl=fn;var Jo=fn.prototype=new an;Jo.brighter=function(n){return new fn(this.h,this.c,Math.min(100,this.l+Qo*(arguments.length?n:1)))},Jo.darker=function(n){return new fn(this.h,this.c,Math.max(0,this.l-Qo*(arguments.length?n:1)))},Jo.rgb=function(){return sn(this.h,this.c,this.l).rgb()},ao.lab=hn;var Qo=18,Go=.95047,na=1,ta=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 ea=mn.prototype=new an;ea.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,r=this.g,e=this.b,i=30;return t||r||e?(t&&i>t&&(t=i),r&&i>r&&(r=i),e&&i>e&&(e=i),new mn(Math.min(255,t/n),Math.min(255,r/n),Math.min(255,e/n))):new mn(i,i,i)},ea.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mn(n*this.r,n*this.g,n*this.b)},ea.hsl=function(){return wn(this.r,this.g,this.b)},ea.toString=function(){return"#"+Mn(this.r)+Mn(this.g)+Mn(this.b)};var ia=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,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});ia.forEach(function(n,t){ia.set(n,_n(t))}),ao.functor=Sn,ao.xhr=En(m),ao.dsv=function(n,t){function r(n,r,u){arguments.length<3&&(u=r,r=null);var o=Nn(n,t,null==r?e:i(r),u);return o.row=function(n){return arguments.length?o.response(null==(r=n)?e:i(n)):r},o}function e(n){return r.parse(n.responseText)}function i(n){return function(t){return r.parse(t.responseText,n)}}function u(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return r.parse=function(n,t){var e;return r.parseRows(n,function(n,r){if(e)return e(n,r-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");e=t?function(n,r){return t(i(n),r)}:i})},r.parseRows=function(n,t){function r(){if(f>=l)return o;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var r=t;r++<l;)if(34===n.charCodeAt(r)){if(34!==n.charCodeAt(r+1))break;++r}f=r+2;var e=n.charCodeAt(r+1);return 13===e?(i=!0,10===n.charCodeAt(r+2)&&++f):10===e&&(i=!0),n.slice(t+1,r).replace(/""/g,'"')}for(;l>f;){var e=n.charCodeAt(f++),a=1;if(10===e)i=!0;else if(13===e)i=!0,10===n.charCodeAt(f)&&(++f,++a);else if(e!==c)continue;return n.slice(t,f-a)}return n.slice(t)}for(var e,i,u={},o={},a=[],l=n.length,f=0,s=0;(e=r())!==o;){for(var h=[];e!==u&&e!==o;)h.push(e),e=r();t&&null==(h=t(h,s++))||a.push(h)}return a},r.format=function(t){if(Array.isArray(t[0]))return r.formatRows(t);var e=new y,i=[];return t.forEach(function(n){for(var t in n)e.has(t)||i.push(e.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},r.formatRows=function(n){return n.map(u).join("\n")},r},ao.csv=ao.dsv(",","text/csv"),ao.tsv=ao.dsv(" ","text/tab-separated-values");var ua,oa,aa,ca,la=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ao.timer=function(){Dn.apply(this,arguments)},ao.timer.flush=function(){Ln(),Rn()},ao.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var fa=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"].map(zn);ao.formatPrefix=function(n,t){var r=0;return(n=+n)&&(0>n&&(n*=-1),t&&(n=ao.round(n,On(n,t))),r=1+Math.floor(1e-12+Math.log(n)/Math.LN10),r=Math.max(-24,Math.min(24,3*Math.floor((r-1)/3)))),fa[8+r/3]};var sa=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,ha=ao.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ao.round(n,On(n,t))).toFixed(Math.max(0,Math.min(20,On(n*(1+1e-15),t))))}}),pa=ao.time={},va=Date;Pn.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){ga.setUTCDate.apply(this._,arguments)},setDay:function(){ga.setUTCDay.apply(this._,arguments)},setFullYear:function(){ga.setUTCFullYear.apply(this._,arguments)},setHours:function(){ga.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){ga.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){ga.setUTCMinutes.apply(this._,arguments)},setMonth:function(){ga.setUTCMonth.apply(this._,arguments)},setSeconds:function(){ga.setUTCSeconds.apply(this._,arguments)},setTime:function(){ga.setTime.apply(this._,arguments)}};var ga=Date.prototype;pa.year=Un(function(n){return n=pa.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),pa.years=pa.year.range,pa.years.utc=pa.year.utc.range,pa.day=Un(function(n){var t=new va(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),pa.days=pa.day.range,pa.days.utc=pa.day.utc.range,pa.dayOfYear=function(n){var t=pa.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var r=pa[n]=Un(function(n){return(n=pa.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var r=pa.year(n).getDay();return Math.floor((pa.dayOfYear(n)+(r+t)%7)/7)-(r!==t)});pa[n+"s"]=r.range,pa[n+"s"].utc=r.utc.range,pa[n+"OfYear"]=function(n){var r=pa.year(n).getDay();return Math.floor((pa.dayOfYear(n)+(r+t)%7)/7)}}),pa.week=pa.sunday,pa.weeks=pa.sunday.range,pa.weeks.utc=pa.sunday.utc.range,pa.weekOfYear=pa.sundayOfYear;var da={"-":"",_:" ",0:"0"},ya=/^\s*\d+/,ma=/^%/;ao.locale=function(n){return{numberFormat:qn(n),timeFormat:Hn(n)}};var _a=ao.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],
+shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ao.format=_a.numberFormat,ao.geo={},ft.prototype={s:0,t:0,add:function(n){st(n,this.t,xa),st(xa.s,this.s,this),this.s?this.t+=xa.t:this.s=xa.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var xa=new ft;ao.geo.stream=function(n,t){n&&Ma.hasOwnProperty(n.type)?Ma[n.type](n,t):ht(n,t)};var Ma={Feature:function(n,t){ht(n.geometry,t)},FeatureCollection:function(n,t){for(var r=n.features,e=-1,i=r.length;++e<i;)ht(r[e].geometry,t)}},ba={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var r=n.coordinates,e=-1,i=r.length;++e<i;)n=r[e],t.point(n[0],n[1],n[2])},LineString:function(n,t){pt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var r=n.coordinates,e=-1,i=r.length;++e<i;)pt(r[e],t,0)},Polygon:function(n,t){vt(n.coordinates,t)},MultiPolygon:function(n,t){for(var r=n.coordinates,e=-1,i=r.length;++e<i;)vt(r[e],t)},GeometryCollection:function(n,t){for(var r=n.geometries,e=-1,i=r.length;++e<i;)ht(r[e],t)}};ao.geo.area=function(n){return wa=0,ao.geo.stream(n,Ca),wa};var wa,ka=new ft,Ca={sphere:function(){wa+=4*qo},point:M,lineStart:M,lineEnd:M,polygonStart:function(){ka.reset(),Ca.lineStart=gt},polygonEnd:function(){var n=2*ka;wa+=0>n?4*qo+n:n,Ca.lineStart=Ca.lineEnd=Ca.point=M}};ao.geo.bounds=function(){function n(n,t){_.push(x=[f=n,h=n]),s>t&&(s=t),t>p&&(p=t)}function t(t,r){var e=dt([t*Fo,r*Fo]);if(y){var i=mt(y,e),u=[i[1],-i[0],0],o=mt(u,i);Mt(o),o=bt(o);var c=t-v,l=c>0?1:-1,g=o[0]*Ho*l,d=xo(c)>180;if(d^(g>l*v&&l*t>g)){var m=o[1]*Ho;m>p&&(p=m)}else if(g=(g+360)%360-180,d^(g>l*v&&l*t>g)){var m=-o[1]*Ho;s>m&&(s=m)}else s>r&&(s=r),r>p&&(p=r);d?v>t?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>v?a(f,t)>a(f,h)&&(h=t):a(t,h)>a(f,h)&&(f=t)}else n(t,r);y=e,v=t}function r(){M.point=t}function e(){x[0]=f,x[1]=h,M.point=n,y=null}function i(n,r){if(y){var e=n-v;m+=xo(e)>180?e+(e>0?360:-360):e}else g=n,d=r;Ca.point(n,r),t(n,r)}function u(){Ca.lineStart()}function o(){i(g,d),Ca.lineEnd(),xo(m)>Oo&&(f=-(h=180)),x[0]=f,x[1]=h,y=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,p,v,g,d,y,m,_,x,M={point:n,lineStart:r,lineEnd:e,polygonStart:function(){M.point=i,M.lineStart=u,M.lineEnd=o,m=0,Ca.polygonStart()},polygonEnd:function(){Ca.polygonEnd(),M.point=n,M.lineStart=r,M.lineEnd=e,0>ka?(f=-(h=180),s=-(p=90)):m>Oo?p=90:-Oo>m&&(s=-90),x[0]=f,x[1]=h}};return function(n){p=h=-(f=s=1/0),_=[],ao.geo.stream(n,M);var t=_.length;if(t){_.sort(c);for(var r,e=1,i=_[0],u=[i];t>e;++e)r=_[e],l(r[0],i)||l(r[1],i)?(a(i[0],r[1])>a(i[0],i[1])&&(i[1]=r[1]),a(r[0],i[1])>a(i[0],i[1])&&(i[0]=r[0])):u.push(i=r);for(var o,r,v=-(1/0),t=u.length-1,e=0,i=u[t];t>=e;i=r,++e)r=u[e],(o=a(i[1],r[0]))>v&&(v=o,f=r[0],h=i[1])}return _=x=null,f===1/0||s===1/0?[[NaN,NaN],[NaN,NaN]]:[[f,s],[h,p]]}}(),ao.geo.centroid=function(n){Aa=Sa=Ea=Na=$a=ja=Da=Ta=La=Ra=Oa=0,ao.geo.stream(n,za);var t=La,r=Ra,e=Oa,i=t*t+r*r+e*e;return zo>i&&(t=ja,r=Da,e=Ta,Oo>Sa&&(t=Ea,r=Na,e=$a),i=t*t+r*r+e*e,zo>i)?[NaN,NaN]:[Math.atan2(r,t)*Ho,tn(e/Math.sqrt(i))*Ho]};var Aa,Sa,Ea,Na,$a,ja,Da,Ta,La,Ra,Oa,za={sphere:M,point:kt,lineStart:At,lineEnd:St,polygonStart:function(){za.lineStart=Et},polygonEnd:function(){za.lineStart=At}},qa=Lt($t,qt,Pt,[-qo,-qo/2]),Ia=1e9;ao.geo.clipExtent=function(){var n,t,r,e,i,u,o={stream:function(n){return i&&(i.valid=!1),i=u(n),i.valid=!0,i},extent:function(a){return arguments.length?(u=Wt(n=+a[0][0],t=+a[0][1],r=+a[1][0],e=+a[1][1]),i&&(i.valid=!1,i=null),o):[[n,t],[r,e]]}};return o.extent([[0,0],[960,500]])},(ao.geo.conicEqualArea=function(){return Bt(Yt)}).raw=Yt,ao.geo.albers=function(){return ao.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ao.geo.albersUsa=function(){function n(n){var u=n[0],o=n[1];return t=null,r(u,o),t||(e(u,o),t)||i(u,o),t}var t,r,e,i,u=ao.geo.albers(),o=ao.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ao.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,r){t=[n,r]}};return n.invert=function(n){var t=u.scale(),r=u.translate(),e=(n[0]-r[0])/t,i=(n[1]-r[1])/t;return(i>=.12&&.234>i&&e>=-.425&&-.214>e?o:i>=.166&&.234>i&&e>=-.214&&-.115>e?a:u).invert(n)},n.stream=function(n){var t=u.stream(n),r=o.stream(n),e=a.stream(n);return{point:function(n,i){t.point(n,i),r.point(n,i),e.point(n,i)},sphere:function(){t.sphere(),r.sphere(),e.sphere()},lineStart:function(){t.lineStart(),r.lineStart(),e.lineStart()},lineEnd:function(){t.lineEnd(),r.lineEnd(),e.lineEnd()},polygonStart:function(){t.polygonStart(),r.polygonStart(),e.polygonStart()},polygonEnd:function(){t.polygonEnd(),r.polygonEnd(),e.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),o.precision(t),a.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),o.scale(.35*t),a.scale(t),n.translate(u.translate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var l=u.scale(),f=+t[0],s=+t[1];return r=u.translate(t).clipExtent([[f-.455*l,s-.238*l],[f+.455*l,s+.238*l]]).stream(c).point,e=o.translate([f-.307*l,s+.201*l]).clipExtent([[f-.425*l+Oo,s+.12*l+Oo],[f-.214*l-Oo,s+.234*l-Oo]]).stream(c).point,i=a.translate([f-.205*l,s+.212*l]).clipExtent([[f-.214*l+Oo,s+.166*l+Oo],[f-.115*l-Oo,s+.234*l-Oo]]).stream(c).point,n},n.scale(1070)};var Pa,Ua,Fa,Ha,Wa,Ba,Ya={point:M,lineStart:M,lineEnd:M,polygonStart:function(){Ua=0,Ya.lineStart=Zt},polygonEnd:function(){Ya.lineStart=Ya.lineEnd=Ya.point=M,Pa+=xo(Ua/2)}},Za={point:Vt,lineStart:M,lineEnd:M,polygonStart:M,polygonEnd:M},Va={point:Jt,lineStart:Qt,lineEnd:Gt,polygonStart:function(){Va.lineStart=nr},polygonEnd:function(){Va.point=Jt,Va.lineStart=Qt,Va.lineEnd=Gt}};ao.geo.path=function(){function n(n){return n&&("function"==typeof a&&u.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=i(u)),ao.geo.stream(n,o)),u.result()}function t(){return o=null,n}var r,e,i,u,o,a=4.5;return n.area=function(n){return Pa=0,ao.geo.stream(n,i(Ya)),Pa},n.centroid=function(n){return Ea=Na=$a=ja=Da=Ta=La=Ra=Oa=0,ao.geo.stream(n,i(Va)),Oa?[La/Oa,Ra/Oa]:Ta?[ja/Ta,Da/Ta]:$a?[Ea/$a,Na/$a]:[NaN,NaN]},n.bounds=function(n){return Wa=Ba=-(Fa=Ha=1/0),ao.geo.stream(n,i(Za)),[[Fa,Ha],[Wa,Ba]]},n.projection=function(n){return arguments.length?(i=(r=n)?n.stream||er(n):m,t()):r},n.context=function(n){return arguments.length?(u=null==(e=n)?new Xt:new tr(n),"function"!=typeof a&&u.pointRadius(a),t()):e},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(u.pointRadius(+t),+t),n):a},n.projection(ao.geo.albersUsa()).context(null)},ao.geo.transform=function(n){return{stream:function(t){var r=new ir(t);for(var e in n)r[e]=n[e];return r}}},ir.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ao.geo.projection=or,ao.geo.projectionMutator=ar,(ao.geo.equirectangular=function(){return or(lr)}).raw=lr.invert=lr,ao.geo.rotation=function(n){function t(t){return t=n(t[0]*Fo,t[1]*Fo),t[0]*=Ho,t[1]*=Ho,t}return n=sr(n[0]%360*Fo,n[1]*Fo,n.length>2?n[2]*Fo:0),t.invert=function(t){return t=n.invert(t[0]*Fo,t[1]*Fo),t[0]*=Ho,t[1]*=Ho,t},t},fr.invert=lr,ao.geo.circle=function(){function n(){var n="function"==typeof e?e.apply(this,arguments):e,t=sr(-n[0]*Fo,-n[1]*Fo,0).invert,i=[];return r(null,null,1,{point:function(n,r){i.push(n=t(n,r)),n[0]*=Ho,n[1]*=Ho}}),{type:"Polygon",coordinates:[i]}}var t,r,e=[0,0],i=6;return n.origin=function(t){return arguments.length?(e=t,n):e},n.angle=function(e){return arguments.length?(r=gr((t=+e)*Fo,i*Fo),n):t},n.precision=function(e){return arguments.length?(r=gr(t*Fo,(i=+e)*Fo),n):i},n.angle(90)},ao.geo.distance=function(n,t){var r,e=(t[0]-n[0])*Fo,i=n[1]*Fo,u=t[1]*Fo,o=Math.sin(e),a=Math.cos(e),c=Math.sin(i),l=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((r=s*o)*r+(r=l*f-c*s*a)*r),c*f+l*s*a)},ao.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ao.range(Math.ceil(u/d)*d,i,d).map(h).concat(ao.range(Math.ceil(l/y)*y,c,y).map(p)).concat(ao.range(Math.ceil(e/v)*v,r,v).filter(function(n){return xo(n%d)>Oo}).map(f)).concat(ao.range(Math.ceil(a/g)*g,o,g).filter(function(n){return xo(n%y)>Oo}).map(s))}var r,e,i,u,o,a,c,l,f,s,h,p,v=10,g=v,d=90,y=360,m=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(p(c).slice(1),h(i).reverse().slice(1),p(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],l=+t[0][1],c=+t[1][1],u>i&&(t=u,u=i,i=t),l>c&&(t=l,l=c,c=t),n.precision(m)):[[u,l],[i,c]]},n.minorExtent=function(t){return arguments.length?(e=+t[0][0],r=+t[1][0],a=+t[0][1],o=+t[1][1],e>r&&(t=e,e=r,r=t),a>o&&(t=a,a=o,o=t),n.precision(m)):[[e,a],[r,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],y=+t[1],n):[d,y]},n.minorStep=function(t){return arguments.length?(v=+t[0],g=+t[1],n):[v,g]},n.precision=function(t){return arguments.length?(m=+t,f=yr(a,o,90),s=mr(e,r,m),h=yr(l,c,90),p=mr(u,i,m),n):m},n.majorExtent([[-180,-90+Oo],[180,90-Oo]]).minorExtent([[-180,-80-Oo],[180,80+Oo]])},ao.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||e.apply(this,arguments),r||i.apply(this,arguments)]}}var t,r,e=_r,i=xr;return n.distance=function(){return ao.geo.distance(t||e.apply(this,arguments),r||i.apply(this,arguments))},n.source=function(r){return arguments.length?(e=r,t="function"==typeof r?null:r,n):e},n.target=function(t){return arguments.length?(i=t,r="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},ao.geo.interpolate=function(n,t){return Mr(n[0]*Fo,n[1]*Fo,t[0]*Fo,t[1]*Fo)},ao.geo.length=function(n){return Xa=0,ao.geo.stream(n,Ka),Xa};var Xa,Ka={sphere:M,point:M,lineStart:br,lineEnd:M,polygonStart:M,polygonEnd:M},Ja=wr(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ao.geo.azimuthalEqualArea=function(){return or(Ja)}).raw=Ja;var Qa=wr(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},m);(ao.geo.azimuthalEquidistant=function(){return or(Qa)}).raw=Qa,(ao.geo.conicConformal=function(){return Bt(kr)}).raw=kr,(ao.geo.conicEquidistant=function(){return Bt(Cr)}).raw=Cr;var Ga=wr(function(n){return 1/n},Math.atan);(ao.geo.gnomonic=function(){return or(Ga)}).raw=Ga,Ar.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Uo]},(ao.geo.mercator=function(){return Sr(Ar)}).raw=Ar;var nc=wr(function(){return 1},Math.asin);(ao.geo.orthographic=function(){return or(nc)}).raw=nc;var tc=wr(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ao.geo.stereographic=function(){return or(tc)}).raw=tc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Uo]},(ao.geo.transverseMercator=function(){var n=Sr(Er),t=n.center,r=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?r([n[0],n[1],n.length>2?n[2]+90:90]):(n=r(),[n[0],n[1],n[2]-90])},r([0,0,90])}).raw=Er,ao.geom={},ao.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i=Sn(r),u=Sn(e),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+i.call(this,n[t],t),+u.call(this,n[t],t),t]);for(a.sort(Dr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=jr(a),f=jr(c),s=f[0]===l[0],h=f[f.length-1]===l[l.length-1],p=[];for(t=l.length-1;t>=0;--t)p.push(n[a[l[t]][2]]);for(t=+s;t<f.length-h;++t)p.push(n[a[f[t]][2]]);return p}var r=Nr,e=$r;return arguments.length?t(n):(t.x=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(e=n,t):e},t)},ao.geom.polygon=function(n){return Co(n,rc),n};var rc=ao.geom.polygon.prototype=[];rc.area=function(){for(var n,t=-1,r=this.length,e=this[r-1],i=0;++t<r;)n=e,e=this[t],i+=n[1]*e[0]-n[0]*e[1];return.5*i},rc.centroid=function(n){var t,r,e=-1,i=this.length,u=0,o=0,a=this[i-1];for(arguments.length||(n=-1/(6*this.area()));++e<i;)t=a,a=this[e],r=t[0]*a[1]-a[0]*t[1],u+=(t[0]+a[0])*r,o+=(t[1]+a[1])*r;return[u*n,o*n]},rc.clip=function(n){for(var t,r,e,i,u,o,a=Rr(n),c=-1,l=this.length-Rr(this),f=this[l-1];++c<l;){for(t=n.slice(),n.length=0,i=this[c],u=t[(e=t.length-a)-1],r=-1;++r<e;)o=t[r],Tr(o,f,i)?(Tr(u,f,i)||n.push(Lr(u,o,f,i)),n.push(o)):Tr(u,f,i)&&n.push(Lr(u,o,f,i)),u=o;a&&n.push(n[0]),f=i}return n};var ec,ic,uc,oc,ac,cc=[],lc=[];Hr.prototype.prepare=function(){for(var n,t=this.edges,r=t.length;r--;)n=t[r].edge,n.b&&n.a||t.splice(r,1);return t.sort(Br),t.length},te.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},re.prototype={insert:function(n,t){var r,e,i;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;r=n}else this._?(n=oe(this._),t.P=null,t.N=n,n.P=n.L=t,r=n):(t.P=t.N=null,this._=t,r=null);for(t.L=t.R=null,t.U=r,t.C=!0,n=t;r&&r.C;)e=r.U,r===e.L?(i=e.R,i&&i.C?(r.C=i.C=!1,e.C=!0,n=e):(n===r.R&&(ie(this,r),n=r,r=n.U),r.C=!1,e.C=!0,ue(this,e))):(i=e.L,i&&i.C?(r.C=i.C=!1,e.C=!0,n=e):(n===r.L&&(ue(this,r),n=r,r=n.U),r.C=!1,e.C=!0,ie(this,e))),r=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,r,e,i=n.U,u=n.L,o=n.R;if(r=u?o?oe(o):u:o,i?i.L===n?i.L=r:i.R=r:this._=r,u&&o?(e=r.C,r.C=n.C,r.L=u,u.U=r,r!==o?(i=r.U,r.U=n.U,n=r.R,i.L=n,r.R=o,o.U=r):(r.U=i,i=r,n=r.R)):(e=n.C,n=r),n&&(n.U=i),!e){if(n&&n.C)return void(n.C=!1);do{if(n===this._)break;if(n===i.L){if(t=i.R,t.C&&(t.C=!1,i.C=!0,ie(this,i),t=i.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,ue(this,t),t=i.R),t.C=i.C,i.C=t.R.C=!1,ie(this,i),n=this._;break}}else if(t=i.L,t.C&&(t.C=!1,i.C=!0,ue(this,i),t=i.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,ie(this,t),t=i.L),t.C=i.C,i.C=t.L.C=!1,ue(this,i),n=this._;break}t.C=!0,n=i,i=i.U}while(!n.C);n&&(n.C=!1)}}},ao.geom.voronoi=function(n){function t(n){var t=new Array(n.length),e=a[0][0],i=a[0][1],u=a[1][0],o=a[1][1];return ae(r(n),a).cells.forEach(function(r,a){var c=r.edges,l=r.site,f=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):l.x>=e&&l.x<=u&&l.y>=i&&l.y<=o?[[e,o],[u,o],[u,i],[e,i]]:[];f.point=n[a]}),t}function r(n){return n.map(function(n,t){return{x:Math.round(u(n,t)/Oo)*Oo,y:Math.round(o(n,t)/Oo)*Oo,i:t}})}var e=Nr,i=$r,u=e,o=i,a=fc;return n?t(n):(t.links=function(n){return ae(r(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return ae(r(n)).cells.forEach(function(r,e){for(var i,u,o=r.site,a=r.edges.sort(Br),c=-1,l=a.length,f=a[l-1].edge,s=f.l===o?f.r:f.l;++c<l;)i=f,u=s,f=a[c].edge,s=f.l===o?f.r:f.l,e<u.i&&e<s.i&&le(o,u,s)<0&&t.push([n[e],n[u.i],n[s.i]])}),t},t.x=function(n){return arguments.length?(u=Sn(e=n),t):e},t.y=function(n){return arguments.length?(o=Sn(i=n),t):i},t.clipExtent=function(n){return arguments.length?(a=null==n?fc:n,t):a===fc?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===fc?null:a&&a[1]},t)};var fc=[[-1e6,-1e6],[1e6,1e6]];ao.geom.delaunay=function(n){return ao.geom.voronoi().triangles(n)},ao.geom.quadtree=function(n,t,r,e,i){function u(n){function u(n,t,r,e,i,u,o,a){if(!isNaN(r)&&!isNaN(e))if(n.leaf){var c=n.x,f=n.y;if(null!=c)if(xo(c-r)+xo(f-e)<.01)l(n,t,r,e,i,u,o,a);else{var s=n.point;n.x=n.y=n.point=null,l(n,s,c,f,i,u,o,a),l(n,t,r,e,i,u,o,a)}else n.x=r,n.y=e,n.point=t}else l(n,t,r,e,i,u,o,a)}function l(n,t,r,e,i,o,a,c){var l=.5*(i+a),f=.5*(o+c),s=r>=l,h=e>=f,p=h<<1|s;n.leaf=!1,n=n.nodes[p]||(n.nodes[p]=he()),s?i=l:a=l,h?o=f:c=f,u(n,t,r,e,i,o,a,c)}var f,s,h,p,v,g,d,y,m,_=Sn(a),x=Sn(c);if(null!=t)g=t,d=r,y=e,m=i;else if(y=m=-(g=d=1/0),s=[],h=[],v=n.length,o)for(p=0;v>p;++p)f=n[p],f.x<g&&(g=f.x),f.y<d&&(d=f.y),f.x>y&&(y=f.x),f.y>m&&(m=f.y),s.push(f.x),h.push(f.y);else for(p=0;v>p;++p){var M=+_(f=n[p],p),b=+x(f,p);g>M&&(g=M),d>b&&(d=b),M>y&&(y=M),b>m&&(m=b),s.push(M),h.push(b)}var w=y-g,k=m-d;w>k?m=d+w:y=g+k;var C=he();if(C.add=function(n){u(C,n,+_(n,++p),+x(n,p),g,d,y,m)},C.visit=function(n){pe(n,C,g,d,y,m)},C.find=function(n){return ve(C,n[0],n[1],g,d,y,m)},p=-1,null==t){for(;++p<v;)u(C,n[p],s[p],h[p],g,d,y,m);--p}else n.forEach(C.add);return s=h=n=f=null,C}var o,a=Nr,c=$r;return(o=arguments.length)?(a=fe,c=se,3===o&&(i=r,e=t,r=t=0),u(n)):(u.x=function(n){return arguments.length?(a=n,u):a},u.y=function(n){return arguments.length?(c=n,u):c},u.extent=function(n){return arguments.length?(null==n?t=r=e=i=null:(t=+n[0][0],r=+n[0][1],e=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,r],[e,i]]},u.size=function(n){return arguments.length?(null==n?t=r=e=i=null:(t=r=0,e=+n[0],i=+n[1]),u):null==t?null:[e-t,i-r]},u)},ao.interpolateRgb=ge,ao.interpolateObject=de,ao.interpolateNumber=ye,ao.interpolateString=me;var sc=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,hc=new RegExp(sc.source,"g");ao.interpolate=_e,ao.interpolators=[function(n,t){var r=typeof t;return("string"===r?ia.has(t.toLowerCase())||/^(#|rgb\(|hsl\()/i.test(t)?ge:me:t instanceof an?ge:Array.isArray(t)?xe:"object"===r&&isNaN(t)?de:ye)(n,t)}],ao.interpolateArray=xe;var pc=function(){return m},vc=ao.map({linear:pc,poly:Se,quad:function(){return ke},cubic:function(){return Ce},sin:function(){return Ee},exp:function(){return Ne},circle:function(){return $e},elastic:je,back:De,bounce:function(){return Te}}),gc=ao.map({"in":m,out:be,"in-out":we,"out-in":function(n){return we(be(n))}});ao.ease=function(n){var t=n.indexOf("-"),r=t>=0?n.slice(0,t):n,e=t>=0?n.slice(t+1):"in";return r=vc.get(r)||pc,e=gc.get(e)||m,Me(e(r.apply(null,co.call(arguments,1))))},ao.interpolateHcl=Le,ao.interpolateHsl=Re,ao.interpolateLab=Oe,ao.interpolateRound=ze,ao.transform=function(n){var t=fo.createElementNS(ao.ns.prefix.svg,"g");return(ao.transform=function(n){if(null!=n){t.setAttribute("transform",n);var r=t.transform.baseVal.consolidate()}return new qe(r?r.matrix:dc)})(n)},qe.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var dc={a:1,b:0,c:0,d:1,e:0,f:0};ao.interpolateTransform=Ze,ao.layout={},ao.layout.bundle=function(){return function(n){for(var t=[],r=-1,e=n.length;++r<e;)t.push(Ke(n[r]));return t}},ao.layout.chord=function(){function n(){var n,l,s,h,p,v={},g=[],d=ao.range(u),y=[];for(r=[],e=[],n=0,h=-1;++h<u;){for(l=0,p=-1;++p<u;)l+=i[h][p];g.push(l),y.push(ao.range(u)),n+=l}for(o&&d.sort(function(n,t){return o(g[n],g[t])}),a&&y.forEach(function(n,t){n.sort(function(n,r){return a(i[t][n],i[t][r])})}),n=(Io-f*u)/n,l=0,h=-1;++h<u;){for(s=l,p=-1;++p<u;){var m=d[h],_=y[m][p],x=i[m][_],M=l,b=l+=x*n;v[m+"-"+_]={index:m,subindex:_,startAngle:M,endAngle:b,value:x}}e[m]={index:m,startAngle:s,endAngle:l,value:g[m]},l+=f}for(h=-1;++h<u;)for(p=h-1;++p<u;){var w=v[h+"-"+p],k=v[p+"-"+h];(w.value||k.value)&&r.push(w.value<k.value?{source:k,target:w}:{source:w,target:k})}c&&t()}function t(){r.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var r,e,i,u,o,a,c,l={},f=0;return l.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,r=e=null,l):i},l.padding=function(n){return arguments.length?(f=n,r=e=null,l):f},l.sortGroups=function(n){return arguments.length?(o=n,r=e=null,l):o},l.sortSubgroups=function(n){return arguments.length?(a=n,r=null,l):a},l.sortChords=function(n){return arguments.length?(c=n,r&&t(),l):c},l.chords=function(){return r||n(),r},l.groups=function(){return e||n(),e},l},ao.layout.force=function(){function n(n){return function(t,r,e,i){if(t.point!==n){var u=t.cx-n.x,o=t.cy-n.y,a=i-r,c=u*u+o*o;if(c>a*a/y){if(g>c){var l=t.charge/c;n.px-=u*l,n.py-=o*l}return!0}if(t.point&&c&&g>c){var l=t.pointCharge/c;n.px-=u*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=ao.event.x,n.py=ao.event.y,c.resume()}var r,e,i,u,o,a,c={},l=ao.dispatch("start","tick","end"),f=[1,1],s=.9,h=yc,p=mc,v=-30,g=_c,d=.1,y=.64,_=[],x=[];return c.tick=function(){if((i*=.99)<.005)return r=null,l.end({type:"end",alpha:i=0}),!0;var t,e,c,h,p,g,y,m,M,b=_.length,w=x.length;for(e=0;w>e;++e)c=x[e],h=c.source,p=c.target,m=p.x-h.x,M=p.y-h.y,(g=m*m+M*M)&&(g=i*o[e]*((g=Math.sqrt(g))-u[e])/g,m*=g,M*=g,p.x-=m*(y=h.weight+p.weight?h.weight/(h.weight+p.weight):.5),p.y-=M*y,h.x+=m*(y=1-y),h.y+=M*y);if((y=i*d)&&(m=f[0]/2,M=f[1]/2,e=-1,y))for(;++e<b;)c=_[e],c.x+=(m-c.x)*y,c.y+=(M-c.y)*y;if(v)for(ei(t=ao.geom.quadtree(_),i,a),e=-1;++e<b;)(c=_[e]).fixed||t.visit(n(c));for(e=-1;++e<b;)c=_[e],c.fixed?(c.x=c.px,c.y=c.py):(c.x-=(c.px-(c.px=c.x))*s,c.y-=(c.py-(c.py=c.y))*s);l.tick({type:"tick",alpha:i})},c.nodes=function(n){return arguments.length?(_=n,c):_},c.links=function(n){return arguments.length?(x=n,c):x},c.size=function(n){return arguments.length?(f=n,c):f},c.linkDistance=function(n){return arguments.length?(h="function"==typeof n?n:+n,c):h},c.distance=c.linkDistance,c.linkStrength=function(n){return arguments.length?(p="function"==typeof n?n:+n,c):p},c.friction=function(n){return arguments.length?(s=+n,c):s},c.charge=function(n){return arguments.length?(v="function"==typeof n?n:+n,c):v},c.chargeDistance=function(n){return arguments.length?(g=n*n,c):Math.sqrt(g)},c.gravity=function(n){return arguments.length?(d=+n,c):d},c.theta=function(n){return arguments.length?(y=n*n,c):Math.sqrt(y)},c.alpha=function(n){return arguments.length?(n=+n,i?n>0?i=n:(r.c=null,r.t=NaN,r=null,l.end({type:"end",alpha:i=0})):n>0&&(l.start({type:"start",alpha:i=n}),r=Dn(c.tick)),c):i},c.start=function(){function n(n,e){if(!r){for(r=new Array(i),c=0;i>c;++c)r[c]=[];for(c=0;l>c;++c){var u=x[c];r[u.source.index].push(u.target),r[u.target.index].push(u.source)}}for(var o,a=r[t],c=-1,f=a.length;++c<f;)if(!isNaN(o=a[c][n]))return o;return Math.random()*e}var t,r,e,i=_.length,l=x.length,s=f[0],g=f[1];for(t=0;i>t;++t)(e=_[t]).index=t,e.weight=0;for(t=0;l>t;++t)e=x[t],"number"==typeof e.source&&(e.source=_[e.source]),"number"==typeof e.target&&(e.target=_[e.target]),++e.source.weight,++e.target.weight;for(t=0;i>t;++t)e=_[t],isNaN(e.x)&&(e.x=n("x",s)),isNaN(e.y)&&(e.y=n("y",g)),isNaN(e.px)&&(e.px=e.x),isNaN(e.py)&&(e.py=e.y);if(u=[],"function"==typeof h)for(t=0;l>t;++t)u[t]=+h.call(this,x[t],t);else for(t=0;l>t;++t)u[t]=h;if(o=[],"function"==typeof p)for(t=0;l>t;++t)o[t]=+p.call(this,x[t],t);else for(t=0;l>t;++t)o[t]=p;if(a=[],"function"==typeof v)for(t=0;i>t;++t)a[t]=+v.call(this,_[t],t);else for(t=0;i>t;++t)a[t]=v;return c.resume()},c.resume=function(){return c.alpha(.1)},c.stop=function(){return c.alpha(0)},c.drag=function(){return e||(e=ao.behavior.drag().origin(m).on("dragstart.force",Ge).on("drag.force",t).on("dragend.force",ni)),arguments.length?void this.on("mouseover.force",ti).on("mouseout.force",ri).call(e):e},ao.rebind(c,l,"on")};var yc=20,mc=1,_c=1/0;ao.layout.hierarchy=function(){function n(i){var u,o=[i],a=[];for(i.depth=0;null!=(u=o.pop());)if(a.push(u),(l=r.call(n,u,u.depth))&&(c=l.length)){for(var c,l,f;--c>=0;)o.push(f=l[c]),f.parent=u,f.depth=u.depth+1;e&&(u.value=0),u.children=l}else e&&(u.value=+e.call(n,u,u.depth)||0),delete u.children;return oi(i,function(n){var r,i;t&&(r=n.children)&&r.sort(t),e&&(i=n.parent)&&(i.value+=n.value)}),a}var t=li,r=ai,e=ci;return n.sort=function(r){return arguments.length?(t=r,n):t},n.children=function(t){return arguments.length?(r=t,n):r},n.value=function(t){return arguments.length?(e=t,n):e},n.revalue=function(t){return e&&(ui(t,function(n){n.children&&(n.value=0)}),oi(t,function(t){var r;t.children||(t.value=+e.call(n,t,t.depth)||0),(r=t.parent)&&(r.value+=t.value)})),t},n},ao.layout.partition=function(){function n(t,r,e,i){var u=t.children;if(t.x=r,t.y=t.depth*i,t.dx=e,t.dy=i,u&&(o=u.length)){var o,a,c,l=-1;for(e=t.value?e/t.value:0;++l<o;)n(a=u[l],r,c=a.value*e,i),r+=c}}function t(n){var r=n.children,e=0;if(r&&(i=r.length))for(var i,u=-1;++u<i;)e=Math.max(e,t(r[u]));return 1+e}function r(r,u){var o=e.call(this,r,u);return n(o[0],0,i[0],i[1]/t(o[0])),o}var e=ao.layout.hierarchy(),i=[1,1];return r.size=function(n){return arguments.length?(i=n,r):i},ii(r,e)},ao.layout.pie=function(){function n(o){var a,c=o.length,l=o.map(function(r,e){return+t.call(n,r,e)}),f=+("function"==typeof e?e.apply(this,arguments):e),s=("function"==typeof i?i.apply(this,arguments):i)-f,h=Math.min(Math.abs(s)/c,+("function"==typeof u?u.apply(this,arguments):u)),p=h*(0>s?-1:1),v=ao.sum(l),g=v?(s-c*p)/v:0,d=ao.range(c),y=[];return null!=r&&d.sort(r===xc?function(n,t){return l[t]-l[n]}:function(n,t){return r(o[n],o[t])}),d.forEach(function(n){y[n]={data:o[n],value:a=l[n],startAngle:f,endAngle:f+=a*g+p,padAngle:h}}),y}var t=Number,r=xc,e=0,i=Io,u=0;return n.value=function(r){return arguments.length?(t=r,n):t},n.sort=function(t){return arguments.length?(r=t,n):r},n.startAngle=function(t){return arguments.length?(e=t,n):e},n.endAngle=function(t){return arguments.length?(i=t,n):i},n.padAngle=function(t){return arguments.length?(u=t,n):u},n};var xc={};ao.layout.stack=function(){function n(a,c){if(!(h=a.length))return a;var l=a.map(function(r,e){return t.call(n,r,e)}),f=l.map(function(t){return t.map(function(t,r){return[u.call(n,t,r),o.call(n,t,r)]})}),s=r.call(n,f,c);l=ao.permute(l,s),f=ao.permute(f,s);var h,p,v,g,d=e.call(n,f,c),y=l[0].length;for(v=0;y>v;++v)for(i.call(n,l[0][v],g=d[v],f[0][v][1]),p=1;h>p;++p)i.call(n,l[p][v],g+=f[p-1][v][1],f[p][v][1]);return a}var t=m,r=vi,e=gi,i=pi,u=si,o=hi;return n.values=function(r){return arguments.length?(t=r,n):t},n.order=function(t){return arguments.length?(r="function"==typeof t?t:Mc.get(t)||vi,n):r},n.offset=function(t){return arguments.length?(e="function"==typeof t?t:bc.get(t)||gi,n):e},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(i=t,n):i},n};var Mc=ao.map({"inside-out":function(n){var t,r,e=n.length,i=n.map(di),u=n.map(yi),o=ao.range(e).sort(function(n,t){return i[n]-i[t]}),a=0,c=0,l=[],f=[];for(t=0;e>t;++t)r=o[t],c>a?(a+=u[r],l.push(r)):(c+=u[r],f.push(r));return f.reverse().concat(l)},reverse:function(n){return ao.range(n.length).reverse()},"default":vi}),bc=ao.map({silhouette:function(n){var t,r,e,i=n.length,u=n[0].length,o=[],a=0,c=[];for(r=0;u>r;++r){for(t=0,e=0;i>t;t++)e+=n[t][r][1];e>a&&(a=e),o.push(e)}for(r=0;u>r;++r)c[r]=(a-o[r])/2;return c},wiggle:function(n){var t,r,e,i,u,o,a,c,l,f=n.length,s=n[0],h=s.length,p=[];for(p[0]=c=l=0,r=1;h>r;++r){for(t=0,i=0;f>t;++t)i+=n[t][r][1];for(t=0,u=0,a=s[r][0]-s[r-1][0];f>t;++t){for(e=0,o=(n[t][r][1]-n[t][r-1][1])/(2*a);t>e;++e)o+=(n[e][r][1]-n[e][r-1][1])/a;u+=o*n[t][r][1]}p[r]=c-=i?u/i*a:0,l>c&&(l=c)}for(r=0;h>r;++r)p[r]-=l;return p},expand:function(n){var t,r,e,i=n.length,u=n[0].length,o=1/i,a=[];for(r=0;u>r;++r){for(t=0,e=0;i>t;t++)e+=n[t][r][1];if(e)for(t=0;i>t;t++)n[t][r][1]/=e;else for(t=0;i>t;t++)n[t][r][1]=o}for(r=0;u>r;++r)a[r]=0;return a},zero:gi});ao.layout.histogram=function(){function n(n,u){for(var o,a,c=[],l=n.map(r,this),f=e.call(this,l,u),s=i.call(this,f,l,u),u=-1,h=l.length,p=s.length-1,v=t?1:1/h;++u<p;)o=c[u]=[],o.dx=s[u+1]-(o.x=s[u]),o.y=0;if(p>0)for(u=-1;++u<h;)a=l[u],a>=f[0]&&a<=f[1]&&(o=c[ao.bisect(s,a,1,p)-1],o.y+=v,o.push(n[u]));return c}var t=!0,r=Number,e=Mi,i=_i;return n.value=function(t){return arguments.length?(r=t,n):r},n.range=function(t){return arguments.length?(e=Sn(t),n):e},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return xi(n,t)}:Sn(t),n):i},n.frequency=function(r){return arguments.length?(t=!!r,n):t},n},ao.layout.pack=function(){function n(n,u){var o=r.call(this,n,u),a=o[0],c=i[0],l=i[1],f=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,oi(a,function(n){n.r=+f(n.value)}),oi(a,Ai),e){var s=e*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;oi(a,function(n){n.r+=s}),oi(a,Ai),oi(a,function(n){n.r-=s})}return Ni(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,r=ao.layout.hierarchy().sort(bi),e=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(r){return arguments.length?(t=null==r||"function"==typeof r?r:+r,n):t},n.padding=function(t){return arguments.length?(e=+t,n):e},ii(n,r)},ao.layout.tree=function(){function n(n,i){var f=o.call(this,n,i),s=f[0],h=t(s);if(oi(h,r),h.parent.m=-h.z,ui(h,e),l)ui(s,u);else{var p=s,v=s,g=s;ui(s,function(n){n.x<p.x&&(p=n),n.x>v.x&&(v=n),n.depth>g.depth&&(g=n)});var d=a(p,v)/2-p.x,y=c[0]/(v.x+a(v,p)/2+d),m=c[1]/(g.depth||1);ui(s,function(n){n.x=(n.x+d)*y,n.y=n.depth*m})}return f}function t(n){for(var t,r={A:null,children:[n]},e=[r];null!=(t=e.pop());)for(var i,u=t.children,o=0,a=u.length;a>o;++o)e.push((u[o]=i={_:u[o],parent:t,children:(i=u[o].children)&&i.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=i);return r.children[0]}function r(n){var t=n.children,r=n.parent.children,e=n.i?r[n.i-1]:null;if(t.length){Ri(n);var u=(t[0].z+t[t.length-1].z)/2;e?(n.z=e.z+a(n._,e._),n.m=n.z-u):n.z=u}else e&&(n.z=e.z+a(n._,e._));n.parent.A=i(n,e,n.parent.A||r[0])}function e(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function i(n,t,r){if(t){for(var e,i=n,u=n,o=t,c=i.parent.children[0],l=i.m,f=u.m,s=o.m,h=c.m;o=Ti(o),i=Di(i),o&&i;)c=Di(c),u=Ti(u),u.a=n,e=o.z+s-i.z-l+a(o._,i._),e>0&&(Li(Oi(o,n,r),n,e),l+=e,f+=e),s+=o.m,l+=i.m,h+=c.m,f+=u.m;o&&!Ti(u)&&(u.t=o,u.m+=s-f),i&&!Di(c)&&(c.t=i,c.m+=l-h,r=n)}return r}function u(n){n.x*=c[0],n.y=n.depth*c[1]}var o=ao.layout.hierarchy().sort(null).value(null),a=ji,c=[1,1],l=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(l=null==(c=t)?u:null,n):l?null:c},n.nodeSize=function(t){return arguments.length?(l=null==(c=t)?null:u,n):l?c:null},ii(n,o)},ao.layout.cluster=function(){function n(n,u){var o,a=t.call(this,n,u),c=a[0],l=0;oi(c,function(n){var t=n.children;t&&t.length?(n.x=qi(t),n.y=zi(t)):(n.x=o?l+=r(n,o):0,n.y=0,o=n)});var f=Ii(c),s=Pi(c),h=f.x-r(f,s)/2,p=s.x+r(s,f)/2;return oi(c,i?function(n){n.x=(n.x-c.x)*e[0],n.y=(c.y-n.y)*e[1]}:function(n){n.x=(n.x-h)/(p-h)*e[0],n.y=(1-(c.y?n.y/c.y:1))*e[1]}),a}var t=ao.layout.hierarchy().sort(null).value(null),r=ji,e=[1,1],i=!1;return n.separation=function(t){return arguments.length?(r=t,n):r},n.size=function(t){return arguments.length?(i=null==(e=t),n):i?null:e},n.nodeSize=function(t){return arguments.length?(i=null!=(e=t),n):i?e:null},ii(n,t)},ao.layout.treemap=function(){function n(n,t){for(var r,e,i=-1,u=n.length;++i<u;)e=(r=n[i]).value*(0>t?0:t),r.area=isNaN(e)||0>=e?0:e}function t(r){var u=r.children;if(u&&u.length){var o,a,c,l=s(r),f=[],h=u.slice(),v=1/0,g="slice"===p?l.dx:"dice"===p?l.dy:"slice-dice"===p?1&r.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/r.value),f.area=0;(c=h.length)>0;)f.push(o=h[c-1]),f.area+=o.area,"squarify"!==p||(a=e(f,g))<=v?(h.pop(),v=a):(f.area-=f.pop().area,i(f,g,l,!1),g=Math.min(l.dx,l.dy),f.length=f.area=0,v=1/0);f.length&&(i(f,g,l,!0),f.length=f.area=0),u.forEach(t)}}function r(t){var e=t.children;if(e&&e.length){var u,o=s(t),a=e.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;u=a.pop();)c.push(u),c.area+=u.area,null!=u.z&&(i(c,u.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);e.forEach(r)}}function e(n,t){for(var r,e=n.area,i=0,u=1/0,o=-1,a=n.length;++o<a;)(r=n[o].area)&&(u>r&&(u=r),r>i&&(i=r));return e*=e,t*=t,e?Math.max(t*i*v/e,e/(t*u*v)):1/0}function i(n,t,r,e){var i,u=-1,o=n.length,a=r.x,l=r.y,f=t?c(n.area/t):0;
+if(t==r.dx){for((e||f>r.dy)&&(f=r.dy);++u<o;)i=n[u],i.x=a,i.y=l,i.dy=f,a+=i.dx=Math.min(r.x+r.dx-a,f?c(i.area/f):0);i.z=!0,i.dx+=r.x+r.dx-a,r.y+=f,r.dy-=f}else{for((e||f>r.dx)&&(f=r.dx);++u<o;)i=n[u],i.x=a,i.y=l,i.dx=f,l+=i.dy=Math.min(r.y+r.dy-l,f?c(i.area/f):0);i.z=!1,i.dy+=r.y+r.dy-l,r.x+=f,r.dx-=f}}function u(e){var i=o||a(e),u=i[0];return u.x=u.y=0,u.value?(u.dx=l[0],u.dy=l[1]):u.dx=u.dy=0,o&&a.revalue(u),n([u],u.dx*u.dy/u.value),(o?r:t)(u),h&&(o=i),i}var o,a=ao.layout.hierarchy(),c=Math.round,l=[1,1],f=null,s=Ui,h=!1,p="squarify",v=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(l=n,u):l},u.padding=function(n){function t(t){var r=n.call(u,t,t.depth);return null==r?Ui(t):Fi(t,"number"==typeof r?[r,r,r,r]:r)}function r(t){return Fi(t,n)}if(!arguments.length)return f;var e;return s=null==(f=n)?Ui:"function"==(e=typeof n)?t:"number"===e?(n=[n,n,n,n],r):r,u},u.round=function(n){return arguments.length?(c=n?Math.round:Number,u):c!=Number},u.sticky=function(n){return arguments.length?(h=n,o=null,u):h},u.ratio=function(n){return arguments.length?(v=n,u):v},u.mode=function(n){return arguments.length?(p=n+"",u):p},ii(u,a)},ao.random={normal:function(n,t){var r=arguments.length;return 2>r&&(t=1),1>r&&(n=0),function(){var r,e,i;do r=2*Math.random()-1,e=2*Math.random()-1,i=r*r+e*e;while(!i||i>1);return n+t*r*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=ao.random.normal.apply(ao,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ao.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,r=0;n>r;r++)t+=Math.random();return t}}},ao.scale={};var wc={floor:m,ceil:m};ao.scale.linear=function(){return Xi([0,1],[0,1],_e,!1)};var kc={s:1,g:1,p:1,r:1,e:1};ao.scale.log=function(){return eu(ao.scale.linear().domain([0,1]),10,!0,[1,10])};var Cc=ao.format(".0e"),Ac={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ao.scale.pow=function(){return iu(ao.scale.linear(),1,[0,1])},ao.scale.sqrt=function(){return ao.scale.pow().exponent(.5)},ao.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},ao.scale.category10=function(){return ao.scale.ordinal().range(Sc)},ao.scale.category20=function(){return ao.scale.ordinal().range(Ec)},ao.scale.category20b=function(){return ao.scale.ordinal().range(Nc)},ao.scale.category20c=function(){return ao.scale.ordinal().range($c)};var Sc=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(xn),Ec=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(xn),Nc=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(xn),$c=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(xn);ao.scale.quantile=function(){return au([],[])},ao.scale.quantize=function(){return cu(0,1,[0,1])},ao.scale.threshold=function(){return lu([.5],[0,1])},ao.scale.identity=function(){return fu([0,1])},ao.svg={},ao.svg.arc=function(){function n(){var n=Math.max(0,+r.apply(this,arguments)),l=Math.max(0,+e.apply(this,arguments)),f=o.apply(this,arguments)-Uo,s=a.apply(this,arguments)-Uo,h=Math.abs(s-f),p=f>s?0:1;if(n>l&&(v=l,l=n,n=v),h>=Po)return t(l,p)+(n?t(n,1-p):"")+"Z";var v,g,d,y,m,_,x,M,b,w,k,C,A=0,S=0,E=[];if((y=(+c.apply(this,arguments)||0)/2)&&(d=u===jc?Math.sqrt(n*n+l*l):+u.apply(this,arguments),p||(S*=-1),l&&(S=tn(d/l*Math.sin(y))),n&&(A=tn(d/n*Math.sin(y)))),l){m=l*Math.cos(f+S),_=l*Math.sin(f+S),x=l*Math.cos(s-S),M=l*Math.sin(s-S);var N=Math.abs(s-f-2*S)<=qo?0:1;if(S&&yu(m,_,x,M)===p^N){var $=(f+s)/2;m=l*Math.cos($),_=l*Math.sin($),x=M=null}}else m=_=0;if(n){b=n*Math.cos(s-A),w=n*Math.sin(s-A),k=n*Math.cos(f+A),C=n*Math.sin(f+A);var j=Math.abs(f-s+2*A)<=qo?0:1;if(A&&yu(b,w,k,C)===1-p^j){var D=(f+s)/2;b=n*Math.cos(D),w=n*Math.sin(D),k=C=null}}else b=w=0;if(h>Oo&&(v=Math.min(Math.abs(l-n)/2,+i.apply(this,arguments)))>.001){g=l>n^p?0:1;var T=v,L=v;if(qo>h){var R=null==k?[b,w]:null==x?[m,_]:Lr([m,_],[k,C],[x,M],[b,w]),O=m-R[0],z=_-R[1],q=x-R[0],I=M-R[1],P=1/Math.sin(Math.acos((O*q+z*I)/(Math.sqrt(O*O+z*z)*Math.sqrt(q*q+I*I)))/2),U=Math.sqrt(R[0]*R[0]+R[1]*R[1]);L=Math.min(v,(n-U)/(P-1)),T=Math.min(v,(l-U)/(P+1))}if(null!=x){var F=mu(null==k?[b,w]:[k,C],[m,_],l,T,p),H=mu([x,M],[b,w],l,T,p);v===T?E.push("M",F[0],"A",T,",",T," 0 0,",g," ",F[1],"A",l,",",l," 0 ",1-p^yu(F[1][0],F[1][1],H[1][0],H[1][1]),",",p," ",H[1],"A",T,",",T," 0 0,",g," ",H[0]):E.push("M",F[0],"A",T,",",T," 0 1,",g," ",H[0])}else E.push("M",m,",",_);if(null!=k){var W=mu([m,_],[k,C],n,-L,p),B=mu([b,w],null==x?[m,_]:[x,M],n,-L,p);v===L?E.push("L",B[0],"A",L,",",L," 0 0,",g," ",B[1],"A",n,",",n," 0 ",p^yu(B[1][0],B[1][1],W[1][0],W[1][1]),",",1-p," ",W[1],"A",L,",",L," 0 0,",g," ",W[0]):E.push("L",B[0],"A",L,",",L," 0 0,",g," ",W[0])}else E.push("L",b,",",w)}else E.push("M",m,",",_),null!=x&&E.push("A",l,",",l," 0 ",N,",",p," ",x,",",M),E.push("L",b,",",w),null!=k&&E.push("A",n,",",n," 0 ",j,",",1-p," ",k,",",C);return E.push("Z"),E.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var r=hu,e=pu,i=su,u=jc,o=vu,a=gu,c=du;return n.innerRadius=function(t){return arguments.length?(r=Sn(t),n):r},n.outerRadius=function(t){return arguments.length?(e=Sn(t),n):e},n.cornerRadius=function(t){return arguments.length?(i=Sn(t),n):i},n.padRadius=function(t){return arguments.length?(u=t==jc?jc:Sn(t),n):u},n.startAngle=function(t){return arguments.length?(o=Sn(t),n):o},n.endAngle=function(t){return arguments.length?(a=Sn(t),n):a},n.padAngle=function(t){return arguments.length?(c=Sn(t),n):c},n.centroid=function(){var n=(+r.apply(this,arguments)+ +e.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Uo;return[Math.cos(t)*n,Math.sin(t)*n]},n};var jc="auto";ao.svg.line=function(){return _u(m)};var Dc=ao.map({linear:xu,"linear-closed":Mu,step:bu,"step-before":wu,"step-after":ku,basis:$u,"basis-open":ju,"basis-closed":Du,bundle:Tu,cardinal:Su,"cardinal-open":Cu,"cardinal-closed":Au,monotone:Iu});Dc.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Tc=[0,2/3,1/3,0],Lc=[0,1/3,2/3,0],Rc=[0,1/6,2/3,1/6];ao.svg.line.radial=function(){var n=_u(Pu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},wu.reverse=ku,ku.reverse=wu,ao.svg.area=function(){return Uu(m)},ao.svg.area.radial=function(){var n=Uu(Pu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ao.svg.chord=function(){function n(n,a){var c=t(this,u,n,a),l=t(this,o,n,a);return"M"+c.p0+e(c.r,c.p1,c.a1-c.a0)+(r(c,l)?i(c.r,c.p1,c.r,c.p0):i(c.r,c.p1,l.r,l.p0)+e(l.r,l.p1,l.a1-l.a0)+i(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,r,e){var i=t.call(n,r,e),u=a.call(n,i,e),o=c.call(n,i,e)-Uo,f=l.call(n,i,e)-Uo;return{r:u,a0:o,a1:f,p0:[u*Math.cos(o),u*Math.sin(o)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function r(n,t){return n.a0==t.a0&&n.a1==t.a1}function e(n,t,r){return"A"+n+","+n+" 0 "+ +(r>qo)+",1 "+t}function i(n,t,r,e){return"Q 0,0 "+e}var u=_r,o=xr,a=Fu,c=vu,l=gu;return n.radius=function(t){return arguments.length?(a=Sn(t),n):a},n.source=function(t){return arguments.length?(u=Sn(t),n):u},n.target=function(t){return arguments.length?(o=Sn(t),n):o},n.startAngle=function(t){return arguments.length?(c=Sn(t),n):c},n.endAngle=function(t){return arguments.length?(l=Sn(t),n):l},n},ao.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),o=r.call(this,n,i),a=(u.y+o.y)/2,c=[u,{x:u.x,y:a},{x:o.x,y:a},o];return c=c.map(e),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=_r,r=xr,e=Hu;return n.source=function(r){return arguments.length?(t=Sn(r),n):t},n.target=function(t){return arguments.length?(r=Sn(t),n):r},n.projection=function(t){return arguments.length?(e=t,n):e},n},ao.svg.diagonal.radial=function(){var n=ao.svg.diagonal(),t=Hu,r=n.projection;return n.projection=function(n){return arguments.length?r(Wu(t=n)):t},n},ao.svg.symbol=function(){function n(n,e){return(Oc.get(t.call(this,n,e))||Zu)(r.call(this,n,e))}var t=Yu,r=Bu;return n.type=function(r){return arguments.length?(t=Sn(r),n):t},n.size=function(t){return arguments.length?(r=Sn(t),n):r},n};var Oc=ao.map({circle:Zu,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*qc)),r=t*qc;return"M0,"+-t+"L"+r+",0 0,"+t+" "+-r+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/zc),r=t*zc/2;return"M0,"+r+"L"+t+","+-r+" "+-t+","+-r+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/zc),r=t*zc/2;return"M0,"+-r+"L"+t+","+r+" "+-t+","+r+"Z"}});ao.svg.symbolTypes=Oc.keys();var zc=Math.sqrt(3),qc=Math.tan(30*Fo);No.transition=function(n){for(var t,r,e=Ic||++Hc,i=Qu(n),u=[],o=Pc||{time:Date.now(),ease:Ae,delay:0,duration:250},a=-1,c=this.length;++a<c;){u.push(t=[]);for(var l=this[a],f=-1,s=l.length;++f<s;)(r=l[f])&&Gu(r,f,i,e,o),t.push(r)}return Xu(u,i,e)},No.interrupt=function(n){return this.each(null==n?Uc:Vu(Qu(n)))};var Ic,Pc,Uc=Vu(Qu()),Fc=[],Hc=0;Fc.call=No.call,Fc.empty=No.empty,Fc.node=No.node,Fc.size=No.size,ao.transition=function(n,t){return n&&n.transition?Ic?n.transition(t):n:ao.selection().transition(n)},ao.transition.prototype=Fc,Fc.select=function(n){var t,r,e,i=this.id,u=this.namespace,o=[];n=E(n);for(var a=-1,c=this.length;++a<c;){o.push(t=[]);for(var l=this[a],f=-1,s=l.length;++f<s;)(e=l[f])&&(r=n.call(e,e.__data__,f,a))?("__data__"in e&&(r.__data__=e.__data__),Gu(r,f,u,i,e[u][i]),t.push(r)):t.push(null)}return Xu(o,u,i)},Fc.selectAll=function(n){var t,r,e,i,u,o=this.id,a=this.namespace,c=[];n=N(n);for(var l=-1,f=this.length;++l<f;)for(var s=this[l],h=-1,p=s.length;++h<p;)if(e=s[h]){u=e[a][o],r=n.call(e,e.__data__,h,l),c.push(t=[]);for(var v=-1,g=r.length;++v<g;)(i=r[v])&&Gu(i,v,a,o,u),t.push(i)}return Xu(c,a,o)},Fc.filter=function(n){var t,r,e,i=[];"function"!=typeof n&&(n=U(n));for(var u=0,o=this.length;o>u;u++){i.push(t=[]);for(var r=this[u],a=0,c=r.length;c>a;a++)(e=r[a])&&n.call(e,e.__data__,a,u)&&t.push(e)}return Xu(i,this.namespace,this.id)},Fc.tween=function(n,t){var r=this.id,e=this.namespace;return arguments.length<2?this.node()[e][r].tween.get(n):H(this,null==t?function(t){t[e][r].tween.remove(n)}:function(i){i[e][r].tween.set(n,t)})},Fc.attr=function(n,t){function r(){this.removeAttribute(a)}function e(){this.removeAttributeNS(a.space,a.local)}function i(n){return null==n?r:(n+="",function(){var t,r=this.getAttribute(a);return r!==n&&(t=o(r,n),function(n){this.setAttribute(a,t(n))})})}function u(n){return null==n?e:(n+="",function(){var t,r=this.getAttributeNS(a.space,a.local);return r!==n&&(t=o(r,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Ze:_e,a=ao.ns.qualify(n);return Ku(this,"attr."+n,t,a.local?u:i)},Fc.attrTween=function(n,t){function r(n,r){var e=t.call(this,n,r,this.getAttribute(i));return e&&function(n){this.setAttribute(i,e(n))}}function e(n,r){var e=t.call(this,n,r,this.getAttributeNS(i.space,i.local));return e&&function(n){this.setAttributeNS(i.space,i.local,e(n))}}var i=ao.ns.qualify(n);return this.tween("attr."+n,i.local?e:r)},Fc.style=function(n,r,e){function i(){this.style.removeProperty(n)}function u(r){return null==r?i:(r+="",function(){var i,u=t(this).getComputedStyle(this,null).getPropertyValue(n);return u!==r&&(i=_e(u,r),function(t){this.style.setProperty(n,i(t),e)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(r="");for(e in n)this.style(e,n[e],r);return this}e=""}return Ku(this,"style."+n,r,u)},Fc.styleTween=function(n,r,e){function i(i,u){var o=r.call(this,i,u,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,i)},Fc.text=function(n){return Ku(this,"text",n,Ju)},Fc.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Fc.ease=function(n){var t=this.id,r=this.namespace;return arguments.length<1?this.node()[r][t].ease:("function"!=typeof n&&(n=ao.ease.apply(ao,arguments)),H(this,function(e){e[r][t].ease=n}))},Fc.delay=function(n){var t=this.id,r=this.namespace;return arguments.length<1?this.node()[r][t].delay:H(this,"function"==typeof n?function(e,i,u){e[r][t].delay=+n.call(e,e.__data__,i,u)}:(n=+n,function(e){e[r][t].delay=n}))},Fc.duration=function(n){var t=this.id,r=this.namespace;return arguments.length<1?this.node()[r][t].duration:H(this,"function"==typeof n?function(e,i,u){e[r][t].duration=Math.max(1,n.call(e,e.__data__,i,u))}:(n=Math.max(1,n),function(e){e[r][t].duration=n}))},Fc.each=function(n,t){var r=this.id,e=this.namespace;if(arguments.length<2){var i=Pc,u=Ic;try{Ic=r,H(this,function(t,i,u){Pc=t[e][r],n.call(t,t.__data__,i,u)})}finally{Pc=i,Ic=u}}else H(this,function(i){var u=i[e][r];(u.event||(u.event=ao.dispatch("start","end","interrupt"))).on(n,t)});return this},Fc.transition=function(){for(var n,t,r,e,i=this.id,u=++Hc,o=this.namespace,a=[],c=0,l=this.length;l>c;c++){a.push(n=[]);for(var t=this[c],f=0,s=t.length;s>f;f++)(r=t[f])&&(e=r[o][i],Gu(r,f,o,u,{time:e.time,ease:e.ease,delay:e.delay+e.duration,duration:e.duration})),n.push(r)}return Xu(a,o,u)},ao.svg.axis=function(){function n(n){n.each(function(){var n,l=ao.select(this),f=this.__chart__||r,s=this.__chart__=r.copy(),h=null==c?s.ticks?s.ticks.apply(s,a):s.domain():c,p=null==t?s.tickFormat?s.tickFormat.apply(s,a):m:t,v=l.selectAll(".tick").data(h,s),g=v.enter().insert("g",".domain").attr("class","tick").style("opacity",Oo),d=ao.transition(v.exit()).style("opacity",Oo).remove(),y=ao.transition(v.order()).style("opacity",1),_=Math.max(i,0)+o,x=Wi(s),M=l.selectAll(".domain").data([0]),b=(M.enter().append("path").attr("class","domain"),ao.transition(M));g.append("line"),g.append("text");var w,k,C,A,S=g.select("line"),E=y.select("line"),N=v.select("text").text(p),$=g.select("text"),j=y.select("text"),D="top"===e||"left"===e?-1:1;if("bottom"===e||"top"===e?(n=no,w="x",C="y",k="x2",A="y2",N.attr("dy",0>D?"0em":".71em").style("text-anchor","middle"),b.attr("d","M"+x[0]+","+D*u+"V0H"+x[1]+"V"+D*u)):(n=to,w="y",C="x",k="y2",A="x2",N.attr("dy",".32em").style("text-anchor",0>D?"end":"start"),b.attr("d","M"+D*u+","+x[0]+"H0V"+x[1]+"H"+D*u)),S.attr(A,D*i),$.attr(C,D*_),E.attr(k,0).attr(A,D*i),j.attr(w,0).attr(C,D*_),s.rangeBand){var T=s,L=T.rangeBand()/2;f=s=function(n){return T(n)+L}}else f.rangeBand?f=s:d.call(n,s,f);g.call(n,f,s),y.call(n,s,s)})}var t,r=ao.scale.linear(),e=Wc,i=6,u=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(r=t,n):r},n.orient=function(t){return arguments.length?(e=t in Bc?t+"":Wc,n):e},n.ticks=function(){return arguments.length?(a=lo(arguments),n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(r){return arguments.length?(t=r,n):t},n.tickSize=function(t){var r=arguments.length;return r?(i=+t,u=+arguments[r-1],n):i},n.innerTickSize=function(t){return arguments.length?(i=+t,n):i},n.outerTickSize=function(t){return arguments.length?(u=+t,n):u},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Wc="bottom",Bc={top:1,right:1,bottom:1,left:1};ao.svg.brush=function(){function n(t){t.each(function(){var t=ao.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(g,m);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Yc[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var c,s=ao.transition(t),h=ao.transition(o);l&&(c=Wi(l),h.attr("x",c[0]).attr("width",c[1]-c[0]),e(s)),f&&(c=Wi(f),h.attr("y",c[0]).attr("height",c[1]-c[0]),i(s)),r(s)})}function r(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+s[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",s[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",s[1]-s[0])}function i(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==ao.event.keyCode&&(N||(_=null,j[0]-=s[1],j[1]-=h[1],N=2),k())}function g(){32==ao.event.keyCode&&2==N&&(j[0]+=s[1],j[1]+=h[1],N=0,k())}function d(){var n=ao.mouse(M),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),N||(ao.event.altKey?(_||(_=[(s[0]+s[1])/2,(h[0]+h[1])/2]),j[0]=s[+(n[0]<_[0])],j[1]=h[+(n[1]<_[1])]):_=null),S&&y(n,l,0)&&(e(C),t=!0),E&&y(n,f,1)&&(i(C),t=!0),t&&(r(C),w({type:"brush",mode:N?"move":"resize"}))}function y(n,t,r){var e,i,u=Wi(t),c=u[0],l=u[1],f=j[r],g=r?h:s,d=g[1]-g[0];return N&&(c-=f,l-=d+f),e=(r?v:p)?Math.max(c,Math.min(l,n[r])):n[r],N?i=(e+=f)+d:(_&&(f=Math.max(c,Math.min(l,2*_[r]-e))),e>f?(i=e,e=f):i=f),g[0]!=e||g[1]!=i?(r?a=null:o=null,g[0]=e,g[1]=i,!0):void 0}function m(){d(),C.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ao.select("body").style("cursor",null),D.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),$(),w({type:"brushend"})}var _,x,M=this,b=ao.select(ao.event.target),w=c.of(M,arguments),C=ao.select(M),A=b.datum(),S=!/^(n|s)$/.test(A)&&l,E=!/^(e|w)$/.test(A)&&f,N=b.classed("extent"),$=X(M),j=ao.mouse(M),D=ao.select(t(M)).on("keydown.brush",u).on("keyup.brush",g);if(ao.event.changedTouches?D.on("touchmove.brush",d).on("touchend.brush",m):D.on("mousemove.brush",d).on("mouseup.brush",m),C.interrupt().selectAll("*").interrupt(),N)j[0]=s[0]-j[0],j[1]=h[0]-j[1];else if(A){var T=+/w$/.test(A),L=+/^n/.test(A);x=[s[1-T]-j[0],h[1-L]-j[1]],j[0]=s[T],j[1]=h[L]}else ao.event.altKey&&(_=j.slice());C.style("pointer-events","none").selectAll(".resize").style("display",null),ao.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),d()}var o,a,c=A(n,"brushstart","brush","brushend"),l=null,f=null,s=[0,0],h=[0,0],p=!0,v=!0,g=Zc[0];return n.event=function(n){n.each(function(){var n=c.of(this,arguments),t={x:s,y:h,i:o,j:a},r=this.__chart__||t;this.__chart__=t,Ic?ao.select(this).transition().each("start.brush",function(){o=r.i,a=r.j,s=r.x,h=r.y,n({type:"brushstart"})}).tween("brush:brush",function(){var r=xe(s,t.x),e=xe(h,t.y);return o=a=null,function(i){s=t.x=r(i),h=t.y=e(i),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(l=t,g=Zc[!l<<1|!f],n):l},n.y=function(t){return arguments.length?(f=t,g=Zc[!l<<1|!f],n):f},n.clamp=function(t){return arguments.length?(l&&f?(p=!!t[0],v=!!t[1]):l?p=!!t:f&&(v=!!t),n):l&&f?[p,v]:l?p:f?v:null},n.extent=function(t){var r,e,i,u,c;return arguments.length?(l&&(r=t[0],e=t[1],f&&(r=r[0],e=e[0]),o=[r,e],l.invert&&(r=l(r),e=l(e)),r>e&&(c=r,r=e,e=c),r==s[0]&&e==s[1]||(s=[r,e])),f&&(i=t[0],u=t[1],l&&(i=i[1],u=u[1]),a=[i,u],f.invert&&(i=f(i),u=f(u)),i>u&&(c=i,i=u,u=c),i==h[0]&&u==h[1]||(h=[i,u])),n):(l&&(o?(r=o[0],e=o[1]):(r=s[0],e=s[1],l.invert&&(r=l.invert(r),e=l.invert(e)),r>e&&(c=r,r=e,e=c))),f&&(a?(i=a[0],u=a[1]):(i=h[0],u=h[1],f.invert&&(i=f.invert(i),u=f.invert(u)),i>u&&(c=i,i=u,u=c))),l&&f?[[r,i],[e,u]]:l?[r,e]:f&&[i,u])},n.clear=function(){return n.empty()||(s=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!l&&s[0]==s[1]||!!f&&h[0]==h[1]},ao.rebind(n,c,"on")};var Yc={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Zc=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Vc=pa.format=_a.timeFormat,Xc=Vc.utc,Kc=Xc("%Y-%m-%dT%H:%M:%S.%LZ");Vc.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?ro:Kc,ro.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},ro.toString=Kc.toString,pa.second=Un(function(n){return new va(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),pa.seconds=pa.second.range,pa.seconds.utc=pa.second.utc.range,pa.minute=Un(function(n){return new va(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),pa.minutes=pa.minute.range,pa.minutes.utc=pa.minute.utc.range,pa.hour=Un(function(n){var t=n.getTimezoneOffset()/60;return new va(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),pa.hours=pa.hour.range,pa.hours.utc=pa.hour.utc.range,pa.month=Un(function(n){return n=pa.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),pa.months=pa.month.range,pa.months.utc=pa.month.utc.range;var Jc=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Qc=[[pa.second,1],[pa.second,5],[pa.second,15],[pa.second,30],[pa.minute,1],[pa.minute,5],[pa.minute,15],[pa.minute,30],[pa.hour,1],[pa.hour,3],[pa.hour,6],[pa.hour,12],[pa.day,1],[pa.day,2],[pa.week,1],[pa.month,1],[pa.month,3],[pa.year,1]],Gc=Vc.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",$t]]),nl={range:function(n,t,r){return ao.range(Math.ceil(n/r)*r,+t,r).map(io)},floor:m,ceil:m};Qc.year=pa.year,pa.scale=function(){return eo(ao.scale.linear(),Qc,Gc)};var tl=Qc.map(function(n){return[n[0].utc,n[1]]}),rl=Xc.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",$t]]);tl.year=pa.year.utc,pa.scale.utc=function(){return eo(ao.scale.linear(),tl,rl)},ao.text=En(function(n){return n.responseText}),ao.json=function(n,t){return Nn(n,"application/json",uo,t)},ao.html=function(n,t){return Nn(n,"text/html",oo,t)},ao.xml=En(function(n){return n.responseXML}),"function"==typeof define&&define.amd?(this.d3=ao,define(ao)):"object"==typeof module&&module.exports?module.exports=ao:this.d3=ao}(),function(){function n(n,t){return n.set(t[0],t[1]),n}function t(n,t){return n.add(t),n}function r(n,t,r){var e=r?r.length:0;switch(e){case 0:return n.call(t);case 1:return n.call(t,r[0]);case 2:return n.call(t,r[0],r[1]);case 3:return n.call(t,r[0],r[1],r[2])}return n.apply(t,r)}function e(n,t){for(var r=-1,e=n.length,i=-1,u=t.length,o=Array(e+u);++r<e;)o[r]=n[r];for(;++i<u;)o[r++]=t[i];return o}function i(n,t){for(var r=-1,e=n.length;++r<e&&t(n[r],r,n)!==!1;);return n}function u(n,t){for(var r=n.length;r--&&t(n[r],r,n)!==!1;);return n}function o(n,t){for(var r=-1,e=n.length;++r<e;)if(!t(n[r],r,n))return!1;return!0}function a(n,t){for(var r=-1,e=n.length,i=-1,u=[];++r<e;){var o=n[r];t(o,r,n)&&(u[++i]=o)}return u}function c(n,t){return!!n.length&&m(n,t,0)>-1}function l(n,t,r){for(var e=-1,i=n.length;++e<i;)if(r(t,n[e]))return!0;return!1}function f(n,t){for(var r=-1,e=n.length,i=Array(e);++r<e;)i[r]=t(n[r],r,n);return i}function s(n,t){for(var r=-1,e=t.length,i=n.length;++r<e;)n[i+r]=t[r];return n}function h(n,t,r,e){var i=-1,u=n.length;for(e&&u&&(r=n[++i]);++i<u;)r=t(r,n[i],i,n);return r}function p(n,t,r,e){var i=n.length;for(e&&i&&(r=n[--i]);i--;)r=t(r,n[i],i,n);return r}function v(n,t){for(var r=-1,e=n.length;++r<e;)if(t(n[r],r,n))return!0;return!1}function g(n,t,r){for(var e=-1,i=n.length;++e<i;){var u=n[e],o=t(u);if(null!=o&&(a===B?o===o:r(o,a)))var a=o,c=u}return c}function d(n,t,r,e){var i;return r(n,function(n,r,u){return t(n,r,u)?(i=e?r:n,!1):void 0}),i}function y(n,t,r){for(var e=n.length,i=r?e:-1;r?i--:++i<e;)if(t(n[i],i,n))return i;return-1}function m(n,t,r){if(t!==t)return L(n,r);for(var e=r-1,i=n.length;++e<i;)if(n[e]===t)return e;return-1}function _(n,t,r,e,i){return i(n,function(n,i,u){r=e?(e=!1,n):t(r,n,i,u)}),r}function x(n,t){var r=n.length;for(n.sort(t);r--;)n[r]=n[r].value;return n}function M(n,t){for(var r,e=-1,i=n.length;++e<i;){var u=t(n[e]);u!==B&&(r=r===B?u:r+u)}return r}function b(n,t){for(var r=-1,e=Array(n);++r<n;)e[r]=t(r);return e}function w(n,t){return f(t,function(t){return[t,n[t]]})}function k(n){return function(t){return n(t)}}function C(n,t){return f(t,function(t){return n[t]})}function A(n,t){for(var r=-1,e=n.length;++r<e&&m(t,n[r],0)>-1;);return r}function S(n,t){for(var r=n.length;r--&&m(t,n[r],0)>-1;);return r}function E(n){return n&&n.Object===Object?n:null}function N(n,t){if(n!==t){var r=null===n,e=n===B,i=n===n,u=null===t,o=t===B,a=t===t;if(n>t&&!u||!i||r&&!o&&a||e&&a)return 1;if(t>n&&!r||!a||u&&!e&&i||o&&i)return-1}return 0}function $(n,t,r){for(var e=-1,i=n.criteria,u=t.criteria,o=i.length,a=r.length;++e<o;){var c=N(i[e],u[e]);if(c){if(e>=a)return c;var l=r[e];return c*("desc"==l?-1:1)}}return n.index-t.index}function j(n){return gr[n]}function D(n){return dr[n]}function T(n){return"\\"+_r[n]}function L(n,t,r){for(var e=n.length,i=t+(r?0:-1);r?i--:++i<e;){var u=n[i];if(u!==u)return i}return-1}function R(n){var t=!1;if(null!=n&&"function"!=typeof n.toString)try{t=!!(n+"")}catch(r){}return t}function O(n,t){return n="number"==typeof n||Mt.test(n)?+n:-1,t=null==t?yn:t,n>-1&&n%1==0&&t>n}function z(n){for(var t,r=[];!(t=n.next()).done;)r.push(t.value);return r}function q(n){var t=-1,r=Array(n.size);return n.forEach(function(n,e){r[++t]=[e,n]}),r}function I(n,t){for(var r=-1,e=n.length,i=-1,u=[];++r<e;)n[r]===t&&(n[r]=wn,u[++i]=r);return u}function P(n){var t=-1,r=Array(n.size);return n.forEach(function(n){r[++t]=n}),r}function U(n){if(!n||!ar.test(n))return n.length;for(var t=or.lastIndex=0;or.test(n);)t++;return t}function F(n){return n.match(or)}function H(n){return yr[n]}function W(E){function qn(n){if(wo(n)&&!nf(n)&&!(n instanceof At)){if(n instanceof Ct)return n;if(Cc.call(n,"__wrapped__"))return Mi(n)}return new Ct(n)}function Mt(){}function Ct(n,t){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=B}function At(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=xn,this.__views__=[]}function St(){var n=new At(this.__wrapped__);return n.__actions__=Ae(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=Ae(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=Ae(this.__views__),n}function Et(){if(this.__filtered__){var n=new At(this);n.__dir__=-1,n.__filtered__=!0}else n=this.clone(),n.__dir__*=-1;return n}function Nt(){var n=this.__wrapped__.value(),t=this.__dir__,r=nf(n),e=0>t,i=r?n.length:0,u=ri(0,i,this.__views__),o=u.start,a=u.end,c=a-o,l=e?a:o-1,f=this.__iteratees__,s=f.length,h=0,p=Vc(c,this.__takeCount__);if(!r||fn>i||i==c&&p==c)return de(n,this.__actions__);var v=[];n:for(;c--&&p>h;){l+=t;for(var g=-1,d=n[l];++g<s;){var y=f[g],m=y.iteratee,_=y.type,x=m(d);if(_==hn)d=x;else if(!x){if(_==sn)continue n;break n}}v[h++]=d}return v}function $t(){}function jt(n,t){return Tt(n,t)&&delete n[t]}function Dt(n,t){if(tl){var r=n[t];return r===gn?B:r}return Cc.call(n,t)?n[t]:B}function Tt(n,t){return tl?n[t]!==B:Cc.call(n,t)}function Lt(n,t,r){n[t]=tl&&r===B?gn:r}function Rt(n){var t=-1,r=n?n.length:0;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function Ot(){this.__data__={hash:new $t,map:Qc?new Qc:[],string:new $t}}function zt(n){var t=this.__data__;return fi(n)?jt("string"==typeof n?t.string:t.hash,n):Qc?t.map["delete"](n):Kt(t.map,n)}function qt(n){var t=this.__data__;return fi(n)?Dt("string"==typeof n?t.string:t.hash,n):Qc?t.map.get(n):Jt(t.map,n)}function It(n){var t=this.__data__;return fi(n)?Tt("string"==typeof n?t.string:t.hash,n):Qc?t.map.has(n):Qt(t.map,n)}function Pt(n,t){var r=this.__data__;return fi(n)?Lt("string"==typeof n?r.string:r.hash,n,t):Qc?r.map.set(n,t):nr(r.map,n,t),this}function Ut(n){var t=-1,r=n?n.length:0;for(this.__data__=new Rt;++t<r;)this.push(n[t])}function Ft(n,t){var r=n.__data__;if(fi(t)){var e=r.__data__,i="string"==typeof t?e.string:e.hash;return i[t]===gn}return r.has(t)}function Ht(n){var t=this.__data__;if(fi(n)){var r=t.__data__,e="string"==typeof n?r.string:r.hash;e[n]=gn}else t.set(n,gn)}function Wt(n){var t=-1,r=n?n.length:0;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function Bt(){this.__data__={array:[],map:null}}function Yt(n){var t=this.__data__,r=t.array;return r?Kt(r,n):t.map["delete"](n)}function Zt(n){var t=this.__data__,r=t.array;return r?Jt(r,n):t.map.get(n)}function Vt(n){var t=this.__data__,r=t.array;return r?Qt(r,n):t.map.has(n)}function Xt(n,t){var r=this.__data__,e=r.array;e&&(e.length<fn-1?nr(e,n,t):(r.array=null,r.map=new Rt(e)));var i=r.map;return i&&i.set(n,t),this}function Kt(n,t){var r=Gt(n,t);if(0>r)return!1;var e=n.length-1;return r==e?n.pop():Uc.call(n,r,1),!0}function Jt(n,t){var r=Gt(n,t);return 0>r?B:n[r][1]}function Qt(n,t){return Gt(n,t)>-1}function Gt(n,t){for(var r=n.length;r--;)if(io(n[r][0],t))return r;return-1}function nr(n,t,r){var e=Gt(n,t);0>e?n.push([t,r]):n[e][1]=r}function tr(n,t,r,e){return n===B||io(n,wc[r])&&!Cc.call(e,r)?t:n}function rr(n,t,r){(r===B||io(n[t],r))&&("number"!=typeof t||r!==B||t in n)||(n[t]=r)}function er(n,t,r){var e=n[t];io(e,r)&&(!io(e,wc[t])||Cc.call(n,t))&&(r!==B||t in n)||(n[t]=r)}function ir(n,t){return n&&Se(t,oa(t),n)}function or(n,t){for(var r=-1,e=null==n,i=t.length,u=Array(i);++r<i;)u[r]=e?B:ra(n,t[r]);return u}function gr(n,t,r){return n===n&&(r!==B&&(n=r>=n?n:r),t!==B&&(n=n>=t?n:t)),n}function dr(n,t,r,e,u,o){var a;if(r&&(a=u?r(n,e,u,o):r(n)),a!==B)return a;if(!bo(n))return n;var c=nf(n);if(c){if(a=ii(n),!t)return Ae(n,a)}else{var l=ti(n),f=l==Nn||l==$n;if(l!=Tn&&l!=kn&&(!f||u))return vr[l]?oi(n,l,t):u?n:{};if(R(n))return u?n:{};if(a=ui(f?{}:n),!t)return Ne(n,ir(a,n))}o||(o=new Wt);var s=o.get(n);return s?s:(o.set(n,a),(c?i:Sr)(n,function(e,i){er(a,i,dr(e,t,r,i,n,o))}),c?a:Ne(n,a))}function yr(n){var t=oa(n),r=t.length;return function(e){if(null==e)return!r;for(var i=r;i--;){var u=t[i],o=n[u],a=e[u];if(a===B&&!(u in Object(e))||!o(a))return!1}return!0}}function mr(n,t,r){if("function"!=typeof n)throw new Mc(vn);return Pc(function(){n.apply(B,r)},t)}function _r(n,t,r,e){var i=-1,u=c,o=!0,a=n.length,s=[],h=t.length;if(!a)return s;r&&(t=f(t,k(r))),e?(u=l,o=!1):t.length>=fn&&(u=Ft,o=!1,t=new Ut(t));n:for(;++i<a;){var p=n[i],v=r?r(p):p;if(o&&v===v){for(var g=h;g--;)if(t[g]===v)continue n;s.push(p)}else u(t,v,e)||s.push(p)}return s}function br(n,t){var r=!0;return fl(n,function(n,e,i){return r=!!t(n,e,i)}),r}function wr(n,t,r,e){var i=n.length;for(r=Uo(r),0>r&&(r=-r>i?0:i+r),
+e=e===B||e>i?i:Uo(e),0>e&&(e+=i),e=r>e?0:Fo(e);e>r;)n[r++]=t;return n}function kr(n,t){var r=[];return fl(n,function(n,e,i){t(n,e,i)&&r.push(n)}),r}function Cr(n,t,r,e){e||(e=[]);for(var i=-1,u=n.length;++i<u;){var o=n[i];lo(o)&&(r||nf(o)||ao(o))?t?Cr(o,t,r,e):s(e,o):r||(e[e.length]=o)}return e}function Ar(n,t){return null==n?n:hl(n,t,aa)}function Sr(n,t){return n&&hl(n,t,oa)}function Er(n,t){return n&&pl(n,t,oa)}function jr(n,t){return a(t,function(t){return _o(n[t])})}function Dr(n,t){t=li(t,n)?[t+""]:he(t);for(var r=0,e=t.length;null!=n&&e>r;)n=n[t[r++]];return r&&r==e?n:B}function Tr(n,t){return Cc.call(n,t)||"object"==typeof n&&t in n&&null===Oc(n)}function Lr(n,t){return t in Object(n)}function Rr(n,t,r){return n>=Vc(t,r)&&n<Zc(t,r)}function Or(n,t,r){for(var e=r?l:c,i=n.length,u=i,o=Array(i),a=[];u--;){var s=n[u];u&&t&&(s=f(s,k(t))),o[u]=!r&&(t||s.length>=120)?new Ut(u&&s):B}s=n[0];var h=-1,p=s.length,v=o[0];n:for(;++h<p;){var g=s[h],d=t?t(g):g;if(!(v?Ft(v,d):e(a,d,r))){for(var u=i;--u;){var y=o[u];if(!(y?Ft(y,d):e(n[u],d,r)))continue n}v&&v.push(d),a.push(g)}}return a}function zr(n,t,e){li(t,n)||(t=he(t),n=di(n,t),t=Ii(t));var i=null==n?n:n[t];return null==i?B:r(i,n,e)}function qr(n,t,r,e,i){return n===t?!0:null==n||null==t||!bo(n)&&!wo(t)?n!==n&&t!==t:Ir(n,t,qr,r,e,i)}function Ir(n,t,r,e,i,u){var o=nf(n),a=nf(t),c=Cn,l=Cn;o||(c=ti(n),c==kn?c=Tn:c!=Tn&&(o=Oo(n))),a||(l=ti(t),l==kn?l=Tn:l!=Tn&&(a=Oo(t)));var f=c==Tn&&!R(n),s=l==Tn&&!R(t),h=c==l;if(h&&!o&&!f)return Xe(n,t,c,r,e,i);var p=i&un;if(!p){var v=f&&Cc.call(n,"__wrapped__"),g=s&&Cc.call(t,"__wrapped__");if(v||g)return r(v?n.value():n,g?t.value():t,e,i,u)}return h?(u||(u=new Wt),(o?Ve:Ke)(n,t,r,e,i,u)):!1}function Pr(n,t,r,e){var i=r.length,u=i,o=!e;if(null==n)return!u;for(n=Object(n);i--;){var a=r[i];if(o&&a[2]?a[1]!==n[a[0]]:!(a[0]in n))return!1}for(;++i<u;){a=r[i];var c=a[0],l=n[c],f=a[1];if(o&&a[2]){if(l===B&&!(c in n))return!1}else{var s=new Wt,h=e?e(l,f,c,n,t,s):B;if(!(h===B?qr(f,l,e,en|un,s):h))return!1}}return!0}function Ur(n){var t=typeof n;return"function"==t?n:null==n?Xa:"object"==t?nf(n)?Yr(n[0],n[1]):Br(n):ec(n)}function Fr(n){return Yc(Object(n))}function Hr(n){n=null==n?n:Object(n);var t=[];for(var r in n)t.push(r);return t}function Wr(n,t){var r=-1,e=co(n)?Array(n.length):[];return fl(n,function(n,i,u){e[++r]=t(n,i,u)}),e}function Br(n){var t=Ge(n);if(1==t.length&&t[0][2]){var r=t[0][0],e=t[0][1];return function(n){return null==n?!1:n[r]===e&&(e!==B||r in Object(n))}}return function(r){return r===n||Pr(r,n,t)}}function Yr(n,t){return function(r){var e=ra(r,n);return e===B&&e===t?ia(r,n):qr(t,e,B,en|un)}}function Zr(n,t,r,e,u){if(n!==t){var o=nf(t)||Oo(t)?B:aa(t);i(o||t,function(i,a){if(o&&(a=i,i=t[a]),bo(i))u||(u=new Wt),Vr(n,t,a,r,Zr,e,u);else{var c=e?e(n[a],i,a+"",n,t,u):B;c===B&&(c=i),rr(n,a,c)}})}}function Vr(n,t,r,e,i,u,o){var a=n[r],c=t[r],l=o.get(c)||o.get(a);if(l)return void rr(n,r,l);var f=u?u(a,c,r+"",n,t,o):B,s=f===B;s&&(f=c,nf(c)||Oo(c)?f=nf(a)?e?Ae(a):a:lo(a)?Ae(a):dr(c):jo(c)||ao(c)?f=ao(a)?Wo(a):!bo(a)||e&&_o(a)?dr(c):e?dr(a):a:s=!1),o.set(c,f),s&&i(f,c,e,u,o),rr(n,r,f)}function Xr(n,t,r){var e=-1,i=Qe();t=f(t.length?t:Array(1),function(n){return i(n)});var u=Wr(n,function(n,r,i){var u=f(t,function(t){return t(n)});return{criteria:u,index:++e,value:n}});return x(u,function(n,t){return $(n,t,r)})}function Kr(n,t){return n=Object(n),h(t,function(t,r){return r in n&&(t[r]=n[r]),t},{})}function Jr(n,t){var r={};return Ar(n,function(n,e){t(n,e)&&(r[e]=n)}),r}function Qr(n){return function(t){return null==t?B:t[n]}}function Gr(n){return function(t){return Dr(t,n)}}function ne(n,t){return te(n,t)}function te(n,t,r){var e=-1,i=t.length,u=n;for(r&&(u=f(n,function(n){return r(n)}));++e<i;)for(var o=0,a=t[e],c=r?r(a):a;(o=m(u,c,o))>-1;)u!==n&&Uc.call(u,o,1),Uc.call(n,o,1);return n}function re(n,t){for(var r=n?t.length:0,e=r-1;r--;){var i=t[r];if(e==r||i!=u){var u=i;if(O(i))Uc.call(n,i,1);else if(li(i,n))delete n[i];else{var o=he(i),a=di(n,o);null!=a&&delete a[Ii(o)]}}}return n}function ee(n,t){return n+Hc(Kc()*(t-n+1))}function ie(n,t,r,e){for(var i=-1,u=Zc(Fc((t-n)/(r||1)),0),o=Array(u);u--;)o[e?u:++i]=n,n+=r;return o}function ue(n,t,r,e){t=li(t,n)?[t+""]:he(t);for(var i=-1,u=t.length,o=u-1,a=n;null!=a&&++i<u;){var c=t[i];if(bo(a)){var l=r;if(i!=o){var f=a[c];l=e?e(f,c,a):B,l===B&&(l=null==f?O(t[i+1])?[]:{}:f)}er(a,c,l)}a=a[c]}return n}function oe(n,t,r){var e=-1,i=n.length;0>t&&(t=-t>i?0:i+t),r=r>i?i:r,0>r&&(r+=i),i=t>r?0:r-t>>>0,t>>>=0;for(var u=Array(i);++e<i;)u[e]=n[e+t];return u}function ae(n,t){var r;return fl(n,function(n,e,i){return r=t(n,e,i),!r}),!!r}function ce(n,t,r){var e=0,i=n?n.length:e;if("number"==typeof t&&t===t&&bn>=i){for(;i>e;){var u=e+i>>>1,o=n[u];(r?t>=o:t>o)&&null!==o?e=u+1:i=u}return i}return le(n,t,Xa,r)}function le(n,t,r,e){t=r(t);for(var i=0,u=n?n.length:0,o=t!==t,a=null===t,c=t===B;u>i;){var l=Hc((i+u)/2),f=r(n[l]),s=f!==B,h=f===f;if(o)var p=h||e;else p=a?h&&s&&(e||null!=f):c?h&&(e||s):null==f?!1:e?t>=f:t>f;p?i=l+1:u=l}return Vc(u,Mn)}function fe(n){return se(n)}function se(n,t){for(var r=0,e=n.length,i=n[0],u=t?t(i):i,o=u,a=0,c=[i];++r<e;)i=n[r],u=t?t(i):i,io(u,o)||(o=u,c[++a]=i);return c}function he(n){return nf(n)?n:mi(n)}function pe(n,t,r){var e=-1,i=c,u=n.length,o=!0,a=[],f=a;if(r)o=!1,i=l;else if(u>=fn){var s=t?null:gl(n);if(s)return P(s);o=!1,i=Ft,f=new Ut}else f=t?[]:a;n:for(;++e<u;){var h=n[e],p=t?t(h):h;if(o&&p===p){for(var v=f.length;v--;)if(f[v]===p)continue n;t&&f.push(p),a.push(h)}else i(f,p,r)||(f!==a&&f.push(p),a.push(h))}return a}function ve(n,t){t=li(t,n)?[t+""]:he(t),n=di(n,t);var r=Ii(t);return null!=n&&ea(n,r)?delete n[r]:!0}function ge(n,t,r,e){for(var i=n.length,u=e?i:-1;(e?u--:++u<i)&&t(n[u],u,n););return r?oe(n,e?0:u,e?u+1:i):oe(n,e?u+1:0,e?i:u)}function de(n,t){var r=n;return r instanceof At&&(r=r.value()),h(t,function(n,t){return t.func.apply(t.thisArg,s([n],t.args))},r)}function ye(n,t,r){for(var e=-1,i=n.length;++e<i;)var u=u?s(_r(u,n[e],t,r),_r(n[e],u,t,r)):n[e];return u&&u.length?pe(u,t,r):[]}function me(n){var t=n.constructor,r=new t(n.byteLength),e=new Tc(r);return e.set(new Tc(n)),r}function _e(t){var r=t.constructor;return h(q(t),n,new r)}function xe(n){var t=n.constructor,r=new t(n.source,gt.exec(n));return r.lastIndex=n.lastIndex,r}function Me(n){var r=n.constructor;return h(P(n),t,new r)}function be(n){return Dc?Object(ol.call(n)):{}}function we(n,t){var r=n.buffer,e=n.constructor;return new e(t?me(r):r,n.byteOffset,n.length)}function ke(n,t,r){for(var e=r.length,i=-1,u=Zc(n.length-e,0),o=-1,a=t.length,c=Array(a+u);++o<a;)c[o]=t[o];for(;++i<e;)c[r[i]]=n[i];for(;u--;)c[o++]=n[i++];return c}function Ce(n,t,r){for(var e=-1,i=r.length,u=-1,o=Zc(n.length-i,0),a=-1,c=t.length,l=Array(o+c);++u<o;)l[u]=n[u];for(var f=u;++a<c;)l[f+a]=t[a];for(;++e<i;)l[f+r[e]]=n[u++];return l}function Ae(n,t){var r=-1,e=n.length;for(t||(t=Array(e));++r<e;)t[r]=n[r];return t}function Se(n,t,r){return Ee(n,t,r)}function Ee(n,t,r,e){r||(r={});for(var i=-1,u=t.length;++i<u;){var o=t[i],a=e?e(r[o],n[o],o,r,n):n[o];er(r,o,a)}return r}function Ne(n,t){return Se(n,ml(n),t)}function $e(n,t){return function(r,e){var i=t?t():{};if(e=Qe(e),nf(r))for(var u=-1,o=r.length;++u<o;){var a=r[u];n(i,a,e(a),r)}else fl(r,function(t,r,u){n(i,t,e(t),u)});return i}}function je(n){return Xu(function(t,r){var e=-1,i=r.length,u=i>1?r[i-1]:B,o=i>2?r[2]:B;for(u="function"==typeof u?(i--,u):B,o&&ci(r[0],r[1],o)&&(u=3>i?B:u,i=1),t=Object(t);++e<i;){var a=r[e];a&&n(t,a,e,u)}return t})}function De(n,t){return function(r,e){if(null==r)return r;if(!co(r))return n(r,e);for(var i=r.length,u=t?i:-1,o=Object(r);(t?u--:++u<i)&&e(o[u],u,o)!==!1;);return r}}function Te(n){return function(t,r,e){for(var i=-1,u=Object(t),o=e(t),a=o.length;a--;){var c=o[n?a:++i];if(r(u[c],c,u)===!1)break}return t}}function Le(n,t,r){function e(){var t=this&&this!==Nr&&this instanceof e?u:n;return t.apply(i?r:this,arguments)}var i=t&Z,u=ze(n);return e}function Re(n){return function(t){t=Yo(t);var r=ar.test(t)?F(t):B,e=r?r[0]:t.charAt(0),i=r?r.slice(1).join(""):t.slice(1);return e[n]()+i}}function Oe(n){return function(t){return h(Ba(Ca(t)),n,"")}}function ze(n){return function(){var t=arguments;switch(t.length){case 0:return new n;case 1:return new n(t[0]);case 2:return new n(t[0],t[1]);case 3:return new n(t[0],t[1],t[2]);case 4:return new n(t[0],t[1],t[2],t[3]);case 5:return new n(t[0],t[1],t[2],t[3],t[4]);case 6:return new n(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new n(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var r=ll(n.prototype),e=n.apply(r,t);return bo(e)?e:r}}function qe(n,t,e){function i(){for(var o=arguments.length,a=o,c=Array(o),l=this&&this!==Nr&&this instanceof i?u:n,f=i.placeholder;a--;)c[a]=arguments[a];var s=3>o&&c[0]!==f&&c[o-1]!==f?[]:I(c,f);return o-=s.length,e>o?Be(n,t,Pe,f,B,c,s,B,B,e-o):r(l,this,c)}var u=ze(n);return i}function Ie(n){return Xu(function(t){t=Cr(t);var r=t.length,e=r,i=Ct.prototype.thru;for(n&&t.reverse();e--;){var u=t[e];if("function"!=typeof u)throw new Mc(vn);if(i&&!o&&"wrapper"==Je(u))var o=new Ct([],!0)}for(e=o?e:r;++e<r;){u=t[e];var a=Je(u),c="wrapper"==a?dl(u):B;o=c&&si(c[0])&&c[1]==(nn|K|Q|tn)&&!c[4].length&&1==c[9]?o[Je(c[0])].apply(o,c[3]):1==u.length&&si(u)?o[a]():o.thru(u)}return function(){var n=arguments,e=n[0];if(o&&1==n.length&&nf(e)&&e.length>=fn)return o.plant(e).value();for(var i=0,u=r?t[i].apply(this,n):e;++i<r;)u=t[i].call(this,u);return u}})}function Pe(n,t,r,e,i,u,o,a,c,l){function f(){for(var m=arguments.length,_=m,x=Array(m);_--;)x[_]=arguments[_];if(e&&(x=ke(x,e,i)),u&&(x=Ce(x,u,o)),v||g){var M=f.placeholder,b=I(x,M);if(m-=b.length,l>m)return Be(n,t,Pe,M,r,x,b,a,c,l-m)}var w=h?r:this,k=p?w[n]:n;return a?x=yi(x,a):d&&x.length>1&&x.reverse(),s&&c<x.length&&(x.length=c),this&&this!==Nr&&this instanceof f&&(k=y||ze(k)),k.apply(w,x)}var s=t&nn,h=t&Z,p=t&V,v=t&K,g=t&J,d=t&rn,y=p?B:ze(n);return f}function Ue(n){return Xu(function(t){return t=f(Cr(t),Qe()),Xu(function(e){var i=this;return n(t,function(n){return r(n,i,e)})})})}function Fe(n,t,r){t=Uo(t);var e=U(n);if(!t||e>=t)return"";var i=t-e;r=r===B?" ":r+"";var u=Ta(r,Fc(i/U(r)));return ar.test(r)?F(u).slice(0,i).join(""):u.slice(0,i)}function He(n,t,e,i){function u(){for(var t=-1,c=arguments.length,l=-1,f=i.length,s=Array(f+c),h=this&&this!==Nr&&this instanceof u?a:n;++l<f;)s[l]=i[l];for(;c--;)s[l++]=arguments[++t];return r(h,o?e:this,s)}var o=t&Z,a=ze(n);return u}function We(n){return function(t,r,e){return e&&"number"!=typeof e&&ci(t,r,e)&&(r=e=B),t=Ho(t),t=t===t?t:0,r===B?(r=t,t=0):r=Ho(r)||0,e=e===B?r>t?1:-1:Ho(e)||0,ie(t,r,e,n)}}function Be(n,t,r,e,i,u,o,a,c,l){var f=t&K,s=a?Ae(a):B,h=f?o:B,p=f?B:o,v=f?u:B,g=f?B:u;t|=f?Q:G,t&=~(f?G:Q),t&X||(t&=~(Z|V));var d=[n,t,i,v,h,g,p,s,c,l],y=r.apply(B,d);return si(n)&&_l(y,d),y.placeholder=e,y}function Ye(n){var t=_c[n];return function(n,r){if(n=Ho(n),r=Uo(r)){var e=(Yo(n)+"e").split("e"),i=t(e[0]+"e"+(+e[1]+r));return e=(Yo(i)+"e").split("e"),+(e[0]+"e"+(+e[1]-r))}return t(n)}}function Ze(n,t,r,e,i,u,o,a){var c=t&V;if(!c&&"function"!=typeof n)throw new Mc(vn);var l=e?e.length:0;if(l||(t&=~(Q|G),e=i=B),o=o===B?o:Zc(Uo(o),0),a=a===B?a:Uo(a),l-=i?i.length:0,t&G){var f=e,s=i;e=i=B}var h=c?B:dl(n),p=[n,t,r,e,i,f,s,u,o,a];if(h&&vi(p,h),n=p[0],t=p[1],r=p[2],e=p[3],i=p[4],a=p[9]=null==p[9]?c?0:n.length:Zc(p[9]-l,0),!a&&t&(K|J)&&(t&=~(K|J)),t&&t!=Z)v=t==K||t==J?qe(n,t,a):t!=Q&&t!=(Z|Q)||i.length?Pe.apply(B,p):He(n,t,r,e);else var v=Le(n,t,r);var g=h?vl:_l;return g(v,p)}function Ve(n,t,r,e,i,u){var o=-1,a=i&un,c=i&en,l=n.length,f=t.length;if(l!=f&&!(a&&f>l))return!1;var s=u.get(n);if(s)return s==t;var h=!0;for(u.set(n,t);++o<l;){var p=n[o],g=t[o];if(e)var d=a?e(g,p,o,t,n,u):e(p,g,o,n,t,u);if(d!==B){if(d)continue;h=!1;break}if(c){if(!v(t,function(n){return p===n||r(p,n,e,i,u)})){h=!1;break}}else if(p!==g&&!r(p,g,e,i,u)){h=!1;break}}return u["delete"](n),h}function Xe(n,t,r,e,i,u){switch(r){case In:return!(n.byteLength!=t.byteLength||!e(new Tc(n),new Tc(t)));case An:case Sn:return+n==+t;case En:return n.name==t.name&&n.message==t.message;case Dn:return n!=+n?t!=+t:n==+t;case Ln:case On:return n==t+"";case jn:var o=q;case Rn:var a=u&un;return o||(o=P),(a||n.size==t.size)&&e(o(n),o(t),i,u|en);case zn:return!!Dc&&ol.call(n)==ol.call(t)}return!1}function Ke(n,t,r,e,i,u){var o=i&un,a=oa(n),c=a.length,l=oa(t),f=l.length;if(c!=f&&!o)return!1;for(var s=c;s--;){var h=a[s];if(!(o?h in t:Tr(t,h)))return!1}var p=u.get(n);if(p)return p==t;var v=!0;u.set(n,t);for(var g=o;++s<c;){h=a[s];var d=n[h],y=t[h];if(e)var m=o?e(y,d,h,t,n,u):e(d,y,h,n,t,u);if(!(m===B?d===y||r(d,y,e,i,u):m)){v=!1;break}g||(g="constructor"==h)}if(v&&!g){var _=n.constructor,x=t.constructor;_!=x&&"constructor"in n&&"constructor"in t&&!("function"==typeof _&&_ instanceof _&&"function"==typeof x&&x instanceof x)&&(v=!1)}return u["delete"](n),v}function Je(n){for(var t=n.name+"",r=cl[t],e=Cc.call(cl,t)?r.length:0;e--;){var i=r[e],u=i.func;if(null==u||u==n)return i.name}return t}function Qe(){var n=qn.iteratee||Ka;return n=n===Ka?Ur:n,arguments.length?n(arguments[0],arguments[1]):n}function Ge(n){for(var t=ga(n),r=t.length;r--;)t[r][2]=pi(t[r][1]);return t}function ni(n,t){var r=null==n?B:n[t];return So(r)?r:B}function ti(n){return Ec.call(n)}function ri(n,t,r){for(var e=-1,i=r.length;++e<i;){var u=r[e],o=u.size;switch(u.type){case"drop":n+=o;break;case"dropRight":t-=o;break;case"take":t=Vc(t,n+o);break;case"takeRight":n=Zc(n,t-o)}}return{start:n,end:t}}function ei(n,t,r){if(null==n)return!1;var e=r(n,t);return e||li(t)||(t=he(t),n=di(n,t),null!=n&&(t=Ii(t),e=r(n,t))),e||Mo(n&&n.length)&&O(t,n.length)&&(nf(n)||Lo(n)||ao(n))}function ii(n){var t=n.length,r=n.constructor(t);return t&&"string"==typeof n[0]&&Cc.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function ui(n){var t=n.constructor;return ll(_o(t)?t.prototype:B)}function oi(n,t,r){var e=n.constructor;switch(t){case In:return me(n);case An:case Sn:return new e(+n);case Pn:case Un:case Fn:case Hn:case Wn:case Bn:case Yn:case Zn:case Vn:return we(n,r);case jn:return _e(n);case Dn:case On:return new e(n);case Ln:return xe(n);case Rn:return Me(n);case zn:return be(n)}}function ai(n){var t=n?n.length:B;return Mo(t)&&(nf(n)||Lo(n)||ao(n))?b(t,String):null}function ci(n,t,r){if(!bo(r))return!1;var e=typeof t;return("number"==e?co(r)&&O(t,r.length):"string"==e&&t in r)?io(r[t],n):!1}function li(n,t){return"number"==typeof n?!0:!nf(n)&&(ot.test(n)||!ut.test(n)||null!=t&&n in Object(t))}function fi(n){var t=typeof n;return"number"==t||"boolean"==t||"string"==t&&"__proto__"!==n||null==n}function si(n){var t=Je(n),r=qn[t];if("function"!=typeof r||!(t in At.prototype))return!1;if(n===r)return!0;var e=dl(r);return!!e&&n===e[0]}function hi(n){var t=n&&n.constructor,r="function"==typeof t&&t.prototype||wc;return n===r}function pi(n){return n===n&&!bo(n)}function vi(n,t){var r=n[1],e=t[1],i=r|e,u=(Z|V|nn)>i,o=e==nn&&r==K||e==nn&&r==tn&&n[7].length<=t[8]||e==(nn|tn)&&t[7].length<=t[8]&&r==K;if(!u&&!o)return n;e&Z&&(n[2]=t[2],i|=r&Z?0:X);var a=t[3];if(a){var c=n[3];n[3]=c?ke(c,a,t[4]):Ae(a),n[4]=c?I(n[3],wn):Ae(t[4])}return a=t[5],a&&(c=n[5],n[5]=c?Ce(c,a,t[6]):Ae(a),n[6]=c?I(n[5],wn):Ae(t[6])),a=t[7],a&&(n[7]=Ae(a)),e&nn&&(n[8]=null==n[8]?t[8]:Vc(n[8],t[8])),null==n[9]&&(n[9]=t[9]),n[0]=t[0],n[1]=i,n}function gi(n,t,r,e,i,u){return bo(n)&&bo(t)&&(u.set(t,n),Zr(n,t,B,gi,u)),n}function di(n,t){return 1==t.length?n:ra(n,oe(t,0,-1))}function yi(n,t){for(var r=n.length,e=Vc(t.length,r),i=Ae(n);e--;){var u=t[e];n[e]=O(u,r)?i[u]:B}return n}function mi(n){var t=[];return Yo(n).replace(at,function(n,r,e,i){t.push(e?i.replace(pt,"$1"):r||n)}),t}function _i(n){return lo(n)?n:[]}function xi(n){return"function"==typeof n?n:Xa}function Mi(n){if(n instanceof At)return n.clone();var t=new Ct(n.__wrapped__,n.__chain__);return t.__actions__=Ae(n.__actions__),t.__index__=n.__index__,t.__values__=n.__values__,t}function bi(n,t){t=Zc(Uo(t),0);var r=n?n.length:0;if(!r||1>t)return[];for(var e=0,i=-1,u=Array(Fc(r/t));r>e;)u[++i]=oe(n,e,e+=t);return u}function wi(n){for(var t=-1,r=n?n.length:0,e=-1,i=[];++t<r;){var u=n[t];u&&(i[++e]=u)}return i}function ki(n,t,r){var e=n?n.length:0;return e?(t=r||t===B?1:Uo(t),oe(n,0>t?0:t,e)):[]}function Ci(n,t,r){var e=n?n.length:0;return e?(t=r||t===B?1:Uo(t),t=e-t,oe(n,0,0>t?0:t)):[]}function Ai(n,t){return n&&n.length?ge(n,Qe(t,3),!0,!0):[]}function Si(n,t){return n&&n.length?ge(n,Qe(t,3),!0):[]}function Ei(n,t,r,e){var i=n?n.length:0;return i?(r&&"number"!=typeof r&&ci(n,t,r)&&(r=0,e=i),wr(n,t,r,e)):[]}function Ni(n,t){return n&&n.length?y(n,Qe(t,3)):-1}function $i(n,t){return n&&n.length?y(n,Qe(t,3),!0):-1}function ji(n,t){var r=n?n.length:0;return r?Cr(f(n,Qe(t,3))):[]}function Di(n){var t=n?n.length:0;return t?Cr(n):[]}function Ti(n){var t=n?n.length:0;return t?Cr(n,!0):[]}function Li(n){for(var t=-1,r=n?n.length:0,e={};++t<r;){var i=n[t];e[i[0]]=i[1]}return e}function Ri(n){return n?n[0]:B}function Oi(n,t,r){var e=n?n.length:0;return e?(r=Uo(r),0>r&&(r=Zc(e+r,0)),m(n,t,r)):-1}function zi(n){return Ci(n,1)}function qi(n,t){return n?Bc.call(n,t):""}function Ii(n){var t=n?n.length:0;return t?n[t-1]:B}function Pi(n,t,r){var e=n?n.length:0;if(!e)return-1;var i=e;if(r!==B&&(i=Uo(r),i=(0>i?Zc(e+i,0):Vc(i,e-1))+1),t!==t)return L(n,i,!0);for(;i--;)if(n[i]===t)return i;return-1}function Ui(n,t){return n&&n.length&&t&&t.length?ne(n,t):n}function Fi(n,t,r){return n&&n.length&&t&&t.length?te(n,t,Qe(r)):n}function Hi(n,t){var r=[];if(!n||!n.length)return r;var e=-1,i=[],u=n.length;for(t=Qe(t,3);++e<u;){var o=n[e];t(o,e,n)&&(r.push(o),i.push(e))}return re(n,i),r}function Wi(n){return n?Jc.call(n):n}function Bi(n,t,r){var e=n?n.length:0;return e?(r&&"number"!=typeof r&&ci(n,t,r)?(t=0,r=e):(t=null==t?0:Uo(t),r=r===B?e:Uo(r)),oe(n,t,r)):[]}function Yi(n,t){return ce(n,t)}function Zi(n,t,r){return le(n,t,Qe(r))}function Vi(n,t){var r=n?n.length:0;if(r){var e=ce(n,t);if(r>e&&io(n[e],t))return e}return-1}function Xi(n,t){return ce(n,t,!0)}function Ki(n,t,r){return le(n,t,Qe(r),!0)}function Ji(n,t){var r=n?n.length:0;if(r){var e=ce(n,t,!0)-1;if(io(n[e],t))return e}return-1}function Qi(n){return n&&n.length?fe(n):[]}function Gi(n,t){return n&&n.length?se(n,Qe(t)):[]}function nu(n){return ki(n,1)}function tu(n,t,r){return n&&n.length?(t=r||t===B?1:Uo(t),oe(n,0,0>t?0:t)):[]}function ru(n,t,r){var e=n?n.length:0;return e?(t=r||t===B?1:Uo(t),t=e-t,oe(n,0>t?0:t,e)):[]}function eu(n,t){return n&&n.length?ge(n,Qe(t,3),!1,!0):[]}function iu(n,t){return n&&n.length?ge(n,Qe(t,3)):[]}function uu(n){return n&&n.length?pe(n):[]}function ou(n,t){return n&&n.length?pe(n,Qe(t)):[]}function au(n,t){return n&&n.length?pe(n,B,t):[]}function cu(n){if(!n||!n.length)return[];var t=0;return n=a(n,function(n){return lo(n)?(t=Zc(n.length,t),!0):void 0}),b(t,function(t){return f(n,Qr(t))})}function lu(n,t){if(!n||!n.length)return[];var e=cu(n);return null==t?e:f(e,function(n){return r(t,B,n)})}function fu(n,t){for(var r=-1,e=n?n.length:0,i=t?t.length:0,u={};++r<e;)ue(u,n[r],i>r?t[r]:B);return u}function su(n){var t=qn(n);return t.__chain__=!0,t}function hu(n,t){return t(n),n}function pu(n,t){return t(n)}function vu(){return su(this)}function gu(){return new Ct(this.value(),this.__chain__)}function du(n){return this.map(n).flatten()}function yu(){this.__values__===B&&(this.__values__=Po(this.value()));var n=this.__index__>=this.__values__.length,t=n?B:this.__values__[this.__index__++];return{done:n,value:t}}function mu(){return this}function _u(n){for(var t,r=this;r instanceof Mt;){var e=Mi(r);e.__index__=0,e.__values__=B,t?i.__wrapped__=e:t=e;var i=e;r=r.__wrapped__}return i.__wrapped__=n,t}function xu(){var n=this.__wrapped__;if(n instanceof At){var t=n;return this.__actions__.length&&(t=new At(this)),t=t.reverse(),t.__actions__.push({func:pu,args:[Wi],thisArg:B}),new Ct(t,this.__chain__)}return this.thru(Wi)}function Mu(){return de(this.__wrapped__,this.__actions__)}function bu(n,t,r){var e=nf(n)?o:br;return r&&ci(n,t,r)&&(t=B),e(n,Qe(t,3))}function wu(n,t){var r=nf(n)?a:kr;return r(n,Qe(t,3))}function ku(n,t){if(t=Qe(t,3),nf(n)){var r=y(n,t);return r>-1?n[r]:B}return d(n,t,fl)}function Cu(n,t){if(t=Qe(t,3),nf(n)){var r=y(n,t,!0);return r>-1?n[r]:B}return d(n,t,sl)}function Au(n,t){return"function"==typeof t&&nf(n)?i(n,t):fl(n,xi(t))}function Su(n,t){return"function"==typeof t&&nf(n)?u(n,t):sl(n,xi(t))}function Eu(n,t,r,e){n=co(n)?n:_a(n),r=r&&!e?Uo(r):0;var i=n.length;return 0>r&&(r=Zc(i+r,0)),Lo(n)?i>=r&&n.indexOf(t,r)>-1:!!i&&m(n,t,r)>-1}function Nu(n,t){var r=nf(n)?f:Wr;return r(n,Qe(t,3))}function $u(n,t,r,e){return null==n?[]:(nf(t)||(t=null==t?[]:[t]),r=e?B:r,nf(r)||(r=null==r?[]:[r]),Xr(n,t,r))}function ju(n,t,r){var e=nf(n)?h:_,i=arguments.length<3;return e(n,Qe(t,4),r,i,fl)}function Du(n,t,r){var e=nf(n)?p:_,i=arguments.length<3;return e(n,Qe(t,4),r,i,sl)}function Tu(n,t){var r=nf(n)?a:kr;return t=Qe(t,3),r(n,function(n,r,e){return!t(n,r,e)})}function Lu(n){var t=co(n)?n:_a(n),r=t.length;return r>0?t[ee(0,r-1)]:B}function Ru(n,t){var r=-1,e=Po(n),i=e.length,u=i-1;for(t=gr(Uo(t),0,i);++r<t;){var o=ee(r,u),a=e[o];e[o]=e[r],e[r]=a}return e.length=t,e}function Ou(n){return Ru(n,xn)}function zu(n){if(null==n)return 0;if(co(n)){var t=n.length;return t&&Lo(n)?U(n):t}return oa(n).length}function qu(n,t,r){var e=nf(n)?v:ae;return r&&ci(n,t,r)&&(t=B),e(n,Qe(t,3))}function Iu(n,t){if("function"!=typeof t)throw new Mc(vn);return n=Uo(n),function(){return--n<1?t.apply(this,arguments):void 0}}function Pu(n,t,r){return t=r?B:t,t=n&&null==t?n.length:t,Ze(n,nn,B,B,B,B,t)}function Uu(n,t){var r;if("function"!=typeof t)throw new Mc(vn);return n=Uo(n),function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=B),r}}function Fu(n,t,r){t=r?B:t;var e=Ze(n,K,B,B,B,B,B,t);return e.placeholder=Fu.placeholder,e}function Hu(n,t,r){t=r?B:t;var e=Ze(n,J,B,B,B,B,B,t);return e.placeholder=Hu.placeholder,e}function Wu(n,t,r){function e(){v&&Lc(v),f&&Lc(f),d=0,l=f=p=v=g=B}function i(t,r){r&&Lc(r),f=v=g=B,t&&(d=Bl(),s=n.apply(p,l),v||f||(l=p=B))}function u(){var n=t-(Bl()-h);0>=n||n>t?i(g,f):v=Pc(u,n)}function o(){return(v&&g||f&&_)&&(s=n.apply(p,l)),e(),s}function a(){i(_,v)}function c(){if(l=arguments,h=Bl(),p=this,g=_&&(v||!y),m===!1)var r=y&&!v;else{f||y||(d=h);var e=m-(h-d),i=0>=e||e>m;i?(f&&(f=Lc(f)),d=h,s=n.apply(p,l)):f||(f=Pc(a,e))}return i&&v?v=Lc(v):v||t===m||(v=Pc(u,t)),r&&(i=!0,s=n.apply(p,l)),!i||v||f||(l=p=B),s}var l,f,s,h,p,v,g,d=0,y=!1,m=!1,_=!0;if("function"!=typeof n)throw new Mc(vn);return t=Ho(t)||0,bo(r)&&(y=!!r.leading,m="maxWait"in r&&Zc(Ho(r.maxWait)||0,t),_="trailing"in r?!!r.trailing:_),c.cancel=e,c.flush=o,c}function Bu(n){return Ze(n,rn)}function Yu(n,t){if("function"!=typeof n||t&&"function"!=typeof t)throw new Mc(vn);var r=function(){var e=arguments,i=t?t.apply(this,e):e[0],u=r.cache;if(u.has(i))return u.get(i);var o=n.apply(this,e);return r.cache=u.set(i,o),o};return r.cache=new Yu.Cache,r}function Zu(n){if("function"!=typeof n)throw new Mc(vn);return function(){return!n.apply(this,arguments)}}function Vu(n){return Uu(2,n)}function Xu(n,t){if("function"!=typeof n)throw new Mc(vn);return t=Zc(t===B?n.length-1:Uo(t),0),function(){for(var e=arguments,i=-1,u=Zc(e.length-t,0),o=Array(u);++i<u;)o[i]=e[t+i];switch(t){case 0:return n.call(this,o);case 1:return n.call(this,e[0],o);case 2:return n.call(this,e[0],e[1],o)}var a=Array(t+1);for(i=-1;++i<t;)a[i]=e[i];return a[t]=o,r(n,this,a)}}function Ku(n){if("function"!=typeof n)throw new Mc(vn);return function(t){return r(n,this,t)}}function Ju(n,t,r){var e=!0,i=!0;if("function"!=typeof n)throw new Mc(vn);return bo(r)&&(e="leading"in r?!!r.leading:e,i="trailing"in r?!!r.trailing:i),Wu(n,t,{leading:e,maxWait:t,trailing:i})}function Qu(n){return Pu(n,1)}function Gu(n,t){return t=null==t?Xa:t,Jl(t,n)}function no(n){return dr(n)}function to(n,t){return dr(n,!1,t)}function ro(n){return dr(n,!0)}function eo(n,t){return dr(n,!0,t)}function io(n,t){return n===t||n!==n&&t!==t}function uo(n,t){return n>t}function oo(n,t){return n>=t}function ao(n){return lo(n)&&Cc.call(n,"callee")&&(!Ic.call(n,"callee")||Ec.call(n)==kn)}function co(n){return null!=n&&!("function"==typeof n&&_o(n))&&Mo(yl(n))}function lo(n){return wo(n)&&co(n)}function fo(n){return n===!0||n===!1||wo(n)&&Ec.call(n)==An}function so(n){return wo(n)&&Ec.call(n)==Sn}function ho(n){return!!n&&1===n.nodeType&&wo(n)&&!jo(n)}function po(n){return!wo(n)||_o(n.splice)?!zu(n):!oa(n).length}function vo(n,t){return qr(n,t)}function go(n,t,r){r="function"==typeof r?r:B;var e=r?r(n,t):B;return e===B?qr(n,t,r):!!e}function yo(n){return wo(n)&&"string"==typeof n.message&&Ec.call(n)==En}function mo(n){return"number"==typeof n&&Wc(n)}function _o(n){var t=bo(n)?Ec.call(n):"";return t==Nn||t==$n}function xo(n){return"number"==typeof n&&n==Uo(n)}function Mo(n){return"number"==typeof n&&n>-1&&n%1==0&&yn>=n}function bo(n){var t=typeof n;return!!n&&("object"==t||"function"==t)}function wo(n){return!!n&&"object"==typeof n}function ko(n,t){return n===t||Pr(n,t,Ge(t))}function Co(n,t,r){return r="function"==typeof r?r:B,Pr(n,t,Ge(t),r)}function Ao(n){return $o(n)&&n!=+n}function So(n){return null==n?!1:_o(n)?$c.test(kc.call(n)):wo(n)&&(R(n)?$c:_t).test(n)}function Eo(n){return null===n}function No(n){return null==n}function $o(n){return"number"==typeof n||wo(n)&&Ec.call(n)==Dn}function jo(n){if(!wo(n)||Ec.call(n)!=Tn||R(n))return!1;var t=wc;if("function"==typeof n.constructor&&(t=Oc(n)),null===t)return!0;var r=t.constructor;return"function"==typeof r&&r instanceof r&&kc.call(r)==Sc}function Do(n){return bo(n)&&Ec.call(n)==Ln}function To(n){return xo(n)&&n>=-yn&&yn>=n}function Lo(n){return"string"==typeof n||!nf(n)&&wo(n)&&Ec.call(n)==On}function Ro(n){return"symbol"==typeof n||wo(n)&&Ec.call(n)==zn}function Oo(n){return wo(n)&&Mo(n.length)&&!!pr[Ec.call(n)]}function zo(n){return n===B}function qo(n,t){return t>n}function Io(n,t){return t>=n}function Po(n){if(!n)return[];if(co(n))return Lo(n)?F(n):Ae(n);if(qc&&n[qc])return z(n[qc]());var t=ti(n),r=t==jn?q:t==Rn?P:_a;return r(n)}function Uo(n){if(!n)return 0===n?n:0;if(n=Ho(n),n===dn||n===-dn){var t=0>n?-1:1;return t*mn}var r=n%1;return n===n?r?n-r:n:0}function Fo(n){return n?gr(Uo(n),0,xn):0}function Ho(n){if(bo(n)){var t=_o(n.valueOf)?n.valueOf():n;n=bo(t)?t+"":t}if("string"!=typeof n)return 0===n?n:+n;n=n.replace(ft,"");var r=mt.test(n);return r||xt.test(n)?Mr(n.slice(2),r?2:8):yt.test(n)?_n:+n}function Wo(n){return Se(n,aa(n))}function Bo(n){return gr(Uo(n),-yn,yn)}function Yo(n){if("string"==typeof n)return n;if(null==n)return"";if(Ro(n))return Dc?al.call(n):"";var t=n+"";return"0"==t&&1/n==-dn?"-0":t}function Zo(n,t){var r=ll(n);return t?ir(r,t):r}function Vo(n,t){return d(n,Qe(t,3),Sr,!0)}function Xo(n,t){return d(n,Qe(t,3),Er,!0)}function Ko(n,t){return null==n?n:hl(n,xi(t),aa)}function Jo(n,t){return null==n?n:pl(n,xi(t),aa)}function Qo(n,t){return n&&Sr(n,xi(t))}function Go(n,t){return n&&Er(n,xi(t))}function na(n){return null==n?[]:jr(n,oa(n))}function ta(n){return null==n?[]:jr(n,aa(n))}function ra(n,t,r){var e=null==n?B:Dr(n,t);return e===B?r:e}function ea(n,t){return ei(n,t,Tr)}function ia(n,t){return ei(n,t,Lr)}function ua(n,t,r){return h(oa(n),function(e,i){var u=n[i];return t&&!r?Cc.call(e,u)?e[u].push(i):e[u]=[i]:e[u]=i,e},{})}function oa(n){var t=hi(n);if(!t&&!co(n))return Fr(n);var r=ai(n),e=!!r,i=r||[],u=i.length;for(var o in n)!Tr(n,o)||e&&("length"==o||O(o,u))||t&&"constructor"==o||i.push(o);return i}function aa(n){for(var t=-1,r=hi(n),e=Hr(n),i=e.length,u=ai(n),o=!!u,a=u||[],c=a.length;++t<i;){var l=e[t];o&&("length"==l||O(l,c))||"constructor"==l&&(r||!Cc.call(n,l))||a.push(l)}return a}function ca(n,t){var r={};return t=Qe(t,3),Sr(n,function(n,e,i){r[t(n,e,i)]=n}),r}function la(n,t){var r={};return t=Qe(t,3),Sr(n,function(n,e,i){r[e]=t(n,e,i)}),r}function fa(n,t){return t=Qe(t,2),Jr(n,function(n,r){return!t(n,r)})}function sa(n,t){return null==n?{}:Jr(n,Qe(t,2))}function ha(n,t,r){if(li(t,n))e=null==n?B:n[t];else{t=he(t);var e=ra(n,t);n=di(n,t)}return e===B&&(e=r),_o(e)?e.call(n):e}function pa(n,t,r){return null==n?n:ue(n,t,r)}function va(n,t,r,e){return e="function"==typeof e?e:B,null==n?n:ue(n,t,r,e)}function ga(n){return w(n,oa(n))}function da(n){return w(n,aa(n))}function ya(n,t,r){var e=nf(n)||Oo(n);if(t=Qe(t,4),null==r)if(e||bo(n)){var u=n.constructor;r=e?nf(n)?new u:[]:ll(_o(u)?u.prototype:B)}else r={};return(e?i:Sr)(n,function(n,e,i){return t(r,n,e,i)}),r}function ma(n,t){return null==n?!0:ve(n,t)}function _a(n){return n?C(n,oa(n)):[]}function xa(n){return null==n?C(n,aa(n)):[]}function Ma(n,t,r){return r===B&&(r=t,t=B),r!==B&&(r=Ho(r),r=r===r?r:0),t!==B&&(t=Ho(t),t=t===t?t:0),gr(Ho(n),t,r)}function ba(n,t,r){return t=Ho(t)||0,r===B?(r=t,t=0):r=Ho(r)||0,n=Ho(n),Rr(n,t,r)}function wa(n,t,r){if(r&&"boolean"!=typeof r&&ci(n,t,r)&&(t=r=B),r===B&&("boolean"==typeof t?(r=t,t=B):"boolean"==typeof n&&(r=n,n=B)),n===B&&t===B?(n=0,t=1):(n=Ho(n)||0,t===B?(t=n,n=0):t=Ho(t)||0),n>t){var e=n;n=t,t=e}if(r||n%1||t%1){var i=Kc();return Vc(n+i*(t-n+xr("1e-"+((i+"").length-1))),t)}return ee(n,t)}function ka(n){return mf(Yo(n).toLowerCase())}function Ca(n){return n=Yo(n),n&&n.replace(bt,j).replace(ur,"")}function Aa(n,t,r){n=Yo(n),t="string"==typeof t?t:t+"";var e=n.length;return r=r===B?e:gr(Uo(r),0,e),r-=t.length,r>=0&&n.indexOf(t,r)==r}function Sa(n){return n=Yo(n),n&&tt.test(n)?n.replace(Gn,D):n}function Ea(n){return n=Yo(n),n&<.test(n)?n.replace(ct,"\\$&"):n}function Na(n,t,r){n=Yo(n),t=Uo(t);var e=U(n);if(!t||e>=t)return n;var i=(t-e)/2,u=Hc(i),o=Fc(i);return Fe("",u,r)+n+Fe("",o,r)}function $a(n,t,r){return n=Yo(n),n+Fe(n,t,r)}function ja(n,t,r){return n=Yo(n),Fe(n,t,r)+n}function Da(n,t,r){return r||null==t?t=0:t&&(t=+t),n=Yo(n).replace(ft,""),Xc(n,t||(dt.test(n)?16:10))}function Ta(n,t){n=Yo(n),t=Uo(t);var r="";if(!n||1>t||t>yn)return r;do t%2&&(r+=n),t=Hc(t/2),n+=n;while(t);return r}function La(){var n=arguments,t=Yo(n[0]);return n.length<3?t:t.replace(n[1],n[2])}function Ra(n,t,r){return Yo(n).split(t,r)}function Oa(n,t,r){return n=Yo(n),r=gr(Uo(r),0,n.length),n.lastIndexOf(t,r)==r}function za(n,t,r){var e=qn.templateSettings;r&&ci(n,t,r)&&(t=B),n=Yo(n),t=ef({},t,e,tr);var i,u,o=ef({},t.imports,e.imports,tr),a=oa(o),c=C(o,a),l=0,f=t.interpolate||wt,s="__p += '",h=xc((t.escape||wt).source+"|"+f.source+"|"+(f===it?vt:wt).source+"|"+(t.evaluate||wt).source+"|$","g"),p="//# sourceURL="+("sourceURL"in t?t.sourceURL:"lodash.templateSources["+ ++hr+"]")+"\n";n.replace(h,function(t,r,e,o,a,c){return e||(e=o),s+=n.slice(l,c).replace(kt,T),r&&(i=!0,s+="' +\n__e("+r+") +\n'"),a&&(u=!0,s+="';\n"+a+";\n__p += '"),e&&(s+="' +\n((__t = ("+e+")) == null ? '' : __t) +\n'"),l=c+t.length,t}),s+="';\n";var v=t.variable;v||(s="with (obj) {\n"+s+"\n}\n"),s=(u?s.replace(Xn,""):s).replace(Kn,"$1").replace(Jn,"$1;"),s="function("+(v||"obj")+") {\n"+(v?"":"obj || (obj = {});\n")+"var __t, __p = ''"+(i?", __e = _.escape":"")+(u?", __j = Array.prototype.join;\nfunction print() { __p += __j.call(arguments, '') }\n":";\n")+s+"return __p\n}";var g=bf(function(){return Function(a,p+"return "+s).apply(B,c)});if(g.source=s,yo(g))throw g;return g}function qa(n){return Yo(n).toLowerCase()}function Ia(n){return Yo(n).toUpperCase()}function Pa(n,t,r){if(n=Yo(n),!n)return n;if(r||t===B)return n.replace(ft,"");if(t+="",!t)return n;var e=F(n),i=F(t);return e.slice(A(e,i),S(e,i)+1).join("")}function Ua(n,t,r){if(n=Yo(n),!n)return n;if(r||t===B)return n.replace(ht,"");if(t+="",!t)return n;var e=F(n);return e.slice(0,S(e,F(t))+1).join("")}function Fa(n,t,r){if(n=Yo(n),!n)return n;if(r||t===B)return n.replace(st,"");if(t+="",!t)return n;var e=F(n);return e.slice(A(e,F(t))).join("")}function Ha(n,t){var r=on,e=an;if(bo(t)){var i="separator"in t?t.separator:i;r="length"in t?Uo(t.length):r,e="omission"in t?Yo(t.omission):e}n=Yo(n);var u=n.length;if(ar.test(n)){var o=F(n);u=o.length}if(r>=u)return n;var a=r-U(e);if(1>a)return e;var c=o?o.slice(0,a).join(""):n.slice(0,a);if(i===B)return c+e;if(o&&(a+=c.length-a),
+Do(i)){if(n.slice(a).search(i)){var l,f=c;for(i.global||(i=xc(i.source,Yo(gt.exec(i))+"g")),i.lastIndex=0;l=i.exec(f);)var s=l.index;c=c.slice(0,s===B?a:s)}}else if(n.indexOf(i,a)!=a){var h=c.lastIndexOf(i);h>-1&&(c=c.slice(0,h))}return c+e}function Wa(n){return n=Yo(n),n&&nt.test(n)?n.replace(Qn,H):n}function Ba(n,t,r){return n=Yo(n),t=r?B:t,t===B&&(t=fr.test(n)?lr:cr),n.match(t)||[]}function Ya(n){var t=n?n.length:0,e=Qe();return n=t?f(n,function(n){if("function"!=typeof n[1])throw new Mc(vn);return[e(n[0]),n[1]]}):[],Xu(function(e){for(var i=-1;++i<t;){var u=n[i];if(r(u[0],this,e))return r(u[1],this,e)}})}function Za(n){return yr(dr(n,!0))}function Va(n){return function(){return n}}function Xa(n){return n}function Ka(n){return wo(n)&&!nf(n)?Ja(n):Ur(n)}function Ja(n){return Br(dr(n,!0))}function Qa(n,t){return Yr(n,dr(t,!0))}function Ga(n,t,r){var e=oa(t),u=jr(t,e);null!=r||bo(t)&&(u.length||!e.length)||(r=t,t=n,n=this,u=jr(t,oa(t)));var o=bo(r)&&"chain"in r?r.chain:!0,a=_o(n);return i(u,function(r){var e=t[r];n[r]=e,a&&(n.prototype[r]=function(){var t=this.__chain__;if(o||t){var r=n(this.__wrapped__),i=r.__actions__=Ae(this.__actions__);return i.push({func:e,args:arguments,thisArg:n}),r.__chain__=t,r}return e.apply(n,s([this.value()],arguments))})}),n}function nc(){return Nr._===this&&(Nr._=Nc),this}function tc(){}function rc(n){return n=Uo(n),function(){return arguments[n]}}function ec(n){return li(n)?Qr(n):Gr(n)}function ic(n){return function(t){return null==n?B:Dr(n,t)}}function uc(n,t){if(n=Uo(n),1>n||n>yn)return[];var r=xn,e=Vc(n,xn);t=xi(t),n-=xn;for(var i=b(e,t);++r<n;)t(r);return i}function oc(n){return nf(n)?f(n,String):mi(n)}function ac(n){var t=++Ac;return Yo(n)+t}function cc(n,t){var r;return n!==B&&(r=n),t!==B&&(r=r===B?t:r+t),r}function lc(n){return n&&n.length?g(n,Xa,uo):B}function fc(n,t){return n&&n.length?g(n,Qe(t),uo):B}function sc(n){return gc(n)/(n?n.length:0)}function hc(n){return n&&n.length?g(n,Xa,qo):B}function pc(n,t){return n&&n.length?g(n,Qe(t),qo):B}function vc(n,t){var r;return n!==B&&(r=n),t!==B&&(r=r===B?t:r-t),r}function gc(n){return n&&n.length?M(n,Xa):B}function dc(n,t){return n&&n.length?M(n,Qe(t)):B}E=E?$r.defaults({},E,$r.pick(Nr,sr)):Nr;var yc=E.Date,mc=E.Error,_c=E.Math,xc=E.RegExp,Mc=E.TypeError,bc=E.Array.prototype,wc=E.Object.prototype,kc=E.Function.prototype.toString,Cc=wc.hasOwnProperty,Ac=0,Sc=kc.call(Object),Ec=wc.toString,Nc=Nr._,$c=xc("^"+kc.call(Cc).replace(ct,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),jc=E.Reflect,Dc=E.Symbol,Tc=E.Uint8Array,Lc=E.clearTimeout,Rc=jc?jc.enumerate:B,Oc=Object.getPrototypeOf,zc=Object.getOwnPropertySymbols,qc="symbol"==typeof(qc=Dc&&Dc.iterator)?qc:B,Ic=wc.propertyIsEnumerable,Pc=E.setTimeout,Uc=bc.splice,Fc=_c.ceil,Hc=_c.floor,Wc=E.isFinite,Bc=bc.join,Yc=Object.keys,Zc=_c.max,Vc=_c.min,Xc=E.parseInt,Kc=_c.random,Jc=bc.reverse,Qc=ni(E,"Map"),Gc=ni(E,"Set"),nl=ni(E,"WeakMap"),tl=ni(Object,"create"),rl=nl&&new nl,el=Qc?kc.call(Qc):"",il=Gc?kc.call(Gc):"",ul=Dc?Dc.prototype:B,ol=Dc?ul.valueOf:B,al=Dc?ul.toString:B,cl={};qn.templateSettings={escape:rt,evaluate:et,interpolate:it,variable:"",imports:{_:qn}};var ll=function(){function n(){}return function(t){if(bo(t)){n.prototype=t;var r=new n;n.prototype=B}return r||{}}}(),fl=De(Sr),sl=De(Er,!0),hl=Te(),pl=Te(!0);Rc&&!Ic.call({valueOf:1},"valueOf")&&(Hr=function(n){return z(Rc(n))});var vl=rl?function(n,t){return rl.set(n,t),n}:Xa,gl=Gc&&2===new Gc([1,2]).size?function(n){return new Gc(n)}:tc,dl=rl?function(n){return rl.get(n)}:tc,yl=Qr("length"),ml=zc||function(){return[]};(Qc&&ti(new Qc)!=jn||Gc&&ti(new Gc)!=Rn)&&(ti=function(n){var t=Ec.call(n),r=t==Tn?n.constructor:null,e="function"==typeof r?kc.call(r):"";if(e){if(e==el)return jn;if(e==il)return Rn}return t});var _l=function(){var n=0,t=0;return function(r,e){var i=Bl(),u=ln-(i-t);if(t=i,u>0){if(++n>=cn)return r}else n=0;return vl(r,e)}}(),xl=Xu(function(n,t){return nf(n)||(n=null==n?[]:[Object(n)]),t=Cr(t),e(n,t)}),Ml=Xu(function(n,t){return lo(n)?_r(n,Cr(t,!1,!0)):[]}),bl=Xu(function(n,t){var r=Ii(t);return lo(r)&&(r=B),lo(n)?_r(n,Cr(t,!1,!0),Qe(r)):[]}),wl=Xu(function(n,t){var r=Ii(t);return lo(r)&&(r=B),lo(n)?_r(n,Cr(t,!1,!0),B,r):[]}),kl=Xu(function(n){var t=f(n,_i);return t.length&&t[0]===n[0]?Or(t):[]}),Cl=Xu(function(n){var t=Ii(n),r=f(n,_i);return t===Ii(r)?t=B:r.pop(),r.length&&r[0]===n[0]?Or(r,Qe(t)):[]}),Al=Xu(function(n){var t=Ii(n),r=f(n,_i);return t===Ii(r)?t=B:r.pop(),r.length&&r[0]===n[0]?Or(r,B,t):[]}),Sl=Xu(Ui),El=Xu(function(n,t){t=f(Cr(t),String);var r=or(n,t);return re(n,t.sort(N)),r}),Nl=Xu(function(n){return pe(Cr(n,!1,!0))}),$l=Xu(function(n){var t=Ii(n);return lo(t)&&(t=B),pe(Cr(n,!1,!0),Qe(t))}),jl=Xu(function(n){var t=Ii(n);return lo(t)&&(t=B),pe(Cr(n,!1,!0),B,t)}),Dl=Xu(function(n,t){return lo(n)?_r(n,t):[]}),Tl=Xu(function(n){return ye(a(n,lo))}),Ll=Xu(function(n){var t=Ii(n);return lo(t)&&(t=B),ye(a(n,lo),Qe(t))}),Rl=Xu(function(n){var t=Ii(n);return lo(t)&&(t=B),ye(a(n,lo),B,t)}),Ol=Xu(cu),zl=Xu(function(n){var t=n.length,r=t>1?n[t-1]:B;return r="function"==typeof r?(n.pop(),r):B,lu(n,r)}),ql=Xu(function(n){n=Cr(n);var t=n.length,r=t?n[0]:0,e=this.__wrapped__,i=function(t){return or(t,n)};return!(t>1||this.__actions__.length)&&e instanceof At&&O(r)?(e=e.slice(r,+r+(t?1:0)),e.__actions__.push({func:pu,args:[i],thisArg:B}),new Ct(e,this.__chain__).thru(function(n){return t&&!n.length&&n.push(B),n})):this.thru(i)}),Il=$e(function(n,t,r){Cc.call(n,r)?++n[r]:n[r]=1}),Pl=$e(function(n,t,r){Cc.call(n,r)?n[r].push(t):n[r]=[t]}),Ul=Xu(function(n,t,e){var i=-1,u="function"==typeof t,o=li(t),a=co(n)?Array(n.length):[];return fl(n,function(n){var c=u?t:o&&null!=n?n[t]:B;a[++i]=c?r(c,n,e):zr(n,t,e)}),a}),Fl=$e(function(n,t,r){n[r]=t}),Hl=$e(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),Wl=Xu(function(n,t){if(null==n)return[];var r=t.length;return r>1&&ci(n,t[0],t[1])?t=[]:r>2&&ci(t[0],t[1],t[2])&&(t.length=1),Xr(n,Cr(t),[])}),Bl=yc.now,Yl=Xu(function(n,t,r){var e=Z;if(r.length){var i=I(r,Yl.placeholder);e|=Q}return Ze(n,e,t,r,i)}),Zl=Xu(function(n,t,r){var e=Z|V;if(r.length){var i=I(r,Zl.placeholder);e|=Q}return Ze(t,e,n,r,i)}),Vl=Xu(function(n,t){return mr(n,1,t)}),Xl=Xu(function(n,t,r){return mr(n,Ho(t)||0,r)}),Kl=Xu(function(n,t){t=f(Cr(t),Qe());var e=t.length;return Xu(function(i){for(var u=-1,o=Vc(i.length,e);++u<o;)i[u]=t[u].call(this,i[u]);return r(n,this,i)})}),Jl=Xu(function(n,t){var r=I(t,Jl.placeholder);return Ze(n,Q,B,t,r)}),Ql=Xu(function(n,t){var r=I(t,Ql.placeholder);return Ze(n,G,B,t,r)}),Gl=Xu(function(n,t){return Ze(n,tn,B,B,B,Cr(t))}),nf=Array.isArray,tf=je(function(n,t){Se(t,oa(t),n)}),rf=je(function(n,t){Se(t,aa(t),n)}),ef=je(function(n,t,r,e){Ee(t,aa(t),n,e)}),uf=je(function(n,t,r,e){Ee(t,oa(t),n,e)}),of=Xu(function(n,t){return or(n,Cr(t))}),af=Xu(function(n){return n.push(B,tr),r(ef,B,n)}),cf=Xu(function(n){return n.push(B,gi),r(sf,B,n)}),lf=Xu(zr),ff=je(function(n,t,r){Zr(n,t,r)}),sf=je(function(n,t,r,e){Zr(n,t,r,e)}),hf=Xu(function(n,t){return null==n?{}:(t=f(Cr(t),String),Kr(n,_r(aa(n),t)))}),pf=Xu(function(n,t){return null==n?{}:Kr(n,Cr(t))}),vf=Oe(function(n,t,r){return t=t.toLowerCase(),n+(r?ka(t):t)}),gf=Oe(function(n,t,r){return n+(r?"-":"")+t.toLowerCase()}),df=Oe(function(n,t,r){return n+(r?" ":"")+t.toLowerCase()}),yf=Re("toLowerCase"),mf=Re("toUpperCase"),_f=Oe(function(n,t,r){return n+(r?"_":"")+t.toLowerCase()}),xf=Oe(function(n,t,r){return n+(r?" ":"")+ka(t)}),Mf=Oe(function(n,t,r){return n+(r?" ":"")+t.toUpperCase()}),bf=Xu(function(n,t){try{return r(n,B,t)}catch(e){return yo(e)?e:new mc(e)}}),wf=Xu(function(n,t){return i(Cr(t),function(t){n[t]=Yl(n[t],n)}),n}),kf=Ie(),Cf=Ie(!0),Af=Xu(function(n,t){return function(r){return zr(r,n,t)}}),Sf=Xu(function(n,t){return function(r){return zr(n,r,t)}}),Ef=Ue(f),Nf=Ue(o),$f=Ue(v),jf=We(),Df=We(!0),Tf=Ye("ceil"),Lf=Ye("floor"),Rf=Ye("round");return qn.prototype=Mt.prototype,Ct.prototype=ll(Mt.prototype),Ct.prototype.constructor=Ct,At.prototype=ll(Mt.prototype),At.prototype.constructor=At,$t.prototype=tl?tl(null):wc,Rt.prototype.clear=Ot,Rt.prototype["delete"]=zt,Rt.prototype.get=qt,Rt.prototype.has=It,Rt.prototype.set=Pt,Ut.prototype.push=Ht,Wt.prototype.clear=Bt,Wt.prototype["delete"]=Yt,Wt.prototype.get=Zt,Wt.prototype.has=Vt,Wt.prototype.set=Xt,Yu.Cache=Rt,qn.after=Iu,qn.ary=Pu,qn.assign=tf,qn.assignIn=rf,qn.assignInWith=ef,qn.assignWith=uf,qn.at=of,qn.before=Uu,qn.bind=Yl,qn.bindAll=wf,qn.bindKey=Zl,qn.chain=su,qn.chunk=bi,qn.compact=wi,qn.concat=xl,qn.cond=Ya,qn.conforms=Za,qn.constant=Va,qn.countBy=Il,qn.create=Zo,qn.curry=Fu,qn.curryRight=Hu,qn.debounce=Wu,qn.defaults=af,qn.defaultsDeep=cf,qn.defer=Vl,qn.delay=Xl,qn.difference=Ml,qn.differenceBy=bl,qn.differenceWith=wl,qn.drop=ki,qn.dropRight=Ci,qn.dropRightWhile=Ai,qn.dropWhile=Si,qn.fill=Ei,qn.filter=wu,qn.flatMap=ji,qn.flatten=Di,qn.flattenDeep=Ti,qn.flip=Bu,qn.flow=kf,qn.flowRight=Cf,qn.fromPairs=Li,qn.functions=na,qn.functionsIn=ta,qn.groupBy=Pl,qn.initial=zi,qn.intersection=kl,qn.intersectionBy=Cl,qn.intersectionWith=Al,qn.invert=ua,qn.invokeMap=Ul,qn.iteratee=Ka,qn.keyBy=Fl,qn.keys=oa,qn.keysIn=aa,qn.map=Nu,qn.mapKeys=ca,qn.mapValues=la,qn.matches=Ja,qn.matchesProperty=Qa,qn.memoize=Yu,qn.merge=ff,qn.mergeWith=sf,qn.method=Af,qn.methodOf=Sf,qn.mixin=Ga,qn.negate=Zu,qn.nthArg=rc,qn.omit=hf,qn.omitBy=fa,qn.once=Vu,qn.orderBy=$u,qn.over=Ef,qn.overArgs=Kl,qn.overEvery=Nf,qn.overSome=$f,qn.partial=Jl,qn.partialRight=Ql,qn.partition=Hl,qn.pick=pf,qn.pickBy=sa,qn.property=ec,qn.propertyOf=ic,qn.pull=Sl,qn.pullAll=Ui,qn.pullAllBy=Fi,qn.pullAt=El,qn.range=jf,qn.rangeRight=Df,qn.rearg=Gl,qn.reject=Tu,qn.remove=Hi,qn.rest=Xu,qn.reverse=Wi,qn.sampleSize=Ru,qn.set=pa,qn.setWith=va,qn.shuffle=Ou,qn.slice=Bi,qn.sortBy=Wl,qn.sortedUniq=Qi,qn.sortedUniqBy=Gi,qn.split=Ra,qn.spread=Ku,qn.tail=nu,qn.take=tu,qn.takeRight=ru,qn.takeRightWhile=eu,qn.takeWhile=iu,qn.tap=hu,qn.throttle=Ju,qn.thru=pu,qn.toArray=Po,qn.toPairs=ga,qn.toPairsIn=da,qn.toPath=oc,qn.toPlainObject=Wo,qn.transform=ya,qn.unary=Qu,qn.union=Nl,qn.unionBy=$l,qn.unionWith=jl,qn.uniq=uu,qn.uniqBy=ou,qn.uniqWith=au,qn.unset=ma,qn.unzip=cu,qn.unzipWith=lu,qn.values=_a,qn.valuesIn=xa,qn.without=Dl,qn.words=Ba,qn.wrap=Gu,qn.xor=Tl,qn.xorBy=Ll,qn.xorWith=Rl,qn.zip=Ol,qn.zipObject=fu,qn.zipWith=zl,qn.extend=rf,qn.extendWith=ef,Ga(qn,qn),qn.add=cc,qn.attempt=bf,qn.camelCase=vf,qn.capitalize=ka,qn.ceil=Tf,qn.clamp=Ma,qn.clone=no,qn.cloneDeep=ro,qn.cloneDeepWith=eo,qn.cloneWith=to,qn.deburr=Ca,qn.endsWith=Aa,qn.eq=io,qn.escape=Sa,qn.escapeRegExp=Ea,qn.every=bu,qn.find=ku,qn.findIndex=Ni,qn.findKey=Vo,qn.findLast=Cu,qn.findLastIndex=$i,qn.findLastKey=Xo,qn.floor=Lf,qn.forEach=Au,qn.forEachRight=Su,qn.forIn=Ko,qn.forInRight=Jo,qn.forOwn=Qo,qn.forOwnRight=Go,qn.get=ra,qn.gt=uo,qn.gte=oo,qn.has=ea,qn.hasIn=ia,qn.head=Ri,qn.identity=Xa,qn.includes=Eu,qn.indexOf=Oi,qn.inRange=ba,qn.invoke=lf,qn.isArguments=ao,qn.isArray=nf,qn.isArrayLike=co,qn.isArrayLikeObject=lo,qn.isBoolean=fo,qn.isDate=so,qn.isElement=ho,qn.isEmpty=po,qn.isEqual=vo,qn.isEqualWith=go,qn.isError=yo,qn.isFinite=mo,qn.isFunction=_o,qn.isInteger=xo,qn.isLength=Mo,qn.isMatch=ko,qn.isMatchWith=Co,qn.isNaN=Ao,qn.isNative=So,qn.isNil=No,qn.isNull=Eo,qn.isNumber=$o,qn.isObject=bo,qn.isObjectLike=wo,qn.isPlainObject=jo,qn.isRegExp=Do,qn.isSafeInteger=To,qn.isString=Lo,qn.isSymbol=Ro,qn.isTypedArray=Oo,qn.isUndefined=zo,qn.join=qi,qn.kebabCase=gf,qn.last=Ii,qn.lastIndexOf=Pi,qn.lowerCase=df,qn.lowerFirst=yf,qn.lt=qo,qn.lte=Io,qn.max=lc,qn.maxBy=fc,qn.mean=sc,qn.min=hc,qn.minBy=pc,qn.noConflict=nc,qn.noop=tc,qn.now=Bl,qn.pad=Na,qn.padEnd=$a,qn.padStart=ja,qn.parseInt=Da,qn.random=wa,qn.reduce=ju,qn.reduceRight=Du,qn.repeat=Ta,qn.replace=La,qn.result=ha,qn.round=Rf,qn.runInContext=W,qn.sample=Lu,qn.size=zu,qn.snakeCase=_f,qn.some=qu,qn.sortedIndex=Yi,qn.sortedIndexBy=Zi,qn.sortedIndexOf=Vi,qn.sortedLastIndex=Xi,qn.sortedLastIndexBy=Ki,qn.sortedLastIndexOf=Ji,qn.startCase=xf,qn.startsWith=Oa,qn.subtract=vc,qn.sum=gc,qn.sumBy=dc,qn.template=za,qn.times=uc,qn.toInteger=Uo,qn.toLength=Fo,qn.toLower=qa,qn.toNumber=Ho,qn.toSafeInteger=Bo,qn.toString=Yo,qn.toUpper=Ia,qn.trim=Pa,qn.trimEnd=Ua,qn.trimStart=Fa,qn.truncate=Ha,qn.unescape=Wa,qn.uniqueId=ac,qn.upperCase=Mf,qn.upperFirst=mf,qn.each=Au,qn.eachRight=Su,qn.first=Ri,Ga(qn,function(){var n={};return Sr(qn,function(t,r){Cc.call(qn.prototype,r)||(n[r]=t)}),n}(),{chain:!1}),qn.VERSION=Y,i(["bind","bindKey","curry","curryRight","partial","partialRight"],function(n){qn[n].placeholder=qn}),i(["drop","take"],function(n,t){At.prototype[n]=function(r){var e=this.__filtered__;if(e&&!t)return new At(this);r=r===B?1:Zc(Uo(r),0);var i=this.clone();return e?i.__takeCount__=Vc(r,i.__takeCount__):i.__views__.push({size:Vc(r,xn),type:n+(i.__dir__<0?"Right":"")}),i},At.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()}}),i(["filter","map","takeWhile"],function(n,t){var r=t+1,e=r==sn||r==pn;At.prototype[n]=function(n){var t=this.clone();return t.__iteratees__.push({iteratee:Qe(n,3),type:r}),t.__filtered__=t.__filtered__||e,t}}),i(["head","last"],function(n,t){var r="take"+(t?"Right":"");At.prototype[n]=function(){return this[r](1).value()[0]}}),i(["initial","tail"],function(n,t){var r="drop"+(t?"":"Right");At.prototype[n]=function(){return this.__filtered__?new At(this):this[r](1)}}),At.prototype.compact=function(){return this.filter(Xa)},At.prototype.find=function(n){return this.filter(n).head()},At.prototype.findLast=function(n){return this.reverse().find(n)},At.prototype.invokeMap=Xu(function(n,t){return"function"==typeof n?new At(this):this.map(function(r){return zr(r,n,t)})}),At.prototype.reject=function(n){return n=Qe(n,3),this.filter(function(t){return!n(t)})},At.prototype.slice=function(n,t){n=Uo(n);var r=this;return r.__filtered__&&(n>0||0>t)?new At(r):(0>n?r=r.takeRight(-n):n&&(r=r.drop(n)),t!==B&&(t=Uo(t),r=0>t?r.dropRight(-t):r.take(t-n)),r)},At.prototype.takeRightWhile=function(n){return this.reverse().takeWhile(n).reverse()},At.prototype.toArray=function(){return this.take(xn)},Sr(At.prototype,function(n,t){var r=/^(?:filter|find|map|reject)|While$/.test(t),e=/^(?:head|last)$/.test(t),i=qn[e?"take"+("last"==t?"Right":""):t],u=e||/^find/.test(t);i&&(qn.prototype[t]=function(){var t=this.__wrapped__,o=e?[1]:arguments,a=t instanceof At,c=o[0],l=a||nf(t),f=function(n){var t=i.apply(qn,s([n],o));return e&&h?t[0]:t};l&&r&&"function"==typeof c&&1!=c.length&&(a=l=!1);var h=this.__chain__,p=!!this.__actions__.length,v=u&&!h,g=a&&!p;if(!u&&l){t=g?t:new At(this);var d=n.apply(t,o);return d.__actions__.push({func:pu,args:[f],thisArg:B}),new Ct(d,h)}return v&&g?n.apply(this,o):(d=this.thru(f),v?e?d.value()[0]:d.value():d)})}),i(["pop","push","shift","sort","splice","unshift"],function(n){var t=bc[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|shift)$/.test(n);qn.prototype[n]=function(){var n=arguments;return e&&!this.__chain__?t.apply(this.value(),n):this[r](function(r){return t.apply(r,n)})}}),Sr(At.prototype,function(n,t){var r=qn[t];if(r){var e=r.name+"",i=cl[e]||(cl[e]=[]);i.push({name:t,func:r})}}),cl[Pe(B,V).name]=[{name:"wrapper",func:B}],At.prototype.clone=St,At.prototype.reverse=Et,At.prototype.value=Nt,qn.prototype.at=ql,qn.prototype.chain=vu,qn.prototype.commit=gu,qn.prototype.flatMap=du,qn.prototype.next=yu,qn.prototype.plant=_u,qn.prototype.reverse=xu,qn.prototype.toJSON=qn.prototype.valueOf=qn.prototype.value=Mu,qc&&(qn.prototype[qc]=mu),qn}var B,Y="4.0.1",Z=1,V=2,X=4,K=8,J=16,Q=32,G=64,nn=128,tn=256,rn=512,en=1,un=2,on=30,an="...",cn=150,ln=16,fn=200,sn=1,hn=2,pn=3,vn="Expected a function",gn="__lodash_hash_undefined__",dn=1/0,yn=9007199254740991,mn=1.7976931348623157e308,_n=NaN,xn=4294967295,Mn=xn-1,bn=xn>>>1,wn="__lodash_placeholder__",kn="[object Arguments]",Cn="[object Array]",An="[object Boolean]",Sn="[object Date]",En="[object Error]",Nn="[object Function]",$n="[object GeneratorFunction]",jn="[object Map]",Dn="[object Number]",Tn="[object Object]",Ln="[object RegExp]",Rn="[object Set]",On="[object String]",zn="[object Symbol]",qn="[object WeakMap]",In="[object ArrayBuffer]",Pn="[object Float32Array]",Un="[object Float64Array]",Fn="[object Int8Array]",Hn="[object Int16Array]",Wn="[object Int32Array]",Bn="[object Uint8Array]",Yn="[object Uint8ClampedArray]",Zn="[object Uint16Array]",Vn="[object Uint32Array]",Xn=/\b__p \+= '';/g,Kn=/\b(__p \+=) '' \+/g,Jn=/(__e\(.*?\)|\b__t\)) \+\n'';/g,Qn=/&(?:amp|lt|gt|quot|#39|#96);/g,Gn=/[&<>"'`]/g,nt=RegExp(Qn.source),tt=RegExp(Gn.source),rt=/<%-([\s\S]+?)%>/g,et=/<%([\s\S]+?)%>/g,it=/<%=([\s\S]+?)%>/g,ut=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,ot=/^\w*$/,at=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]/g,ct=/[\\^$.*+?()[\]{}|]/g,lt=RegExp(ct.source),ft=/^\s+|\s+$/g,st=/^\s+/,ht=/\s+$/,pt=/\\(\\)?/g,vt=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,gt=/\w*$/,dt=/^0x/i,yt=/^[-+]0x[0-9a-f]+$/i,mt=/^0b[01]+$/i,_t=/^\[object .+?Constructor\]$/,xt=/^0o[0-7]+$/i,Mt=/^(?:0|[1-9]\d*)$/,bt=/[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g,wt=/($^)/,kt=/['\n\r\u2028\u2029\\]/g,Ct="\\ud800-\\udfff",At="\\u0300-\\u036f\\ufe20-\\ufe23",St="\\u20d0-\\u20f0",Et="\\u2700-\\u27bf",Nt="a-z\\xdf-\\xf6\\xf8-\\xff",$t="\\xac\\xb1\\xd7\\xf7",jt="\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf",Dt="\\u2018\\u2019\\u201c\\u201d",Tt=" \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Lt="A-Z\\xc0-\\xd6\\xd8-\\xde",Rt="\\ufe0e\\ufe0f",Ot=$t+jt+Dt+Tt,zt="["+Ct+"]",qt="["+Ot+"]",It="["+At+St+"]",Pt="\\d+",Ut="["+Et+"]",Ft="["+Nt+"]",Ht="[^"+Ct+Ot+Pt+Et+Nt+Lt+"]",Wt="\\ud83c[\\udffb-\\udfff]",Bt="(?:"+It+"|"+Wt+")",Yt="[^"+Ct+"]",Zt="(?:\\ud83c[\\udde6-\\uddff]){2}",Vt="[\\ud800-\\udbff][\\udc00-\\udfff]",Xt="["+Lt+"]",Kt="\\u200d",Jt="(?:"+Ft+"|"+Ht+")",Qt="(?:"+Xt+"|"+Ht+")",Gt=Bt+"?",nr="["+Rt+"]?",tr="(?:"+Kt+"(?:"+[Yt,Zt,Vt].join("|")+")"+nr+Gt+")*",rr=nr+Gt+tr,er="(?:"+[Ut,Zt,Vt].join("|")+")"+rr,ir="(?:"+[Yt+It+"?",It,Zt,Vt,zt].join("|")+")",ur=RegExp(It,"g"),or=RegExp(Wt+"(?="+Wt+")|"+ir+rr,"g"),ar=RegExp("["+Kt+Ct+At+St+Rt+"]"),cr=/[a-zA-Z0-9]+/g,lr=RegExp([Xt+"?"+Ft+"+(?="+[qt,Xt,"$"].join("|")+")",Qt+"+(?="+[qt,Xt+Jt,"$"].join("|")+")",Xt+"?"+Jt+"+",Xt+"+",Pt,er].join("|"),"g"),fr=/[a-z][A-Z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,sr=["Array","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Reflect","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],hr=-1,pr={};pr[Pn]=pr[Un]=pr[Fn]=pr[Hn]=pr[Wn]=pr[Bn]=pr[Yn]=pr[Zn]=pr[Vn]=!0,pr[kn]=pr[Cn]=pr[In]=pr[An]=pr[Sn]=pr[En]=pr[Nn]=pr[jn]=pr[Dn]=pr[Tn]=pr[Ln]=pr[Rn]=pr[On]=pr[qn]=!1;var vr={};vr[kn]=vr[Cn]=vr[In]=vr[An]=vr[Sn]=vr[Pn]=vr[Un]=vr[Fn]=vr[Hn]=vr[Wn]=vr[jn]=vr[Dn]=vr[Tn]=vr[Ln]=vr[Rn]=vr[On]=vr[zn]=vr[Bn]=vr[Yn]=vr[Zn]=vr[Vn]=!0,vr[En]=vr[Nn]=vr[qn]=!1;var gr={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","Ç":"C","ç":"c","Ð":"D","ð":"d","È":"E","É":"E","Ê":"E","Ë":"E","è":"e","é":"e","ê":"e","ë":"e","Ì":"I","Í":"I","Î":"I","Ï":"I","ì":"i","í":"i","î":"i","ï":"i","Ñ":"N","ñ":"n","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","Ù":"U","Ú":"U","Û":"U","Ü":"U","ù":"u","ú":"u","û":"u","ü":"u","Ý":"Y","ý":"y","ÿ":"y","Æ":"Ae","æ":"ae","Þ":"Th","þ":"th","ß":"ss"},dr={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},yr={"&":"&","<":"<",">":">",""":'"',"'":"'","`":"`"},mr={"function":!0,object:!0},_r={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},xr=parseFloat,Mr=parseInt,br=mr[typeof exports]&&exports&&!exports.nodeType?exports:null,wr=mr[typeof module]&&module&&!module.nodeType?module:null,kr=E(br&&wr&&"object"==typeof global&&global),Cr=E(mr[typeof self]&&self),Ar=E(mr[typeof window]&&window),Sr=wr&&wr.exports===br?br:null,Er=E(mr[typeof this]&&this),Nr=kr||Ar!==(Er&&Er.window)&&Ar||Cr||Er||Function("return this")(),$r=W();(Ar||Cr||{})._=$r,"function"==typeof define&&"object"==typeof define.amd&&define.amd?define(function(){return $r}):br&&wr?(Sr&&((wr.exports=$r)._=$r),br._=$r):Nr._=$r}.call(this),function(n,t,r){"use strict";function e(n,t,r){if(!n)throw dn("areq","Argument '{0}' is {1}",t||"?",r||"required");return n}function i(n,t){return n||t?n?t?(H(n)&&(n=n.join(" ")),H(t)&&(t=t.join(" ")),n+" "+t):n:t:""}function u(n){var t={};return n&&(n.to||n.from)&&(t.to=n.to,t.from=n.from),t}function o(n,t,r){var e="";return n=H(n)?n:n&&W(n)&&n.length?n.split(/\s+/):[],F(n,function(n,i){n&&n.length>0&&(e+=i>0?" ":"",e+=r?t+n:n+t)}),e}function a(n,t){var r=n.indexOf(t);t>=0&&n.splice(r,1)}function c(n){if(n instanceof U)switch(n.length){case 0:return[];case 1:if(n[0].nodeType===K)return n;break;default:return U(l(n))}return n.nodeType===K?U(n):void 0}function l(n){if(!n[0])return n;for(var t=0;t<n.length;t++){var r=n[t];if(r.nodeType==K)return r}}function f(n,t,r){F(t,function(t){n.addClass(t,r)})}function s(n,t,r){F(t,function(t){n.removeClass(t,r)})}function h(n){return function(t,r){r.addClass&&(f(n,t,r.addClass),r.addClass=null),r.removeClass&&(s(n,t,r.removeClass),r.removeClass=null)}}function p(n){if(n=n||{},!n.$$prepared){var t=n.domOperation||q;n.domOperation=function(){n.$$domOperationFired=!0,t(),t=q},n.$$prepared=!0}return n}function v(n,t){g(n,t),d(n,t)}function g(n,t){t.from&&(n.css(t.from),t.from=null)}function d(n,t){t.to&&(n.css(t.to),t.to=null)}function y(n,t,r){var e=(t.addClass||"")+" "+(r.addClass||""),i=(t.removeClass||"")+" "+(r.removeClass||""),u=m(n.attr("class"),e,i);r.preparationClasses&&(t.preparationClasses=C(r.preparationClasses,t.preparationClasses),delete r.preparationClasses);var o=t.domOperation!==q?t.domOperation:null;return P(t,r),o&&(t.domOperation=o),u.addClass?t.addClass=u.addClass:t.addClass=null,u.removeClass?t.removeClass=u.removeClass:t.removeClass=null,t}function m(n,t,r){function e(n){W(n)&&(n=n.split(" "));var t={};return F(n,function(n){n.length&&(t[n]=!0)}),t}var i=1,u=-1,o={};n=e(n),t=e(t),F(t,function(n,t){o[t]=i}),r=e(r),F(r,function(n,t){o[t]=o[t]===i?null:u});var a={addClass:"",removeClass:""};return F(o,function(t,r){var e,o;t===i?(e="addClass",o=!n[r]):t===u&&(e="removeClass",o=n[r]),o&&(a[e].length&&(a[e]+=" "),a[e]+=r)}),a}function _(n){return n instanceof t.element?n[0]:n}function x(n,t,r){var e="";t&&(e=o(t,G,!0)),r.addClass&&(e=C(e,o(r.addClass,J))),r.removeClass&&(e=C(e,o(r.removeClass,Q))),e.length&&(r.preparationClasses=e,n.addClass(e))}function M(n,t){t.preparationClasses&&(n.removeClass(t.preparationClasses),t.preparationClasses=null),t.activeClasses&&(n.removeClass(t.activeClasses),t.activeClasses=null)}function b(n,t){var r=t?"-"+t+"s":"";return k(n,[vn,r]),[vn,r]}function w(n,t){var r=t?"paused":"",e=O+fn;return k(n,[e,r]),[e,r]}function k(n,t){var r=t[0],e=t[1];n.style[r]=e}function C(n,t){return n?t?n+" "+t:n:t}function A(n){return[pn,n+"s"]}function S(n,t){var r=t?hn:vn;return[r,n+"s"]}function E(n,t,r){var e=Object.create(null),i=n.getComputedStyle(t)||{};return F(r,function(n,t){var r=i[n];if(r){var u=r.charAt(0);("-"===u||"+"===u||u>=0)&&(r=N(r)),0===r&&(r=null),e[t]=r}}),e}function N(n){var t=0,r=n.split(/\s*,\s*/);return F(r,function(n){"s"==n.charAt(n.length-1)&&(n=n.substring(0,n.length-1)),n=parseFloat(n)||0,t=t?Math.max(n,t):n}),t}function $(n){return 0===n||null!=n}function j(n,t){var r=L,e=n+"s";return t?r+=un:e+=" linear all",[r,e]}function D(){var n=Object.create(null);return{flush:function(){n=Object.create(null)},count:function(t){var r=n[t];return r?r.total:0},get:function(t){var r=n[t];return r&&r.value},put:function(t,r){n[t]?n[t].total++:n[t]={total:1,value:r}}}}function T(n,t,r){F(r,function(r){n[r]=Z(n[r])?n[r]:t.style.getPropertyValue(r)})}var L,R,O,z,q=t.noop,I=t.copy,P=t.extend,U=t.element,F=t.forEach,H=t.isArray,W=t.isString,B=t.isObject,Y=t.isUndefined,Z=t.isDefined,V=t.isFunction,X=t.isElement,K=1,J="-add",Q="-remove",G="ng-",nn="-active",tn="ng-animate",rn="$$ngAnimateChildren",en="";Y(n.ontransitionend)&&Z(n.onwebkittransitionend)?(en="-webkit-",L="WebkitTransition",R="webkitTransitionEnd transitionend"):(L="transition",R="transitionend"),Y(n.onanimationend)&&Z(n.onwebkitanimationend)?(en="-webkit-",O="WebkitAnimation",z="webkitAnimationEnd animationend"):(O="animation",z="animationend");var un="Duration",on="Property",an="Delay",cn="TimingFunction",ln="IterationCount",fn="PlayState",sn=9999,hn=O+an,pn=O+un,vn=L+an,gn=L+un,dn=t.$$minErr("ng"),yn=["$$rAF",function(n){function t(n){e=e.concat(n),r()}function r(){if(e.length){for(var t=e.shift(),u=0;u<t.length;u++)t[u]();i||n(function(){i||r()})}}var e,i;return e=t.queue=[],t.waitUntilQuiet=function(t){i&&i(),i=n(function(){i=null,t(),r()})},t}],mn=[function(){return function(n,r,e){var i=e.ngAnimateChildren;t.isString(i)&&0===i.length?r.data(rn,!0):e.$observe("ngAnimateChildren",function(n){n="on"===n||"true"===n,r.data(rn,n)})}}],_n="$$animateCss",xn=1e3,Mn=3,bn=1.5,wn={transitionDuration:gn,transitionDelay:vn,transitionProperty:L+on,animationDuration:pn,animationDelay:hn,animationIterationCount:O+ln},kn={transitionDuration:gn,transitionDelay:vn,animationDuration:pn,animationDelay:hn},Cn=["$animateProvider",function(n){var t=D(),r=D();this.$get=["$window","$$jqLite","$$AnimateRunner","$timeout","$$forceReflow","$sniffer","$$rAFScheduler","$$animateQueue",function(n,e,i,c,l,f,s,y){function m(n,t){var r="$$ngAnimateParentKey",e=n.parentNode,i=e[r]||(e[r]=++P);return i+"-"+n.getAttribute("class")+"-"+t}function x(r,e,i,u){var o=t.get(i);return o||(o=E(n,r,u),"infinite"===o.animationIterationCount&&(o.animationIterationCount=1)),t.put(i,o),o}function M(i,u,a,c){var l;if(t.count(a)>0&&(l=r.get(a),!l)){var f=o(u,"-stagger");e.addClass(i,f),l=E(n,i,c),l.animationDuration=Math.max(l.animationDuration,0),l.transitionDuration=Math.max(l.transitionDuration,0),e.removeClass(i,f),r.put(a,l)}return l||{}}function C(n){U.push(n),s.waitUntilQuiet(function(){t.flush(),r.flush();for(var n=l(),e=0;e<U.length;e++)U[e](n);U.length=0})}function N(n,t,r){var e=x(n,t,r,wn),i=e.animationDelay,u=e.transitionDelay;return e.maxDelay=i&&u?Math.max(i,u):i||u,e.maxDuration=Math.max(e.animationDuration*e.animationIterationCount,e.transitionDuration),e}var D=h(e),P=0,U=[];return function(n,r){function l(){h()}function s(){h(!0)}function h(t){Z||X&&V||(Z=!0,V=!1,W.$$skipPreparationClasses||e.removeClass(n,wn),e.removeClass(n,An),w(Y,!1),b(Y,!1),F(fn,function(n){Y.style[n[0]]=""}),D(n,W),v(n,W),Object.keys(B).length&&F(B,function(n,t){n?Y.style.setProperty(t,n):Y.style.removeProperty(t)}),W.onDone&&W.onDone(),vn&&vn.length&&n.off(vn.join(" "),P),K&&K.complete(!t))}function x(n){Pn.blockTransition&&b(Y,n),Pn.blockKeyframeAnimation&&w(Y,!!n)}function E(){return K=new i({end:l,cancel:s}),C(q),h(),{$$willAnimate:!1,start:function(){return K},end:l}}function P(n){n.stopPropagation();var t=n.originalEvent||n,r=t.$manualTimeStamp||Date.now(),e=parseFloat(t.elapsedTime.toFixed(Mn));Math.max(r-ln,0)>=en&&e>=un&&(X=!0,h())}function U(){function t(){if(!Z){if(x(!1),F(fn,function(n){var t=n[0],r=n[1];Y.style[t]=r}),D(n,W),e.addClass(n,An),Pn.recalculateTimingStyles){if(Cn=Y.className+" "+wn,Nn=m(Y,Cn),qn=N(Y,Cn,Nn),In=qn.maxDelay,rn=Math.max(In,0),un=qn.maxDuration,0===un)return void h();Pn.hasTransitions=qn.transitionDuration>0,Pn.hasAnimations=qn.animationDuration>0}if(Pn.applyAnimationDelay&&(In="boolean"!=typeof W.delay&&$(W.delay)?parseFloat(W.delay):In,rn=Math.max(In,0),qn.animationDelay=In,Un=S(In,!0),fn.push(Un),Y.style[Un[0]]=Un[1]),en=rn*xn,an=un*xn,W.easing){var t,i=W.easing;Pn.hasTransitions&&(t=L+cn,fn.push([t,i]),Y.style[t]=i),Pn.hasAnimations&&(t=O+cn,fn.push([t,i]),Y.style[t]=i)}qn.transitionDuration&&vn.push(R),qn.animationDuration&&vn.push(z),ln=Date.now();var u=en+bn*an,o=ln+u,a=n.data(_n)||[],l=!0;if(a.length){var f=a[0];l=o>f.expectedEndTime,l?c.cancel(f.timer):a.push(h)}if(l){var s=c(r,u,!1);a[0]={timer:s,expectedEndTime:o},a.push(h),n.data(_n,a)}vn.length&&n.on(vn.join(" "),P),W.to&&(W.cleanupStyles&&T(B,Y,Object.keys(W.to)),d(n,W))}}function r(){var t=n.data(_n);if(t){for(var r=1;r<t.length;r++)t[r]();n.removeData(_n)}}if(!Z){if(!Y.parentNode)return void h();var i=function(n){if(X)V&&n&&(V=!1,h());else if(V=!n,qn.animationDuration){var t=w(Y,V);V?fn.push(t):a(fn,t)}},u=On>0&&(qn.transitionDuration&&0===$n.transitionDuration||qn.animationDuration&&0===$n.animationDuration)&&Math.max($n.animationDelay,$n.transitionDelay);u?c(t,Math.floor(u*On*xn),!1):t(),tn.resume=function(){i(!0)},tn.pause=function(){i(!1)}}}var W=r||{};W.$$prepared||(W=p(I(W)));var B={},Y=_(n);if(!Y||!Y.parentNode||!y.enabled())return E();var Z,V,X,K,tn,rn,en,un,an,ln,fn=[],hn=n.attr("class"),pn=u(W),vn=[];if(0===W.duration||!f.animations&&!f.transitions)return E();var gn=W.event&&H(W.event)?W.event.join(" "):W.event,dn=gn&&W.structural,yn="",mn="";dn?yn=o(gn,G,!0):gn&&(yn=gn),W.addClass&&(mn+=o(W.addClass,J)),W.removeClass&&(mn.length&&(mn+=" "),mn+=o(W.removeClass,Q)),W.applyClassesEarly&&mn.length&&D(n,W);var wn=[yn,mn].join(" ").trim(),Cn=hn+" "+wn,An=o(wn,nn),Sn=pn.to&&Object.keys(pn.to).length>0,En=(W.keyframeStyle||"").length>0;if(!En&&!Sn&&!wn)return E();var Nn,$n;if(W.stagger>0){var jn=parseFloat(W.stagger);$n={transitionDelay:jn,animationDelay:jn,transitionDuration:0,animationDuration:0}}else Nn=m(Y,Cn),$n=M(Y,wn,Nn,kn);W.$$skipPreparationClasses||e.addClass(n,wn);var Dn;if(W.transitionStyle){var Tn=[L,W.transitionStyle];k(Y,Tn),fn.push(Tn)}if(W.duration>=0){Dn=Y.style[L].length>0;var Ln=j(W.duration,Dn);k(Y,Ln),fn.push(Ln)}if(W.keyframeStyle){var Rn=[O,W.keyframeStyle];k(Y,Rn),fn.push(Rn)}var On=$n?W.staggerIndex>=0?W.staggerIndex:t.count(Nn):0,zn=0===On;zn&&!W.skipBlocking&&b(Y,sn);var qn=N(Y,Cn,Nn),In=qn.maxDelay;rn=Math.max(In,0),un=qn.maxDuration;var Pn={};if(Pn.hasTransitions=qn.transitionDuration>0,Pn.hasAnimations=qn.animationDuration>0,Pn.hasTransitionAll=Pn.hasTransitions&&"all"==qn.transitionProperty,Pn.applyTransitionDuration=Sn&&(Pn.hasTransitions&&!Pn.hasTransitionAll||Pn.hasAnimations&&!Pn.hasTransitions),Pn.applyAnimationDuration=W.duration&&Pn.hasAnimations,Pn.applyTransitionDelay=$(W.delay)&&(Pn.applyTransitionDuration||Pn.hasTransitions),Pn.applyAnimationDelay=$(W.delay)&&Pn.hasAnimations,Pn.recalculateTimingStyles=mn.length>0,(Pn.applyTransitionDuration||Pn.applyAnimationDuration)&&(un=W.duration?parseFloat(W.duration):un,Pn.applyTransitionDuration&&(Pn.hasTransitions=!0,qn.transitionDuration=un,Dn=Y.style[L+on].length>0,fn.push(j(un,Dn))),Pn.applyAnimationDuration&&(Pn.hasAnimations=!0,qn.animationDuration=un,fn.push(A(un)))),0===un&&!Pn.recalculateTimingStyles)return E();if(null!=W.delay){var Un;"boolean"!=typeof W.delay&&(Un=parseFloat(W.delay),rn=Math.max(Un,0)),Pn.applyTransitionDelay&&fn.push(S(Un)),Pn.applyAnimationDelay&&fn.push(S(Un,!0))}return null==W.duration&&qn.transitionDuration>0&&(Pn.recalculateTimingStyles=Pn.recalculateTimingStyles||zn),en=rn*xn,an=un*xn,W.skipBlocking||(Pn.blockTransition=qn.transitionDuration>0,Pn.blockKeyframeAnimation=qn.animationDuration>0&&$n.animationDelay>0&&0===$n.animationDuration),W.from&&(W.cleanupStyles&&T(B,Y,Object.keys(W.from)),g(n,W)),Pn.blockTransition||Pn.blockKeyframeAnimation?x(un):W.skipBlocking||b(Y,!1),{$$willAnimate:!0,end:l,start:function(){return Z?void 0:(tn={end:l,cancel:s,resume:null,pause:null},K=new i(tn),C(U),K)}}}}]}],An=["$$animationProvider",function(n){function t(n){return n.parentNode&&11===n.parentNode.nodeType}n.drivers.push("$$animateCssDriver");var r="ng-animate-shim",e="ng-anchor",i="ng-anchor-out",u="ng-anchor-in";this.$get=["$animateCss","$rootScope","$$AnimateRunner","$rootElement","$sniffer","$$jqLite","$document",function(n,o,a,c,l,f,s){function p(n){return n.replace(/\bng-\S+\b/g,"");
+}function v(n,t){return W(n)&&(n=n.split(" ")),W(t)&&(t=t.split(" ")),n.filter(function(n){return-1===t.indexOf(n)}).join(" ")}function g(t,o,c){function l(n){var t={},r=_(n).getBoundingClientRect();return F(["width","height","top","left"],function(n){var e=r[n];switch(n){case"top":e+=m.scrollTop;break;case"left":e+=m.scrollLeft}t[n]=Math.floor(e)+"px"}),t}function f(){var t=n(d,{addClass:i,delay:!0,from:l(o)});return t.$$willAnimate?t:null}function s(n){return n.attr("class")||""}function h(){var t=p(s(c)),r=v(t,y),e=v(y,t),o=n(d,{to:l(c),addClass:u+" "+r,removeClass:i+" "+e,delay:!0});return o.$$willAnimate?o:null}function g(){d.remove(),o.removeClass(r),c.removeClass(r)}var d=U(_(o).cloneNode(!0)),y=p(s(d));o.addClass(r),c.addClass(r),d.addClass(e),M.append(d);var x,b=f();if(!b&&(x=h(),!x))return g();var w=b||x;return{start:function(){function n(){r&&r.end()}var t,r=w.start();return r.done(function(){return r=null,!x&&(x=h())?(r=x.start(),r.done(function(){r=null,g(),t.complete()}),r):(g(),void t.complete())}),t=new a({end:n,cancel:n})}}}function d(n,t,r,e){var i=y(n,q),u=y(t,q),o=[];return F(e,function(n){var t=n.out,e=n["in"],i=g(r,t,e);i&&o.push(i)}),i||u||0!==o.length?{start:function(){function n(){F(t,function(n){n.end()})}var t=[];i&&t.push(i.start()),u&&t.push(u.start()),F(o,function(n){t.push(n.start())});var r=new a({end:n,cancel:n});return a.all(t,function(n){r.complete(n)}),r}}:void 0}function y(t){var r=t.element,e=t.options||{};t.structural&&(e.event=t.event,e.structural=!0,e.applyClassesEarly=!0,"leave"===t.event&&(e.onDone=e.domOperation)),e.preparationClasses&&(e.event=C(e.event,e.preparationClasses));var i=n(r,e);return i.$$willAnimate?i:null}if(!l.animations&&!l.transitions)return q;var m=s[0].body,x=_(c),M=U(t(x)||m.contains(x)?x:m);h(f);return function(n){return n.from&&n.to?d(n.from,n.to,n.classes,n.anchors):y(n)}}]}],Sn=["$animateProvider",function(n){this.$get=["$injector","$$AnimateRunner","$$jqLite",function(t,r,e){function i(r){r=H(r)?r:r.split(" ");for(var e=[],i={},u=0;u<r.length;u++){var o=r[u],a=n.$$registeredAnimations[o];a&&!i[o]&&(e.push(t.get(a)),i[o]=!0)}return e}var u=h(e);return function(n,t,e,o){function a(){o.domOperation(),u(n,o)}function c(){h=!0,a(),v(n,o)}function l(n,t,e,i,u){var o;switch(e){case"animate":o=[t,i.from,i.to,u];break;case"setClass":o=[t,y,m,u];break;case"addClass":o=[t,y,u];break;case"removeClass":o=[t,m,u];break;default:o=[t,u]}o.push(i);var a=n.apply(n,o);if(a)if(V(a.start)&&(a=a.start()),a instanceof r)a.done(u);else if(V(a))return a;return q}function f(n,t,e,i,u){var o=[];return F(i,function(i){var a=i[u];a&&o.push(function(){var i,u,o=!1,c=function(n){o||(o=!0,(u||q)(n),i.complete(!n))};return i=new r({end:function(){c()},cancel:function(){c(!0)}}),u=l(a,n,t,e,function(n){var t=n===!1;c(t)}),i})}),o}function s(n,t,e,i,u){var o=f(n,t,e,i,u);if(0===o.length){var a,c;"beforeSetClass"===u?(a=f(n,"removeClass",e,i,"beforeRemoveClass"),c=f(n,"addClass",e,i,"beforeAddClass")):"setClass"===u&&(a=f(n,"removeClass",e,i,"removeClass"),c=f(n,"addClass",e,i,"addClass")),a&&(o=o.concat(a)),c&&(o=o.concat(c))}if(0!==o.length)return function(n){var t=[];return o.length&&F(o,function(n){t.push(n())}),t.length?r.all(t,n):n(),function(n){F(t,function(t){n?t.cancel():t.end()})}}}var h=!1;3===arguments.length&&B(e)&&(o=e,e=null),o=p(o),e||(e=n.attr("class")||"",o.addClass&&(e+=" "+o.addClass),o.removeClass&&(e+=" "+o.removeClass));var g,d,y=o.addClass,m=o.removeClass,_=i(e);if(_.length){var x,M;"leave"==t?(M="leave",x="afterLeave"):(M="before"+t.charAt(0).toUpperCase()+t.substr(1),x=t),"enter"!==t&&"move"!==t&&(g=s(n,t,o,_,M)),d=s(n,t,o,_,x)}if(g||d){var b;return{$$willAnimate:!0,end:function(){return b?b.end():(c(),b=new r,b.complete(!0)),b},start:function(){function n(n){c(n),b.complete(n)}function t(t){h||((e||q)(t),n(t))}if(b)return b;b=new r;var e,i=[];return g&&i.push(function(n){e=g(n)}),i.length?i.push(function(n){a(),n(!0)}):a(),d&&i.push(function(n){e=d(n)}),b.setHost({end:function(){t()},cancel:function(){t(!0)}}),r.chain(i,n),b}}}}}]}],En=["$$animationProvider",function(n){n.drivers.push("$$animateJsDriver"),this.$get=["$$animateJs","$$AnimateRunner",function(n,t){function r(t){var r=t.element,e=t.event,i=t.options,u=t.classes;return n(r,e,u,i)}return function(n){if(n.from&&n.to){var e=r(n.from),i=r(n.to);if(!e&&!i)return;return{start:function(){function n(){return function(){F(u,function(n){n.end()})}}function r(n){o.complete(n)}var u=[];e&&u.push(e.start()),i&&u.push(i.start()),t.all(u,r);var o=new t({end:n(),cancel:n()});return o}}}return r(n)}}]}],Nn="data-ng-animate",$n="$ngAnimatePin",jn=["$animateProvider",function(n){function t(n){if(!n)return null;var t=n.split(f),r=Object.create(null);return F(t,function(n){r[n]=!0}),r}function r(n,r){if(n&&r){var e=t(r);return n.split(f).some(function(n){return e[n]})}}function i(n,t,r,e){return s[n].some(function(n){return n(t,r,e)})}function u(n,t){n=n||{};var r=(n.addClass||"").length>0,e=(n.removeClass||"").length>0;return t?r&&e:r||e}var o=1,a=2,f=" ",s=this.rules={skip:[],cancel:[],join:[]};s.join.push(function(n,t,r){return!t.structural&&u(t.options)}),s.skip.push(function(n,t,r){return!t.structural&&!u(t.options)}),s.skip.push(function(n,t,r){return"leave"==r.event&&t.structural}),s.skip.push(function(n,t,r){return r.structural&&r.state===a&&!t.structural}),s.cancel.push(function(n,t,r){return r.structural&&t.structural}),s.cancel.push(function(n,t,r){return r.state===a&&t.structural}),s.cancel.push(function(n,t,e){var i=t.options.addClass,u=t.options.removeClass,o=e.options.addClass,a=e.options.removeClass;return Y(i)&&Y(u)||Y(o)&&Y(a)?!1:r(i,a)||r(u,o)}),this.$get=["$$rAF","$rootScope","$rootElement","$document","$$HashMap","$$animation","$$AnimateRunner","$templateRequest","$$jqLite","$$forceReflow",function(t,r,f,s,g,d,m,b,w,k){function C(){var n=!1;return function(t){n?t():r.$$postDigest(function(){n=!0,t()})}}function A(n,t){return y(n,t,{})}function S(n,t,r){var e=_(t),i=_(n),u=[],o=q[r];return o&&F(o,function(n){G.call(n.node,e)?u.push(n.callback):"leave"===r&&G.call(n.node,i)&&u.push(n.callback)}),u}function E(n,e,l){function f(r,e,i,u){E(function(){var r=S(b,n,e);r.length&&t(function(){F(r,function(t){t(n,i,u)})})}),r.progress(e,i,u)}function h(t){M(n,w),Q(n,w),v(n,w),w.domOperation(),k.complete(!t)}var g,b,w=I(l);n=c(n),n&&(g=_(n),b=n.parent()),w=p(w);var k=new m,E=C();if(H(w.addClass)&&(w.addClass=w.addClass.join(" ")),w.addClass&&!W(w.addClass)&&(w.addClass=null),H(w.removeClass)&&(w.removeClass=w.removeClass.join(" ")),w.removeClass&&!W(w.removeClass)&&(w.removeClass=null),w.from&&!B(w.from)&&(w.from=null),w.to&&!B(w.to)&&(w.to=null),!g)return h(),k;var j=[g.className,w.addClass,w.removeClass].join(" ");if(!J(j))return h(),k;var z=["enter","move","leave"].indexOf(e)>=0,q=!O||s[0].hidden||R.get(g),P=!q&&L.get(g)||{},U=!!P.state;if(q||U&&P.state==o||(q=!D(n,b,e)),q)return h(),k;z&&N(n);var Y={structural:z,element:n,event:e,close:h,options:w,runner:k};if(U){var Z=i("skip",n,Y,P);if(Z)return P.state===a?(h(),k):(y(n,P.options,w),P.runner);var V=i("cancel",n,Y,P);if(V)if(P.state===a)P.runner.end();else{if(!P.structural)return y(n,P.options,Y.options),P.runner;P.close()}else{var X=i("join",n,Y,P);if(X){if(P.state!==a)return x(n,z?e:null,w),e=Y.event=P.event,w=y(n,P.options,Y.options),P.runner;A(n,w)}}}else A(n,w);var K=Y.structural;if(K||(K="animate"===Y.event&&Object.keys(Y.options.to||{}).length>0||u(Y.options)),!K)return h(),$(n),k;var G=(P.counter||0)+1;return Y.counter=G,T(n,o,Y),r.$$postDigest(function(){var t=L.get(g),r=!t;t=t||{};var i=n.parent()||[],o=i.length>0&&("animate"===t.event||t.structural||u(t.options));if(r||t.counter!==G||!o)return r&&(Q(n,w),v(n,w)),(r||z&&t.event!==e)&&(w.domOperation(),k.end()),void(o||$(n));e=!t.structural&&u(t.options,!0)?"setClass":t.event,T(n,a);var c=d(n,e,t.options);c.done(function(t){h(!t);var r=L.get(g);r&&r.counter===G&&$(_(n)),f(k,e,"close",{})}),k.setHost(c),f(k,e,"start",{})}),k}function N(n){var t=_(n),r=t.querySelectorAll("["+Nn+"]");F(r,function(n){var t=parseInt(n.getAttribute(Nn)),r=L.get(n);if(r)switch(t){case a:r.runner.end();case o:L.remove(n)}})}function $(n){var t=_(n);t.removeAttribute(Nn),L.remove(t)}function j(n,t){return _(n)===_(t)}function D(n,t,r){var e,i=U(s[0].body),u=j(n,i)||"HTML"===n[0].nodeName,o=j(n,f),a=!1,c=R.get(_(n)),l=n.data($n);for(l&&(t=l);t&&t.length;){o||(o=j(t,f));var h=t[0];if(h.nodeType!==K)break;var p=L.get(h)||{};if(!a){var v=R.get(h);if(v===!0&&c!==!1){c=!0;break}v===!1&&(c=!1),a=p.structural}if(Y(e)||e===!0){var g=t.data(rn);Z(g)&&(e=g)}if(a&&e===!1)break;if(u||(u=j(t,i)),u&&o)break;t=o||!(l=t.data($n))?t.parent():l}var d=(!a||e)&&c!==!0;return d&&o&&u}function T(n,t,r){r=r||{},r.state=t;var e=_(n);e.setAttribute(Nn,t);var i=L.get(e),u=i?P(i,r):r;L.put(e,u)}var L=new g,R=new g,O=null,z=r.$watch(function(){return 0===b.totalPendingRequests},function(n){n&&(z(),r.$$postDigest(function(){r.$$postDigest(function(){null===O&&(O=!0)})}))}),q={},V=n.classNameFilter(),J=V?function(n){return V.test(n)}:function(){return!0},Q=h(w),G=Node.prototype.contains||function(n){return this===n||!!(16&this.compareDocumentPosition(n))};return{on:function(n,t,r){var e=l(t);q[n]=q[n]||[],q[n].push({node:e,callback:r})},off:function(n,t,r){function e(n,t,r){var e=l(t);return n.filter(function(n){var t=n.node===e&&(!r||n.callback===r);return!t})}var i=q[n];i&&(q[n]=1===arguments.length?null:e(i,t,r))},pin:function(n,t){e(X(n),"element","not an element"),e(X(t),"parentElement","not an element"),n.data($n,t)},push:function(n,t,r,e){return r=r||{},r.domOperation=e,E(n,t,r)},enabled:function(n,t){var r=arguments.length;if(0===r)t=!!O;else{var e=X(n);if(e){var i=_(n),u=R.get(i);1===r?t=!u:R.put(i,!t)}else t=O=!!n}return t}}}]}],Dn=["$animateProvider",function(n){function t(n,t){n.data(a,t)}function r(n){n.removeData(a)}function e(n){return n.data(a)}var u="ng-animate-ref",o=this.drivers=[],a="$$animationRunner";this.$get=["$$jqLite","$rootScope","$injector","$$AnimateRunner","$$HashMap","$$rAFScheduler",function(n,a,c,l,f,s){function g(n){function t(n){if(n.processed)return n;n.processed=!0;var r=n.domNode,e=r.parentNode;u.put(r,n);for(var o;e;){if(o=u.get(e)){o.processed||(o=t(o));break}e=e.parentNode}return(o||i).children.push(n),n}function r(n){var t,r=[],e=[];for(t=0;t<n.children.length;t++)e.push(n.children[t]);var i=e.length,u=0,o=[];for(t=0;t<e.length;t++){var a=e[t];0>=i&&(i=u,u=0,r.push(o),o=[]),o.push(a.fn),a.children.forEach(function(n){u++,e.push(n)}),i--}return o.length&&r.push(o),r}var e,i={children:[]},u=new f;for(e=0;e<n.length;e++){var o=n[e];u.put(o.domNode,n[e]={domNode:o.domNode,fn:o.fn,children:[]})}for(e=0;e<n.length;e++)t(n[e]);return r(i)}var d=[],y=h(n);return function(f,h,m){function x(n){var t="["+u+"]",r=n.hasAttribute(u)?[n]:n.querySelectorAll(t),e=[];return F(r,function(n){var t=n.getAttribute(u);t&&t.length&&e.push(n)}),e}function M(n){var t=[],r={};F(n,function(n,e){var i=n.element,o=_(i),a=n.event,c=["enter","move"].indexOf(a)>=0,l=n.structural?x(o):[];if(l.length){var f=c?"to":"from";F(l,function(n){var t=n.getAttribute(u);r[t]=r[t]||{},r[t][f]={animationID:e,element:U(n)}})}else t.push(n)});var e={},i={};return F(r,function(r,u){var o=r.from,a=r.to;if(!o||!a){var c=o?o.animationID:a.animationID,l=c.toString();return void(e[l]||(e[l]=!0,t.push(n[c])))}var f=n[o.animationID],s=n[a.animationID],h=o.animationID.toString();if(!i[h]){var p=i[h]={structural:!0,beforeStart:function(){f.beforeStart(),s.beforeStart()},close:function(){f.close(),s.close()},classes:b(f.classes,s.classes),from:f,to:s,anchors:[]};p.classes.length?t.push(p):(t.push(f),t.push(s))}i[h].anchors.push({out:o.element,"in":a.element})}),t}function b(n,t){n=n.split(" "),t=t.split(" ");for(var r=[],e=0;e<n.length;e++){var i=n[e];if("ng-"!==i.substring(0,3))for(var u=0;u<t.length;u++)if(i===t[u]){r.push(i);break}}return r.join(" ")}function w(n){for(var t=o.length-1;t>=0;t--){var r=o[t];if(c.has(r)){var e=c.get(r),i=e(n);if(i)return i}}}function k(){f.addClass(tn),j&&n.addClass(f,j)}function C(n,t){function r(n){e(n).setHost(t)}n.from&&n.to?(r(n.from.element),r(n.to.element)):r(n.element)}function A(){var n=e(f);!n||"leave"===h&&m.$$domOperationFired||n.end()}function S(t){f.off("$destroy",A),r(f),y(f,m),v(f,m),m.domOperation(),j&&n.removeClass(f,j),f.removeClass(tn),N.complete(!t)}m=p(m);var E=["enter","move","leave"].indexOf(h)>=0,N=new l({end:function(){S()},cancel:function(){S(!0)}});if(!o.length)return S(),N;t(f,N);var $=i(f.attr("class"),i(m.addClass,m.removeClass)),j=m.tempClasses;return j&&($+=" "+j,m.tempClasses=null),d.push({element:f,classes:$,event:h,structural:E,options:m,beforeStart:k,close:S}),f.on("$destroy",A),d.length>1?N:(a.$$postDigest(function(){var n=[];F(d,function(t){e(t.element)?n.push(t):t.close()}),d.length=0;var t=M(n),r=[];F(t,function(n){r.push({domNode:_(n.from?n.from.element:n.element),fn:function(){n.beforeStart();var t,r=n.close,i=n.anchors?n.from.element||n.to.element:n.element;if(e(i)){var u=w(n);u&&(t=u.start)}if(t){var o=t();o.done(function(n){r(!n)}),C(n,o)}else r()}})}),s(g(r))}),N)}}]}];t.module("ngAnimate",[]).directive("ngAnimateChildren",mn).factory("$$rAFScheduler",yn).provider("$$animateQueue",jn).provider("$$animation",Dn).provider("$animateCss",Cn).provider("$$animateCssDriver",An).provider("$$animateJs",Sn).provider("$$animateJsDriver",En)}(window,window.angular);
\ No newline at end of file
diff --git a/xos/core/xoslib/static/js/xosDiagnostic.js b/xos/core/xoslib/static/js/xosDiagnostic.js
new file mode 100644
index 0000000..d515105
--- /dev/null
+++ b/xos/core/xoslib/static/js/xosDiagnostic.js
@@ -0,0 +1,1847 @@
+//Autogenerated, do not edit!!!
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic', ['ngResource', 'ngCookies', 'ngLodash', 'ngAnimate', 'ui.router', 'xos.helpers']).config(["$stateProvider", function ($stateProvider) {
+ $stateProvider.state('home', {
+ url: '/',
+ template: '<diagnostic-container></diagnostic-container>'
+ });
+ }]).config(["$httpProvider", function ($httpProvider) {
+ $httpProvider.interceptors.push('NoHyperlinks');
+ }]).run(["$log", function ($log) {
+ $log.info('Diagnostic Started');
+ }]);
+})();
+angular.module("xos.diagnostic").run(["$templateCache", function($templateCache) {$templateCache.put("templates/diagnostic.tpl.html","<div class=\"container-fluid\">\n <div ng-hide=\"vm.error && vm.loader\" style=\"height: 900px\">\n <div class=\"onethird-height\">\n <service-topology service-chain=\"vm.serviceChain\"></service-topology>\n </div>\n <div class=\"twothird-height\">\n <logic-topology ng-if=\"vm.subscribers\" subscribers=\"vm.subscribers\" selected=\"vm.selectedSubscriber\"></logic-topology>\n </div>\n </div>\n <div class=\"row\" ng-if=\"vm.error\">\n <div class=\"col-xs-12\">\n <div class=\"alert alert-danger\">\n {{vm.error}}\n </div>\n </div>\n </div>\n <div class=\"row\" ng-if=\"vm.loader\">\n <div class=\"col-xs-12\">\n <div class=\"loader\">Loading</div>\n </div>\n </div>\n</div>");
+$templateCache.put("templates/logicTopology.tpl.html","<select-subscriber-modal open=\"vm.openSelectSubscriberModal\" subscribers=\"vm.subscribers\"></select-subscriber-modal>\n<subscriber-status-modal open=\"vm.openSubscriberStatusModal\" subscriber=\"vm.currentSubscriber\"></subscriber-status-modal>\n<div class=\"instances-stats animate\" ng-hide=\"vm.hideInstanceStats\">\n <div class=\"row\">\n <div class=\"col-sm-3 col-sm-offset-8\">\n <div class=\"panel panel-primary\" ng-repeat=\"instance in vm.selectedInstances\">\n <div class=\"panel-heading\">\n {{instance.humanReadableName}}\n </div>\n <ul class=\"list-group\">\n <li class=\"list-group-item\">Backend Status: {{instance.backend_status}}</li>\n <li class=\"list-group-item\">IP Address: {{instance.ip}}</li>\n </ul>\n <ul class=\"list-group\">\n <li class=\"list-group-item\" ng-repeat=\"stat in instance.stats\">\n <span class=\"badge\">{{stat.value}}</span>\n {{stat.meter}}\n </li>\n </ul>\n </div>\n </div> \n </div>\n </div>\n</div>");
+$templateCache.put("templates/select-subscriber-modal.tpl.html","<div class=\"modal fade\" ng-class=\"{in: vm.open}\" tabindex=\"-1\" role=\"dialog\">\n <div class=\"modal-dialog modal-sm\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span></button>\n <h4 class=\"modal-title\">Select a subscriber:</h4>\n </div>\n <div class=\"modal-body\">\n <select class=\"form-control\" ng-options=\"s as s.humanReadableName for s in vm.subscribers\" ng-model=\"vm.selected\"></select>\n </div>\n <div class=\"modal-footer\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>\n <button ng-click=\"vm.select(vm.selected)\" type=\"button\" class=\"btn btn-primary\">Select</button>\n </div>\n </div><!-- /.modal-content -->\n </div><!-- /.modal-dialog -->\n</div><!-- /.modal -->");
+$templateCache.put("templates/subscriber-status-modal.tpl.html","<div class=\"modal fade\" ng-class=\"{in: vm.open}\" tabindex=\"-1\" role=\"dialog\">\n <div class=\"modal-dialog modal-sm\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">×</span></button>\n <h4 class=\"modal-title\">Manage subscriber:</h4>\n </div>\n <form name=\"vm.subscriber-detail\">\n <div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-xs-12\">\n <label>Status</label>\n </div>\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'enabled\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'enabled\' ,\'btn-default\': vm.subscriber.status !== \'enabled\'}\"\n >Enabled</a>\n </div>\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'suspended\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'suspended\' ,\'btn-default\': vm.subscriber.status !== \'suspended\'}\"\n >Suspended</a>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'delinquent\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'delinquent\' ,\'btn-default\': vm.subscriber.status !== \'delinquent\'}\"\n >Delinquent <br> payment</a>\n </div>\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'copyrightviolation\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'copyrightviolation\' ,\'btn-default\': vm.subscriber.status !== \'copyrightviolation\'}\"\n >Copyright <br> violation</a>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <label>Uplink Speed</label>\n <input type=\"number\" class=\"form-control\" ng-model=\"vm.subscriber.uplink_speed\"/>\n </div>\n <div class=\"col-xs-6\">\n <label>Downlink Speed</label>\n <input type=\"number\" class=\"form-control\" ng-model=\"vm.subscriber.downlink_speed\"/>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <label>Enable Uverse</label>\n </div>\n <div class=\"col-xs-6\">\n <a \n ng-click=\"vm.subscriber.enable_uverse = !vm.subscriber.enable_uverse\" \n ng-class=\"{\'btn-success\': vm.subscriber.enable_uverse, \'btn-danger\': !vm.subscriber.enable_uverse}\"\n class=\"btn btn-block\">\n <span ng-show=\"vm.subscriber.enable_uverse === true\">Enabled</span>\n <span ng-show=\"vm.subscriber.enable_uverse !== true\">Disabled</span>\n </a>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\" ng-show=\"vm.success || vm.formError\">\n <div class=\"alert alert-success\" ng-show=\"vm.success\">\n {{vm.success}}\n </div>\n <div class=\"alert alert-danger\" ng-show=\"vm.formError\">\n {{vm.formError}}\n </div>\n </div>\n <div class=\"modal-footer\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>\n <button ng-click=\"vm.updateSubscriber(vm.subscriber)\" type=\"button\" class=\"btn btn-primary\">Save</button>\n </div>\n </form>\n </div><!-- /.modal-content -->\n </div><!-- /.modal-dialog -->\n</div><!-- /.modal -->");}]);
+'use strict';
+
+(function () {
+ 'use strict';
+ angular.module('xos.diagnostic').directive('selectSubscriberModal', function () {
+ return {
+ scope: {
+ subscribers: '=',
+ open: '='
+ },
+ bindToController: true,
+ restrict: 'E',
+ templateUrl: 'templates/select-subscriber-modal.tpl.html',
+ controllerAs: 'vm',
+ controller: ["$rootScope", function controller($rootScope) {
+ var _this = this;
+
+ this.close = function () {
+ _this.open = false;
+ };
+
+ this.select = function (subscriber) {
+ $rootScope.$emit('subscriber.selected', subscriber);
+ _this.close();
+ };
+ }]
+ };
+ }).directive('subscriberStatusModal', function () {
+ return {
+ scope: {
+ open: '=',
+ subscriber: '='
+ },
+ bindToController: true,
+ restrict: 'E',
+ templateUrl: 'templates/subscriber-status-modal.tpl.html',
+ controllerAs: 'vm',
+ controller: ["$log", "$timeout", "$scope", "Subscribers", function controller($log, $timeout, $scope, Subscribers) {
+ var _this2 = this;
+
+ $scope.$watch(function () {
+ return _this2.open;
+ }, function () {
+ _this2.success = null;
+ _this2.formError = null;
+ });
+
+ this.close = function () {
+ _this2.open = false;
+ };
+
+ this.updateSubscriber = function (subscriber) {
+
+ Subscribers.update(subscriber).$promise.then(function () {
+ _this2.success = 'Subscriber successfully updated!';
+ })['catch'](function (e) {
+ _this2.formError = e;
+ })['finally'](function () {
+ $timeout(function () {
+ _this2.close();
+ }, 1500);
+ });
+ };
+ }]
+ };
+ });
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic').service('ServiceTopologyHelper', ["$rootScope", "$window", "$log", "lodash", "ServiceRelation", "serviceTopologyConfig", "d3", function ($rootScope, $window, $log, lodash, ServiceRelation, serviceTopologyConfig, d3) {
+
+ var _svg, _layout, _source, _el;
+
+ var i = 0;
+
+ // given a canvas, a layout and a data source, draw a tree layout
+ var updateTree = function updateTree(svg, layout, source) {
+ var el = arguments.length <= 3 || arguments[3] === undefined ? _el : arguments[3];
+
+ if (el) {
+ _el = el;
+ }
+
+ //cache data
+ _svg = svg;
+ _layout = layout;
+ _source = source;
+
+ var maxDepth = ServiceRelation.depthOf(source);
+
+ var diagonal = d3.svg.diagonal().projection(function (d) {
+ return [d.y, d.x];
+ });
+
+ // Compute the new tree layout.
+ var nodes = layout.nodes(source).reverse(),
+ links = layout.links(nodes);
+
+ // Normalize for fixed-depth.
+ nodes.forEach(function (d) {
+ // position the child node horizontally
+ var step = (_el.clientWidth - serviceTopologyConfig.widthMargin * 2) / maxDepth;
+ d.y = d.depth * step;
+ });
+
+ // Update the nodes…
+ var node = svg.selectAll('g.node').data(nodes, function (d) {
+ return d.id || (d.id = ++i);
+ });
+
+ // Enter any new nodes at the parent's previous position.
+ var nodeEnter = node.enter().append('g').attr({
+ 'class': function _class(d) {
+ return 'node ' + d.type;
+ },
+ transform: function transform(d) {
+ return d.x && d.y ? 'translate(' + d.y + ', ' + d.x + ')' : 'translate(' + source.y0 + ', ' + source.x0 + ')';
+ }
+ });
+
+ var subscriberNodes = nodeEnter.filter('.subscriber');
+ var internetNodes = nodeEnter.filter('.router');
+ var serviceNodes = nodeEnter.filter('.service');
+
+ subscriberNodes.append('rect').attr(serviceTopologyConfig.square)
+ // add event listener to subscriber
+ .on('click', function () {
+ $rootScope.$emit('subscriber.modal.open');
+ });
+
+ internetNodes.append('rect').attr(serviceTopologyConfig.square);
+
+ serviceNodes.append('circle').attr('r', 1e-6).style('fill', function (d) {
+ return d._children ? 'lightsteelblue' : '#fff';
+ }).on('click', serviceClick);
+
+ nodeEnter.append('text').attr({
+ x: function x(d) {
+ return d.children ? -serviceTopologyConfig.circle.selectedRadius - 3 : serviceTopologyConfig.circle.selectedRadius + 3;
+ },
+ dy: '.35em',
+ transform: function transform(d) {
+ if (d.children && d.parent) {
+ if (d.parent.x < d.x) {
+ return 'rotate(-30)';
+ }
+ return 'rotate(30)';
+ }
+ },
+ 'text-anchor': function textAnchor(d) {
+ return d.children ? 'end' : 'start';
+ }
+ }).text(function (d) {
+ return d.name;
+ }).style('fill-opacity', 1e-6);
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node.transition().duration(serviceTopologyConfig.duration).attr({
+ 'transform': function transform(d) {
+ return 'translate(' + d.y + ',' + d.x + ')';
+ }
+ });
+
+ nodeUpdate.select('circle').attr('r', function (d) {
+ return d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius;
+ }).style('fill', function (d) {
+ return d.selected ? 'lightsteelblue' : '#fff';
+ });
+
+ nodeUpdate.select('text').style('fill-opacity', 1);
+
+ // Transition exiting nodes to the parent's new position.
+ var nodeExit = node.exit().transition().duration(serviceTopologyConfig.duration).remove();
+
+ nodeExit.select('circle').attr('r', 1e-6);
+
+ nodeExit.select('text').style('fill-opacity', 1e-6);
+
+ // Update the links…
+ var link = svg.selectAll('path.link').data(links, function (d) {
+ return d.target.id;
+ });
+
+ // Enter any new links at the parent's previous position.
+ link.enter().insert('path', 'g').attr('class', function (d) {
+ return 'link ' + d.target.type + ' ' + (d.target.active ? '' : 'active');
+ }).attr('d', function (d) {
+ var o = { x: source.x0, y: source.y0 };
+ return diagonal({ source: o, target: o });
+ });
+
+ // Transition links to their new position.
+ link.transition().duration(serviceTopologyConfig.duration).attr('d', diagonal);
+
+ // Transition exiting nodes to the parent's new position.
+ link.exit().transition().duration(serviceTopologyConfig.duration).attr('d', function (d) {
+ var o = { x: source.x, y: source.y };
+ return diagonal({ source: o, target: o });
+ }).remove();
+
+ // Stash the old positions for transition.
+ nodes.forEach(function (d) {
+ d.x0 = d.x;
+ d.y0 = d.y;
+ });
+ };
+
+ var serviceClick = function serviceClick(d) {
+
+ // if was selected
+ if (d.selected) {
+ d.selected = !d.selected;
+ $rootScope.$emit('instance.detail.hide', {});
+ return updateTree(_svg, _layout, _source);
+ }
+
+ $rootScope.$emit('instance.detail', { name: d.name, service: d.service, tenant: d.tenant });
+
+ // unselect all
+ _svg.selectAll('circle').each(function (d) {
+ return d.selected = false;
+ });
+
+ // toggling selected status
+ d.selected = !d.selected;
+
+ updateTree(_svg, _layout, _source);
+ };
+
+ this.updateTree = updateTree;
+ }]);
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic').directive('serviceTopology', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ serviceChain: '='
+ },
+ bindToController: true,
+ controllerAs: 'vm',
+ template: '',
+ controller: ["$element", "$window", "$scope", "d3", "serviceTopologyConfig", "ServiceRelation", "Slice", "Instances", "Subscribers", "ServiceTopologyHelper", function controller($element, $window, $scope, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers, ServiceTopologyHelper) {
+ var _this = this;
+
+ var el = $element[0];
+
+ d3.select(window).on('resize', function () {
+ draw(_this.serviceChain);
+ });
+
+ var root, svg;
+
+ var draw = function draw(tree) {
+
+ if (!tree) {
+ console.error('Tree is missing');
+ return;
+ }
+
+ // TODO update instead clear and redraw
+
+ // clean
+ d3.select($element[0]).select('svg').remove();
+
+ var width = el.clientWidth - serviceTopologyConfig.widthMargin * 2;
+ var height = el.clientHeight - serviceTopologyConfig.heightMargin * 2;
+
+ var treeLayout = d3.layout.tree().size([height, width]);
+
+ svg = d3.select($element[0]).append('svg').style('width', el.clientWidth + 'px').style('height', el.clientHeight + 'px');
+
+ var treeContainer = svg.append('g').attr('transform', 'translate(' + serviceTopologyConfig.widthMargin * 4 + ',' + serviceTopologyConfig.heightMargin + ')');
+
+ root = tree;
+ root.x0 = height / 2;
+ root.y0 = width / 2;
+
+ // ServiceTopologyHelper.drawLegend(svg);
+ ServiceTopologyHelper.updateTree(treeContainer, treeLayout, root, el);
+ };
+
+ $scope.$watch(function () {
+ return _this.serviceChain;
+ }, function (chain) {
+ if (angular.isDefined(chain)) {
+ draw(chain);
+ }
+ });
+ }]
+ };
+ });
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic').service('Services', ["$resource", function ($resource) {
+ return $resource('/xos/services/:id', { id: '@id' });
+ }]).service('Tenant', ["$resource", function ($resource) {
+ return $resource('/xos/tenants', { id: '@id' }, {
+ queryVsgInstances: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: function response(res) {
+
+ // NOTE
+ // Note that VCPETenant is now VSGTenant.
+
+ var instances = [];
+
+ angular.forEach(res.data, function (tenant) {
+ var info = JSON.parse(tenant.service_specific_attribute);
+ if (info && info.instance_id) {
+ instances.push(info.instance_id);
+ }
+ });
+
+ return instances;
+ }
+ }
+ },
+ getSubscriberTag: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: function response(res) {
+ // NOTE we should receive only one vOLT tenant here
+ return JSON.parse(res.data[0].service_specific_attribute);
+ }
+ }
+ }
+ });
+ }]).service('Ceilometer', ["$http", "$q", "Instances", function ($http, $q, Instances) {
+ var _this = this;
+
+ /**
+ * Get stats for a single instance
+ */
+ this.getInstanceStats = function (instanceUuid) {
+ var deferred = $q.defer();
+
+ $http.get('/xoslib/xos-instance-statistics', { params: { 'instance-uuid': instanceUuid } }).then(function (res) {
+ deferred.resolve(res.data);
+ })['catch'](function (e) {
+ deferred.reject(e);
+ });
+
+ return deferred.promise;
+ };
+
+ /**
+ * Collect stats for an array of instances
+ */
+ this.getInstancesStats = function (instances) {
+ var deferred = $q.defer();
+ var instancePromises = [];
+ var instanceList = [];
+
+ // retrieve instance details
+ instances.forEach(function (instanceId) {
+ instancePromises.push(Instances.get({ id: instanceId }).$promise);
+ });
+
+ // get all instance data
+ $q.all(instancePromises).then(function (_instanceList) {
+ instanceList = _instanceList;
+ var promises = [];
+ // foreach instance query stats
+ instanceList.forEach(function (instance) {
+ promises.push(_this.getInstanceStats(instance.instance_uuid));
+ });
+ return $q.all(promises);
+ }).then(function (stats) {
+ // augment instance with stats information
+ instanceList.map(function (instance, i) {
+ instance.stats = stats[i];
+ });
+ deferred.resolve(instanceList);
+ })['catch'](deferred.reject);
+
+ return deferred.promise;
+ };
+
+ this.getContainerStats = function (containerName) {
+ var deferred = $q.defer();
+
+ var res = {};
+
+ $http.get('/xoslib/meterstatistics', { params: { 'resource': containerName } }).then(function (containerStats) {
+ res.stats = containerStats.data;
+ return $http.get('/xoslib/meterstatistics', { params: { 'resource': containerName + '-eth0' } });
+ }).then(function (portStats) {
+ res.port = {
+ eth0: portStats.data
+ };
+ return $http.get('/xoslib/meterstatistics', { params: { 'resource': containerName + '-eth1' } });
+ }).then(function (portStats) {
+ res.port.eth1 = portStats.data;
+ deferred.resolve(res);
+ })['catch'](function (e) {
+ deferred.reject(e);
+ });
+
+ return deferred.promise;
+ };
+ }]).service('Slice', ["$resource", function ($resource) {
+ return $resource('/xos/slices', { id: '@id' });
+ }]).service('Instances', ["$resource", function ($resource) {
+ return $resource('/xos/instances/:id', { id: '@id' });
+ }]).service('Node', ["$resource", "$q", "Instances", function ($resource, $q, Instances) {
+ return $resource('/xos/nodes', { id: '@id' }, {
+ queryWithInstances: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: function response(res) {
+
+ // TODO update the API to include instances in nodes
+ // http://stackoverflow.com/questions/14573102/how-do-i-include-related-model-fields-using-django-rest-framework
+
+ var deferred = $q.defer();
+
+ var requests = [];
+
+ angular.forEach(res.data, function (node) {
+ requests.push(Instances.query({ node: node.id }).$promise);
+ });
+
+ $q.all(requests).then(function (list) {
+ res.data.map(function (node, i) {
+ node.instances = list[i];
+ return node;
+ });
+ deferred.resolve(res.data);
+ });
+
+ return deferred.promise;
+ }
+ }
+ }
+ });
+ }]).service('Subscribers', ["$resource", "$q", "SubscriberDevice", function ($resource, $q, SubscriberDevice) {
+ return $resource('/xoslib/cordsubscriber/:id', { id: '@id' }, {
+ update: {
+ method: 'PUT',
+ isArray: false
+ },
+ queryWithDevices: {
+ method: 'GET',
+ isArray: true,
+ interceptor: {
+ response: function response(res) {
+
+ /**
+ * For each subscriber retrieve devices and append them
+ */
+
+ var deferred = $q.defer();
+
+ var requests = [];
+
+ angular.forEach(res.data, function (subscriber) {
+ requests.push(SubscriberDevice.query({ id: subscriber.id }).$promise);
+ });
+
+ $q.all(requests).then(function (list) {
+
+ // adding devices
+
+ res.data.map(function (subscriber, i) {
+ subscriber.devices = list[i];
+ subscriber.type = 'subscriber';
+
+ subscriber.devices.map(function (d) {
+ return d.type = 'device';
+ });
+
+ return subscriber;
+ });
+
+ // faking to have 2 subscriber
+ // res.data.push(angular.copy(res.data[0]));
+
+ deferred.resolve(res.data);
+ });
+
+ return deferred.promise;
+ }
+ }
+ },
+ getWithDevices: {
+ method: 'GET',
+ isArray: false,
+ interceptor: {
+ response: function response(res) {
+ var d = $q.defer();
+
+ SubscriberDevice.query({ id: res.data.id }).$promise.then(function (devices) {
+ devices.map(function (d) {
+ return d.type = 'device';
+ });
+ res.data.devices = devices;
+ res.data.type = 'subscriber';
+ d.resolve(res.data);
+ })['catch'](function (err) {
+ d.reject(err);
+ });
+
+ return d.promise;
+ }
+ }
+ }
+ });
+ }]).service('SubscriberDevice', ["$resource", function ($resource) {
+ return $resource('/xoslib/rs/subscriber/:id/users/', { id: '@id' });
+ }]).service('ServiceRelation', ["$q", "lodash", "Services", "Tenant", "Slice", "Instances", function ($q, lodash, Services, Tenant, Slice, Instances) {
+
+ // count the mas depth of an object
+ var depthOf = function depthOf(obj) {
+ var depth = 0;
+ if (obj.children) {
+ obj.children.forEach(function (d) {
+ var tmpDepth = depthOf(d);
+ if (tmpDepth > depth) {
+ depth = tmpDepth;
+ }
+ });
+ }
+ return 1 + depth;
+ };
+
+ // find all the relation defined for a given root
+ var findLevelRelation = function findLevelRelation(tenants, rootId) {
+ return lodash.filter(tenants, function (service) {
+ return service.subscriber_service === rootId;
+ });
+ };
+
+ var findSpecificInformation = function findSpecificInformation(tenants, rootId) {
+ var tenants = lodash.filter(tenants, function (service) {
+ return service.provider_service === rootId && service.subscriber_tenant;
+ });
+
+ var info;
+
+ tenants.forEach(function (tenant) {
+ if (tenant.service_specific_attribute) {
+ info = JSON.parse(tenant.service_specific_attribute);
+ }
+ });
+
+ return info;
+ };
+
+ // find all the service defined by a given array of relations
+ var findLevelServices = function findLevelServices(relations, services) {
+ var levelServices = [];
+ lodash.forEach(relations, function (tenant) {
+ var service = lodash.find(services, { id: tenant.provider_service });
+ levelServices.push(service);
+ });
+ return levelServices;
+ };
+
+ var buildLevel = function buildLevel(tenants, services, rootService, rootTenant) {
+ var parentName = arguments.length <= 4 || arguments[4] === undefined ? null : arguments[4];
+
+ // build an array of unlinked services
+ // these are the services that should still placed in the tree
+ var unlinkedServices = lodash.difference(services, [rootService]);
+
+ // find all relations relative to this rootElement
+ var levelRelation = findLevelRelation(tenants, rootService.id);
+ // find all items related to rootElement
+ var levelServices = findLevelServices(levelRelation, services);
+
+ // remove this item from the list (performance
+ unlinkedServices = lodash.difference(unlinkedServices, levelServices);
+
+ rootService.service_specific_attribute = findSpecificInformation(tenants, rootService.id);
+
+ var tree = {
+ name: rootService.humanReadableName,
+ parent: parentName,
+ type: 'service',
+ service: rootService,
+ tenant: rootTenant,
+ children: []
+ };
+
+ lodash.forEach(levelServices, function (service) {
+ var tenant = lodash.find(tenants, { subscriber_tenant: rootTenant.id, provider_service: service.id });
+ tree.children.push(buildLevel(tenants, unlinkedServices, service, tenant, rootService.humanReadableName));
+ });
+
+ // if it is the last element append internet
+ if (tree.children.length === 0) {
+ tree.children.push({
+ name: 'Router',
+ type: 'router',
+ children: []
+ });
+ }
+
+ return tree;
+ };
+
+ var buildSubscriberServiceTree = function buildSubscriberServiceTree(services, tenants) {
+ var subscriber = arguments.length <= 2 || arguments[2] === undefined ? { id: 1, name: 'fakeSubs' } : arguments[2];
+
+ // find the root service
+ // it is the one attached to subsriber_root
+ // as now we have only one root so this can work
+ var rootTenant = lodash.find(tenants, { subscriber_root: subscriber.id });
+ var rootService = lodash.find(services, { id: rootTenant.provider_service });
+
+ var serviceTree = buildLevel(tenants, services, rootService, rootTenant);
+
+ return {
+ name: subscriber.name || subscriber.humanReadableName,
+ parent: null,
+ type: 'subscriber',
+ children: [serviceTree]
+ };
+ };
+
+ // applying domain knowledge to build the global service tree
+ var buildServiceTree = function buildServiceTree(services, tenants) {
+
+ // TODO refactor
+ var buildChild = function buildChild(services, tenants, currentService) {
+
+ var response = {
+ type: 'service',
+ name: currentService.humanReadableName,
+ service: currentService
+ };
+
+ var tenant = lodash.find(tenants, { subscriber_service: currentService.id });
+ if (tenant) {
+ var next = lodash.find(services, { id: tenant.provider_service });
+ response.children = [buildChild(services, tenants, next)];
+ } else {
+ response.children = [{
+ name: 'Router',
+ type: 'router',
+ children: []
+ }];
+ }
+ delete currentService.id; // conflict with d3
+ return response;
+ };
+
+ var baseService = lodash.find(services, { id: 3 });
+
+ if (!angular.isDefined(baseService)) {
+ console.error('Missing Base service!');
+ return;
+ }
+
+ var baseData = {
+ name: 'Subscriber',
+ type: 'subscriber',
+ parent: null,
+ children: [buildChild(services, tenants, baseService)]
+ };
+ return baseData;
+ };
+
+ var getBySubscriber = function getBySubscriber(subscriber) {
+ var deferred = $q.defer();
+ var services, tenants;
+ Services.query().$promise.then(function (res) {
+ services = res;
+ return Tenant.query().$promise;
+ }).then(function (res) {
+ tenants = res;
+ deferred.resolve(buildSubscriberServiceTree(services, tenants, subscriber));
+ })['catch'](function (e) {
+ throw new Error(e);
+ });
+
+ return deferred.promise;
+ };
+
+ var get = function get() {
+ var deferred = $q.defer();
+ var services, tenants;
+ Services.query().$promise.then(function (res) {
+ services = res;
+ return Tenant.query({ kind: 'coarse' }).$promise;
+ }).then(function (res) {
+ tenants = res;
+ deferred.resolve(buildServiceTree(services, tenants));
+ })['catch'](function (e) {
+ throw new Error(e);
+ });
+
+ return deferred.promise;
+ };
+
+ // export APIs
+ return {
+ get: get,
+ buildServiceTree: buildServiceTree,
+ getBySubscriber: getBySubscriber,
+ buildLevel: buildLevel,
+ buildSubscriberServiceTree: buildSubscriberServiceTree,
+ findLevelRelation: findLevelRelation,
+ findLevelServices: findLevelServices,
+ depthOf: depthOf,
+ findSpecificInformation: findSpecificInformation
+ };
+ }]);
+})();
+'use strict';
+
+var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
+
+(function () {
+ angular.module('xos.diagnostic').service('RackHelper', ["serviceTopologyConfig", "lodash", function (serviceTopologyConfig, lodash) {
+ var _this = this;
+
+ this.getComputeNodeLabelSize = function () {
+ return serviceTopologyConfig.computeNode.labelHeight + serviceTopologyConfig.instance.margin * 2;
+ };
+
+ /**
+ * Given a list of instance should get the Compute Node size.
+ * They are placed in rows of 2 with 5px margin on each side.
+ */
+
+ this.getComputeNodeSize = lodash.memoize(function (instances) {
+ var width = serviceTopologyConfig.instance.margin * 3 + serviceTopologyConfig.instance.width * 2;
+
+ var rows = Math.round(instances.length / 2);
+
+ var labelSpace = _this.getComputeNodeLabelSize();
+
+ var height = serviceTopologyConfig.instance.height * rows + serviceTopologyConfig.instance.margin * (rows + 1) + labelSpace;
+
+ return [width, height];
+ });
+
+ /**
+ * Give a list on Compute Node should calculate the Rack Size.
+ * Compute nodes are placed in a single column with 5px margin on each side.
+ */
+ this.getRackSize = function (nodes) {
+
+ var width = 0;
+ var height = serviceTopologyConfig.computeNode.margin;
+
+ lodash.forEach(nodes, function (node) {
+ var _getComputeNodeSize = _this.getComputeNodeSize(node.instances);
+
+ var _getComputeNodeSize2 = _slicedToArray(_getComputeNodeSize, 2);
+
+ var nodeWidth = _getComputeNodeSize2[0];
+ var nodeHeight = _getComputeNodeSize2[1];
+
+ width = nodeWidth + serviceTopologyConfig.computeNode.margin * 2;
+ height += nodeHeight + serviceTopologyConfig.computeNode.margin;
+ });
+
+ return [width, height];
+ };
+
+ /**
+ * Given an instance index, return the coordinates
+ */
+
+ this.getInstancePosition = function (position) {
+ var row = Math.floor(position / 2);
+ var column = position % 2 ? 1 : 0;
+
+ // add ComputeNode label size
+ var labelSpace = _this.getComputeNodeLabelSize();
+
+ // x = margin + (width * column) + ( maring * column)
+ var x = serviceTopologyConfig.instance.margin + serviceTopologyConfig.instance.width * column + serviceTopologyConfig.instance.margin * column;
+
+ // y = label + margin + (height * row) + ( maring * row)
+ var y = labelSpace + serviceTopologyConfig.instance.margin + serviceTopologyConfig.instance.height * row + serviceTopologyConfig.instance.margin * row;
+ return [x, y];
+ };
+
+ /**
+ * Given an Compute Node index, return the coordinates
+ */
+
+ this.getComputeNodePosition = function (nodes, position) {
+
+ var x = serviceTopologyConfig.computeNode.margin;
+
+ var previousElEight = lodash.reduce(nodes.slice(0, position), function (val, node) {
+ return val + _this.getComputeNodeSize(node.instances)[1];
+ }, 0);
+
+ var y = serviceTopologyConfig.computeNode.margin + serviceTopologyConfig.computeNode.margin * position + previousElEight;
+
+ return [x, y];
+ };
+ }]);
+})();
+'use strict';
+
+var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
+
+(function () {
+ 'use strict';
+
+ var shapes = {
+ cloud: ' M 79.72 49.60 C 86.00 37.29 98.57 29.01 111.96 26.42 C 124.27 24.11 137.53 26.15 148.18 32.90 C 158.08 38.78 165.39 48.87 167.65 60.20 C 176.20 57.90 185.14 56.01 194.00 57.73 C 206.08 59.59 217.92 66.01 224.37 76.66 C 227.51 81.54 228.85 87.33 229.23 93.06 C 237.59 93.33 246.22 95.10 253.04 100.19 C 256.69 103.13 259.87 107.67 258.91 112.59 C 257.95 118.43 252.78 122.38 247.78 124.82 C 235.27 130.43 220.23 130.09 207.98 123.93 C 199.33 127.88 189.76 129.43 180.30 128.57 C 173.70 139.92 161.70 147.65 148.86 149.93 C 133.10 153.26 116.06 148.15 104.42 137.08 C 92.98 143.04 78.96 143.87 66.97 139.04 C 57.75 135.41 49.70 128.00 46.60 118.43 C 43.87 109.95 45.81 100.29 51.30 93.32 C 57.38 85.18 67.10 80.44 76.99 78.89 C 74.38 69.20 74.87 58.52 79.72 49.60 Z'
+ };
+
+ var computeNodeId = 0;
+ var instanceId = 0;
+
+ angular.module('xos.diagnostic').service('NodeDrawer', ["d3", "serviceTopologyConfig", "RackHelper", "lodash", function (d3, serviceTopologyConfig, RackHelper, lodash) {
+ var _this2 = this;
+
+ var _this = this;
+
+ this.addNetworks = function (nodes) {
+ nodes.append('path').attr({
+ d: shapes.cloud,
+ transform: 'translate(-63, -52), scale(0.5)',
+ 'class': 'cloud'
+ });
+
+ nodes.append('text').attr({
+ 'text-anchor': 'middle'
+ }).text(function (d) {
+ return d.name;
+ });
+
+ nodes.each(function (n) {
+ var currentNode = d3.select(this);
+ // cicle trouch node to add Tags and Public IP
+ if (n.name === 'LAN' && angular.isDefined(n.subscriberTag)) {
+ currentNode.append('text').attr({
+ 'text-anchor': 'middle',
+ y: 40
+ }).text(function () {
+ return 'C-Tag: ' + n.subscriberTag.cTag;
+ });
+
+ currentNode.append('text').attr({
+ 'text-anchor': 'middle',
+ y: 60
+ }).text(function () {
+ return 'S-Tag: ' + n.subscriberTag.sTag;
+ });
+ }
+
+ if (n.name === 'WAN' && angular.isDefined(n.subscriberIP)) {
+ currentNode.append('text').attr({
+ 'text-anchor': 'middle',
+ y: 40
+ }).text(function () {
+ return 'Public IP: ' + n.subscriberIP;
+ });
+ }
+ });
+ };
+
+ this.addRack = function (nodes) {
+
+ // loop because of D3
+ // rack will be only one
+ nodes.each(function (d) {
+ var _RackHelper$getRackSize = RackHelper.getRackSize(d.computeNodes);
+
+ var _RackHelper$getRackSize2 = _slicedToArray(_RackHelper$getRackSize, 2);
+
+ var w = _RackHelper$getRackSize2[0];
+ var h = _RackHelper$getRackSize2[1];
+
+ // TODO update instead of delete and redraw
+ nodes.select('g').remove();
+
+ var rack = nodes.append('g');
+
+ rack.attr({
+ transform: 'translate(0,0)'
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ transform: function transform() {
+ return 'translate(' + -(w / 2) + ', ' + -(h / 2) + ')';
+ }
+ });
+
+ rack.append('rect').attr({
+ width: 0,
+ height: 0
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ width: w,
+ height: h
+ });
+
+ rack.append('text').attr({
+ 'text-anchor': 'middle',
+ y: -10,
+ x: w / 2,
+ opacity: 0
+ }).text(function (d) {
+ return d.name;
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+
+ _this2.drawComputeNodes(rack, d.computeNodes);
+ });
+ };
+
+ this.drawComputeNodes = function (container, nodes) {
+
+ var elements = container.selectAll('.compute-nodes').data(nodes, function (d) {
+ if (!angular.isString(d.d3Id)) {
+ d.d3Id = 'compute-node-' + ++computeNodeId;
+ }
+ return d.d3Id;
+ });
+
+ var _container$node$getBoundingClientRect = container.node().getBoundingClientRect();
+
+ var width = _container$node$getBoundingClientRect.width;
+ var height = _container$node$getBoundingClientRect.height;
+
+ var nodeContainer = elements.enter().append('g');
+
+ nodeContainer.attr({
+ transform: 'translate(' + width / 2 + ', ' + height / 2 + ')',
+ 'class': 'compute-node'
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ transform: function transform(d) {
+ return 'translate(' + RackHelper.getComputeNodePosition(nodes, d.d3Id.replace('compute-node-', '') - 1) + ')';
+ }
+ });
+
+ nodeContainer.append('rect').attr({
+ width: 0,
+ height: 0
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ width: function width(d) {
+ return RackHelper.getComputeNodeSize(d.instances)[0];
+ },
+ height: function height(d) {
+ return RackHelper.getComputeNodeSize(d.instances)[1];
+ }
+ });
+
+ nodeContainer.append('text').attr({
+ 'text-anchor': 'start',
+ y: 15, //FIXME
+ x: 10, //FIXME
+ opacity: 0
+ }).text(function (d) {
+ return d.humanReadableName.split('.')[0];
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+
+ // if there are Compute Nodes
+ if (nodeContainer.length > 0) {
+ // draw instances for each compute node
+ nodeContainer.each(function (a) {
+ _this.drawInstances(d3.select(this), a.instances);
+ });
+ }
+ };
+
+ // NOTE Stripping unuseful names to shorten labels.
+ // This is not elegant
+ var formatInstanceName = function formatInstanceName(name) {
+ return name.replace('app_', '').replace('service_', '')
+ // .replace('ovs_', '')
+ .replace('mysite_', '').replace('_instance', '');
+ };
+
+ var getInstanceStatusColor = function getInstanceStatusColor(instance) {
+ function startWith(val, string) {
+ return string.substring(0, val.length) === val;
+ }
+
+ if (startWith('0 - ', instance.backend_status)) {
+ return 'provisioning';
+ }
+ if (startWith('1 - ', instance.backend_status)) {
+ return 'good';
+ }
+ if (startWith('2 - ', instance.backend_status)) {
+ return 'bad';
+ } else {
+ return '';
+ }
+ };
+
+ var drawContainer = function drawContainer(container, docker) {
+
+ var containerBox = container.append('g').attr({
+ 'class': 'container',
+ transform: 'translate(' + serviceTopologyConfig.instance.margin + ', 115)'
+ });
+
+ containerBox.append('rect').attr({
+ width: 250 - serviceTopologyConfig.container.margin * 2,
+ height: serviceTopologyConfig.container.height
+ });
+
+ containerBox.append('text').attr({
+ y: 20,
+ x: serviceTopologyConfig.instance.margin,
+ 'class': 'name'
+ }).text(docker.name);
+
+ // add stats
+ var interestingMeters = ['memory', 'memory.usage', 'cpu_util'];
+
+ interestingMeters.forEach(function (m, i) {
+ var meter = lodash.find(docker.stats, { meter: m });
+ // if there is no meter stats skip rendering
+ if (!angular.isDefined(meter)) {
+ return;
+ }
+ containerBox.append('text').attr({
+ y: 40 + i * 15,
+ x: serviceTopologyConfig.instance.margin,
+ opacity: 0
+ }).text(meter.description + ': ' + Math.round(meter.value) + ' ' + meter.unit).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+ });
+
+ // add port stats
+ var ports = ['eth0', 'eth1'];
+ var interestingPortMeters = [{
+ meter: 'network.incoming.bytes.rate',
+ label: 'Incoming'
+ }, {
+ meter: 'network.outgoing.bytes.rate',
+ label: 'Outgoing'
+ }];
+
+ ports.forEach(function (p, j) {
+
+ // if there are no port stats skip rendering
+ if (docker.port[p].length === 0) {
+ return;
+ }
+
+ containerBox.append('text').attr({
+ y: 90,
+ x: serviceTopologyConfig.instance.margin + 120 * j,
+ 'class': 'name'
+ }).text(docker.name + '-' + p);
+
+ interestingPortMeters.forEach(function (m, i) {
+
+ var meter = lodash.find(docker.port[p], { meter: m.meter });
+ // if there is no meter stats skip rendering
+ if (!angular.isDefined(meter)) {
+ return;
+ }
+ containerBox.append('text').attr({
+ y: 105 + i * 15,
+ x: serviceTopologyConfig.instance.margin + 120 * j,
+ opacity: 0
+ }).text(m.label + ': ' + Math.round(meter.value) + ' ' + meter.unit).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+ });
+ });
+ };
+
+ var showInstanceStats = function showInstanceStats(container, instance) {
+
+ // NOTE this should be dinamically positioned
+ // base on the number of element present
+ var statsContainer = container.append('g').attr({
+ transform: 'translate(200, -120)',
+ 'class': 'stats-container'
+ });
+
+ statsContainer.append('line').attr({
+ x1: -160,
+ y1: 120,
+ x2: 0,
+ y2: 50,
+ stroke: 'black',
+ opacity: 0
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+
+ // NOTE rect should be dinamically sized base on the presence of a container
+ var statsHeight = 110;
+ var statsWidth = 250;
+
+ if (instance.container) {
+ statsHeight += serviceTopologyConfig.container.height + serviceTopologyConfig.container.margin * 2;
+ }
+
+ statsContainer.append('rect').attr({
+ width: statsWidth,
+ height: statsHeight,
+ opacity: 0
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+
+ // add instance info
+ statsContainer.append('text').attr({
+ y: 15,
+ x: serviceTopologyConfig.instance.margin,
+ 'class': 'name',
+ opacity: 0
+ }).text(instance.humanReadableName).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+
+ statsContainer.append('text').attr({
+ y: 30,
+ x: serviceTopologyConfig.instance.margin,
+ 'class': 'ip',
+ opacity: 0
+ }).text(instance.ip).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+
+ // add stats
+ var interestingMeters = ['memory', 'memory.usage', 'cpu', 'vcpus'];
+
+ interestingMeters.forEach(function (m, i) {
+ var meter = lodash.find(instance.stats, { meter: m });
+ statsContainer.append('text').attr({
+ y: 55 + i * 15,
+ x: serviceTopologyConfig.instance.margin,
+ opacity: 0
+ }).text(meter.description + ': ' + Math.round(meter.value) + ' ' + meter.unit).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+ });
+
+ if (instance.container) {
+ // draw container
+ drawContainer(statsContainer, instance.container);
+ }
+ };
+
+ this.drawInstances = function (container, instances) {
+
+ // TODO check for stats field in instance and draw popup
+
+ var _container$node$getBoundingClientRect2 = container.node().getBoundingClientRect();
+
+ var width = _container$node$getBoundingClientRect2.width;
+ var height = _container$node$getBoundingClientRect2.height;
+
+ var elements = container.selectAll('.instances').data(instances, function (d) {
+ return angular.isString(d.d3Id) ? d.d3Id : d.d3Id = 'instance-' + ++instanceId;
+ });
+
+ var instanceContainer = elements.enter().append('g');
+
+ instanceContainer.attr({
+ transform: 'translate(' + width / 2 + ', ' + height / 2 + ')',
+ 'class': function _class(d) {
+ return 'instance ' + (d.selected ? 'active' : '') + ' ' + getInstanceStatusColor(d);
+ }
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ transform: function transform(d, i) {
+ return 'translate(' + RackHelper.getInstancePosition(i) + ')';
+ }
+ });
+
+ instanceContainer.append('rect').attr({
+ width: 0,
+ height: 0
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ width: serviceTopologyConfig.instance.width,
+ height: serviceTopologyConfig.instance.height
+ });
+
+ instanceContainer.append('text').attr({
+ 'text-anchor': 'middle',
+ y: 23, //FIXME
+ x: 40, //FIXME
+ opacity: 0
+ }).text(function (d) {
+ return formatInstanceName(d.humanReadableName);
+ }).transition().duration(serviceTopologyConfig.duration).attr({
+ opacity: 1
+ });
+
+ // if stats are attached and instance is active,
+ // draw stats
+ instanceContainer.each(function (instance, i) {
+
+ var container = d3.select(this);
+
+ if (angular.isDefined(instance.stats) && instance.selected) {
+ showInstanceStats(container, instance, i);
+ }
+ });
+
+ instanceContainer.on('click', function (d) {
+ console.log('Draw vignette with stats for instance: ' + d.name);
+ });
+ };
+
+ this.addPhisical = function (nodes) {
+ nodes.append('rect').attr(serviceTopologyConfig.square);
+
+ nodes.append('text').attr({
+ 'text-anchor': 'middle',
+ y: serviceTopologyConfig.square.y - 10
+ }).text(function (d) {
+ return d.name || d.humanReadableName;
+ });
+ };
+
+ this.addDevice = function (nodes) {
+ nodes.append('circle').attr(serviceTopologyConfig.circle);
+
+ nodes.append('text').attr({
+ 'text-anchor': 'end',
+ x: -serviceTopologyConfig.circle.r - 10,
+ y: serviceTopologyConfig.circle.r / 2
+ }).text(function (d) {
+ return d.name || d.mac;
+ });
+ };
+ }]);
+})();
+'use strict';
+
+var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic').service('LogicTopologyHelper', ["$window", "$log", "$rootScope", "lodash", "serviceTopologyConfig", "NodeDrawer", "ChartData", function ($window, $log, $rootScope, lodash, serviceTopologyConfig, NodeDrawer, ChartData) {
+ var _this = this;
+
+ var diagonal,
+ nodes,
+ links,
+ i = 0,
+ svgWidth,
+ svgHeight,
+ layout;
+
+ var baseData = ChartData.logicTopologyData;
+
+ /**
+ * Calculate the horizontal position for each element.
+ * subsrcribers, devices and routers have the same fixed width 20
+ * network have a fixed width 104
+ * rack have a fixed width 105
+ * build and array of 6 elements representing the position of each element in the svg
+ * to equally space them
+ */
+
+ this.computeElementPosition = function (svgWidth) {
+
+ var xPos = [];
+
+ var totalElWidth = lodash.reduce(serviceTopologyConfig.elWidths, function (el, val) {
+ return val + el;
+ }, 0);
+
+ var remainingSpace = svgWidth - totalElWidth - serviceTopologyConfig.widthMargin * 2;
+
+ var step = remainingSpace / (serviceTopologyConfig.elWidths.length - 1);
+
+ lodash.forEach(serviceTopologyConfig.elWidths, function (el, i) {
+
+ // get half of the previous elements width
+ var previousElWidth = 0;
+ if (i !== 0) {
+ previousElWidth = lodash.reduce(serviceTopologyConfig.elWidths.slice(0, i), function (el, val) {
+ return val + el;
+ }, 0);
+ }
+
+ var elPos = serviceTopologyConfig.widthMargin // right margin
+ + step * i // space between elements
+ + el / 2 // this el width
+ + previousElWidth; // previous elements width
+
+ xPos.push(svgWidth - elPos);
+ });
+
+ return xPos;
+ };
+
+ /**
+ * from a nested data structure,
+ * create nodes and links for a D3 Tree Layout
+ */
+ var computeLayout = function computeLayout(data) {
+ var nodes = layout.nodes(data);
+
+ // Normalize for fixed-depth.
+ nodes.forEach(function (d) {
+ // position the child node horizontally
+ d.y = _this.computeElementPosition(svgWidth)[d.depth];
+ });
+
+ var links = layout.links(nodes);
+
+ return [nodes, links];
+ };
+
+ /**
+ * Draw the containing group for any node or update the existing one
+ */
+ var drawNodes = function drawNodes(svg, nodes) {
+ // Update the nodes…
+ var node = svg.selectAll('g.node').data(nodes, function (d) {
+ if (!angular.isString(d.d3Id)) {
+ d.d3Id = 'tree-' + ++i;
+ }
+ return d.d3Id;
+ });
+
+ // Enter any new nodes
+ var nodeEnter = node.enter().append('g').attr({
+ 'class': function _class(d) {
+ return 'node ' + d.type;
+ },
+ transform: 'translate(' + svgWidth / 2 + ', ' + svgHeight / 2 + ')'
+ });
+
+ // create Nodes
+ NodeDrawer.addNetworks(node.filter('.network'));
+ NodeDrawer.addRack(node.filter('.rack'));
+ NodeDrawer.addPhisical(node.filter('.router'));
+ NodeDrawer.addPhisical(node.filter('.subscriber'));
+ NodeDrawer.addDevice(node.filter('.device'));
+
+ // add event listener to subscriber
+ node.filter('.subscriber').on('click', function () {
+ $rootScope.$emit('subscriber.modal.open');
+ });
+
+ //update nodes
+ // TODO if data change, only update them
+ // NodeDrawer.updateRack(node.filter('.rack'));
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node.transition().duration(serviceTopologyConfig.duration).attr({
+ 'transform': function transform(d) {
+ return 'translate(' + d.y + ',' + d.x + ')';
+ }
+ });
+
+ // TODO handle node remove
+ var nodeExit = node.exit().remove();
+ };
+
+ /**
+ * Handle links in the tree layout
+ */
+ var drawLinks = function drawLinks(svg, links) {
+
+ diagonal = d3.svg.diagonal().projection(function (d) {
+ return [d.y, d.x];
+ });
+
+ // Update the links…
+ var link = svg.selectAll('path.link').data(links, function (d) {
+ return d.target.d3Id;
+ });
+
+ // Enter any new links at the parent's previous position.
+ link.enter().insert('path', 'g').attr('class', function (d) {
+ return 'link ' + d.target.type;
+ }).attr('d', function (d) {
+ var o = { x: svgHeight / 2, y: svgWidth / 2 };
+ return diagonal({ source: o, target: o });
+ });
+
+ // Transition links to their new position.
+ link.transition().duration(serviceTopologyConfig.duration).attr('d', diagonal);
+
+ link.exit().remove();
+ };
+
+ /**
+ * Calculate the svg size and setup tree layout
+ */
+ this.setupTree = function (svg) {
+
+ svgWidth = svg.node().getBoundingClientRect().width;
+ svgHeight = svg.node().getBoundingClientRect().height;
+
+ var width = svgWidth - serviceTopologyConfig.widthMargin * 2;
+ var height = svgHeight - serviceTopologyConfig.heightMargin * 2;
+
+ layout = d3.layout.tree().size([height, width]);
+ };
+
+ /**
+ * Update the tree layout
+ */
+
+ this.updateTree = function (svg) {
+
+ // console.log(baseData);
+
+ var _computeLayout = computeLayout(baseData);
+
+ // Compute the new tree layout.
+
+ var _computeLayout2 = _slicedToArray(_computeLayout, 2);
+
+ nodes = _computeLayout2[0];
+ links = _computeLayout2[1];
+ drawNodes(svg, nodes);
+ drawLinks(svg, links);
+ };
+ }]);
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+ angular.module('xos.diagnostic').directive('logicTopology', function () {
+ return {
+ restrict: 'E',
+ scope: {
+ subscribers: '=',
+ selected: '='
+ },
+ bindToController: true,
+ controllerAs: 'vm',
+ templateUrl: 'templates/logicTopology.tpl.html',
+ controller: ["$element", "$log", "$scope", "$rootScope", "$timeout", "d3", "LogicTopologyHelper", "Node", "Tenant", "Ceilometer", "serviceTopologyConfig", "ChartData", function controller($element, $log, $scope, $rootScope, $timeout, d3, LogicTopologyHelper, Node, Tenant, Ceilometer, serviceTopologyConfig, ChartData) {
+ var _this = this;
+
+ $log.info('Logic Plane');
+
+ var svg;
+ this.selectedInstances = [];
+ this.hideInstanceStats = true;
+
+ var handleSvg = function handleSvg(el) {
+
+ svg = d3.select(el).append('svg').style('width', el.clientWidth + 'px').style('height', el.clientHeight + 'px');
+ };
+
+ ChartData.getLogicTree().then(function (tree) {
+ LogicTopologyHelper.updateTree(svg);
+ });
+
+ $scope.$watch(function () {
+ return _this.selected;
+ }, function (selected) {
+ if (selected) {
+ ChartData.selectSubscriber(selected);
+ LogicTopologyHelper.updateTree(svg);
+ }
+ });
+
+ $rootScope.$on('instance.detail.hide', function () {
+ _this.hideInstanceStats = true;
+ $timeout(function () {
+ _this.selectedInstances = [];
+ ChartData.highlightInstances([]);
+ LogicTopologyHelper.updateTree(svg);
+ }, 500);
+ });
+
+ $rootScope.$on('instance.detail', function (evt, service) {
+ ChartData.getInstanceStatus(service).then(function (instances) {
+ LogicTopologyHelper.updateTree(svg);
+ });
+ });
+
+ handleSvg($element[0]);
+ LogicTopologyHelper.setupTree(svg);
+
+ this.selectSubscriberModal = function () {
+ _this.openSelectSubscriberModal = true;
+ $scope.$apply();
+ };
+
+ this.subscriberStatusModal = function () {
+ _this.openSubscriberStatusModal = true;
+ $scope.$apply();
+ };
+
+ // listen for subscriber modal event
+ $rootScope.$on('subscriber.modal.open', function () {
+
+ if (ChartData.currentSubscriber) {
+ _this.subscriberStatusModal();
+ } else {
+ _this.selectSubscriberModal();
+ }
+ });
+
+ // listen for subscriber modal event
+ $rootScope.$on('subscriber.modal.open', function () {
+
+ if (ChartData.currentSubscriber) {
+ _this.currentSubscriber = ChartData.currentSubscriber;
+ _this.subscriberStatusModal();
+ } else {
+ _this.selectSubscriberModal();
+ }
+ });
+ }]
+ };
+ });
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+ angular.module('xos.diagnostic').directive('diagnosticContainer', function () {
+ return {
+ restrict: 'E',
+ templateUrl: 'templates/diagnostic.tpl.html',
+ controllerAs: 'vm',
+ controller: ["ChartData", "Subscribers", "ServiceRelation", "$rootScope", "$log", function controller(ChartData, Subscribers, ServiceRelation, $rootScope, $log) {
+ var _this = this;
+
+ this.loader = true;
+ this.error = false;
+ Subscribers.query().$promise.then(function (subscribers) {
+ _this.subscribers = subscribers;
+ return ServiceRelation.get();
+ }).then(function (serviceChain) {
+ _this.serviceChain = serviceChain;
+ })['catch'](function (e) {
+ throw new Error(e);
+ _this.error = e;
+ })['finally'](function () {
+ _this.loader = false;
+ });
+
+ $rootScope.$on('subscriber.selected', function (evt, subscriber) {
+ ServiceRelation.getBySubscriber(subscriber).then(function (serviceChain) {
+ _this.serviceChain = serviceChain;
+ ChartData.currentServiceChain = serviceChain;
+ return Subscribers.getWithDevices({ id: subscriber.id }).$promise;
+ }).then(function (subscriber) {
+ _this.selectedSubscriber = subscriber;
+ ChartData.currentSubscriber = subscriber;
+ });
+ });
+ }]
+ };
+ });
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic').factory('d3', ["$window", function ($window) {
+ return $window.d3;
+ }]);
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic').constant('serviceTopologyConfig', {
+ widthMargin: 20,
+ heightMargin: 30,
+ duration: 750,
+ elWidths: [20, 104, 105, 104, 20], //this is not true
+ circle: {
+ radius: 10,
+ r: 10,
+ selectedRadius: 15
+ },
+ square: {
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10
+ },
+ rack: {
+ width: 105,
+ height: 50,
+ x: -30,
+ y: -25
+ },
+ computeNode: {
+ width: 50,
+ height: 20,
+ margin: 5,
+ labelHeight: 10,
+ x: -25,
+ y: -10
+ },
+ instance: {
+ width: 80,
+ height: 36,
+ margin: 5,
+ x: -40,
+ y: -18
+ },
+ container: {
+ width: 60,
+ height: 130,
+ margin: 5,
+ x: -30,
+ y: -15
+ }
+ });
+})();
+'use strict';
+
+(function () {
+ 'use strict';
+
+ angular.module('xos.diagnostic').service('ChartData', ["$rootScope", "$q", "lodash", "Tenant", "Node", "serviceTopologyConfig", "Ceilometer", "Instances", function ($rootScope, $q, lodash, Tenant, Node, serviceTopologyConfig, Ceilometer, Instances) {
+ var _this = this;
+
+ this.currentSubscriber = null;
+ this.currentServiceChain = null;
+
+ this.logicTopologyData = {
+ name: 'Router',
+ type: 'router',
+ children: [{
+ name: 'WAN',
+ type: 'network',
+ children: [{
+ name: 'Rack',
+ type: 'rack',
+ computeNodes: [],
+ children: [{
+ name: 'LAN',
+ type: 'network',
+ children: [{
+ name: 'Subscriber',
+ type: 'subscriber'
+ }] //subscribers goes here
+ }]
+ }]
+ }]
+ };
+
+ this.getLogicTree = function () {
+ var deferred = $q.defer();
+
+ Node.queryWithInstances().$promise.then(function (computeNodes) {
+ _this.logicTopologyData.children[0].children[0].computeNodes = computeNodes;
+ // LogicTopologyHelper.updateTree(svg);
+ deferred.resolve(_this.logicTopologyData);
+ });
+
+ return deferred.promise;
+ };
+
+ /**
+ * Add Subscriber tag to LAN Network
+ */
+ this.addSubscriberTag = function (tags) {
+ _this.logicTopologyData.children[0].children[0].children[0].subscriberTag = {
+ cTag: tags.cTag,
+ sTag: tags.sTag
+ };
+ };
+
+ /**
+ * Add Subscribers to the tree
+ */
+ this.addSubscriber = function (subscriber) {
+ subscriber.children = subscriber.devices;
+
+ // add subscriber to data tree
+ _this.logicTopologyData.children[0].children[0].children[0].children = [subscriber];
+ return _this.logicTopologyData;
+ };
+
+ this.getSubscriberTag = function (subscriber) {
+ var tags = {
+ cTag: subscriber.c_tag,
+ sTag: subscriber.s_tag
+ };
+
+ _this.addSubscriberTag(tags);
+ // add tags info to current subscriber
+ _this.currentSubscriber.tags = tags;
+ };
+
+ this.getSubscriberIP = function (subscriber) {
+ // const ip = JSON.parse(this.currentServiceChain.children[0].children[0].tenant.service_specific_attribute).wan_container_ip;
+ // const ip = this.currentServiceChain.children[0].children[0].tenant.wan_container_ip;
+ _this.logicTopologyData.children[0].subscriberIP = subscriber.wan_container_ip;
+ };
+
+ this.selectSubscriber = function (subscriber) {
+ // append the device with to config settings
+ serviceTopologyConfig.elWidths.push(160);
+
+ _this.addSubscriber(angular.copy(subscriber));
+
+ //clean selected instances
+ _this.highlightInstances([]);
+
+ _this.getSubscriberTag(subscriber);
+ _this.getSubscriberIP(subscriber);
+ };
+
+ this.highlightInstances = function (instances) {
+
+ var computeNodes = _this.logicTopologyData.children[0].children[0].computeNodes;
+
+ // unselect all
+ computeNodes.map(function (node) {
+ node.instances.map(function (instance) {
+ instance.selected = false;
+ return instance;
+ });
+ });
+
+ lodash.forEach(instances, function (instance) {
+ computeNodes.map(function (node) {
+ node.instances.map(function (d3instance) {
+ if (d3instance.id === instance.id) {
+ // console.log(d3instance, instance);
+ d3instance.selected = true;
+ d3instance.stats = instance.stats; //add stats to d3 node
+ d3instance.container = instance.container; // container info to d3 node
+ }
+ return d3instance;
+ });
+ });
+ });
+ };
+
+ this.getInstanceStatus = function (service) {
+ var deferred = $q.defer();
+
+ var p = undefined;
+
+ // subscriber specific
+ if (_this.currentSubscriber) {
+
+ var attr = undefined;
+ try {
+ attr = JSON.parse(service.tenant.service_specific_attribute);
+ } catch (e) {
+ attr = null;
+ }
+
+ // if no instances are associated to the subscriber
+ if (!attr || !attr.instance_id) {
+ var d = $q.defer();
+ d.resolve([]);
+ p = d.promise;
+ }
+ // if ther is an instance
+ else {
+ (function () {
+ var instance = {};
+ p = Instances.get({ id: attr.instance_id }).$promise.then(function (_instance) {
+ instance = _instance;
+ return Ceilometer.getInstanceStats(instance.instance_uuid);
+ }).then(function (stats) {
+ instance.stats = stats;
+ var containerName = 'vcpe-' + _this.currentSubscriber.tags.sTag + '-' + _this.currentSubscriber.tags.cTag;
+ // append containers
+ instance.container = {
+ name: containerName
+ };
+
+ // TODO fetch container stats
+ return Ceilometer.getContainerStats(containerName);
+ }).then(function (containerStats) {
+ instance.container.stats = containerStats.stats;
+ instance.container.port = containerStats.port;
+ return [instance];
+ });
+ })();
+ }
+ }
+ // global scope
+ else {
+ var param = {
+ 'service_vsg': { kind: 'vCPE' },
+ 'service_vbng': { kind: 'vBNG' },
+ 'service_volt': { kind: 'vOLT' }
+ };
+
+ p = Tenant.queryVsgInstances(param[service.name]).$promise.then(function (instances) {
+
+ return Ceilometer.getInstancesStats(instances);
+ });
+ }
+
+ p.then(function (instances) {
+ _this.highlightInstances(instances);
+ deferred.resolve(instances);
+ })['catch'](function (e) {
+ deferred.reject(e);
+ });
+
+ return deferred.promise;
+ };
+ }]);
+})();
+angular.module('xos.diagnostic').run(function($location){
+ $location.path('/')
+});
+angular.bootstrap(angular.element('#xosDiagnostic'), ['xos.diagnostic']);
\ No newline at end of file
diff --git a/xos/services/ceilometer/templates/ceilometeradmin.html b/xos/services/ceilometer/templates/ceilometeradmin.html
index 8149666..40f57e8 100644
--- a/xos/services/ceilometer/templates/ceilometeradmin.html
+++ b/xos/services/ceilometer/templates/ceilometeradmin.html
@@ -1,6 +1,6 @@
-<div class = "left-nav">
-<ul>
-<li><a href="/admin/ceilometer/monitoringchannel/">Monitoring Channels</a></li>
-</ul>
+<div class = "row text-center">
+ <div class="col-xs-12">
+ <a class="btn btn-primary" href="/admin/ceilometer/monitoringchannel/">Monitoring Channels</a>
+ </div>
</div>
diff --git a/xos/services/ceilometer/templates/sflowadmin.html b/xos/services/ceilometer/templates/sflowadmin.html
index da2a8dd..3cbb333 100644
--- a/xos/services/ceilometer/templates/sflowadmin.html
+++ b/xos/services/ceilometer/templates/sflowadmin.html
@@ -1,6 +1,6 @@
-<div class = "left-nav">
-<ul>
-<li><a href="/admin/ceilometer/sflowtenant/">sFlow Tenants</a></li>
-</ul>
+<div class = "row text-center">
+ <div class="col-xs-12">
+ <a class="btn btn-primary" href="/admin/ceilometer/sflowtenant/">sFlow Tenants</a>
+ </div>
</div>
diff --git a/xos/services/onos/templates/onosadmin.html b/xos/services/onos/templates/onosadmin.html
index 1e8d42c..e50660e 100644
--- a/xos/services/onos/templates/onosadmin.html
+++ b/xos/services/onos/templates/onosadmin.html
@@ -1,6 +1,6 @@
-<div class = "left-nav">
-<ul>
-<li><a href="/admin/onos/onosapp/">ONOS Apps</a></li>
-</ul>
+<div class = "row text-center">
+ <div class="col-xs-12">
+ <a class="btn btn-primary" href="/admin/onos/onosapp/">ONOS Apps</a>
+ </div>
</div>